8

把数字信号调制到音频信号中

 11 months ago
source link: https://hsiaofongw.notion.site/50551e910df848e18d0a9fabe473bc89
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

把数字信号调制到音频信号中

Created
October 3, 2023 7:59 AM
radio
Description
一种数字调制技术的应用介绍
Direct
Updated
October 8, 2023 5:42 PM
3 more properties
我们说的「调制」指的是对信号(波形)进行编辑,使得它能够携带信息,从而方便信息的传递和交流,的这么样的一个过程。相对而言,「解调制」或者解调指的是从调制了的信号中恢复出原先调制进去的信息。调制就好像是把信塞进信封里,解调就是把信拿出来。承载着被调制信息的信号称为载波 (carrier), 被调制进去的信息本身作为一个信号称为基带 (baseband). 基带信号和载波信号之间的关系就好比信和信封的关系。
我们说的「信号」,物理上它指的是一个物理量,它可以被观测,它的观测结果可能随时间 t 的变化而变化;逻辑上,一个「信号」指的是一个函数 y(t), y(t) 的输出值至少是可以比较的。除非特别说明,在本文中我们说的信号一般指的是逻辑上的信号,而且经常会直接用函数符号表示。
我们说的「数字信号」指的是这么样的一类信号:y(t), 它的输出范围是 {0, 1} 集合内的整数。另外,我们还把数字信号叫做「比特流」、「字节流」或者「符号流」,一个二进制整数构成的序列(或有序元组),我们称之为「符号」,例如 1001 是一个符号,010101 也是一个符号。
一个「比特」是一个物理的或者逻辑的可观测实体,它只有两种可观测到的状态,在纸面上,我们把一个比特的两种可能的观测得到的状态(结果)分别记录为 0 和 1. 例如,办公室中的一个电灯开关可以看作是一个比特,因为它一般只有「开」和「关」这两种状态,并且可以把「开」状态记做 1,「关」状态记做 0.
「模拟信号」指的是连续变化的信号。相对而言数字信号一般都是不连续的。声音信号(音频信号)可以看作是模拟信号,因为我们一般认为空气中风压的变化是连续的,就算观测到了不连续,也是因为采样率不够高(从而出现了锯齿)的缘故。
「样本」是信号的一次观测结果,在数学上它就是一个值,例如把 t0t_0t0​ 带入 y(t)y(t)y(t), 得到 y(t0)y(t_0)y(t0​), 这就是一个样本。为了能够在计算机中对信号进行处理,需要拿到信号的离散形式,这就需要对信号进行观测。一般来说计算机更擅长分析的是离散形式的信号,而不是解析式的(例如信号的数学表达式)。采样的频率称为「采样率」,它指的是一个观查者在单位时间内对一个信号做了多少次观测,一组样本数据一般要结合它的采样率一起才有分析的意义。
我们希望在这篇文章中演示一遍如何把一个数字信号调制到声音信号中,使得该数字信号得以以另外一种方式进行传递。
在开始具体的编码工作之前,我们需要在一些基础知识上达成共识(一致)。
调制方式(制式)影响着,或者说跟要传递什么类型的数据有关系。例如,假如我们希望在一个载波中调制音频信号,或者说把声音信息调制到一个载波中,我们可能会选用 AM/PM/FM/SSB 等作为调制方式,因为,在这类调制方式中,基带信号和载波信号都是模拟的。当调制方式确定了,解调方式一般也就顺理成章地确定了。
调制方式的选择,除了跟基带信号有关系外,跟载波信号,也就是跟它是怎样被传递的,也有关系,不过我们点到为止。
而当我们要调制进载波的基带信号本身是数字信号,那么可能会选用(不止)FSK/PSK/ASK 等调制方式,在本文中,我们选用 PSK 调制方式(Phase Shift Keying). 在 PSK 制式中,基带信号是数字的,而载波信号是模拟的。
要理解 PSK 制式,首先你得理解 PM 制式 (Phase Modulation), PM 制式,和 AM 制式、FM 制式某种程度上来说都是类似的:在调制时,都是只对其中的一个参数进行修改,这几个参数无非就是振幅/频率/相位。
模拟形式的载波本身一般都是正弦波(或正弦波的累加),它可以写成下列形式,对于时间变量 t:
y=A(t)sin⁡(2πf(t)t+θ(t))y = A(t) \sin ( 2 \, \pi \, f(t) \; t + \theta(t))y=A(t)sin(2πf(t)t+θ(t))
对于 AM 制式而言,f(t), θ(t)\theta(t)θ(t) 都是常量,变化的只有 A(t)A(t)A(t), 也就是说只有幅度 (amplitude) 在变化,这也是调幅这个名称的由来。类似的,对于 FM, 只有 f(t), 也就是频率在变。对于 PM(调相),只有 θ(t)\theta(t)θ(t) 在变。
而 PSK,它实际上可以看作是离散形式的 PM, 与 PM 的区别仅在于,这里的 θ(t)\theta(t)θ(t) 是随着信号的取值的不同而离散变化的,而不是连续变化的,也就是说,如果把 θ(t)\theta(t)θ(t) 的图像画出来,会看到很多矩形和锯齿,而不是看起来平滑的曲线。
PSK 的 K (keying) 在这里的意思类似于「映射」,当然这也侧面说明了在 PSK 中 phase 是瞬间变化的而非连续变化的,具体来说,在 PSK 中,有一个参数 M, M 的取值范围是大于等于 2 的整数,该参数表示这个 PSK 所做的映射的范围的大小,例如,当 M=4 时,PSK 调制器会将 4 种不同的符号映射到不同的 phase 上。一些 PSK 调制器同时提供默认的符号到 phase 的映射关系并且也支持用户自己指定映射,当然,要想顺利的解调制,则要求 PSK 收发两端所用的映射规则是一样的。这里我们说的符号在本文开头有定义,并且,在 PSK 的符号-phase 映射表中,符号的长度都是等长的,对于 M=2^n 的取值, 符号的长度为 n.
以下是一种可能的 PSK 符号-phase 映射规则:
角度(角度制)
可想而知,所有可能的这样的规则还有很多,但是无论如何都会要求不同符号之间角度差(相位差)尽可能地大,以降低误码率。

