无线电自学教程 05:第一台收音机——从矿石机到 SDR

系列导航 :目录 | 上一章:时域与频域 | 下一章:业余无线电入门(编写中)

本章目标:读完本章,你将能够

  • 说出一台接收机必须完成的几件事:选择、解调、还原;
  • 推导二极管包络检波器时间常数的上下限,解释"对角切割失真"和纹波;
  • 理解真实二极管的平方律检波,说清楚矿石机为什么要用锗二极管;
  • 动手做一台不用电池的矿石收音机,并估算它从电波里能得到多大的电压;
  • 理解软件无线电(SDR)的基本结构,装好 RTL-SDR 并收听 FM 广播;
  • 用 Python 实现 FM 广播的鉴相解调和去加重,解码一条真实格式的 ADS-B 飞机报文。

前置知识:第 01 章的 AM 与平方反比、第 03 章的 RC 电路和调谐回路、第 04 章的频谱与 FFT。

前四章我们准备了足够多的理论,现在该听到声音了。

本章分两半。前一半回到一百年前,做一台矿石收音机 :一个线圈、一个可变电容、一个二极管、一副耳机,不用电池,靠电台发来的电波本身的能量就能发声。它是理解"接收机到底在做什么"的最好起点。后一半跳到今天,用一个两百元左右、U 盘大小的 RTL-SDR 接收器,把一大段频谱直接变成数字,交给电脑和 Python 处理。我们会用 Python 解调 FM 广播,再解码一条飞机发出的 ADS-B 报文。

一、接收机要做的三件事

图 1:一台最简单的 AM 接收机。上:功能方框;下:每一级的信号波形。

不论简单还是复杂,接收机都要完成三件事:

  1. 选择:天线收到的是所有电台信号的叠加(图 1 左下)。接收机要从中选出一个。矿石机靠第 03 章的 LC 调谐回路,现代接收机靠超外差(第 15 章)或数字滤波(第 20 章);
  2. 解调 :选出的信号是已调载波,信息藏在它的幅度、频率或相位里。接收机要把信息取出来。对 AM 来说,就是取出包络;
  3. 还原:把解调出的信号变成人能感知的东西,比如声音、图像或文字。

真正的接收机通常还要做第四件事:放大。矿石机没有任何放大,所以只能收听强信号。

二、包络检波

1. 工作原理

AM 信号的信息在包络里,所以只要能"描出"包络就行。最简单的电路是一个二极管加一个 RC 并联网络:

图 2:左:二极管包络检波器。中:检波输出跟随 AM 信号的包络。右:放大看,每个载波周期的峰值附近二极管导通、给电容充电,其余时间电容经电阻放电。

  • 输入电压高于电容电压时,二极管导通,电容迅速充电到输入的峰值;
  • 输入电压低于电容电压时,二极管截止,电容经电阻 R 缓慢放电,时间常数 τ=RC\tau = RC τ=RC。

于是电容电压"骑"在每个载波周期的峰值上,大致描出了包络,只是带有锯齿状的纹波。

2. 时间常数的上下限

RC 的选择有两个相互矛盾的要求。

**下限:要远大于载波周期。**否则电容在两个载波峰之间就放电完了,输出跟着载波起落,纹波很大。所以要求 RC≫1/fcRC \gg 1/f_c RC≫1/fc。

**上限:放电要跟得上包络的下降。**包络 1+μcos⁡ωmt1 + \mu\cos\omega_m t 1+μcosωmt 下降时,电容电压必须至少以同样快的速度下降,否则输出会脱离包络,变成一段指数曲线。电容放电的速率是 v/RCv/RC v/RC,包络下降的速率是 μωmsin⁡ωmt \mu\omega_m\sin\omega_m t μωmsinωmt,要求在任何时刻
1+μcos⁡θRC ≥μ ωmsin⁡θ,θ=ωmt \frac{1 + \mu\cos\theta}{RC} \ge \mu\,\omega_m\sin\theta,\qquad \theta = \omega_m t RC1+μcosθ≥μωmsinθ,θ=ωmt

即 RC≤ 1+μcos⁡θ μ ωmsin⁡θ RC \le \dfrac{1 + \mu\cos\theta}{\mu\,\omega_m\sin\theta} RC≤μωmsinθ1+μcosθ 对所有 θ\theta θ 成立。对 θ\theta θ 求右边的最小值:令导数为零,得到 cos⁡θ=−μ\cos\theta = -\mu cosθ=−μ,代回去,
RC≤ 1−μ2 μ ωm \boxed{\ RC \le \frac{\sqrt{1 - \mu^2}}{\mu\,\omega_m}\ } RC≤μωm1−μ2

违反这个条件产生的失真称为对角切割失真。调制度越深、音频频率越高,上限越严格。

图 3:三种时间常数下的检波输出(载波 100 kHz,音频 1 kHz,调制度 0.8)。

以载波 100 kHz(为了仿真方便,按比例缩小)、音频 1 kHz、调制度 0.8 为例,上限是 1−0.64 /(0.8×2π×1000)=119.4 \sqrt{1 - 0.64}/(0.8\times2\pi\times1000) = 119.4 1−0.64 /(0.8×2π×1000)=119.4 μs,载波周期是 10 μs。附录代码用理想二极管模型逐点仿真了三种时间常数:

RC 音频幅度(理想 0.8) 总谐波失真 100 kHz 纹波幅度
3 μs(太小) 0.373 1.00 % 0.3907
100 μs(合适) 0.764 0.03 % 0.0302
1 ms(太大) 0.375 36.21 % 0.0043

RC 太小时纹波很大,输出幅度只有一半;RC 太大时纹波最小,但对角切割让失真高达 36%;100 μs 时两者兼顾。剩下的纹波频率很高(100 kHz),耳朵听不到,耳机也跟不上,所以无关紧要。

3. 真实的二极管:小信号时是平方律

上面的仿真假设二极管是理想开关。真实二极管的电流由肖克利方程描述:
i=Is(e v/(nVT) −1) i = I_s\left(e^{v/(nV_T)} - 1\right) i=Is(ev/(nVT)−1)

其中 VT=kT/q≈25.85 V_T = kT/q \approx 25.85 VT=kT/q≈25.85 mV 是热电压(300 K), Is I_s Is 是反向饱和电流, nn n 是理想因子。当输入 Acos⁡ωtA\cos\omega t Acosωt 加在二极管和负载 RR R 上,稳态时检波输出是一个直流电压 VV V(这里只看载波,不加调制)。在一个载波周期内,二极管的平均电流必须等于流过负载的电流 V/RV/R V/R:
Is 12π ∫02π e(Acos⁡θ−V)/(nVT) dθ−1 =VR I_s\left\\frac{1}{2\\pi}\\int_0\^{2\\pi} e\^{(A\\cos\\theta - V)/(nV_T)}\\,d\\theta - 1\\right = \frac{V}{R} Is2π1∫02πe(Acosθ−V)/(nVT)dθ−1=RV

