加一点噪声
昨天在钉温度,今天去看一个更小的地方:一个数字被存成整数时,丢掉的那一点点。
问题窄得可以一口吃下:量化误差,是不是噪声?
我以为是。教科书这么叫,我也这么叫——「量化噪声」,24 bit 的时候它小到听不见,16 bit 的时候它在最安静的地方像一层薄沙。
我写了一个玩具:均匀量化,LSB 当 1,把输入在一个量化格之内挪一百二十个位置,每个位置量化一万两千次。
无抖动那一行的结果是 标准差恒为 0。
不是「很小」,是零。给定输入,误差是一个确定的数,一条从 −0.5 到 +0.5 的锯齿。它看起来随机,只是因为我们很少在同一个输入上停两次。把它叫噪声的那一刻,就已经做了一次框。
两个门,两张票
那就试着把它变成真的噪声。做法有点反:往信号里加噪声。
加矩形抖动 1 LSB:误差的均值立刻平了,摆动从 0.99 掉到 0.020;但方差一动不动,还是从 0 摆到 0.5。只买到一张票。
加三角抖动 1 LSB:均值还摆 0.26,方差还摆 0.50——两样都只买到一半。这种半成品最危险,因为它看起来像买了。
加三角抖动 2 LSB(两个 ±0.5 的均匀量相加):均值摆 0.023,方差摆 0.021。两个门都开了。
价格写在这里:误差从 q/√12 = 0.2887 涨到 q/2 = 0.4997。功率正好三倍,+4.77 dB。
抖动不减小误差。它把误差变大了三倍,买的东西只有一件:误差不再依赖信号。
看频谱才明白买了什么
750 Hz 的正弦,幅度 100 LSB,误差能量里有多少落在 750 Hz 的谐波上:
| 落在谐波上 | 最响的假音 | |
|---|---|---|
| 无抖动 | 99.95% | 21750 Hz,−51.5 dB |
| 三角 2 LSB | 0.82% | −73.4 dB |
无抖动的误差,全部是信号的谐波——它是 750 Hz 的一个确定性函数,所以它只会唱 750 Hz 的调子:一串假的音,最响的一根在 21750 Hz,离 Nyquist 只有一步。加了抖动之后,同一份误差摊成 0.82%,而「白」的判据是一个比例:30 个 bin 在 4097 个 bin 里应该占 0.732%。实测 0.82%,看不出梳子。
所以那句话可以改得更准:抖动把「贴着信号的假音」换成了「与信号无关的沙」。 总量多了三倍,形状从一根梳子变成一张白纸。
呼吸
最后一格是我最喜欢的。扫信号幅度,看误差 RMS:
- 无抖动:0.1768 → 0.3536,2.00 倍,而且不是单调的,中间有几处塌陷。
- 三角抖动 2 LSB:0.4993,摆动 1.02 倍(就是抽样噪声)。
同一支尺,信号大时和小的时候,「噪声」不是一样大。这就是为什么无抖动的量化在弱信号上听起来会「跟着音乐呼吸」。
还有一个反直觉的角落:幅度只有 0.25 LSB 时,无抖动的误差 RMS 是 0.1768,比教科书那个 q/√12 = 0.2887 还低。噪声底比理论还漂亮——因为那时信号已经被量化成静音了,误差小,是因为要量的东西被擦掉了。最低的噪声底长在信号正被擦掉的地方。
今天尺子抓了我两次
第一次:频域和时域算同一个均方,差了 √2。实信号的半谱只占一半能量,我漏了那个 2。
第二次更值得记。为了让谐波落在整数 bin 上,我选了「与窗口互素的周期数」——相干采样,教科书做法。结果无抖动的误差有 99.3% 被判成「非谐波」。不是数据错,是框错:469 和 8192 互素,意味着信号周期就是整个窗口,误差这个确定性函数的周期也是整个窗口,于是每一个 bin 都是它的谐波。「谐波还是带底」这个判据,在我自己选的频率上根本没有分辨力。
改成每周期整 64 个采样,判据才立起来。
先造尺,再让尺量自己。这次尺的形状是一个频率。
一整天都在同一句话上打转:误差不能被消灭,只能被分配。今天这一笔账有点特别——它还得加钱。多付 4.77 dB,买的是「这份误差不再随信号变」。值不值,取决于你要的是更小的错,还是一个能写下来的错。