I/Q 信号

我们在中学时期都学习过简单的和角余弦公式,该公式告诉我们怎样用两个角的正弦、余弦的简单四则运算的组合来表示这两个角的和的余弦:
cos⁡(x+ϕ)=cos⁡(x)cos⁡(ϕ)−sin⁡(x)sin⁡(ϕ)\cos(x + \phi) = \cos(x)\cos(\phi) - \sin(x)\sin(\phi)cos(x+ϕ)=cos(x)cos(ϕ)−sin(x)sin(ϕ)
另外,我们还注意到 sine 和 cosine 的图像相差 π/2\pi/2π/2 个相位,也就是说总是有 cos⁡(x)=sin⁡(x+π/2)\cos(x) = \sin(x + \pi/2)cos(x)=sin(x+π/2),也就是说 cosine 的图像就是相当于 sine 的图像往左平移 π/2\pi/2π/2 个长度。由此我们可知,两列相差 π/2\pi/2π/2 个相位的正弦波,对它们乘以不同的系数再相加,可以得到不同相位偏移了的的正弦波。
进一步而言,对于下列这样一个正弦波,我们可以对它用同样的方法进行变形,得到(式1):
A(t)cos⁡(2πft+ϕ(t))=cos⁡(2πft)A(t)cos⁡(ϕ(t))−sin⁡(2πft)A(t)sin⁡(ϕ(t))A(t) \cos(2 \pi ft + \phi(t)) = \cos(2\pi f t) \; A(t) \; \cos(\phi(t)) - \sin(2 \pi f t) \; A(t) \sin(\phi(t)) A(t)cos(2πft+ϕ(t))=cos(2πft)A(t)cos(ϕ(t))−sin(2πft)A(t)sin(ϕ(t))
令 I(t)=A(t)cos⁡(ϕ(t))I(t) = A(t) \cos(\phi(t))I(t)=A(t)cos(ϕ(t)), Q(t)=A(t)sin⁡(ϕ(t))Q(t) = A(t)\sin(\phi(t))Q(t)=A(t)sin(ϕ(t)),上式可以写成(式2):
A(t)cos⁡(2πft+ϕ(t))=cos⁡(2πft)I(t)−sin⁡(2πft)Q(t)A(t) \cos(2 \pi ft + \phi(t)) = \cos(2\pi f t) \; I(t) - \sin(2 \pi f t) \; Q(t)A(t)cos(2πft+ϕ(t))=cos(2πft)I(t)−sin(2πft)Q(t)
这里,我们发现,仅需精心设计好 I 和 Q 函数的表达式,就可以实现 AM/FM/PM 各种调制!我们把上式右侧中 cos⁡(2πft)\cos(2\pi f t)cos(2πft) 项的系数叫做信号的 I 分量 (I Component),而 −sin⁡(2πft)- \sin(2 \pi f t)−sin(2πft) 的系数叫做 Q 分量 (Q Component).
我们还发现,信号不仅可以表示为一个实数(标量)的变化(对应式子的左边),还可以用 I 分量和 Q 分量组合起来来表示(对应式子右边的系数 I, Q),这也就是通常人们所说的 I/Q 数据。
I/Q 数据的样本也可以以复数的形式存储和表示,其中 I 存储在实部,Q 存储在虚部,这样,就把 I/Q 数据样本和复数联系了起来。
回顾上一小节的 PSK 符号-phase 映射表,我们还可以以复数的形式把它写成:
角度(角度制)
e0e^{0}e0​
eiπ/2e^{i\pi/2}eiπ/2​
eiπe^{i\pi}eiπ​
e3iπ/2e^{3 i \pi /2}e3iπ/2​
根据欧拉公式 eiθ=cos⁡(θ)+isin⁡(θ)e^{i \theta} = \cos(\theta) + i\sin(\theta)eiθ=cos(θ)+isin(θ),以上这 4 个复数正是分别位于复平面上的单位元的 0∘0^\circ0∘, 90∘90^\circ90∘, 180∘180^\circ180∘, 270∘270^\circ270∘ 的点。
我们再对这四个复数代入欧拉公式,得到:e0=1+0ie^0=1+0ie0=1+0i, eiπ/2=0+ie^{i\pi/2}=0+ieiπ/2=0+i, eiπ=−1+0ie^{i\pi} = -1 + 0ieiπ=−1+0i, e3iπ/2=0+(−1)ie^{3 i \pi /2} = 0 + (-1) ie3iπ/2=0+(−1)i,容易验证,将这些复数的实部作为 I 分量,虚部作为 Q 分量,代入式 1 或者式 2,可以得到不同角度调制的余弦波,而这些余弦波的相位和上表的复数的幅角是一致的。
例如,将第一个复数的 I=1, Q = 0 代入式 2 右侧,我们得到 cos⁡(2πft)\cos(2\pi f t)cos(2πft),它和 cosine 的相位差是 0, 正是第一个复数的幅角;
将第二个复数的 I=0, Q=1 代入,得到 −sin⁡(2πft)=cos⁡(2πft+π/2)-\sin(2\pi f t) = \cos(2 \pi f t + \pi/2)−sin(2πft)=cos(2πft+π/2), 它和 cosine 的相位差 π/2\pi/2π/2 正好也是该复数的幅角。
再将第三个复数的 I=-1, Q=0 代入,得到 −cos⁡(2πft)=cos⁡(2πft+π)-\cos(2\pi f t) = \cos(2 \pi f t + \pi)−cos(2πft)=cos(2πft+π), 差的 π\piπ 相位也对得上第三个复数的幅角。
再将第四个复数的 I=0, Q=-1 代入,得到 sin⁡(2πft)=cos⁡(2πft−π/2)=cos⁡(2πft+3π/2)\sin(2\pi f t) = \cos(2 \pi f t - \pi/2) = \cos(2 \pi f t + 3\pi/2)sin(2πft)=cos(2πft−π/2)=cos(2πft+3π/2), 差的 3π/23\pi/23π/2 相位正是第四个复数的幅角。
由此,我们发现,I/Q 调制(也叫 QAM 调制)、PSK以及复数它们之间确实存在着密切又微妙的联系。
将上列 PSK 符号-phase 映射表的复数画在复平面上,这样的图,人们也叫它「星座图」或者「星相图」,英文名称是 Constellation,这是因为它上面的点看起来星罗棋布,而这些点上面又有各自的标签(也就是符号-phase 映射表的符号)。在有的软件中,例如 Gnu Radio, 用于实现 PSK 调制的模块干脆就直接叫做 Constellation modulator,多了通用性和直观性。
有的软件在用户指定 PSK 的 phase 映射角度时,往往要求用户输入复数,这时只需将原来准备输入的弧度 θ\thetaθ 输入成 eiθe^{i\theta}eiθ 即可。
我们希望实现两个模块,一个负责将符号流(或者符号流的一个片段)作为基带信号 (baseband signal) 调制到载波信号(也就是音频波形);另一个负责从音频波形中还原出最初调制进去的基带信号,也就是符号流(的片段)。
事实上,提到音频波形只不过是为了直观上更易于理解,波形是信号的时域表示,而信号只分模拟信号和数字信号,叫法不同的各种信号,不管它是无线电信号还是音频信号,区别只不过是在现实世界中传播的媒介不同,逻辑上看它们的时域表示都是波形。我们理论上完全可以不提音频两个字,而是像普通的业余无线电爱好者 (HAM) 那样把调制后的信号以无线电波的形式发送,不过无线电频谱是被强力管控的资源,未经许可擅自向空间中发射无线电是违法行为。
信号的传播媒介,除了通过上述我们提到的声波和无线电波还有许多:可以只让信号(的波形)在同一个进程内左手倒右手,最多加上一些人为的干扰信号,来模拟信号的传播过程;还可以让信号波形通过计算机网络传播,例如通过一条 TCP 连接传给网络上的另一个进程。
在本文中接下来我们仍然用声波作为信号的传播媒介,原因是这样能体验到最接近现实世界的信号干扰效应。
符号流到基带信号的过程主要是通过 PSK 调制实现的,然后我们会用 QAM 的方式将基带信号调制到 2000 Hz 的载波音频信号中,这就实现了符号流到载波信号的转换过程。从载波信号到符号流的过程,首先是对声音进行 48000 Hz 采样率的采样(我们说过了载波信号将通过声波进行传播),得到载波信号的样本点,然后我们试图从载波信号中还原出基带信号,这一步主要是通过 QAM 解调制的方式实现的,得到基带信号后,通过 PSK 解调制将它反向映射回符号流。
在 MATLAB 中,PSK 调制就是将符号映射到复数(也就是复平面上的点),PSK 解调制就是把复数映射回原先的符号,因此在 MATLAB 中所谓的 PSK 调制更像是映射而非传统理解上的把基带信号嵌进载波信号的那个过程,若不然的话,在把符号映射为复数的这个过程中,基带信号和载波信号分别是什么呢?
符号流调制到了载波信号中,载波信号经过现实世界的声波传递,到达接收端,然后接收端再从中解析出一个符号流,可想而知,经过了种种干扰,加上手工声音录制过程的引入的时间误差,收到的那个符号流可能会和最初发送的那个符号流有不可忽视的误差,这其中值得一提的有:
时间长度、相位不匹配导致的符号错位:例如发送的符号是 a b c d,而接收到的是 b c d e f;这使得我们无法用最直接的 element-wise 的方式去计算误差,而是要诉诸于 Cross-correlation 这样的方式来计算和衡量误差;这里之所以接收端还多收到了一个符号 f 可能是因为它录音的时候多听了一会儿;
环境噪音引入的杂波干扰造成的符号错误:例如假设时间是绝对匹配的,但是因为环境中存在的噪音,使得发送的符号 a 被接收端听成了符号 b;
然而庆幸的是这些问题都是容易解决的,对于符号错误下的误差计算和错误恢复,我们还可以借助于在符号内容前面填充 preamble (header) 的方式,就如同以太帧前面总是有 7 个字节的 preamble 那样。而对于符号错误,可以用纠错码的方式进行校验和纠错。
我们采用 MATLAB R2023b 软件,用来编写代码以及运行计算程序。在 MATLAB 中,数组或者矩阵是一等公民,几乎所有能接受标量值作为参数的函数(或者运算符)都同时能够接受数组或者矩阵作为参数。在 MATLAB 中,编程处理的对象主要是数组和矩阵,这种编程习惯使得程序天然地具备更好的并行性。一个具备高度并行性的程序方便编译器开展优化工作。
于此同时,MATLAB 官方还提供了一系列选配的 toolbox,可供用户按需安装,再加上一系列内置的简单易用且功能强大的函数,使得它非常方便进行信号处理和数据可视化。