括号里的积分恰好是零阶修正贝塞尔函数 I0 ⁣ ( A nVT ) I_0\!\left(\frac{A}{nV_T}\right) I0(nVTA) 乘以 e−V/(nVT)e^{-V/(nV_T)} e−V/(nVT)。附录代码用二分法解出 VV V。小信号时 I0(x)≈1+x2/4 I_0(x) \approx 1 + x^2/4 I0(x)≈1+x2/4,可以推出 V∝A2V \propto A^2 V∝A2:输出与输入振幅的平方成正比 ,这叫平方律检波 。只有信号足够大时,输出才接近理想的 V≈AV \approx A V≈A。

图 4:两种二极管的检波特性(负载 100 kΩ,双对数坐标)。

附录代码对比了两种二极管模型( Is I_s Is 取数量级上的典型值):

输入振幅 A 低阈值二极管( Is=10−7 I_s = 10^{-7} Is=10−7 A) 普通硅二极管( Is=10−12 I_s = 10^{-12} Is=10−12 A)
10 mV 0.270 mV 3.8×10−63.8\times10^{-6} 3.8×10−6 mV
30 mV 2.43 mV 3.7×10−53.7\times10^{-5} 3.7×10−5 mV
100 mV 26.4 mV 9.1×10−49.1\times10^{-4} 9.1×10−4 mV
300 mV 170 mV 1.24 mV
1000 mV 815 mV 529 mV

Is I_s Is 大的二极管(锗管、肖特基管)在几十毫伏时就能检出可用的信号;普通硅管在 300 mV 以下几乎没有输出,这就是人们常说的硅管"导通电压高"。矿石机收到的信号只有几十到几百毫伏,所以必须用锗二极管。

平方律检波还有一个代价:对 AM 信号 (1+μcos⁡ωmt)(1 + \mu\cos\omega_m t) (1+μcosωmt) 取平方,会产生二倍音频的分量。附录代码算出 μ=1\mu = 1 μ=1 时二次谐波失真为 25.0%,理论值是 μ/4\mu/4 μ/4。所以弱信号在矿石机里听起来会有些失真。

4. 数字包络检波

在软件无线电里,包络检波变得异常简单。SDR 给出的是复基带信号 I+jQI + jQ I+jQ(第 19 章详细讲),AM 信号的包络就是它的模:
m^(t)=∣I(t)+jQ(t)∣= I2+Q2 \hat m(t) = |I(t) + jQ(t)| = \sqrt{I^2 + Q^2} m^(t)=∣I(t)+jQ(t)∣=I2+Q2

附录代码对任意载波相位的复基带 AM 信号直接取模,得到的音频幅度正好是 0.800,总谐波失真只有 7.7×10−147.7\times10^{-14} 7.7×10−14%,也就是计算机的舍入误差。没有纹波,也没有时间常数的取舍,因为复基带信号里已经没有载波了。

三、动手做一台矿石收音机

1. 电路

图 5:矿石收音机电路。左半部分是第 03 章设计的调谐回路,右半部分是本章的包络检波器。

元件 规格 作用
天线 10~30 m 的导线,尽量高、尽量远离房屋 接收电波
地线 接地棒,或者自来水管(金属管) 为天线电流提供回路
线圈 L 240 μH:直径 50 mm 的骨架上用 0.3 mm 漆包线密绕 67 匝(第 03 章) 与 C 组成调谐回路
可变电容 C 单联,约 15~365 pF 调谐,覆盖 530~1600 kHz
二极管 锗二极管,如 1N34A、2AP9 检波
电容 1 nF 与 100 kΩ 组成 RC = 100 μs 的检波负载,同时旁路载波
电阻 100 kΩ 电容的放电通路
耳机 高阻耳机(晶体耳机) 发声

几点说明:

  • 为什么要 100 kΩ 电阻 :晶体耳机本身几乎就是一个电容,没有直流通路。如果不并联电阻,检波电容充上电后无处放电,几秒钟后就没声音了。 RC=100 kΩ×1 nF=100RC = 100\ \mathrm{k\Omega}\times1\ \mathrm{nF} = 100 RC=100 kΩ×1 nF=100 μs,正好满足第二节的条件;
  • 为什么要高阻耳机:普通的 32 Ω 耳机需要毫瓦级的功率才能听到声音,而且会严重加重调谐回路的负载,让 Q 值大大下降,选择性变差。晶体耳机几乎不消耗功率;
  • 安全:天线不要靠近任何电力线;雷雨天气要把天线断开并接地;地线不能接燃气管道。

2. 能量从哪里来

矿石机没有电池,耳机里的每一点声音都来自电台发出的电波。能有多少呢?

由第 01 章,各向同性辐射的功率密度 S=PtG/(4πd2) S = P_tG/(4\pi d^2) S=PtG/(4πd2),又 S=E2/η0 S = E^2/\eta_0 S=E2/η0, η0≈120π \eta_0 \approx 120\pi η0≈120π,所以电场强度
E= η0PtG 4πd2 = 30 Pt G d E = \sqrt{\frac{\eta_0 P_t G}{4\pi d^2}} = \frac{\sqrt{30\,P_t\,G}}{d} E=4πd2η0PtG =d30PtG

附录代码以 50 km 外一座 100 kW 的中波电台为例(按自由空间粗略估算, G=1G = 1 G=1): E=34.6E = 34.6 E=34.6 mV/m。天线的开路电压近似等于场强乘以天线的有效高度 ,对于一根挂在十来米高处的导线,有效高度取 5 m,开路电压约 173 mV。

这个电压再经过调谐回路(谐振时有一定的电压增益)送到二极管上,正好落在图 4 中锗二极管能工作、硅管几乎不工作的区间。这也说明了为什么矿石机需要一根又长又高的天线:天线越高,有效高度越大,收到的电压越高。

需要说明的是,中波主要靠地波传播,经过地面时有额外的损耗,实际场强通常低于自由空间的估算;离电台越近,效果越好。在大城市里,附近的大功率电台很容易收到;在偏远地区,晚上靠天波可能听到远方的电台。

3. 矿石机的局限

  • 没有放大:只能听强台;
  • 选择性差:只有一个调谐回路,而且天线和耳机都会降低它的 Q。第 03 章练习 5 算过,Q = 150 的回路对相邻 10 kHz 的电台只衰减 2 dB 多。强台常常会"盖住"整个波段;
  • 只能收 AM。

这些局限,正是后面几章要一一解决的问题:放大器(第 16 章)、超外差接收机(第 15 章)、FM 与 SSB 的解调(第 12、13 章)。不过,我们也可以绕过这些模拟电路,直接走到最前沿:软件无线电。

