如何在软件中解调 AFSK 信号

信息处理 自由度 解调 fsk
2022-01-05 04:33:35

我正在尝试通过音频通道(扬声器/麦克风)将二进制数据从一个设备传输到另一个设备。我在分组无线电中使用 AFSK(音频频移键控),具有和两个频率我在 Ruby 中玩了一会儿,我的第一个实现只是模仿了一个经典的不连贯解调器,到目前为止它工作得很好。1200 Baudfmark=1200 Hzfspace=2200 Hz

问题是,我正在尝试将其移植到性能受到关注且我当前的解决方案太慢的移动平台。我发现了许多在软件中解调 AFSK 的方法:

  • 滑动 DFT (FFT)
  • 滑动 Görtzel 过滤器
  • 锁相环
  • 过零

要走的路是什么?有太多的选择可供选择。我相信还有更多的选择。也许存在比我上面提到的更好的解决方案?有人甚至有我的代码示例吗?我很关心

  • 性能(应该在移动平台上运行,比如 iOS 或 Android 设备)
  • 稳定性(应该能够处理一些噪音)

非常感谢任何建议和提示!

3个回答

我认为您可以在具有锁相环的解调器误码率 (BER) 方面获得最佳性能。不过,您需要它快速。我认为对于仍然表现相当好的快速算法,你最好的选择是过零。

在旁注中,我建议您将 2200 Hz 更改为 2400 Hz。1200/2200 Hz 方案的幼稚实现会产生不连续性,如下图所示大约三分之二,其中 2200 Hz 转换为 1200 Hz。

1200 赫兹和 2200 赫兹

为了最小化您正在使用的带宽并避免会使信号失真的不连续性,您需要使相位连续。但是,即使您使发射机相位连续,仍然存在由于相位不同而导致 2200 Hz 符号不会始终具有相同数量的过零的问题。通常他们会有四个过零,但有时他们会有三个。另一方面,1200 Hz 符号将始终有两个过零,因为波特率均匀地划分为 FSK 频率。

您可以通过将 2200 Hz 更改为 2400 Hz 来解决这两个问题。然后符号将始终以 0 度开始和结束(因此自动使它们相位连续),并且它们将始终具有相同数量的零交叉 - 两个和四个。

1200 赫兹和 2400 赫兹

我使用 1200 Hz 和 2200 Hz 的相关接收器为 AFSK(Bell 202 标准)制作了一个解码器,效果非常好。

由于符号期间信号的相位是未知的,因此解决方案是在复域中工作:而不是乘以实数正弦曲线,而是乘以复指数。这意味着分别乘以,然后将它们积分,并计算绝对(平方)值。sincos

产生的幅度与信号相位完全无关,输出 SNR 非常好。

在 RTTY 45.45 波特的情况下,您还将拥有不是整数个样本的符号,因此您需要一个可以调用每个样本的函数,然后在该符号结束时发出其返回值的信号。你需要一个相位累加器,它可以记录正弦波的相位。

要发送长度不是采样率整数倍的符号,您需要此功能...

int millisecondTimer(double milliseconds, double samplerate, int resettime)
{

    static int fracsample=0;
    static int counter=0;
    static int retvalue=0;
    static int first=1;
    static double oldmilliseconds=1.0;
    static int whole_samples=0;
    static int samerror=32768;
    if(resettime==1)
    {
        samerror=0;
        counter=0;
        retvalue=1;
        first=1;
    }
    if(first==1 || milliseconds !=oldmilliseconds)
    {
        double samplesneeded=1;
        double wholesamples=0;
        samplesneeded=(samplerate) * (milliseconds /1000.0);
        samerror=(modf(samplesneeded, &wholesamples)) * 32768.0;
        whole_samples=wholesamples;
        first=0;
    }

    if(counter<=whole_samples)
    {
        retvalue=2;
        counter++;
    }
    else
    {
        counter-=whole_samples;
        retvalue=1;
        fracsample+=samerror;
        oldmilliseconds=milliseconds;
        if(fracsample>=32768)
        {
            fracsample-=32768;
            counter--;
        }

    }
    return retvalue;
}

要使用它,请生成下一个正弦波样本并调用此函数,然后检查返回值是否不等于 2。如果不等于 2,则前进到下一个符号并决定是否发送空格标记,然后在发现返回值不等于 2 时执行的代码块内再次调用此函数。

这是 Rockbox 固件中的相位累加器,经过更改以允许幅度变化(全音量为 32767,180 度异相全音量为 -32768)。

signed short lerpsin(float frequency,signed short amplitude,unsigned long samplerate)
{
    /* 128 sixteen bit sine samples + guard point */
    static unsigned long phase=0;
    unsigned int pos =0;
    unsigned short frac=0;
    static unsigned long step=0;
    static float old_frequency=0;
    signed short diff=0;
    static const signed short sinetab[129] =
    {
        0,   1607,   3211,   4807,   6392,   7961,   9511,  11038,
        12539,  14009,  15446,  16845,  18204,  19519,  20787,  22004,
        23169,  24278,  25329,  26318,  27244,  28105,  28897,  29621,
        30272,  30851,  31356,  31785,  32137,  32412,  32609,  32727,
        32767,  32727,  32609,  32412,  32137,  31785,  31356,  30851,
        30272,  29621,  28897,  28105,  27244,  26318,  25329,  24278,
        23169,  22004,  20787,  19519,  18204,  16845,  15446,  14009,
        12539,  11038,   9511,   7961,   6392,   4807,   3211,   1607,
        0,  -1607,  -3211,  -4807,  -6392,  -7961,  -9511, -11038,
        -12539, -14009, -15446, -16845, -18204, -19519, -20787, -22004,
        -23169, -24278, -25329, -26318, -27244, -28105, -28897, -29621,
        -30272, -30851, -31356, -31785, -32137, -32412, -32609, -32727,
        -32767, -32727, -32609, -32412, -32137, -31785, -31356, -30851,
        -30272, -29621, -28897, -28105, -27244, -26318, -25329, -24278,
        -23169, -22004, -20787, -19519, -18204, -16845, -15446, -14009,
        -12539, -11038, -9511,   -7961,  -6392,  -4807,  -3211,  -1607,
        0,
    };
    if(frequency!=old_frequency)
    {
        step = 0x100000000ull*frequency / samplerate;
    }
    phase+=step;
    pos = phase >> 25;
    frac = (phase & 0x01ffffff) >> 9;
    diff = sinetab[pos + 1] - sinetab[pos];
    old_frequency=frequency;
    return ((-((sinetab[pos] + (frac*diff >> 16)))) * amplitude) >> 15;
}