18 / 11 / 03
本文翻译自Let’s hack it -GOES Satellite Hunt (Part 2 – Demodulator), 仅供学习参考。
首先,我们已经成功从GOES卫星得到了LRIT信号。解调的第一步为使用SDR(Software Defined Radio),例如Airspy或RTLSDR,将基带信号转化为符号流。这里我们将使用GNU Radio来制作我们的解调器。
由参数表可知,LRIT信号使用BPSK(Binary Phase Shift Keying,双相相移键控)调制,符号率为293883符号/秒。我们面临的第一个问题是,BPSK调制是如何工作的?
首先,我们有两个信号:载波和我们想要发送的二进制流。载波是一个正弦信号,它将沿路径传送调制数据。它的频率通常y远高于比特率或符号率。在BPSK调制中,我们基本上是通过改变信号的相位来达到调制的目的。由于它是二进制相移,我们基本上只反转信号的相位,即反转信号的局部极性。可以在下图中看到,当我们改变极性时,我们会在载波上产生“陷波”。这就是使用二进制相移调制比特到载波中的方法。改变相位的优点是我们只需要在接收端中得到相同的相位,而不需要很高的信号幅值。
BPSK调制
因此:
其实很简单对吧?所以我们可以在相位图中表示出我们的数据。以下是一些PSK调制的相位图(BPSK,QPSK,8-PSK)
因此对于相位图中的BPSK,我们基本上有:
仅供参考,在**QPSK(b)**中我们将使用_格雷编码_的二进制值:00,01,11,10。
这同样适用于8-PSK,但8-PSK使用3比特来组成数据。通常二进制代码是从0开始的_格雷码_并逆时针增加。
那么,反向进行这一调制过程,也就是我们将要研究的解调信号方法。
首先,我们需要确保我们的载波位于基带(频谱中心)。你可能认为只需在1691MHz进行调谐即可确保这一点,但我们需要考虑以下因素:
怎么办呢?我们需要找到信号的真实位置,并转换到基带(频谱中心)。但是我们遇到了一个问题:
LRIT信号实际上并不包含载波,只包含调制信号(因此它的信号是调制载波的结果)。
如果信号中包含载波,我们可以使用简单的PLL(锁相环)来跟踪载波并转换信号。但是由于载波会消耗传输功率,为了达到跟踪信号的目的,来自卫星的传输功率将被“浪费”在信号中。对于像LRIT这样的无载波信号,我们需要使用更复杂的PLL系统。有几种系统可以做到这一点,但我将使用Trango在_#hearsat_中建议的著名的Costas循环。
Costas Loop Basic Diagram
Costas Loop基本上是一个双PLL系统。主要思想是:如果我们假设的载波没有居中,则会出现相位误差,该误差会在正交信号中累积。使用此误差可以将我们的信号转换为基带信号。关于Costas循环如何工作的实际细节将不在这里讨论,因为有很多文档解释它的工作原理。对于BPSK,我们将使用二阶Costas循环,同时还存在对于QPSK的3阶Costas循环和8-PSK的4阶Costas循环。
好的,在将信号移到基带之后,我们需要恢复第二个信息:符号时钟。问题是:我们在载波上调制了一个二进制信号,但如果我们有一个10位的字符串,我们怎么知道字符串有10位,而不是5位或7位?我们需要使用**M&M(Mueller和Müller)**算法恢复原始时钟。
为什么我们可以恢复原始数据时钟?
因为我们有两个信息:
我们怎样才能用这些信息恢复?很简单,我们可以用估计的符号率做一个振荡器(时钟发生器),并与一个载波相位转换同步,之后我们可以认为找到了一个同步的时钟。另外由于数据的随机化(将在本文中进一步展示),二进制数据几乎是随机的,这意味着我们将具有基本相同的几率来获得位1或0.这将使我们的时钟同步随着时间推移更加一致。此外,M&M算法还做了一个额外的事情,即时钟频率校正。由于符号率只是一个估计假设,这意味着其取值可能会随着时间的推移而变化,M&M算法可以为我们纠正。
恢复时钟之后,在IQ Sample的I向量中我们将会得到信号对应的符号。
让我们打开GNU Radio并构建我们的解调器。在GNU Radio中,我们将在Costas Loop和M&M Recovery中增加一些额外的块。本设计中参数的值适用于Airspy R2 / Mini,但我很快也会为RTLSDR提供一个版本(仅在第一步中取不同的值)。正如Trango在_#hearsat_中所建议的那样,为了避免USB数据包丢失,最好在airspy中使用较低采样率(说实话我使用时从来没有任何USB数据包丢失,但这将在很大程度上取决于CPU和你的USB控制器。所以我们选择采样率时谨慎一些比较好。对于Airspy R2,我们将使用2.5Msps,而对于Airspy mini,我们可以使用3Msps。我们整个过程的目标采样率是1.25Msps(实际上我们可以使用接近该值的任何值)。那么让我们从osmocom块开始:
Osmocom Source
让我们将采样率设置为3e6,将中心频率设置为1691e6,将所有增益设置为15.对于增益设置,您可以尝试自己的值,但我发现当所有参数使用最大值时我获得了最佳信噪比(这种情况不是很常见)。另外Osmocom Source对于airspy有一个“bug”,就是它没有得到混音器增益可用(因为它不是BB增益,这是愚蠢的)。我做了一个补丁(由于Gain名称而被主动拒绝)将混音器增益映射到BB增益(就像RTLSDR一样)。在将来,我可能会使用正确的名称编写一个新的GRC块与airspy一起使用,不过现在你可以从源代码编译: [gr-osmosdr](https://github.com/racerxdl/gr- osmosdr) 使用我的fork。
下一步是抽取达到2.5e6的采样率。对于airspy mini,在3e6采样率时,为15/18。因此,让我们创建一个Rational Resampler块,并将15作为插值,将18作为抽取值。Taps可以为空,因为GNU Radio会自动生成。这不是最理想的,但至少现在可以使用。我将在未来为每个SDR发布更好的版本。
现在我们有2.5 Msps,我们需要将抽样率降低为二分之一。但我们也会将输入通过低通滤波器以达到我们的抽样率。因此,让我们创建一个低通滤波器,其抽取值为2,采样率为2.5e6,截止频率为symbol_rate * 2(即587766),过渡宽度为50e3。
在这一步之后采样率将变为1.25e6
为了获得更好的性能,无论输入信号如何,我们都应将信号保持在恒定水平。为此,我们将使用自动增益控制,它将执行软件增益(基本上只是将信号以乘法的方式放大),不会改变分辨率(因此它不会输出更好的信号),但能够保持我们的电平不变。我们可以使用GNU Radio的AGC模块。
速率为10e-3,参考值为0.5,增益为0.5,最大增益为4000。
另一步是RRC滤波器(根余弦滤波器)。这是针对nPSK调制优化的滤波器,并将其用作符号率的参数。滤波器不是很难生成(它是一个具有一些特定Taps的FIR),但幸运的是GNU Radio为我们提供了一个模块。
对于RRC滤波器的参数,我们将使用1.25e6的采样率,符号率293883,Alpha=0.5,Num Taps=361。Alpha和符号率取自LRIT的参数。Taps的取值可以自由尝试,但我发现取361时能达到质量与性能之间的良好平衡。在滤波之后,我们应该能得到只包含BPSK调制信号(或同一频段的噪声)的信号。然后我们可以进入同步和时钟恢复的步骤。
正如我之前所说,我们将使用二阶Costas循环作为载波恢复(同步)和M&M时钟恢复算法来恢复符号时钟。 GNU Radio为这两种算法都提供了对应的模块。让我们从Costas Loop开始吧。
Costas Loop
对于Costas loop的参数,我们只需要设置循环带宽为0.00199,阶数为2。之后我们应该在基带中拥有我们的虚拟载波。现在我们只需要使用M&M时钟恢复算法将我们的样本与时钟同步。
M&M Clock Recovery
对于M&M算法的参数,我们将Omega取4.25339,这基本上是每个采样率上传输的符号数量,或sample_rate / symbol_rate。这是对M&M算法的第一个象征性猜测。对于Gain Omega,我们使用**(alpha ^ 2)/ 4**,即alpha = 3.7e-3,所以我们的增益Omega为3.4225e-6,Mu为0.5,增益Mu为alpha(或3.7e-3),Omega Relative限制为5e-3。
所以你可以注意到我在M&M算法中调用了一个新的参数alpha,它不是模块的直接参数。该alpha是一个参数,用于调整M&M时钟恢复算法偏离初始猜测的程度。你可以尝试使用自己的值,但3.7e-3对我来说是最好的选择。
现在,在M&M的输出中,我们将以正确的速率抽出复符号。现在我们只需要提取其中的数值。
所以我们现在可以直接将所得到的信号映射得到二进制数据,但由于我将在文章的下一部分中解释的原因,我将保持符号不变,仅把他们转换为字节。所以基本上我们GNU Radio的输出将是一个有符号的字节,范围-128到127之间变化,-128为100%几率为0,127为100%有机会为1位。中间值为相应的机会。基本上我将有一个字节代表一个位为0或1的概率。我将在本文的下一部分解释更多。
现在我们需要做的是从M&M模块获取Complex输出,只获得Real Part(Component I)转换为byte并输出(输出至文件或TCP管道)。
这是一个使用两个模块的简单操作。首先,我们使用Complex to Real块,它将使用复数的Real组件输出Float,然后转换为char乘以127(因为Complex已标准化)。之后,我们可以使用File Sink输出到文件或创建矢量流以输出到TCP Socket。我将使用TCP套接字。
在通过TCP发送之前,Stream to Vector只聚合每16个字节。这将减少TCP数据包开销。 TCP Sink参数为:
使用此参数,当我们运行GRC流程时,它将尝试通过端口5000连接到localhost,并通过TCP发送每组16个字节(或者你也可以选择发送符号)。本文的下一部分将讨论解码此问题的软件部分,并生成用于创建输出文件的数据包。您可以在下面查看我的最终流程图。它还有一些其他块可以更灵活地更改参数,还可以显示输入信号的瀑布/fft图以及星座图。
GRC源文件下载地址