四、软件无线电

1. 把一段频谱变成数字

图 6:RTL-SDR 的结构。硬件只负责把一段频谱搬下来、变成数字,剩下的工作全部由电脑完成。

软件无线电(Software Defined Radio,SDR)的思想是:让硬件只做最少的事,即放大、把一段频谱搬到低频、数字化,然后把数字样本交给电脑;选择、解调、解码全部用软件完成。想收 FM 就运行 FM 解调程序,想收飞机信号就运行 ADS-B 解码程序,硬件完全不用改。

RTL-SDR 本来是一种 DVB-T 数字电视接收棒。2012 年前后,爱好者们发现它的芯片 RTL2832U 有一个可以直接输出原始 IQ 样本的模式,于是开源社区为它写了驱动程序。从此,花两百元左右就能拥有一台覆盖几十 MHz 到上千 MHz 的宽带接收机。

2. 关键参数

参数 数值 含义
频率范围 约 24~1766 MHz(R820T2 调谐器) 覆盖 FM 广播、航空波段、业余 2 米和 70 厘米波段、ADS-B 等。部分改进型号(如 RTL-SDR Blog V3、V4)可通过直接采样或内置上变频器接收 HF
ADC 8 位 理想信噪比 6.02×8+1.76=49.96.02\times8 + 1.76 = 49.9 6.02×8+1.76=49.9 dB,动态范围有限,强信号附近容易过载(第 18 章)
采样率 稳定工作约 2.4 MS/s(复数) 一次能看到 2.4 MHz 宽的频谱
数据率 2.4 MS/s × 2 字节 = 4.8 MB/s 经 USB 2.0 传给电脑
FFT 分辨率 2.4 MS/s ÷ 2048 = 1171.9 Hz 用 2048 点 FFT 画瀑布图时每个频点的宽度(第 04 章)

3. 安装与第一次开机

  1. 驱动 :Windows 上用 Zadig 工具把接收棒的驱动换成 WinUSB;Linux 上安装 rtl-sdr 软件包;
  2. 软件:推荐跨平台的 SDR++,Windows 上也可以用 SDR#,Linux 和 macOS 上可以用 GQRX;
  3. 天线:接收棒附带的拉杆天线就能用。收 FM 广播时把两根振子各拉到约 75 cm(100 MHz 的 λ/4,第 01 章),水平或竖直放置都试一试;
  4. 收听 FM 广播:把频率调到 87~108 MHz 之间,解调方式选 WFM(宽带调频),带宽约 200 kHz。在瀑布图上,每个电台是一条约 200 kHz 宽的亮带;
  5. 调增益:8 位 ADC 很容易过载。增益太高时,瀑布图上会冒出很多原本不存在的"假信号",这时要把增益降下来。

收听业余 2 米波段的中继台(144~148 MHz,NFM 窄带调频),以及航空波段(118~137 MHz,AM),步骤类似,只是解调方式不同。请遵守无线电管理规定,不要录制、传播收听到的非公开通信内容。

五、用 Python 解调 FM 广播

现在我们用 Python 实现 SDR 软件里的 WFM 解调器。为了让没有硬件的读者也能运行,附录代码先仿真一个 FM 广播信号,再对它进行解调;最后给出接上 RTL-SDR 时需要替换的几行代码。

1. FM 广播的参数

参数 数值
频段 87~108 MHz
最大频偏 ±75 kHz
音频带宽 15 kHz
预加重时间常数 50 μs(我国和欧洲;美国为 75 μs)
卡森带宽 2(Δf+fm)2(\Delta f + f_m) 2(Δf+fm) 2×(75+15)=1802\times(75 + 15) = 180 2×(75+15)=180 kHz

仿真中的音频是两个单音:1 kHz(幅度 0.6)和 6 kHz(幅度 0.15)。复基带采样率取 240 kHz。

2. 鉴相解调

FM 信号的复基带形式是 x(t)=ejφ(t)x(t) = e^{j\varphi(t)} x(t)=ejφ(t),其中相位 φ(t)=2πΔf∫m(τ) dτ\varphi(t) = 2\pi\Delta f\int m(\tau)\,d\tau φ(t)=2πΔf∫m(τ)dτ。瞬时频率是相位的导数:
fi(t)= 12π dφdt =Δf⋅m(t) f_i(t) = \frac{1}{2\pi}\frac{d\varphi}{dt} = \Delta f\cdot m(t) fi(t)=2π1dtdφ=Δf⋅m(t)

数字实现时,用相邻两个样本的相位差代替导数。一个巧妙的写法是:
Δφn=arg⁡(xn x∗n−1),m^n= Δφn fs2πΔf \Delta\varphin = \arg\left(xn\,x^*n-1\right),\qquad \hat mn = \frac{\Delta\varphin\,f_s}{2\pi\Delta f} Δφn=arg(xnx∗n−1),m^n=2πΔfΔφnfs

xnx∗n−1xnx^*n-1 xnx∗n−1 的辐角正好是两个样本的相位之差(第 02 章:复数相乘,辐角相加;乘以共轭,辐角相减),而且结果自动落在 (−π,π](-\pi, \pi] (−π,π] 内,不用担心相位卷绕。前提是相邻样本间的相位差不能超过 π\pi π,所以采样率必须足够高(练习 6)。

3. 预加重与去加重

FM 解调后的噪声有一个特点:频率越高,噪声越大(第 13 章会推导出噪声幅度谱随频率线性上升)。而音乐和话音的能量主要在低频,高频分量本来就弱,于是高频部分的信噪比特别差。

对策是:发射端先把高频提升 (预加重 ),接收端再把高频衰减 回来(去加重 )。信号经过两次处理后恢复原状,而接收机引入的高频噪声只经过了去加重,被压低了。去加重就是第 03 章的 RC 低通滤波器,时间常数 50 μs,转折频率 1/(2π×50 μs)=31831/(2\pi\times50\ \mu\mathrm{s}) = 3183 1/(2π×50 μs)=3183 Hz。数字实现是一个单极点滤波器:
yn=a yn−1+(1−a) xn,a=e −1/(fsτ) yn = a\,yn-1 + (1-a)\,xn,\qquad a = e^{-1/(f_s\tau)} yn=ayn−1+(1−a)xn,a=e−1/(fsτ)

4. 仿真结果

图 7:左:接收到的 FM 信号的频谱(载噪比 20 dB)。右:解调后的音频频谱,红色为不用预加重和去加重,蓝色为使用。

附录代码在载噪比 20 dB 的条件下,分别不用和使用预加重/去加重,解调出音频,再低通滤波到 15 kHz、抽取到 48 kHz 采样率:

不用预加重 / 去加重 使用预加重 / 去加重
调制信号峰值(满偏 = 1) 0.750 0.944
解调出的 1 kHz 幅度(原 0.6) 0.5999 0.5999
解调出的 6 kHz 幅度(原 0.15) 0.1500 0.1500
音频信噪比 44.6 dB 54.3 dB

两种方式都准确还原了两个单音的幅度,而预加重/去加重把信噪比提高了约 10 dB。代价是预加重让调制信号的峰值变大(0.750 → 0.944),广播电台要相应控制高频的音量,以免超出允许的频偏。

附录代码还测出这个 FM 信号的 99% 功率带宽为 144.0 kHz,比卡森公式 2×(75+6)=1622\times(75 + 6) = 162 2×(75+6)=162 kHz 略窄。卡森公式只是一个保守的估计,第 13 章会用贝塞尔函数精确计算 FM 的频谱。

5. 接上真实的 RTL-SDR

如果你有 RTL-SDR,安装 Python 库 pyrtlsdr(python -m pip install pyrtlsdr),把附录代码中生成仿真信号的部分换成从接收棒读取样本即可。下面是一个最小的例子,它把解调出的音频保存成 WAV 文件:

python 复制代码
import wave
import numpy as np
from rtlsdr import RtlSdr

FS, DEV, TAU = 240e3, 75e3, 50e-6
sdr = RtlSdr()
sdr.sample_rate = FS              # RTL-SDR 支持 225~300 kS/s 和 0.9~3.2 MS/s 两个区间
sdr.center_freq = 97.4e6          # 改成你所在地区一个 FM 电台的频率
sdr.gain = "auto"
x = sdr.read_samples(int(FS * 5))   # 读 5 秒
sdr.close()

d = np.angle(x[1:] * np.conj(x[:-1])) * FS / (2 * np.pi * DEV)     # 鉴相
a = np.exp(-1 / (FS * TAU))
y = np.empty_like(d)
acc = 0.0
for i, v in enumerate(d):                                          # 去加重
    acc = a * acc + (1 - a) * v
    y[i] = acc
k = np.arange(101) - 50
h = 2 * 15e3 / FS * np.sinc(2 * 15e3 / FS * k) * np.hanning(101)
audio = np.convolve(y, h / h.sum(), mode="same")[::5]              # 低通并抽取到 48 kHz
pcm = (np.clip(audio / np.abs(audio).max(), -1, 1) * 32767 * 0.8).astype(np.int16)
with wave.open("fm.wav", "wb") as w:
    w.setnchannels(1)
    w.setsampwidth(2)
    w.setframerate(48000)
    w.writeframes(pcm.tobytes())

说明 :这段代码需要接上 RTL-SDR 才能运行。编写本文时手边没有硬件,它没有经过实际运行,只是附录代码中已验证的解调算法加上 pyrtlsdr 的读取接口。如果遇到问题,可以先检查驱动是否装好、center_freq 是不是本地真实存在的电台频率。

六、解码一条 ADS-B 报文

1. 什么是 ADS-B

民航飞机通过 ADS-B(广播式自动相关监视)在 1090 MHz 上不停地广播自己的身份、位置、高度和速度,每秒好几次。这些信号没有加密,任何人都可以接收,用 RTL-SDR 加上 dump1090 之类的软件,就能在地图上看到周围几百公里内的飞机。

1090 MHz 的波长是 27.5 cm,一根 6.9 cm 长的 λ/4 竖直天线(第 01 章)就能收得很好。飞机在万米高空,无线电视距很远(练习 8),接收距离常常超过 300 km。

2. 信号格式

ADS-B 用脉冲位置调制(PPM),速率 1 Mbit/s:

  • 前导码:8 μs,在 0、1.0、3.5、4.5 μs 处各有一个 0.5 μs 的脉冲,用来让接收机发现报文的开头;
  • 数据位:每位 1 μs,脉冲在前半个微秒表示 1,在后半个微秒表示 0。

扩展电文(DF17)共 112 位:

字段 位数 内容
DF 5 下行格式,17 表示 ADS-B
CA 3 应答机能力
ICAO 24 飞机的全球唯一地址
ME 56 消息内容:前 5 位是类型码 TC,1~4 表示身份识别(航班号),9~18 表示空中位置......
PI 24 CRC-24 校验

图 8:一条 ADS-B 报文的开头。灰线是发射的脉冲,蓝色柱是 2 MS/s 采样得到的幅度(噪声标准差 0.2),上方的 0/1 是逐位判决的结果。

3. 解码过程

附录代码使用的报文是 ADS-B 解码教材中常用的一个例子:8D4840D6202CC371C32CE0576098。代码先把它变成 PPM 波形,加上噪声,然后像真正的接收机一样处理:

  1. 找前导码:用前导码的波形模板和接收信号做相关,相关峰的位置就是报文的开头;
  2. 判决每一位:比较每一位前半和后半两个采样的大小;
  3. 校验 CRC :把 112 位当作一个二进制多项式,除以生成多项式 0x1FFF4090x1FFF409 0x1FFF409(25 位,模 2 运算),余数为零表示报文正确(第 26 章会讲 CRC 的原理);
  4. 解析字段 :取出 DF、ICAO 地址、类型码,航班号的每个字符用 6 位编码,查表 #ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789###### 得到字符。

运行结果:

项目 结果
无噪声时的 CRC 余数 0x0(正确)
噪声 σ = 0.2 前导码位置 123(真值 123),误码 0 位,校验通过
噪声 σ = 0.8 前导码位置 190(找错了),误码 57 位,校验失败,丢弃
翻转第 50 位 CRC 余数 0x6159c0,错误被检出
解码内容 DF = 17,ICAO 地址 4840D6,类型码 TC = 4(身份识别),航班号 KLM1023

CRC 的作用在这里体现得很清楚:噪声太大时,接收机可能连报文开头都找错,解出一串乱码;CRC 保证了这样的报文会被可靠地丢弃,而不会在地图上显示一架"幽灵飞机"。哪怕只错一位,CRC 也能发现。

飞机的位置用一种叫 CPR 的紧凑编码,需要两条报文配合才能解出经纬度,留到第 21 章的 SDR 实战再讲。