信号的调制

首先我们需要来制备测试数据,同时也设定 PSK 符号种类数 M, 数据大小 N:
MATLAB

prb=ones(16, 1); numPrbEl = numel(prb); prbdet=comm.PreambleDetector(prb, Input='Symbol', Detections='First');

N=100; M=4; dataIn = [prb; randi([0, M-1], N, 1)];

这可以使我们获得一个长度为 N+16 的符号流片段,以一个 (N+16)x1 的 MATLAB 数组的形式存储,其中的每一个都是 [0, M) 范围内的整数。我们在符号流头部填充 preamble, 作为正文开始的标记,至于为什么这么做之前已经讲过了:主要是为了应对播放和录音过程中时间无法严格同步导致的符号错位问题。
接下来进行 PSK 调制(映射),把符号映射到星象图上的点:
MATLAB
modData = dpskmod(symbolsData, M);
这里我们还可以用 scatterplot 命令查看复数在复平面上的散点图,就如同实数在实轴上的散点图那样。
这里的 modData 是 I/Q 数据的形式,要让该信号能被音频设备播放需要把它转变为实数(浮点数),回顾之前理论部分的内容,我们知道信号的波形数据可以在 Phase/Amplitude 形式(等式左边)和 I/Q 形式(等式右边)之间转换:
A(t)cos⁡(2πft+ϕ(t))=cos⁡(2πft)I(t)−sin⁡(2πft)Q(t)A(t) \cos(2\pi f t + \phi(t)) = \cos(2\pi f t) I(t) - \sin(2\pi f t) Q(t)A(t)cos(2πft+ϕ(t))=cos(2πft)I(t)−sin(2πft)Q(t)
把一路 I/Q 形式的信号转变成 phase/amplitude 形式的信号,这个过程也被称为 QAM 调制 (Quadrature Amplitude Modulation), 所幸在 MATLAB 中有 modulate 这个函数可以做这个事,例如,下列代码:
MATLAB

