如何通过水中的声音查看混响或视频传输-2

你好,亲爱的!



今天,我们将再次使用超声波通过水传输图像:我们将在字面上看到混响和回声,甚至根据情况如何变化。 我要告诉您的一切都很简单,我自己重复一遍很有趣,几乎任何人都可以做到。

如果您的话语在您的灵魂中飞舞,欢迎来到凯特,走进我们池塘的黑暗水域!




“最好的休息就是解释众所周知的事实。” (C)二十二世纪中午,ABS


前戏


水声目击者俱乐部的基本规则是,在水体的中部或多或少有效距离(超过几米)使用水声的视频无法传输,并且永远不会传输。
这有很严重的原因-具有非常低的带宽,低的信号传播速度(在水中仅1,500 m / s)和较高的出错概率的通信信道。 可用频带只有几十千赫兹。
但这还不是全部-如果相对而言,如果信号在10 kHz左右的频率在水中以大约8-10 km的距离传播,那么在20 kHz的频率下已经是3-5 km,并且频率越高,衰减越强。 例如,我们世界上最小的uWAVE调制解调器工作在20-30 kHz频段,并以每1000米78比特/秒的速度传输数据,而RedLINE则在5-15至8000米的频段内传输数据。 商用设备中的记录属于EvoLogics-每300米68 kBits
,无法欺骗物理,不可能对此达成共识-它可以非常缓慢且抗噪音的方式传播,也可以快速传播,但传播距离很短。
但是,在某些情况下,可以“切掉一些角”,这一次我们要切掉的角较低。

我们今天将要做什么?为此需要什么?


在之前的文章中,我们已经通过声音传输了带有声音的“视频” ,我想提醒您那里的帧是“绘制在频谱上的”,也就是说,频谱,或者说信号的频谱图就是一幅图片。
后来,我们用垃圾制成了简单的水 声天线,并制造了最简单的水声调制解调器 。 在那里,我们还为天线做了一个前置放大器( 此处仍由LUT-ohm自行生产PCB的设计)。

我们认为您还可以尝试传达图片,以便甚至是幼儿园的学生都可以理解它,而且在我们看来,他们想出了一种比以前更简单的方法。

因此,总而言之,请草拟一份我们需要的清单:

-一对拾音器的声纳天线
-LUT制造的前置放大器
-C#项目源代码
-一对12伏铅电池
-TDA上的放大器,我在阿里只花了50卢布

一点理论


回想一下,我们的声纳调制解调器基于简单的音频检测器,其频率比采样频率低4倍。 简要回顾一下它是如何工作的。


图片显示了在Pi / 2上两个相对偏移的振荡-即正弦和余弦相位。 而且,如果频率恰好是采样频率的四倍,则该周期只有四个采样。
细心的habuchitel确实注意到两个信号都转移到Pi / 4上。 经过这样的移位,信号仅取两个值:√2/ 2和-√2/ 2。
特定的值甚至都不重要,重要的是您只能使用符号“ +”和“-”。

现在我们可以将正弦相位表示为符号“ +”“ +”“-”“-”的序列,并将余弦相位表示为“ +”“-”“-”“ +”。

在扰流板下,重复检测器:
假设输入信号在sn缓冲区中,则对于正弦和余弦相位,我们有两个环形平均缓冲区-大小为N的bs和bc。它们具有共同的头和尾指针-bH和bT。 在初始时间,bH = N-1,bT =0。平均周期计数器C = 0。
我们从输入缓冲区中提取4个样本,然后根据字符序列将其相加。

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


在每个处理了四个样本之后,我们检查平均周期的计数器,如果它超过了N,则我们计算载波的振幅cA:

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]是图像b中具有x和y坐标的像素的亮度值。

让我们估计一下传输速度。
在示例中,我使用了120x120像素的帧大小。 这意味着要转移一帧,我们需要

120x120x4 = 57600个样本,

如果采样频率为96 kHz,则一帧的传输将花费时间:

57600/96000 = 0.6秒