小结

  1. 接收机要完成选择、解调、还原三件事,通常还要放大。矿石机只靠调谐回路选择,没有放大。
  2. 包络检波器 是二极管加 RC。下限 RC≫1/fcRC \gg 1/f_c RC≫1/fc,上限 RC≤ 1−μ2 /(μωm) RC \le \sqrt{1 - \mu^2}/(\mu\omega_m) RC≤1−μ2 /(μωm);太小纹波大,太大产生对角切割失真。
  3. 真实二极管 小信号时是平方律 检波,输出 ∝ 输入²,并带来 μ/4\mu/4 μ/4 的二次谐波失真;锗管、肖特基管的 Is I_s Is 大,在几十毫伏就能工作,硅管需要几百毫伏。
  4. 数字包络检波 就是复基带信号取模 ∣I+jQ∣|I + jQ| ∣I+jQ∣,没有纹波也没有时间常数的问题。
  5. 矿石机的能量全部来自电波:100 kW 中波电台 50 km 外的场强约 34.6 mV/m,5 m 有效高度的天线约 173 mV。需要高阻耳机和并联电阻,天线要长、要高。
  6. 软件无线电让硬件只负责放大、变频和数字化,选择和解调交给软件。RTL-SDR 覆盖约 24~1766 MHz,8 位 ADC,约 2.4 MS/s。
  7. FM 鉴相解调 : m^n∝arg⁡(xnx∗n−1) \hat mn \propto \arg(xnx^*n-1) m^n∝arg(xnx∗n−1)。预加重/去加重(50 μs)在仿真中把音频信噪比从 44.6 dB 提高到 54.3 dB。
  8. ADS-B:1090 MHz,PPM,1 Mbit/s,112 位报文,CRC-24 校验。解码出 ICAO 地址 4840D6、航班号 KLM1023。

下一章,我们暂时放下技术,看看怎样成为一名合法的业余无线电爱好者:执照考试、呼号、频率规划和通联规范。

练习题

  1. (检波器设计) 广播的调制度最大取 0.5,音频最高 3 kHz。检波负载电阻为 100 kΩ 时,电容最大可以取多少?中波载波频率约 1 MHz,下限条件满足吗?
  2. (平方律失真) 调制度 μ=0.3\mu = 0.3 μ=0.3 的 AM 信号经过平方律检波,二次谐波失真是多少?
  3. (场强) 一座 50 kW 的电台在 20 km 外的场强约是多少(自由空间估算)?有效高度 5 m 的天线上开路电压是多少?
  4. (ADC 位数) 如果把 8 位 ADC 换成 12 位(一些更高档的 SDR 采用 12 位或 14 位 ADC),理想信噪比提高多少 dB?
  5. (去加重) 50 μs 和 75 μs 两种去加重网络的转折频率各是多少?
  6. (鉴相的采样率) FM 广播的最大频偏是 75 kHz。复基带采样率为 240 kHz 时,相邻样本之间的最大相位差是多少弧度?如果采样率降到 120 kHz 会怎样?
  7. (编程:门限效应) 修改附录代码第 5 部分,把载噪比依次改为 15、10、6、3 dB(使用预加重/去加重),音频信噪比各是多少?你观察到了什么?
  8. (ADS-B 的接收距离) 一架飞机在 10000 m 高度,你的接收天线架在 10 m 高处。用第 01 章的公式估算无线电视距。
  9. (手工解码) 航班号字段中三个 6 位编码分别是 001011、110001、100000,查表得到哪三个字符?
  10. (耳机) 为什么矿石机不能用普通的 32 Ω 耳机?说出至少两个原因。

参考答案

  1. RC≤ 1−0.25 0.5×2π×3000 =91.9 RC \le \dfrac{\sqrt{1 - 0.25}}{0.5\times2\pi\times3000} = 91.9 RC≤0.5×2π×30001−0.25 =91.9 μs, C≤91.9 μs/100 kΩ=C \le 91.9\ \mu\mathrm{s}/100\ \mathrm{k\Omega} = C≤91.9 μs/100 kΩ= 919 pF 。载波周期 1 μs, RC=91.9RC = 91.9 RC=91.9 μs 远大于它,下限满足。图 5 中取 1 nF,对应 100 μs,在这个最苛刻的条件下略微超出,实际广播很少同时出现深调制和 3 kHz 的高音,影响不大;也可以改用 820 pF。
  2. μ/4=\mu/4 = μ/4= 7.5%。调制越浅,平方律失真越小。
  3. E= 30×5×104 /(2×104)= E = \sqrt{30\times5\times10^4}/(2\times10^4) = E=30×5×104 /(2×104)= 61.2 mV/m ,开路电压约 306 mV。
  4. 6.02×12+1.76=74.06.02\times12 + 1.76 = 74.0 6.02×12+1.76=74.0 dB,比 8 位的 49.9 dB 提高 24.1 dB(每多一位提高约 6 dB)。
  5. 50 μs: 1/(2π×50×10−6)=1/(2\pi\times50\times10^{-6}) = 1/(2π×50×10−6)= 3183 Hz ;75 μs:2122 Hz。
  6. 最大相位差 2π×75/240=2\pi\times75/240 = 2π×75/240= 1.96 rad ,小于 π,没有问题。采样率降到 120 kHz 时为 3.93 rad ,超过 π, arg⁡(⋅)\arg(\cdot) arg(⋅) 会把它卷绕成 3.93−2π=−2.363.93 - 2\pi = -2.36 3.93−2π=−2.36 rad,解调出完全错误的结果。所以鉴相解调要求采样率大于最大瞬时频率的 2 倍。
  7. 附录代码改参数后的结果:载噪比 15 dB → 49.3 dB ,10 dB → 44.1 dB ,6 dB → 24.9 dB ,3 dB → 14.8 dB 。载噪比在 10 dB 以上时,每降低 5 dB,音频信噪比也只降低 5 dB;降到 10 dB 以下后,音频信噪比急剧恶化,从 10 dB 到 6 dB,载噪比只降了 4 dB,音频信噪比却降了 19 dB。这就是 FM 的门限效应:信号弱到一定程度后,噪声会引起相位的整周跳变,在耳机里听到"喀喀"的噪声,第 13 章会详细分析。
  8. d≈4.12×(10000+10)=4.12×(100+3.16)≈d \approx 4.12\times(\sqrt{10000} + \sqrt{10}) = 4.12\times(100 + 3.16) \approx d≈4.12×(10000 +10 )=4.12×(100+3.16)≈ 425 km。
  9. 001011 = 11 → K ;110001 = 49 → 1 ;100000 = 32 → 空格。
  10. (a) 32 Ω 耳机需要毫瓦级的功率才能发出可听的声音,矿石机提供的功率远远不够,而晶体耳机几乎只需要电压;(b) 低阻负载会严重降低调谐回路的 Q,使选择性变得极差;(c) 低阻负载使检波器的时间常数和工作点完全改变,检波效率大大降低。

参考资料

  1. ARRL. The ARRL Handbook for Radio Communications. 最新版. "Receiving" 与 "Software Defined Radio" 相关章节。
  2. W. Hayward, R. Campbell, B. Larkin. Experimental Methods in RF Design. ARRL, 2003. 检波器与接收机相关章节。
  3. M. Lichtman. PySDR: A Guide to SDR and DSP using Python . pysdr.org
  4. J. Sun. The 1090 Megahertz Riddle: A Guide to Decoding Mode S and ADS-B Signals . 2nd ed. TU Delft OPEN Publishing, 2021. mode-s.org/decode
  5. RTL-SDR Blog. www.rtl-sdr.com
  6. Osmocom rtl-sdr 项目. osmocom.org/projects/rt...