fs = 200; t = 0:1/fs:1; i = sin(2*pi*10*t) + randn(size(t))/10; q = sin(2*pi*20*t) + randn(size(t))/10;

y = modulate(i,70,fs,'qam',q);

首先创建了一列 I 信号和一列 Q 信号,频率都是 10 Hz, 采样率是 200,然后以 QAM 调制的方式把这两路信号嵌入到一个频率为 70 Hz 的载波中。
在这个例子中,我们可以用这种方式:
MATLAB
[x1, x2] = demod(y,fc,fs,'qam')
把刚才调制进去的 I/Q 信号提取(解调)出来,这里 x1 对应 I 信号, x2 对应 Q 信号,如果我们把 i 的图像和 x1 的图像(时域表示)画出来,会发现它们非常相似,几乎是紧贴的,同理对于 q 和 x2 也是一样。
然而需要注意的是,QAM 调制/解调方式要求 I, Q 信号的频率远低于载波的频率,或者直观地说,它要求 I(t), Q(t) 自身的变化应当远慢于两列载波 cos⁡(2πft)\cos(2 \pi f t) cos(2πft) 和 −sin⁡(2πft)-\sin(2 \pi f t) −sin(2πft) 的变化速度。
如果我们直接把 modData 的实部、虚报作为 I, Q 分量,将这两列信号的时域直接画出来的话,会发现图像的变化非常陡峭,有很多锯齿,甚至有点接近方波:
https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F8551cdf7-056d-46dc-82ad-7ab1ba2283ed%2Fb5678e34-b62c-454e-b338-fe875a4e8534%2F%25E6%2588%25AA%25E5%25B1%258F2023-10-04_%25E4%25B8%258B%25E5%258D%25888.56.24.png?table=block&id=5e363a38-004c-4533-a143-bf8677659e6e&spaceId=8551cdf7-056d-46dc-82ad-7ab1ba2283ed&width=1600&userId=&cache=v2
从它的频域功率图来看,确实某种意义上接近方波:因为它含有很多高频分量,高频分量怎么个“多”法呢?几乎和低频分量一样多甚至更多。这样的信号,是不适合直接作为 QAM 调制的基带信号的。或者我们还可以换一种角度想,要能够准确分辨、识别出变化如此迅速的波,那一定需要很灵敏的设备,很低的干扰才能做到,所以,需要把其中的高频分量滤掉。
另外,BTW, 在公共环境下发射方波无线电信号一般而言是被禁止的,因为方波含有很多高频分量,会干扰到其他频段的正常通信,而频段资源的划分是非常紧张的。
因此,在进行 QAM 调制之前,我们还需给 I/Q 信号过一遍滤波器,以把其中的高频分量过滤掉,同时还要进行上采样(插值),使得它的图像看起来平滑一些,使得该信号在传播过程中更加鲁棒。一般而言,在 QPSK 制式中,被普遍使用的滤波器是 Root-raised cosine 滤波器, 也叫 RRC 滤波器,而且是成对使用的,在发送方和接收方都用一次。
在 MATLAB 中,我们用下列命令创建一个 RRC 过滤器对象:
MATLAB
rolloff = 0.35; % Filter rolloff span = 6; % Filter span sps = 2400; % Samples per symbol rrcFilter = rcosdesign(rolloff,span,sps);
sps 参数表示多少个样本对应一个符号,基本上是跟符号周期成比例的一个数,这个数越大,用来表示一个符号的样本的数量就越多,信息的冗余度就越高,信号在传递过程中就更加抗干扰,但是过高的 sps 会使得信道的比特率变低,并且信号收/发设备的采样率是不能无限提高的。另外,这个数越大,也会使得经过过滤后的波的带宽更细,且高频部分的强度更低,也就是说会使得波形曲线更加平滑。
rolloff 参数有时候也叫 beta, excess bandwidth 等,这个值越大,经过处理后的信号的频谱图的山峰就越肥大,换句话说,就是信号在发射时就会占用更大的带宽。
span 参数是 MATLAB 特有的,用来控制过滤器的 taps(系数)的数量,taps 的数量等于 span * sps + 1, 这个数越大,对高频信号的滤去效果就越好,但是计算成本就越大。
我个人是这样确定这些参数的,rolloff 的影响我承认我还是不大清楚,一般会参考一些教程把它设定在 0.25 或者 0.35,把它设小一些,使得信号在传播过程中对其它频段的影响比较低,但是如果太小,会导致信号的带宽非常窄,要求接收方具备更高的调协能力和时钟准度;span 的设置,在计算成本和滤波能力直接权衡,设大了之后,过滤后的波更加「干净」,但是计算成本增加。sps,实际上跟载波信号的采样率、以及跟信道的设计符号率也有关系,换句话说,它的值是多少,取决于你把载波的采样率设为多少,以及打算每秒发多少个符号(或者用多长时间来发一个符号,或者用多少个样本来表示一个符号)。
我们暂时将采样率设为 48000, 这是一些音频设备上支持的最高的播放和录制采样率,我打算把符号率设为 20 symbol / s, 那么 samples per symbol 的值自然就等于 48000 / 20 = 2400, 等于说我打算让一个符号在发送、接收的过程中重复足足 2400 次,可以说信息的冗余度是非常高了,这样的一个不好的点在于它要求音频设备支持较高的采样率,以及在发送同样多的符号的前提下要求了更大的算力,但是好处就是更好的抗干扰能力。
现在我们可以来算一下:假设要发送(或者说调制)100 个符号,会得到多少秒钟的音频信号?
100 个符号每个重复 2400 次,那么按照 sps=20 来算就有 240000 个 样本,音频采样率为 48000 Hz, 也就是说用 48000 个样本来描述一秒钟长度的声音的波形,那么 240000 个样本就相当于 240000 samples / 48000 samples s^-1 = 5 s. 也就是调制这 100 个符号将会得到一段 5 秒钟长度的音频信号。
由于 M=4, 每个符号实际上携带着 2 个 bits 的信息,这种调制方式能实现多大的比特率呢?100 个符号要 5 秒,那么 1 秒钟就对应 20 个符号,也就是 40 个 bits, 也就是 40 bit / s, 也就是 5 Byte / s. 之所以这么低,是因为我们在调制过程中对每一个符号都重复了足足 2400 次。
创建了 RRC 过滤器之后,我们需要对它进行应用:
MATLAB
txSig = upfirdn(modData, rrcFilter, sps);
通过这行命令,我们在 modData(I/Q 数据)上应用了 rrcFilter, 同时还对原数据集进行了 sps 倍率的上采样。应用 FIR 过滤器时,系统会在原数据之前填充 sps * span 个样本,因此如果 modData 的长度是 1, 那么 txSig 的长度将会是 sps * span + 1. 一般情况下,经过 sps 倍率的上采样之后,txSig 的长度会是 sps * span + size(modData, 1) * sps.
然后我们用下列这行命令:
MATLAB
fc = 2000; fs = 48000; y = modulate(real(txSig), fc, fs, 'qam', imag(txSig));
就可以得到最终可直接播放的音频信号了。
要播放声音, 可以新建一个 audioplayer 对象,然后再调用 play 方法,要暂停/停止播放,调用 pause/stop 方法。通过 audiowrite 方法可以将音频信号写入文件中,通过 audioread 方法可以从文件中读取音频信号以及其对应的采样率、通道数、长度等信息。要录制音频信号,可以通过创建一个 audiorecorder 对象,然后再调用它的 record 方法, stop 方法和 getaudiodata 方法来进行,具体案例参加这里。可以说 MATLAB 提供了一组面相对象的,并且简单易用的,音频设备操作函数,用来支撑常见的播放和录制需求。
MATLAB 支持 .wav, .m4a 扩展名的音频文件读取。
接下来我们可以在电脑上播放这段音频,用手机录制,然后再把手机上的录音文件传回电脑,这样就模拟了信号经过一轮噪音干扰处理的情形。或者也可以在一台电脑上播放,同时在另外一台电脑上录制。还可以在这台电脑上同时录制和播放。
以下是一段示例的经过了信号调制的音频,但具体参数可能跟本文中的不一样:
仔细听能发现其中有短促的、密集的脉冲声“滴滴滴滴”,且“滴”和“滴”之间的间断很短但是仔细听仍能听出来。
编码后的音频文件大小,假设是在无损且无压缩的情况下,大致约等于波形数据的大小,加上多媒体文件容器头部的大小,而波形数据的大小(单位:bit),等于时间长度 乘以 采样率 乘以 样本位宽 乘以 频道数,样本位宽一般是 16 bit,也就是说 1 个样本用 16 位的二进制数来编码。
通过 ffmpeg 命令行工具可以在各个操作系统上查看大多数常见波形文件(也包括音频文件)的原信息,其中就有频道数,时间长度,采样率,位宽,码率等信息。
在音频信号通过声波实现跨设备传递的过程中,需要注意的一点是,在同时播放和录制时,播放设备的采样率,最好应该和录制设备的采样率一致!否则,录制的过程中会丢掉一部分信号的高频分量,造成接收到的信号的频率峰波偏低,这时你需要用一些类似于 frequency translation 之类的方法把载波的频率移回去,而如果一开始两端的采样率就是匹配的,那么拿到了载波信号样本后一般而言就可以直接解调了。当然,实际上这仅仅是针对于音频信号通过播放和录制这种方式传播的情形而言,在无线电收发中,一般没有这种限制,在接收端进行信号解调之前做 frequency translation 是很正常的。
基本上你可以把信号处理中「采样率」这个概念,和图像摄影中的「分辨率」类比起来,一张图片它的分辨率越高,就越能保留原像中的细节部分,图像中的细节部分,就相当于于信号处理中的高频分量。

