Здравствуйте, дорогие! Сегодня мы снова будем передавать изображение ультразвуком через воду: мы буквально увидим реверберацию и эхо, и даже то, как они меняются в зависимости от условий.
Все, что я вам расскажу, не сложно, интересно повторять самостоятельно, и это под силу практически каждому.
Если эти слова что-то тронули в вашей душе, добро пожаловать к коту, в темные воды нашего пруда!
«Лучшее расслабление — это объяснение общеизвестных истин».(С) АБС, Полдень XXII века
Прелюдия
Основное правило Клуба свидетелей Гидроакустики заключается в том, что невозможно и всегда будет невозможно передать видео с помощью гидроакустики на более или менее значительное расстояние (более нескольких метров) в среднестатистическом водоёме.Для этого есть серьезные причины – канал связи с очень низкой пропускной способностью, низкой скоростью распространения сигнала (в воде всего 1500 м/с) и высокой вероятностью ошибки.
Доступная полоса частот составляет всего несколько десятков килогерц.
Но это еще не все – если, условно говоря, сигнал на частотах порядка 10 кГц распространяется в воде на расстояние около 8–10 км, то на частоте 20 кГц – уже на 3–5 км, а чем выше частота, тем сильнее затухание.
Например, самые маленькие в мире модемы uWAVE работать в диапазоне 20-30 кГц и передавать данные со скоростью 78 бит/с на 1000 метров, и Красная линия с полосой 5-15 на 8000 метров.
Рекорд среди коммерческих устройств принадлежит ЭвоЛогикс - 68 кбит на 300 метров.
Физику, увы, не обмануть и с ней нельзя договориться — можно передавать либо очень медленно и помехоустойчиво, либо быстро, но на короткие расстояния.
Однако в некоторых случаях можно «срезать некоторые углы», какие именно углы мы срежем на этот раз, описано ниже.
Что мы будем делать сегодня и что для этого нужно?
В предыдущих статьях мы уже говорили передавали «видео» звуком через воду , напомню, что там кадр «рисовался по спектру», то есть спектр, а точнее спектрограмма сигнала, представлял собой картинку.Позже мы сделал простые гидроакустические антенны из мусора и сделал простой гидроакустический модем .
Там же мы сделали предусилитель для антенны (проект печатной платы для самостоятельного изготовления ЛУТ еще лежит Здесь ).
Мы подумали, как еще можно попытаться передать картинку так, чтобы ее мог понять даже детсадовец или школьник, и нам кажется, что мы придумали еще более простой способ, чем раньше.
Итак, подведем итоги и составим список того, что нам нужно: - пара гидроакустические антенны из пьезо-твитеров — предусилитель , производство ЛУТ — источник проект на C# - пара свинцовых аккумуляторов напряжением 12 В.
— усилитель на ТДА, я взял такой всего за 50 рублей на Али
Немного теории
Давайте помнить, что наш сонарный модем был основан на простом тональном детекторе, частота которого в 4 раза меньше частоты дискретизации.
Давайте вкратце вспомним, как это работает.
На рисунке показаны два колебания, сдвинутые друг относительно друга на Pi/2 – то есть синусоидальная и косинусоидальная фазы.
А если частота ровно в четыре раза меньше частоты дискретизации, то за период приходится всего 4 выборки.
Внимательный читатель хабра конечно заметил, что оба сигнала сдвинуты на Pi/4. При таком сдвиге сигнал принимает всего два значения: √2/2 и -√2/2. И даже не важны конкретные значения, важно, что можно использовать только знаки: «+» и «-».
Теперь мы можем представить фазу синуса как последовательность знаков «+» «+» «-» «-», а фазу косинуса как «+» «-» «-» «+».
Под спойлером повторяем работу детектора: Пусть входной сигнал лежит в буфере sn, у нас есть два кольцевых буфера усреднения по синусоидальной и косинусной фазе — bs и bc размера N. Они имеют общие головной и хвостовой указатели — bH и bT. В начальный момент времени bH = N-1, bT = 0. Счетчик циклов усреднения C = 0. Берем 4 выборки из входного буфера и складываем их в соответствии с последовательностями символов.
После каждых обработанных четырех отсчетов проверяем счетчик циклов усреднения и, если он превысил N, то вычисляем амплитуду cA несущей:a = sn(i) bs(bH) = a bc(bH) = a s1 = s1 + a - bs(bT) s2 = s2 + a - bc(bT) bH = (bH + 1) % N bT = (bT + 1) % N a = sn(i+1) bs(bH) = a bc(bH) = -a s1 = s1 + a - bs(bT) s2 = s2 - a - bc(bT) bH = (bH + 1) % N bT = (bT + 1) % N a = sn(i+2) bs(bH) = -a bc(bH) = -a s1 = s1 - a - bs(bT) s2 = s2 - a - bc(bT) bH = (bH + 1) % N bT = (bT + 1) % N a = sn(i+3) bs(bH) = -a bc(bH) = a s1 = s1 - a - bs(bT) s2 = s2 + a - bc(bT) bH = (bH + 1) % N bT = (bT + 1) % N
if ++cycle >= N
cA = sqrt(s1 * s1 + s2 * s2)
cycle = 0
end
Именно этот метод мы берем за основу; он будет отвечать за «синхронизацию».
Теперь разберемся, как кодируется изображение.
Я предлагаю использовать манипуляция амплитудой .
Манипуляция — это когда сигнал делится на равные сегменты, называемые чипами или символами, и по длине чипа сохраняется какой-то переменный параметр (в нашем случае — амплитуда).
Если, например, мы можем варьировать амплитуду от 0 до 32767 (16-битные выборки), а нам нужно передать 255 значений яркости пикселей, то за единицу изменения яркости пикселя амплитуда чипа изменится на 32768/255. = 128. Еще одним важным параметром является длина чипа, начнем с одного периода несущей — в нашем случае четырех выборок.
Это значит, что изображение будет передаваться попиксельно, каждый пиксель длится 4 отсчета, а амплитуда на этом периоде будет равна b[x,y]*128, где b[x,y] — значение яркости пиксель с координатами x и y на изображении b. Прикинем, какая будет скорость передачи.
В примере я использовал размер кадра 120х120 пикселей.
Это значит, что для передачи одного кадра нам нужно 120х120х4 = 57600 семплов, Если частота дискретизации 96 кГц, то передача одного кадра займет время: 57600/96000 = 0,6 секунды Очевидно, нам понадобится какая-то пауза, какой-то защитный интервал, чтобы детектор мог определить начало следующего кадра.
Из гуманных соображений будем считать, что нам достаточно 0,1 секунды, в течение которой все эхо затухнут (на самом деле нет).
Тогда в итоге скорость передачи составит: 1/(0,6 + 0,1) = 1,428 кадров в секунду.
Здесь очень легко ошибиться и попытаться вычислить скорость в битах в секунду.
Посмотрите, насколько невероятна скорость передачи данных: 120*120*8/1,428 = 80 627 бит/с Но что будет, если у меня будут не 8-битные пиксели, а 16-битные? 120*120*16/1,428 = 161344 бит/с Подвох здесь в том, что опять-таки этот способ передачи нельзя назвать цифровым, и понятие битрейта для него не совсем справедливо.
Попробуйте рассчитать скорость передачи аналогового телевизионного сигнала.
А для приемника детектора? :)
Например, так будет выглядеть фрагмент сигнала, передающий яркость 10 пикселей, значения которых изменяются поочередно: 1 2 1 2 1 2 1 2 1 2
Теперь давайте посмотрим, как это работает на примере.
Методы Encode и Decode находятся в классе.
Кодер и отвечают за модуляцию и демодуляцию изображения: public double[] Encode(Bitmap source, double carrier, int pSize, int interframePauseMs)
{
Bitmap frame;
if (source.PixelFormat != System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
frame = Grayscale.CommonAlgorithms.RMY.Apply(source);
else
frame = source;
if (!frame.Size.Equals(frameSize))
frame = resizer.Apply(frame);
int cols = frameSize.Width;
int rows = frameSize.Height;
int col = 0;
int row = 0;
double delta = Math.PI * 2 * carrier / sampleRate;
double alpha = 0;
double phase = 0;
double pxAmplitude = 0;
double chipLimit = Math.PI * 2 * chipSize;
double pLimit = Math.PI * 2;
List<double> samples = new List<double>();
bool isFinished = false;
for (int i = 0; i < pSize; i++)
{
alpha = Math.Sin(phase);
phase += delta;
if (phase >= pLimit)
{
phase -= pLimit;
}
samples.Add(alpha * short.MaxValue);
}
while (!isFinished)
{
alpha = Math.Sin(phase);
phase += delta;
if (phase >= chipLimit)
{
phase -= chipLimit;
pxAmplitude = (((double)frame.GetPixel(col, row).
R) / 255.0) * short.MaxValue;
if (++col >= cols)
{
if (++row >= rows)
isFinished = true;
else
col = 0;
}
}
samples.Add(alpha * pxAmplitude);
}
if (interframePauseMs > 0)
{
samples.AddRange(new double[(int)((((double)interframePauseMs) / 1000.0) * (double)sampleRate)]);
}
return samples.ToArray();
}
Из кода понятно, что перед модуляцией изображения к выходному сигналу добавляется префикс синхронизации, состоящий из чистого тона (отсчетов pSize) — это необходимо для того, чтобы на приемной стороне могла произойти синхронизация до начала само изображение и вообще могло возникнуть в неблагоприятных условиях.
Метод Decode выглядит следующим образом: public Bitmap Decode(double[] samples, double carrier, int pSize)
{
int cols = frameSize.Width;
int rows = frameSize.Height;
int col = 0;
int row = 0;
Bitmap result = new Bitmap(cols, rows);
double delta = Math.PI * 2 * carrier / sampleRate;
double alpha = 0;
double phase = 0;
double chipLimit = Math.PI * 2 * chipSize;
double chipAmplitude = 0;
double maxAmplitude = WaveUtils.GetMaxAmplitude(samples);
double pxMax = -maxAmplitude;
double pxMin = maxAmplitude;
double smp;
for (int i = pSize; (i < samples.Length) && (row < rows); i++)
{
alpha = Math.Sin(phase);
phase += delta;
if (phase >= chipLimit)
{
phase -= chipLimit;
chipAmplitude = (Math.Max(Math.Abs(pxMax), Math.Abs(pxMin)) / maxAmplitude);
pxMin = maxAmplitude;
pxMax = -maxAmplitude;
var gs = Convert.ToByte(chipAmplitude * 255);
result.SetPixel(col, row, Color.FromArgb(255, gs, gs, gs));
if (++col >= cols)
{
col = 0;
row++;
}
}
else
{
smp = samples[i] * alpha;
if (smp > pxMax)
pxMax = smp;
if (smp < pxMin)
pxMin = smp;
}
}
return result;
}
Видно, что оба метода не привязаны к какой-либо конкретной частоте и могут использоваться с другим детектором.
Сам поиск сигнала (обнаружение, синхронизация) происходит так же, как и в нашем простейший гидроакустический модем , с той лишь разницей, что здесь я это вынес в отдельный класс FsBy4CarrierDetector для разнообразия.
Вся простая магия происходит в методе bool ProcessSample(short a).
public bool ProcessSample(short a)
{
bool result = false;
if (smpCount == 0)
{
ring1[ringHead] = a;
ring2[ringHead] = a;
s1 += a - ring1[ringTail];
s2 += a - ring2[ringTail];
}
else if (smpCount == 1)
{
ring1[ringHead] = a;
ring2[ringHead] = -a;
s1 += a - ring1[ringTail];
s2 += - a - ring2[ringTail];
}
else if (smpCount == 2)
{
ring1[ringHead] = -a;
ring2[ringHead] = -a;
s1 += -a - ring1[ringTail];
s2 += -a - ring2[ringTail];
}
else if (smpCount == 3)
{
ring1[ringHead] = -a;
ring2[ringHead] = a;
s1 += -a - ring1[ringTail];
s2 += a - ring2[ringTail];
}
ringHead = (ringHead + 1) % ringSize;
ringTail = (ringTail + 1) % ringSize;
if (++smpCount >= 4)
{
smpCount = 0;
if (++cycle >= ringSize)
{
s = Math.Sqrt(s1 * s1 + s2 * s2) / ringSize;
cycle = 0;
result = (s - sPrev) >= Threshold;
sPrev = s;
}
}
return result;
}
Он вызывается для каждой входящей выборки и возвращает true, если несущая обнаружена.
Так как детектор далек от совершенства и легко может синхронизироваться по середине линии, я добавил специальный ползунок, перемещая который можно добиться более точной синхронизации.
Теперь, когда мы вкратце рассмотрели, как все это работает, давайте перейдем к самому приятному: что вы можете получить от всего этого.
Немного практики
Для начала проверим, как все работает без гидроакустического канала – просто приложив друг к другу приемную и передающую антенны.
Во-первых, картинка крупнее (240х120), чтобы хоть что-то можно было разобрать:
А потом быстро, чтобы было поживее, больше похоже на видео:
Не кажется плохим? Но не будем спешить с выводами и отправимся в бассейн:
И здесь, как я и обещал в заголовке, мы увидим эхо своими глазами:
Как тебе это, Илон Маск? тебе нравится HD? Почему это происходит?
А все очень просто – эхо – это, по сути, задержанная копия исходного сигнала, энергично мешающая ему в момент приема, складывающаяся в разных фазах и дающая такую картину.
Поскольку мы передаем изображение, мы в конечном итоге получаем много-много изображений, наложенных друг на друга с разной амплитудой.
Все это в конечном итоге приводит к размытию и размножению.
Оглядываясь назад, давайте проверим все на большой модельной картинке.
Я сделал случайное фото:
Я его промодулировал, потом добавил эхо и немного шума, потом декодировал, и да — результат напоминает то, что мы получили в пуле:
В принципе, можно сделать деконволюция , и вычтем отражения, но пусть это будут люди не из нашей области, оставим этот пункт для самостоятельной работы.
Кстати, предыдущий метод в пуле работает немного лучше, но тоже плохо - на широкополосных сигналах многолучевость и реверберация приводят к частотно-избирательному замиранию, которое на картинке (читай по спектру) выглядит как черно-белые полосы - где сигнал в противофазе , и где это развивалось на фазе (на самом деле есть еще куча промежуточных вариантов):
В апреле мы воспользовались случаем, вывезли модель на пруд и тоже там повеселились:
Результат мало чем отличается от результатов, полученных в бассейне:
И просто для сравнения предыдущий метод:
А вот GIF-анимации, собранные из сохраненных кадров, метод 1:
И способ 2, о котором мы поговорим в этой статье:
Окончательно
Как и обещали, мы показали, как в буквальном смысле выглядят эхо и реверберация, хорошо провели время и кое-что сделали своими руками.В таком виде, конечно, метод на практике неприменим, но работа с ним будет очень полезна новичкам.
В общем, мы тестировали на небольшом пруду, где условия очень неблагоприятные, и было бы здорово, если бы кто-нибудь повторил наши эксперименты на других водоемах и обязательно рассказал бы нам о своих результатах.
Если читатель просто хочет попробовать (даже на природе с микрофоном и колонками), то вот ссылки непосредственно на релизы: Метод-1 Способ-2 (из этой статьи)
P.S.
Мы очень ждем отзывов читателей, так как очень важно понимать, что вы делаете что-то не зря (или зря, и тогда это нужно немедленно прекратить).
P.S./2
Сразу отвечу на часто задаваемый вопрос: для рыб и других морских обитателей при таких детских возможностях это все просто незаметно.Теги: #Производство и разработка электроники #Звук #Сделай сам или Сделай сам #Гаджеты #C++ #Обработка изображения #модем #ультразвук #манипуляция #передача видео #гидроакустика #детектор #подводная связь #амплитудная модуляция #цифровая обработка
-
Отношения В Интернете: Встреча С Кем-То
19 Oct, 24 -
Еженедельный Геймдев: № 25 — 4 Июля 2021 Г.
19 Oct, 24 -
Почему Мы Откладываем Задачи На Потом?
19 Oct, 24 -
Биполь Адекватности - Ищи И Найдешь
19 Oct, 24 -
Устранение Неполадок Jiayu G3S
19 Oct, 24