附录:完整代码

下面的代码复现了本章所有数值:包络检波在三种时间常数下的失真与纹波、数字包络检波、二极管检波特性的数值求解与平方律失真、矿石机的场强估算、RTL-SDR 的几个参数、FM 广播的仿真与解调(含预加重和去加重的对比、99% 带宽),以及 ADS-B 报文的 PPM 波形生成、前导码检测、CRC-24 校验和字段解析,最后画出 FM 解调前后的频谱。只依赖 numpy 和 matplotlib,保存为 example.py 后运行 python example.py 即可,几秒钟就能运行完。

python 复制代码
"""无线电自学教程 05:第一台收音机------从矿石机到 SDR ------ 附录代码

1. 包络检波:理想二极管 + RC,三种时间常数下的失真与纹波;数字包络检波(取 IQ 的模);
2. 二极管的检波特性:小信号时平方律,大信号时线性;
3. 矿石机的"能源":远处中波电台在天线上感应出多大的电压;
4. RTL-SDR 的几个数字:8 位 ADC 的动态范围、带宽、数据率;
5. 用 Python 解调 FM 广播:仿真发射端(含预加重)与接收端(鉴相、去加重、抽取);
6. 解码一条 ADS-B 报文:PPM 波形、前导码检测、CRC-24 校验、航班号。
最后画出 FM 解调前后的频谱。
运行:python example.py
"""
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False


def thd(x, fs, f0, n_harm=10):
    """总谐波失真:2~n_harm 次谐波的有效值与基波之比。x 须包含整数个 f0 周期。"""
    X = np.abs(np.fft.rfft(x - x.mean()))
    k0 = int(round(f0 * len(x) / fs))
    harm = np.sqrt(sum(X[h * k0] ** 2 for h in range(2, n_harm + 1)))
    return harm / X[k0], X[k0] * 2 / len(x)


# ---------------- 1. 包络检波 ----------------
print("=== 1. 包络检波:载波 100 kHz,音频 1 kHz,调制度 0.8 ===")
FC, FM, MU = 100e3, 1e3, 0.8
fs = 10e6
t = np.arange(int(12e-3 * fs)) / fs
am = (1 + MU * np.cos(2 * np.pi * FM * t)) * np.cos(2 * np.pi * FC * t)
rc_max = np.sqrt(1 - MU**2) / (MU * 2 * np.pi * FM)
print(f"不产生对角切割失真的条件:RC <= sqrt(1-μ^2)/(μ ω_m) = {rc_max * 1e6:.1f} μs;载波周期 {1e6 / FC:.0f} μs")


def envelope_detector(x, rc, fs):
    """理想二极管(正向导通电阻为零)+ 并联 RC:输入高于电容电压就充电到输入,否则放电。"""
    decay = np.exp(-1 / (fs * rc))
    v = np.empty_like(x)
    vc = 0.0
    for i, xi in enumerate(x):
        vc = xi if xi > vc else vc * decay
        v[i] = vc
    return v


seg = slice(int(2e-3 * fs), None)                      # 去掉前 2 ms 的建立过程,剩下 10 个音频周期
for rc in (3e-6, 100e-6, 1e-3):
    v = envelope_detector(am, rc, fs)[seg]
    d, a1 = thd(v, fs, FM)
    V = np.abs(np.fft.rfft(v - v.mean())) * 2 / len(v)
    ripple = V[int(FC * len(v) / fs)]
    print(f"RC = {rc * 1e6:6.0f} μs:音频幅度 {a1:.3f}(理想 {MU}),总谐波失真 {d * 100:6.2f} %,"
          f"100 kHz 纹波幅度 {ripple:.4f}")
fs_bb = 100e3
tb = np.arange(int(10e-3 * fs_bb)) / fs_bb
iq = (1 + MU * np.cos(2 * np.pi * FM * tb)) * np.exp(1j * 0.7)   # 复基带 AM,任意载波相位
d, a1 = thd(np.abs(iq), fs_bb, FM)
print(f"数字包络检波 |I + jQ|:音频幅度 {a1:.3f},总谐波失真 {d * 100:.2e} %")

# ---------------- 2. 二极管的检波特性 ----------------
print("\n=== 2. 二极管检波特性:输入 A cos(ωt),负载 100 kΩ ===")
VT = 0.02585                                           # 热电压 kT/q(300 K)
R_LOAD = 100e3


def detector_dc(A, Is, n=1.0, R=R_LOAD):
    """解 Is[I0(A/nVt) exp(-V/nVt) - 1] = V/R,得到检波输出的直流电压 V。"""
    x = A / (n * VT)

    def f(V):                                          # A <= 1 V 时 x < 40,np.i0 不会溢出
        return Is * (np.i0(x) * np.exp(-V / (n * VT)) - 1) - V / R

    lo, hi = 0.0, A
    for _ in range(200):
        mid = (lo + hi) / 2
        lo, hi = (mid, hi) if f(mid) > 0 else (lo, mid)
    return (lo + hi) / 2


amps = np.array([0.003, 0.01, 0.03, 0.1, 0.3, 1.0])
for name, Is in (("低阈值二极管 Is = 1e-7 A", 1e-7), ("普通硅二极管 Is = 1e-12 A", 1e-12)):
    V = np.array([detector_dc(A, Is) for A in amps])
    print(name)
    for k, (A, v) in enumerate(zip(amps, V)):
        slope = np.log(V[k] / V[k - 1]) / np.log(amps[k] / amps[k - 1]) if k else float("nan")
        print(f"  A = {A * 1e3:6.0f} mV:输出 {v * 1e3:10.3e} mV,V/A = {v / A:.2e},"
              f"对数斜率 {slope:.2f}")
th = np.linspace(0, 2 * np.pi, 4096, endpoint=False)
sq = (1 + 1.0 * np.cos(th)) ** 2                       # 平方律检波,μ = 1
S = np.abs(np.fft.rfft(sq)) * 2 / len(th)
print(f"平方律检波 μ = 1:基波 {S[1]:.3f},二次谐波 {S[2]:.3f},二次谐波失真 {S[2] / S[1] * 100:.1f} %(理论 μ/4)")

# ---------------- 3. 矿石机的"能源" ----------------
print("\n=== 3. 100 kW 中波电台,相距 50 km(自由空间估算) ===")
P, d_km, h_eff = 100e3, 50.0, 5.0
E = np.sqrt(30 * P) / (d_km * 1e3)                     # E = sqrt(30 P G)/d,G = 1
print(f"场强 E = {E * 1e3:.1f} mV/m,有效高度 {h_eff:.0f} m 的天线开路电压 {E * h_eff * 1e3:.0f} mV")