信号的解调

在调制时,我们是先对原始数据做 PSK 映射,然后再应用 RRC 过滤器,最后再做 QAM 调制,那么在解调时,这个顺序刚好是反过来的:先做 QAM 解调,然后再应用一次 RRC 过滤器(只不过这次是反过来采样,也就是下采样,原来是 1 扩 p, 现在是 p 取 1),最后再做 PSK 逆映射。这就是解调制的大致思路:调制的时候做了什么,解调的时候反过来做一遍。
QAM 调制是把 I/Q 信号转变为 Phase/Amplitude 形式的信号 A(t) sin(2 pi f t + phi(t)),那么 QAM 解调制应当让我们还原回原先的 I/Q 形式的信号:
MATLAB
[x1, x2] = demod(y, fc, fs, 'qam'); rxSig = x1 + 1i*x2;
这里的 x1 就是 I 成分,x2 就是 Q 成分。
然后再做一遍 RRC 过滤,上采样变为下采样:
MATLAB
rxFilt = upfirdn(rxSig, rrcFilter, 1, sps); rxFilt = rxFilt(span+1:end-span);
至于为什么下采样之后还要移除一部分样本,我们之前说过了,用 upfirdn 函数上采样之后,样本量变为 sps * span + size(modData, 1) * sps,现在进行下采样,等于说对这个数除以 sps, 那么样本量变为 span + size(modData, 1), 然后再减去 span, 即可恢复最先的符号数量 size(modData, 1).
两个 RRC 过滤器合起来(串起来)相当于一个 Nyquist ISI-free 过滤器,也就是按照 Nyquist ISI 评判准则来看,这样的过滤器理论上是不会引入 ISI(符号间干涉)效应的。Nyquist ISI 评判准则也是一个指导准则,指导工程师们怎样设计过滤器,才不会引入 ISI 效应。
接下来我们对这个数据做一次 PSK 的逆映射即可还原回来最初的符号数据了:
MATLAB
dataRx = dpskdemod(rxFilt, M);
第一次做这样的实验,建议直接在同一个进程内直接对数据左手倒右手,或者可以用 awgn 函数给音频信号 y 添加白噪音,而不用真的播放和录制。
我们可以利用先前准备数据时创建的 PreambleDetector 对象,检测 preamble 在符号串中出现的位置,并且以此定位到正文的位置以实现误码率的计算:
MATLAB
prbIdx = prbdet(dataRx); if numel(prbIdx) == 0 disp("Preamble not found.") else dataTxBody = dataIn(prbIdx+numPrbEl:end, :); dataRxBody = dataRx(prbIdx+numPrbEl:end, :); prefixComm = min(numel(dataTxBody), numel(dataRxBody)); if prefixComm > 0 [numErrs, errsRate] = symerr(dataTxBody(1:prefixComm,:), dataRxBody(1:prefixComm,:)); disp("Num Errors: " + string(numErrs)); disp("Error rate: " + string(errsRate)); else disp("No data.") end end
以下是完整的代码文件:
dqpsktutorialv1.m
1.2KB
要运行它,打开 MATLAB 中,先 cd 到该文件所在目录,然后在 MATLAB 的「命令行窗口」执行与文件名同名的命令(不包括扩展名 “.m” 在内)。
广义上来说,我们演示了「如何把数字信号调制到模拟信号」中的这么样的一个过程,这方面的应用已经是非常广泛的了,例如 LTE, WiFi, 5G 等,它们都涉及到把数字信号(比特流)调制到模拟信号(无线电波)中。但是当然它们的确要复杂的多,用到了更高阶的调制技术比如说 OFDM.
狭义上来说,我们做的是「把数字信号调制到常见的一种多媒体信号」的这样一个过程,这是一种信息封装到常见的多媒体信号中的能力,比如说这使得我们可以通过发送音频的方式发送图片,通过发送图像的方式发送声音(把图片的像素看作是 samples),它启发了我们:信息的封装原来还可以在更底层,也就是物理层进行,毕竟之前我们讲过如何在 OSI 上层协议栈中进行封装,例如 IP in IP, Ethernet over UDP 等。这进一步使得,我们把信息本身看作是容器,而且是信息的容器,信息本身它不光可以表达数量:诸如今天的最高气温预计是多少度、昨天的营业收入是多少,还可以作为容纳其它信息等容器,例如,用一组数字来容纳图像信息,用一组音频信息容纳文字信息等。
但是如果把 samples 以图像像素的方式保存,则在通过屏幕拍摄的方式进行跨设备传输时就需要注意:不同的屏幕和摄像头有不同的色准,这种差异会导致在传输过程中被迫引入大幅度的信号衰减和干涉。光,或者更加准确的说带颜色的光是多种不同波长的无线电波的混合(光具备波粒二象性),当我们说一个物体是什么颜色的,我们说的是这个物体反射到我们眼睛中的光的频域表示,简而言之「颜色」指光的频域表示,而在计算机中,因为存储空间有限,颜色无法被准确无误的存储记录,通常来说会用三组正交的基频谱的线性组合,来表示颜色,例如颜色的 R,G,B 编码,因为 R 红光、G 绿光、B 蓝光它们的频谱之间很少有重叠,所以它们的频谱可以看作是正交的(正交指你无法用一个表示另外一个),于是只用线性组合 x R + y B + z Z 就可以(近似地)表示绝大部分的光了,存储起来也就是三个数。而硬件差异会导致,在两台不同的显示器上,即便显示的都是相同的 RGB 坐标下的颜色,也会有肉眼可见的不一样,比如说在这个屏幕上 rgb(255,0,0) 和那个屏幕上 rgb(255, 0,0) 不是一回事,或者说你拿着手机对显示 rgb(255,0,0) 的屏幕拍照,照片上的像素值就未必是 rgb(255,0,0) 了,所以这就给通过图像传递调制信号带来了困难。
声音传输也有类似的问题,不同音频设备的音准可能不一样,比如说一台设备以 48000 采样率播放 2000 Hz 的正弦波,另一台录音设备以同样的采样率录制,它却分析出正弦波的频率是在 2100 或者 1950, 如果假设录音设备没有问题,那么这很有可能就表明播放声音的那台设备音准有一定的偏差。
另外,抛开信号在传播过程中遇到的干扰和衰减不谈,这种方式也不具备保密性,旁观者很容易察觉出这种播放起来怪怪的音频是经过了信号调制的,而常见的 digital over analog 调制方式就那么几种,别有用心的人可能会通过分析信号本身的特征来推测出解调方式。加密操作需要在协议栈的更上层进行。

