你好,亲爱的!
在本文中,根据大众的需求,我们将告诉您如何制作一个简单的水声调制解调器:一些数字信号处理,一些编程,一些自制的印刷电路板以及一些实用的水文学。
对于所有有兴趣的人-我们恳请您在切切的水下通信世界中受到欢迎!
以下是相关图片,以引起注意:

“最终,我们存在的意义是耗费精力……而且,如果可能的话,您知道的,这样对您自己和他人都很有帮助。
(C)ABS,二十二世纪中午
节省时间-摘要
- 速卖通尚未在Aliexpress上出售
- 一种简单,不需要计算资源的音调检测方法,其频率比采样频率小4倍; Arduino足以实现
- PC的示例代码位于GitHub上
- 我们用每个10 r的拾饼机制作接收和发送天线
- 我们以50卢布的价格在阿里的TDA2030上购买(或自行制造)放大器板
- 我们制造了一个LUT欧姆前置放大器,总成本约为100卢布
- 我们连接并去池塘
- 欢喜
励志序曲
现在,您可以在Aliexpress或eBay上购买几乎任何东西。 尤其是基于Arduino的电子产品的独立制造中有很多不同的东西。 您可以(如果您不感兴趣的话)制作一个具有互联网连接的环境中途停留气象站,自动送猫器,家用啤酒厂控制器,但是您仍然无法购买声纳调制解调器,制造商的设计师或至少用于adruino的模块。 好,好! 并且不要-现在我们将告诉您如何制作,以及如何运作。
我们整个实验室长期以来一直在思考可以为爱好者提供独立制造的产品。 一个非常简单的东西,可以由学生从每个人手
头上的棍子和绳索中收集,但同时必须至少代表最小的实践或教育价值。
有希望实现长期而激动人心的改进的东西
,如果不对
,可以后来转移到arduino。
如果我们实质性地解决这个问题,我们想提供一个制造简单设备的详细教程,该设备或多或少能够在浅水体中传输数据(浅声纳通道是最复杂的),这意味着使用LUT可以最大程度地生产印刷电路板,最低消费总额不超过几百卢布。
今天我们要做什么?
- 记住如何制作合适的声纳天线并配对。
- 通过TDA上的放大器将一根天线连接到PC上,费用约为50卢布,并获得发射器;
- 第二次,我们将借助LUT制作一个前置放大器,价格约为100卢布;
- 我们
将使用C# 编写一个简单的调制解调器(我已经将所有内容写到了Git上,并放在Git上) ,然后尝试在最近的水域中进行一切操作;
为此我们需要什么?
- 两个压电元件。 例如, 从时钟或明信片 ;
- RG-174 / U电缆(或类似电缆)〜5米;
- 醋酸密封胶
- 防水清漆
- 铝箔织物,总计约100x200毫米;
- TDA2030的放大器(例如, 一个 50卢布);
- 前置放大器组件
如何运作?
同样,最简单的调制解调器的整个思想是建立在某种音调的最简单(巧合?)检测器的基础上的,令我感到羞耻的是,我还没有听说过。 偶然地完全把他告诉了我
andrey_9999a 。 顺便说一句,他还制作了前置放大器板。
在这方面,我回想起伦纳德·萨斯金德(Leonard Sasskind)的书“黑洞之战”中的一句话:
“作为葡萄酒鉴赏家,我或多或少确信即使闭着眼睛,我也能分辨出红色和白色。 更可靠的是,我将葡萄酒与啤酒区分开来。 但是那滋味会让我失望。”
我可以对自己说,作为一名真正的电子工程师,我或多或少会确定我绝对可以焊接两条粗线。 更可靠的是,即使我闭上眼睛,我也能将冷热烙铁与冷烙铁区分开来,但是这种技巧会让我失望。 因此,与开发和制造电路板有关的所有事情都是我的同志和同事
andrey_9999a和
StDmitriev的工作 。
因此,回到检测器。 这是计算傅立叶积分的简化特殊情况:
在数字信号的情况下,要计算任意谐波的幅度,有必要执行离散傅里叶变换,这对于Arduina来说很困难,但是诀窍在于,如果将
Fc作为载波频率,使其恰好小于采样频率
Fs 4倍,则该谐波的幅度可以很容易地计算得出。
在这种情况下,
dt =2π*(Fs / 4)/ Fs =π/ 2 ,并且只有四个样本落在载波周期上:

如果一切都偏移
π/ 4,则样本将仅取两个值:√2/ 2和-√2/ 2,为简单起见,我们仅保留符号
“ +”和“-” 。
该方法的本质是将正弦相位表示为符号
“ +”,“ +”,“-”,“-”的序列,并将余弦相位表示为
“ +”,“-”,“-”,“ +” 。
假设输入信号在
sn缓冲区中,我们有两个用于正弦和余弦相位的环形平均缓冲区
-bs和
bc大小为
N。 指向头部和尾部的指针很常见
-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
这是理想信号的外观:

信号本身以蓝色显示,载波幅度值以红色显示(所有值均减小到-1..1范围)。 在这种情况下,
N = 2,因为 没有噪音,而且一切正常。
现在添加一些白噪声,看看我们的探测器如何对此做出响应:

我添加了白噪声,以使信噪比为0 dB。 在上图中,噪声信号显示为蓝色,源信号显示为绿色,幅度值显示为红色。 在这种情况下,位于
N = 2的检测器已经没有检测到任何东西,并且可以正常工作的最小N为32。 样本中处理窗口的大小为
32 * 4 = 128个样本。
也就是说,现在我们可以分析输入信号并评估一个量化存在频率的参数,该频率比采样频率小四倍。 如果为此参数设置某个阈值,那么所有内容都可以二值化,并且以简单的方式讲,我们可以回答这个问题:输入信号中是否有给定的音调?
这非常好,但是我们需要传输位,并且位可以采用两个值。
在一个信号的帮助下实现具有两个信号状态的系统是一个不错的主意,因此,我们
不会使用静默(暂停)对其中一个状态进行编码。 这将使检测变得非常困难:有必要以某种方式突出显示前提的起点,弄清楚如何安排其终点等。
相反,我们将使用不同长度的脉冲对“ 1”和“ 0”进行编码,在位之间存在所谓的 保护间隔-因为我们仍然需要处理多径传播和混响。 简单来说,保护间隔是指前一位的所有反射,所有余音和回声消失的位置(时间)。
展望未来,我们考虑到采用这种信号结构,接收机的操作算法已大大简化:我们等待提示音出现,注意开始,等待提示音消失,然后再次记下时间-如果接收到的时间长度更像是“ 1”,如果看起来更像是“ 0”,那么我们可能会花一点点“ 1”的值-显然,我们会花一点点“ 0”的值。
通常,我们可以说这是某种莫尔斯电码。
调制解调器的软件部分
不耐烦-GitHub上有
一个例子 。 它是用C#编写的(因为我写的是PC,因此对我来说更方便,更快)。
精彩的
NAudio库用于播放和捕获来自麦克风输入的声音。
所有调制解调器逻辑都在
SUAModem (简单水下声学调制解调器)类中。
以下参数传递给构造函数:
double sRateHz-采样率,以赫兹为单位;
int wSize-样本中处理窗口的大小;
int oneMultiplier-多少个“窗口”持续出现,值为“ 1”
int zeroMultiplier-多少个“窗口”以“ 0”的值持续一点
double eThreshold-阈值,稍后再讨论
为了从字节数组生成信号,有一个ModulateData(字节[]数据)方法,该方法返回一个16位带符号采样的数组。
公共短[] ModulateData(字节[]数据) public short[] ModulateData(byte[] data) { double alpha = 0; double phase = 0; List<short> samples = new List<short>(); BitArray bits = new BitArray(data); for (int i = 0; i < bits.Length; i++) { int sLim = (bits[i]) ? oneDurationSmp : zeroDurationSmp; alpha = 0; phase = 0; for (int sIdx = 0; sIdx <= sLim; sIdx++) { alpha = Math.Sin(phase); phase += delta; if (phase >= alimit) phase -= alimit; samples.Add(Convert.ToInt16(alpha * short.MaxValue)); } samples.AddRange(new short[defenseIntervalSmp]); } return samples.ToArray(); }
在主循环中,按发送的位填充样本列表。 根据当前发送的位,设置样本中生成信号的长度sLim。 在每个位之后添加一个保护间隔。
当然...许多人可能会注意到,在生成信号时,可以不用正弦函数,但是此示例通过相应地更改增量值,可以更改生成的音调的频率。
产生具有频率的音调
以给定的采样率
对应值
简单计算:
为了产生和发出信号,有一个TransmitData(字节[]数据)方法,该方法在内部调用ModulateData:
公共双精度TransmitData(字节[]数据) public double TransmitData(byte[] data) { var samples = ModulateData(data); double txTime = ((double)samples.Length) / SampleRateHz; var rawBytes = new byte[samples.Length * 2]; for (int i = 0; i < samples.Length; i++) { var bts = BitConverter.GetBytes(samples[i]); rawBytes[i * 2] = bts[0]; rawBytes[i * 2 + 1] = bts[1]; } using (var ms = new MemoryStream(rawBytes)) { using (var rs = new RawSourceWaveStream(ms, new WaveFormat(Convert.ToInt32(SampleRateHz), 16, 1))) { using (var wo = new WaveOutEvent()) { wo.Init(rs); wo.Play(); while (wo.PlaybackState == PlaybackState.Playing) { Thread.SpinWait(1); } } rs.Close(); } ms.Close(); } return txTime; }
SUAModem类使用DataReceivedEventHandler事件报告接受下一个字节。
使用ProcessInputSignal(短[]数据)方法将输入样本发送到分析,然后将其写入到环形缓冲区。 该分析在Receiver方法的单独线程中进行。
接收者本身也可以使用Receive方法:
私人void Receive() private void Receive() int a; while (rCnt >= 4) { a = ring[rRPos]; rRPos = (rRPos + 1) % rSize; rCnt--; dRing1[rHead] = a; dRing2[rHead] = a; s1 += a - dRing1[rTail]; s2 += a - dRing2[rTail]; rHead = (rHead + 1) % windowSize; rTail = (rTail + 1) % windowSize; a = ring[rRPos]; rRPos = (rRPos + 1) % rSize; rCnt--; dRing1[rHead] = a; dRing2[rHead] = -a; s1 += a - dRing1[rTail]; s2 += -a - dRing2[rTail]; rHead = (rHead + 1) % windowSize; rTail = (rTail + 1) % windowSize; a = ring[rRPos]; rRPos = (rRPos + 1) % rSize; rCnt--; dRing1[rHead] = -a; dRing2[rHead] = -a; s1 += -a - dRing1[rTail]; s2 += -a - dRing2[rTail]; rHead = (rHead + 1) % windowSize; rTail = (rTail + 1) % windowSize; a = ring[rRPos]; rRPos = (rRPos + 1) % rSize; rCnt--; dRing1[rHead] = -a; dRing2[rHead] = a; s1 += -a - dRing1[rTail]; s2 += a - dRing2[rTail]; rHead = (rHead + 1) % windowSize; rTail = (rTail + 1) % windowSize; if (++cycle >= windowSize) { cycle = 0; currentEnergy = Math.Sqrt(s1 * s1 + s2 * s2) / windowSize; double de = currentEnergy - prevEnergy; #region analysis if (skip > 0) skip -= windowSize * 4; else { if (isRise) { if (de > -Threshold) { riseSmp += windowSize * 4; } else { // analyse symbol isRise = false; double oneDiff = Math.Abs(oneDurationSmp - riseSmp); double zeroDiff = Math.Abs(zeroDurationSmp - riseSmp); if (oneDiff > zeroDiff) { // Mostly likely "0" AddBit(false); } else { // Mostly likely "1" AddBit(true); } samplesSinceLastBit = 0; skip = defenseIntervalSmp / 2; } } else { if (de > Threshold) { isRise = true; riseSmp = windowSize * 4; } } } #endregion prevEnergy = currentEnergy; if (bPos > 0) { samplesSinceLastBit += 4 * windowSize; if (samplesSinceLastBit >= defenseIntervalSmp * 2 + zeroDurationSmp + oneDurationSmp) { DiscardBits(); } } } } }
代码显示分析是在4个样本中进行的,如果需要,您可以保存状态并处理一个样本,这在转移到某些弱MK时将很有用。
接收到数据后,以频率sRateHz / 4计算振幅s的值。 计算幅度的先前值与当前值之间的差,然后将其与通过实验选择的给定阈值进行比较。 一个示例使您可以实时更改此阈值。
幅度的急剧增加表示“位”的开始,而急剧的下降(由于混响而不太尖锐)则表示“位”的结束。
收到下一个比特后,我们确定保护间隔-我们跳过指定数量的样本-它们中有各种各样的回声,它们只会干扰我们。
调制解调器的铁部分
因此,借助信号的结构,一切都变得清晰,如何清晰地接收它。 小事情是学会将信号辐射到水中并从水中接收信号。
如果您还没有声纳天线,那么该在
我们上一教程中进行介绍了 。
从那时起我就和他们在一起,所以我跳过了这一步。
我们
使用速卖通将打算传输的天线连接到
放大器板上 。 对于几十米(甚至数百米)来说,这对我们来说已经足够了。 这里没有技巧-笔记本电脑声卡的输出将输入到放大器的输入,该放大器由12伏4 Ah铅酸电池供电。 我们的压电高音扬声器的水声发射天线连接到输出。 就我而言,它看起来像这样:


在上面的照片中,屏幕上有一个小的扰流板,用于下一篇文章。 下次在相同的腺体上,我们将再次尝试通过水传输“视频”声音,但方式与
上次完全不同。
使用接收天线要稍微复杂一些。 尽管压电高音扬声器非常敏感,但这还不够。 我们将不得不组装一个带有5-35 kHz频带滤波器的前置放大器板。
我们获得的收益是1000。
电路,电路板的设计以及前置放大器组件的列表在我们的GitHub上:
电路 ,轨道-
顶层和
底层 ,
BOM 。
LUT
技术已经讨论了数百次,但让我们也尽力而为。
处理照片因此,我们准备了一本合适的杂志,我们手上只有一本:

我们从那里取几页纸,然后使用激光打印机在其上打印图层。

如图所示,用针和胶在一侧结合,如图所示:

在使用熨斗之前,我们先用异丙醇润湿碳粉:

铁透A4板折叠四次:

在水龙头下浸泡在温水中:

然后我们洗掉残留的纸张。 之后,我们准备好要蚀刻的工件:

我们用剪刀剪掉多余的金属,或者对谁更方便。
我们在氯化铁中毒。 尤其是对于这篇文章,我们投入了新的精力,事实证明它是如此挑剔,以至于未来的董事会会积极地冒出气泡:

结果,在蚀刻和清洗了碳粉之后,我们得到了这样的半成品:


焊接好组件并清洗后,电路板看起来像这样。

它看起来像接收零件的装配体。 由同一块12伏铅电池供电:

小过滤器免责声明如果读者想更改频段,那么我们建议重新叙述组装在便宜的4通道运算放大器TL084C(
图中的 DA2),电阻器R10-R13,R15-R23和电容器C5-C8,C11,C12,C14和C8上的8阶滤波器。 C15。
以防万一,这是当前滤波器实现的频率响应:

这是在
Qucs应用程序中为此过滤器创建的另一个
项目 经验和测试
要连接到笔记本电脑,我们使用普通的3.5毫米插孔,最尖端是一个信号,中间没有连接,是大地-
灰尘到地面。 必须关闭麦克风的所有放大和任何效果,并且必须演奏音量才能达到最佳水平。 当您用手指触摸连接到前置放大器的天线并轻轻抚摸它时,应该出现节奏。
如果您只是简单地将一块放到另一块上而没有放大器和前置放大器,然后将它们连接到音频输入和输出,那么一切都将完美运行。 以下是信号的一部分,您甚至可以通过肉眼确定这些位的值在哪里:

信号本身以蓝色显示,幅度的当前值和先前值之间的差(前)以红色显示,而幅度值的当前值与当前值之间的差(下降)以绿色显示。 您可以轻松地“解调”前提的这一部分:1 0 0 0 1 10。我们的零是单位长度的两倍,保护间隔的持续时间等于零的持续时间。
此外,即使没有放大器和前置放大器,我们也将天线放到尺寸为3x1.5x1.5的金属储罐中。 我们在实验室里有这个,我们定了一个规则,如果她因某种原因无法在此水箱中工作,我们将不进行任何交流。 事实是,在如此封闭的能量中,无处可走-声音从金属壁上反复美妙地反射,并在接收点得到粥。 考虑到我们通常用数千米计算出的能量来检查现成的设备,您可以想象在那里发生了什么。
例如,我们的两个
RedLINE调制解调器只能在不超过两米的距离
内在此槽中稳定
工作 ,而两个
uWAVE则可以在约1米处稳定地工作。 第一个在开放水域中可以工作8000米,第二个
可以在公里中工作 。
当然,所有商业产品都不使用本文中讨论的原始调制方案,并且更为复杂,但是对于我们现在来说重要的是了解基本知识并有用地做一些事情。
通常,我们将天线放到储罐中的距离大约为50厘米,与直接接触天线相比,我们已经获得了比其美观得多的东西:

尽管此处使用了更长的保护间隔,但仍然可以看到,回声几乎移到了下一位,但前沿,尤其是骤降非常模糊。 但是您仍然可以确定消息的内容:1 0 0 0 1 1 0
在这两种情况下,我都发送了消息“ 123”,这七个位属于单位符号。
看起来像这样,然后界面稍作重做

从上面的屏幕可以看到,通过这些设置,消息“ Hello,
habr !!!! :-)“由19个字节组成需要9.132秒,即,传输速度为16.6位/秒。 顺便说一句,为了使调制解调器能够在我们的储罐中工作,我们必须增加保护间隔,以使传输速度降至〜3 bit / s。
我们在游泳池检查了自制作品,在那里稳步赚了10米。

我们还沉迷于池塘上的自制工作。 我使用的有源水听器的设计与本文中建议的非常相似,只是代替了压电高音扬声器,在那里使用了驻车传感器的传感器,电池安装在线圈中,并在该线圈中缠绕电缆:


接收器和发射器的天线直接从岸边下降,那里的深度从0.5米急剧上升到2米。 奇怪的是,在上图所示的实验中,最糟糕的条件是,距离只有约5米-这通常是初始设置。 在发送的20条消息中,每个3个字节,其中六个被打了1个字节。
然后,当我们将接收器连接到第二台笔记本电脑并将其移动到池塘的另一侧(距离约30米)时,传输情况变得更好了-只有几个错误,40条消息的大小从3到13字节不等。

在地图上的下一张照片中,可以看到天线所在的位置。

结论和进一步研究
如所承诺的,几卢布我们组装了一个工作装置。 尽管其实用价值令人怀疑,但池塘的制造和调试过程对于初学者将非常有用。 使用所描述的载波检测方法,很可能会为业余爱好者提供各种简单的导航系统,而且特别好,计算复杂度允许您在简单的微控制器上实现该方法。
为了对基于简单信号的导航系统的构建不遗余力,请看一下他们构建了完整的远程导航系统的
有趣工作 。 在该系统中,确定了周期性发送其深度的pinger的位置。 深度值由特定频率下两个简单脉冲之间的距离编码。 所以是的,是的,锅不是被众神所烧,步行者,耐心和工作,学习,学习,学习的道路将被压倒-仅此而已。
也许,如果有时间,我们将做一个DIY项目来定位发出简单信号的自主ping传感器。 在我们的
uWAVE调制解调器的基础上,我们已经做过类似的事情,但没有做DIY,甚至尝试
拍摄有关的视频 。 听到您对此事的看法将非常有趣-确认您所做的事情没有白费,这非常重要。
尽管如此,回到主要主题,我们注意到在提议的方案中可以改进的地方:
- 使阈值计算自适应
- 自动分析信号宽度
- 尝试对不同的位组合使用不同的长度
加强纠错编码- 全部转移到arduino
- 选择的音量和阈值必须冗长而乏味,因此最好在前置放大器中添加AGC
在本次会议上,我将宣布关闭,如果您对该主题感兴趣,这里是我们以前的文章列表:
每年从零开始的水下GPS水下机器人上的水下GPS:体验我们制造了世界上最小的声纳调制解调器蓝细菌对总裁言语功能的影响用垃圾桶制作一个简单的声纳天线通过曝光进行水声视频传输的会话两个收发器上的水下GPS水下航行:承载而不承载-您注定要成功水下GPS:继续聚苯乙烯
与往常一样,我们很高兴听到您的意见和建议,声音批评和欢呼声)
PPS
不要将腺体移开-下次我们将再次使用它们来通过水传输“视频”。