# ---------------- 4. RTL-SDR 的几个数字 ----------------
print("\n=== 4. RTL-SDR ===")
bits, rate = 8, 2.4e6
print(f"{bits} 位 ADC 的理想信噪比 6.02N + 1.76 = {6.02 * bits + 1.76:.1f} dB")
print(f"采样率 {rate / 1e6} MS/s(复数 IQ):可同时看到 {rate / 1e6} MHz 宽的频谱,"
      f"2048 点 FFT 的分辨率 {rate / 2048:.1f} Hz,USB 数据率 {rate * 2 / 1e6:.1f} MB/s")

# ---------------- 5. FM 广播的解调 ----------------
print("\n=== 5. FM 广播:频偏 75 kHz,预加重 50 μs,复基带采样率 240 kHz ===")
FS, DEV, TAU = 240e3, 75e3, 50e-6
rng = np.random.default_rng(5)
n = int(0.5 * FS)
t5 = np.arange(n) / FS
audio = 0.6 * np.cos(2 * np.pi * 1000 * t5) + 0.15 * np.cos(2 * np.pi * 6000 * t5)
a = np.exp(-1 / (FS * TAU))                            # 单极点去加重 y[n] = a y[n-1] + (1-a) x[n]


def deemphasis(x):
    y = np.empty_like(x)
    acc = 0.0
    for i, xi in enumerate(x):
        acc = a * acc + (1 - a) * xi
        y[i] = acc
    return y


def preemphasis(y):
    """去加重的逆运算(假设 y 在开始之前一直保持 y[0],避免开头出现阶跃)。"""
    return (y - a * np.r_[y[0], y[:-1]]) / (1 - a)


def lowpass_taps(cutoff, fs, n_taps=101):
    m = np.arange(n_taps) - (n_taps - 1) / 2
    h = 2 * cutoff / fs * np.sinc(2 * cutoff / fs * m) * np.hanning(n_taps)
    return h / h.sum()


taps = lowpass_taps(15e3, FS)
noise = (rng.standard_normal(n) + 1j * rng.standard_normal(n)) * np.sqrt(0.01 / 2)   # 载噪比 20 dB
for use_emph in (False, True):
    m = preemphasis(audio) if use_emph else audio
    print(f"{'有' if use_emph else '无'}预加重:调制信号峰值 {np.abs(m).max():.3f}(满偏 = 1)")
    x = np.exp(2j * np.pi * DEV * np.cumsum(m) / FS)
    if use_emph:
        X = np.abs(np.fft.fft(x)) ** 2
        fr = np.fft.fftfreq(n, 1 / FS)
        order = np.argsort(np.abs(fr))
        cum = np.cumsum(X[order]) / X.sum()
        print(f"  99% 功率带宽 {2 * np.abs(fr[order][np.searchsorted(cum, 0.99)]) / 1e3:.1f} kHz,"
              f"卡森公式 2(75 + 6) = 162 kHz")
    rx = x + noise
    demod = np.angle(rx[1:] * np.conj(rx[:-1])) * FS / (2 * np.pi * DEV)   # 相邻样本的相位差 → 频率
    if use_emph:
        demod = deemphasis(demod)
    out = np.convolve(demod, taps, mode="same")[::5]                  # 低通 15 kHz,抽取到 48 kHz
    out = out[2400:2400 + 19200]                                       # 去掉开头 50 ms,取 0.4 s
    O = np.abs(np.fft.rfft(out)) * 2 / len(out)
    k1, k6 = int(1000 * len(out) / 48e3), int(6000 * len(out) / 48e3)
    P = np.abs(np.fft.rfft(out)) ** 2
    band = slice(1, int(15e3 * len(out) / 48e3))
    sig = P[k1] + P[k6]
    print(f"  解调输出:1 kHz 幅度 {O[k1]:.4f}(原 0.6),6 kHz 幅度 {O[k6]:.4f}(原 0.15),"
          f"音频信噪比 {10 * np.log10(sig / (P[band].sum() - sig)):.1f} dB")

# ---------------- 6. ADS-B ----------------
print("\n=== 6. ADS-B:1090 MHz,PPM,1 Mbit/s,112 位报文 ===")
MSG = "8D4840D6202CC371C32CE0576098"
GEN = 0x1FFF409                                        # CRC-24 的生成多项式(25 位)


def hex_to_bits(h):
    return np.array([int(b) for b in bin(int(h, 16))[2:].zfill(len(h) * 4)])


def crc24_remainder(bits):
    """对全部 112 位做模 2 除法,余数为 0 表示校验通过。"""
    reg = int("".join(map(str, bits)), 2)
    for shift in range(len(bits) - 1, 23, -1):
        if reg >> shift & 1:
            reg ^= GEN << (shift - 24)
    return reg


def ppm_waveform(bits, fs=2e6):
    """前导码 8 μs(在 0、1、3.5、4.5 μs 处各一个 0.5 μs 脉冲)+ 每位 1 μs:1 = 先高后低,0 = 先低后高。"""
    pre = np.zeros(16)
    pre[[0, 2, 7, 9]] = 1
    body = np.ravel([[1, 0] if b else [0, 1] for b in bits])
    return np.r_[pre, body].astype(float)


CHARSET = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"
bits = hex_to_bits(MSG)
print(f"报文 {MSG}:{len(bits)} 位,CRC 余数 = {crc24_remainder(bits):#x}")
template = ppm_waveform(bits)[:16]
for sigma in (0.2, 0.8):
    rng = np.random.default_rng(6)
    clean = np.zeros(1000)
    clean[123:123 + 240] = ppm_waveform(bits)
    iq = clean * np.exp(1j * 1.3) + sigma * (rng.standard_normal(1000) + 1j * rng.standard_normal(1000)) / np.sqrt(2)
    mag = np.abs(iq)                                   # 接收机只看幅度
    corr = np.correlate(mag - mag.mean(), template - template.mean(), mode="valid")
    start = int(np.argmax(corr))
    data = mag[start + 16:start + 16 + 224].reshape(112, 2)
    rx_bits = (data[:, 0] > data[:, 1]).astype(int)
    rem = crc24_remainder(rx_bits)
    print(f"噪声 σ = {sigma}:前导码位置 {start}(真值 123),误码 {np.sum(rx_bits != bits)} 位,"
          f"CRC 余数 {rem:#x},{'校验通过' if rem == 0 else '校验失败,丢弃'}")