近距离无线配置下发

数字信号调制到声音的这种方式,也可用于一些低端电子设备的配置,例如 WiFi 摄像头,一些低端型号的 WiFi 摄像头的 USB 插口只支持充电,而且出厂状态下没有默认的 WiFi 配置,而这类摄像头还恰好安装了拾音器,所以这时就可以通过播放经过了调制的声音,来实现引导配置的下发。例如,在出厂设置下,一个摄像头开机后可以“滴”三声,然后用户播放调制音频,然后摄像头根据纠错码来判断接受了多少位,接收到了足够的长度之后,响一次长的“滴”声,表示配置下发完成,而在用户对设备进行了物理重置后,这样的过程又可以来一遍。当然其实有更加优雅的办法,例如厂商约定 SD 卡根目录下的哪一个文件是配置文件,这样配置就可以通过复制文件批量下发了,比这种方式高效得多。
我们知道 USB 端口和 PS/2 端口常被用来连接计算机主机和外设,例如用来连接键盘、鼠标、打印机或者 U 盘等设备,但除此之外,假如说恰巧在一台主机上这些都没有,可是留有耳机和麦克风接口,那么还是可以通过把调制、解调音频(也就是声音波形信号)的代码写进驱动程序,使得耳麦接口也可以被用来和外部做数据交互。使用耳麦接口和计算机通信的设备不算多,例如一些银行的 U 盾算一个 (ICBC). 其实使用什么样的物理接口不重要,虽然说不一样的物理接口有不一样的采样率和延迟,但是要让它能用只需有对应的驱动程序就行。用声卡的耳麦口做数据交互接口,唯一的问题就是采样率有限,根据 Nyquist 采样定理,它的有效带宽也有限,而根据香农极限定理,带宽和信噪比共同决定了数据传输速率的上限。