显然,我们需要某种暂停,一定的保护间隔,以便检测器可以确定下一帧的开始。 出于人道的原因,假设对我们来说0.1秒就足够了,在此期间所有回声都消失了(实际上不是)。 然后,最终传输速率将变为:

1 /(0.6 + 0.1)= 1.428帧/秒

在此处犯错并尝试计算速度(以位/秒为单位)非常容易。 看看传输速度是多么令人难以置信:

120 * 120 * 8 / 1.428 = 80627 bps

但是,如果我没有8位像素,而是16位像素,会发生什么情况?

120 * 120 * 16 / 1.428 = 161344 bps

这里的问题是,再次,这种传输方法不能称为数字传输,并且比特率的概念对此并不完全有效。
尝试计算模拟电视信号的比特率。 对于探测器接收器? :)

因此,例如,一条信号看起来像,传输了10个像素的亮度,其值交替变化:1 2 1 2 1 2 1 2 1 2


现在,让我们在示例中看看它是如何工作的。 Encode和Decode方法位于Encoder类中,负责调制和解调图像:

 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样本)添加到输出信号-这是必要的,以便在接收端可以在图像本身之前发生同步, 并且通常可能在不利条件下发生
解码方法如下:

 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方法中(简称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。

由于检测器远非完美,并且可以轻松地在行的中间进行同步,因此我添加了一个特殊的滑块,移动该滑块可以实现更准确的同步。

现在,在我们简要研究了所有工作原理之后,让我们继续进行最美味的部分:可以从所有这些中获得什么。

一点练习


首先,让我们检查一下没有声纳通道的情况下的工作原理-只需将接收天线和发射天线相互连接即可。
首先,图片较大(240x120),因此至少可以看出:


然后快速播放,使生活更像视频:


好像还不错吗? 但不要急于下结论,而去游泳池:


正如我在标题中所承诺的,在这里,我们将亲眼看到回声:


埃隆·马斯克(Elon Musk),您觉得怎么样? 你喜欢高清吗? 为什么会这样呢?
一切都非常简单-回波本质上是原始信号的延迟副本,在接收点会产生干扰,在不同的相位折叠并给出这样的图像。 由于我们传输了图像,最终我们得到了许多幅彼此重叠且幅值不同的图像。 所有这些最终导致模糊和再现。

往回看,让我们检查一下模型大图片上的所有内容。 我随机拍了张照片:


我对其进行了调制,然后添加了回声和一点噪声,然后对其进行解码,是的-结果类似于我们在池中得到的结果:


原则上,可以进行去卷积并减去反射,但是让我们这个地区的人们离开这一点进行独立工作。

顺便说一下,池中的前一种方法效果更好,但效果也很差-在宽带信号上,多径和混响会导致频率选择性衰落,在图片中(在频谱上读取)看起来像是黑白条纹-信号处于反相状态,以及它的开发阶段(实际上,还有很多中间选项):


4月,我们抓住了片刻,带着面包板模型去了池塘,并在那里宠爱自己:




结果与池中获得的结果没有太大不同:




立即比较一下以前的方法:


这是从保存的帧中收集的gif动画,方法1:


在本文中我们将讨论方法2:


总结


如所承诺的,我们展示了回声和混响的字面外观,如何度过了有益的时光并用手完成了一些工作。

当然,以这种形式,该方法在实践中不适用,但是对初学者来说,使用它会非常有用。

一般而言,我们在条件很不利的浅水池中进行检查,如果有人在其他水库中重复我们的实验并肯定会告诉他们结果,那将是很酷的。

如果读者只是想尝试(即使是戴着麦克风和扬声器在空中),也可以使用以下链接:
方法1
方法2(来自本文)

聚苯乙烯

我们非常希望获得读者的反馈,因为了解您正在徒劳(或徒劳,然后需要立即停止)是非常重要的。

PS / 2

我会立即回答一个常见问题:对于这类儿童设施中的鱼类和其他海洋生物而言,这一切都鲜为人知。

Source: https://habr.com/ru/post/zh-CN453678/


All Articles