df = int("".join(map(str, bits[:5])), 2)
icao = int("".join(map(str, bits[8:32])), 2)
me = bits[32:88]
tc = int("".join(map(str, me[:5])), 2)
callsign = "".join(CHARSET[int("".join(map(str, me[8 + 6 * i:14 + 6 * i])), 2)] for i in range(8))
print(f"DF = {df},ICAO 地址 {icao:06X},类型码 TC = {tc}(身份识别),航班号 "{callsign.strip()}"")
flip = bits.copy()
flip[50] ^= 1
print(f"把第 50 位翻转后,CRC 余数 = {crc24_remainder(flip):#x}(不为 0,错误被检出)")

# ---------------- 画图:FM 解调前后的频谱 ----------------
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4))
Xr = np.fft.fftshift(np.abs(np.fft.fft(rx)) ** 2)
ax1.plot(np.fft.fftshift(np.fft.fftfreq(n, 1 / FS)) / 1e3, 10 * np.log10(Xr / Xr.max()), linewidth=0.5)
ax1.set_xlabel("频率 / kHz")
ax1.set_title("接收到的 FM 信号(复基带)")
ax2.plot(np.fft.rfftfreq(len(out), 1 / 48e3) / 1e3, 20 * np.log10(O + 1e-9), linewidth=0.8)
ax2.set_xlabel("频率 / kHz")
ax2.set_title("解调并去加重后的音频")
for ax in (ax1, ax2):
    ax.grid(True, color="#e6e5e1")
fig.tight_layout()
plt.show()

运行后的完整输出:

text 复制代码
=== 1. 包络检波:载波 100 kHz,音频 1 kHz,调制度 0.8 ===
不产生对角切割失真的条件:RC <= sqrt(1-μ^2)/(μ ω_m) = 119.4 μs;载波周期 10 μs
RC =      3 μs:音频幅度 0.373(理想 0.8),总谐波失真   1.00 %,100 kHz 纹波幅度 0.3907
RC =    100 μs:音频幅度 0.764(理想 0.8),总谐波失真   0.03 %,100 kHz 纹波幅度 0.0302
RC =   1000 μs:音频幅度 0.375(理想 0.8),总谐波失真  36.21 %,100 kHz 纹波幅度 0.0043
数字包络检波 |I + jQ|:音频幅度 0.800,总谐波失真 7.74e-14 %

=== 2. 二极管检波特性:输入 A cos(ωt),负载 100 kΩ ===
低阈值二极管 Is = 1e-7 A
  A =      3 mV:输出  2.428e-02 mV,V/A = 8.09e-03,对数斜率 nan
  A =     10 mV:输出  2.699e-01 mV,V/A = 2.70e-02,对数斜率 2.00
  A =     30 mV:输出  2.434e+00 mV,V/A = 8.11e-02,对数斜率 2.00
  A =    100 mV:输出  2.638e+01 mV,V/A = 2.64e-01,对数斜率 1.98
  A =    300 mV:输出  1.701e+02 mV,V/A = 5.67e-01,对数斜率 1.70
  A =   1000 mV:输出  8.150e+02 mV,V/A = 8.15e-01,对数斜率 1.30
普通硅二极管 Is = 1e-12 A
  A =      3 mV:输出  3.370e-07 mV,V/A = 1.12e-07,对数斜率 nan
  A =     10 mV:输出  3.776e-06 mV,V/A = 3.78e-07,对数斜率 2.01
  A =     30 mV:输出  3.661e-05 mV,V/A = 1.22e-06,对数斜率 2.07
  A =    100 mV:输出  9.092e-04 mV,V/A = 9.09e-06,对数斜率 2.67
  A =    300 mV:输出  1.238e+00 mV,V/A = 4.13e-03,对数斜率 6.57
  A =   1000 mV:输出  5.289e+02 mV,V/A = 5.29e-01,对数斜率 5.03
平方律检波 μ = 1:基波 2.000,二次谐波 0.500,二次谐波失真 25.0 %(理论 μ/4)

=== 3. 100 kW 中波电台,相距 50 km(自由空间估算) ===
场强 E = 34.6 mV/m,有效高度 5 m 的天线开路电压 173 mV

=== 4. RTL-SDR ===
8 位 ADC 的理想信噪比 6.02N + 1.76 = 49.9 dB
采样率 2.4 MS/s(复数 IQ):可同时看到 2.4 MHz 宽的频谱,2048 点 FFT 的分辨率 1171.9 Hz,USB 数据率 4.8 MB/s

=== 5. FM 广播:频偏 75 kHz,预加重 50 μs,复基带采样率 240 kHz ===
无预加重:调制信号峰值 0.750(满偏 = 1)
  解调输出:1 kHz 幅度 0.5999(原 0.6),6 kHz 幅度 0.1500(原 0.15),音频信噪比 44.6 dB
有预加重:调制信号峰值 0.944(满偏 = 1)
  99% 功率带宽 144.0 kHz,卡森公式 2(75 + 6) = 162 kHz
  解调输出:1 kHz 幅度 0.5999(原 0.6),6 kHz 幅度 0.1500(原 0.15),音频信噪比 54.3 dB

=== 6. ADS-B:1090 MHz,PPM,1 Mbit/s,112 位报文 ===
报文 8D4840D6202CC371C32CE0576098:112 位,CRC 余数 = 0x0
噪声 σ = 0.2:前导码位置 123(真值 123),误码 0 位,CRC 余数 0x0,校验通过
噪声 σ = 0.8:前导码位置 190(真值 123),误码 57 位,CRC 余数 0x87706,校验失败,丢弃
DF = 17,ICAO 地址 4840D6,类型码 TC = 4(身份识别),航班号 "KLM1023"
把第 50 位翻转后,CRC 余数 = 0x6159c0(不为 0,错误被检出)
相关推荐
IPIPD20 分钟前
Node.js请求遇到503怎么重试?先解析Retry-After,再做有上限退避
前端
葡萄城技术团队27 分钟前
dsh-spreadjs-editor 的集成链路:把 SpreadJS Designer 嵌进 Agent 工作台
前端
FlyWIHTSKY1 小时前
vue2和vue3有什么区别,用法上有哪些区别
前端·javascript·vue.js
码云之上1 小时前
10 颗一模一样的「Edit」:中后台 Web Agent 怎么找到对的那一行
前端·人工智能·浏览器
量化分析码农2 小时前
【Python量化多因子实战 04】因子暴露不是 alpha:市值与行业中性化实战
前端·后端
不可能片场2 小时前
Electron 发布 正文写入的三条通道与读回口径
前端·electron
jerrywus2 小时前
让 AI 帮你部署 Vercel,报错了也不用手动搬日志
前端·agent·mcp
补码缩写3 小时前
11 行 Python 拆 PNG 块,解压后的字节数能手算
前端
coding漫漫长路3 小时前
PNG压缩先看图是哪一类,先无损再量化多半白做
前端