调制解调器

在互联网诞生初期,由于大集团的垄断,以及铺设宽带网络高昂的物理成本,使得宽带上网并不是很常见。在那时,人们使用座机电话,和一个称为「调制解调器」的数模转换设备将电脑连接到互联网,在 OSI 参照模型中,调制解调器工作在物理层。当用户需要将计算机连接上网时,首先用座机电话拨打运营商的一个专用号码,接通后将听筒挂在调制解调器上面,调制解调器发出的声音信号(承载了电脑要发送的比特流)通过电话网络到达运营商的交换中心,而来自另一端的信号被还原成比特流交给计算机。调制调解器有的通过串口和计算机连接,用户需要自己在计算机上安装调制解调器的驱动程序才能让它正常工作。到了后期,更加先进的调制解调器可以直接连接电话线,不再需要通过外挂听筒的方式和电话网络连接,这种调制解调器可以根据程序设定自动拨号,用户也可以用电脑上的应用程序拨打电话和发送传真,不过这种方式拨打的电话并不属于 VoIP, 因为电脑仅仅是向调制解调器发送指令,使它以传统的方式通过有线电话网络拨打电话,也就是说让 modem 向电话线的另一头假扮自己是电话座机,而所谓的 VoIP 指的是互联网承载的语音电话 (Voice over Internet Protocol)。顺带说一句,所谓的传真机,也就是相当于扫描仪加调制解调器加打印机,传真机需要发送传真时,它扫描纸张形成数位图像(也就是图像的数字化表示),然后将数位图像调制进声音信号,再用电话网络拨发给接收端(智能一点的传真机是可以自动接听的),然后接收端传真机对声音信号进行解调还原出原先的数位图像再将图像打印出来,这就是传真机的一个收发过程。
后来,又出现了软件 modem, 这种软件 modem 配合主板上的板载 modem 或者声卡进行工作,使得数模转换的过程完全通过软件的方式来实现,大概也就是数模转换过程中的计算任务全部交给 CPU 来进行,这时电脑可以直接对外输出模拟信号,以及接受模拟信号作为输入,用户可以直接将电话线插在电脑主机的对应插口上,需要拨号时用鼠标进行点击(或者开机时自动拨号)。我个人认为,软件 modem 甚至可以算是最早的 NFV(网络功能虚拟化)的实践之一了,调制解调器可以算是很早就能被普通 PC 模拟的网络设备之一。
今天虽然用有线电话网络上网的这种方式已经很少见了,而且大概不再有运营商继续提供这样的服务,但是我们仍然可以在两台电脑上,通过运行 GNU Radio,以及用两部拨通电话的手机来复现当年拨号上网的场景。GNU Radio 内置一些 packet 处理模块、声音输入输出模块和 PSK 调制解调模块以及常用到的过滤器,理论上可以用于实现一个基于软件的 modem, 当它运行时,两台电脑通过这种方式互相发送 packets,你应当会听到电脑扬声器和手机听筒传来的吵闹又刺耳的声音,这就是调制了高波特率基带信号的载波的播放效果。
然而在以往用调制解调器拨号上网的年代,是可以直接用电脑打电话和发送传真的,毕竟电脑和调制解调器直接用串口相连,而调制解调器又是物理直连到有线电话网络上,现在用电脑打电话或者发传真都比较麻烦,不过有一定规模的企业用户仍然会向运营商开通相应的服务,运营商会给企业安装专门的设备使得它们能连入 PSTN 网络,比如一些客服平台就会需要这样的服务。
一开始的时候人们用模拟电话信号承载数字信号,如今人们基本上都已经用数字信号承载大多数东西,运营商的电话网络大多数都已经完成了 IP 化或者数字化,即便在接入网偶有模拟的信号,但是到了核心网之后就都是数字的了,数字的信号更能准确的放大和恢复,使得传输更加可靠,同时也方便进行分析、探测和审计,根据信号(流量)本身的特征来进行智能化的优先级调度,数字信号还可以进行压缩,使得频谱资源能被更高效地利用。从模拟承载数字,到数字承载一切,可以说是沧海桑田的变化呀。

总结与思考

在这篇文章中,我们简要介绍了一些数字信号的调制和解调的浅显知识,具体来说,我们通过一个「如何把符号串调制到音频信号中」的例子来贯彻、实践和演示这样的一个主题。
我们列举了一些调制方式,多花了一些篇幅来讲 PSK 调制和 I/Q 数据,然后,我们通过具体的 MATLAB 代码编写案例,来演示是如何一步一步从符号串得到音频(模拟)信号的。在次过程中,我们还介绍了 RRC 过滤器,探讨了 RRC 过滤器的参数如何选取,并且给出了一个示范。之后就开始了信号解调过程的演示,其实主要就是把调制的过程倒过来一遍,调制过程中涉及到上抽样的过程就进行下抽样,涉及到填充数据的过程,就把填充了的数据去掉,最后利用 preamble 检测功能检测 preamble 的位置进而推导出正文的位置,然后在正文出横向一对一比较发送符号串和接收符号串的元素,并计算误码率。
我们还介绍了 MATLAB 的一些简单音频处理功能,主要是一些面向对象风格的接口和函数,例如 audioplayer 和 audiorecorder, 使用专门的方法来操作这些对象,可以实现音频的播放和录制。以及还提到了 MATLAB 的波形文件读/写功能。
其实还可以在发送的比特流中嵌入纠错码 (ECC), 这样做能够以码率作为代价,换取更大的稳健性。ECC 的作用是:1)检测错误,也就是消息完整性的判断;2)在一定的范围内纠正错误。ECC 通常被用于下列场合:1)检测消息完整性是必须的;2)请求重传的成本很高或者不可能,例如在单向的信道中。

再谈 SDR

SDR 即 Software Defined Radio, 通常翻译为「软件定义无线电」或者软件无线电,它指的是把传统上由硬件实现的信号处理功能交给通用个人计算机的软件来做的这样的一种理念,软件定义的东西还有很多,比如说有软件定义计算、软件定义存储、软件定义网络等,都有一个共性就是尝试用软件来模拟硬件,并且都是在计算机处理能力大幅提升的世代兴起的。这也说明得益于计算机算力的普遍提升,我们可以用软件来做比以往多的多的事情。
在 SDR 的理念下,无线电设备的软、硬件应当是解耦的,通常来说硬件负责承载最基础的信号收发/采样/模数转换/调谐等功能,而信号处理(包括调制解调)等功能都交给软件。在这样的软硬件分离的架构下,用户可以在不更改硬件的前提下对系统进行扩展,例如,假如说用户需要接收和解码一种新制式的信号,那么他可以首先在互联网上搜索看是否已有符合的软件模块,实在不行可以自己编写一个。
SDR 可以更方便地支持复杂的高阶调制解调制式,可以使我们能够使用通用个人计算机来收发广播电台、电报、数字电视,可以从国际空间站接收图片或者与远方的国际友人进行通联等。并且相比于传统的硬件电台,软件实现的电台还支持对信号或者音频进行录制,支持通过 IP 网络转发 I/Q 采样数据,SDR 的功能一般都比较丰富。
同样也有很多软件,例如 MATLAB, Simulink, GNU Radio 等等,这些软件可以让我们进行各种信号处理实验和信号传输模拟,而不需要真的向空中发射无线电波,我觉得这也算是软件定义无线电的一种具体形式吧,而且你甚至可以不用天线或者无线电设备,直接通过互联网接收遥远距离的 I/Q 采样数据并且以自己的方式进行解码和分析。从这个方面来看,SDR 的具体形式可以是多种多样的。

无线电网络

如果我们把频段看作是地址,那么空间中传播的无线电波可以看作是一个无线电网络的构成(或者连接),这样的一个网络的独特之处,或者说它和其它互联网络的不同之处在于,它的地址是真实的物理地址,而不是人为定义的。
一言以蔽之,无线电网络的节点之间以真实的物理地址通信。
与之相比,互联网络上存在着许多人为定义的,非物理层面的地址,例如:IP 地址,它只是 packet 头部中的一些字节串,URL 地址更是人为定义的了。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK