我正在尝试通过音频通道(扬声器/麦克风)将二进制数据从一台设备传输到另一台设备。我在分组无线电中使用AFSK(音频频移键控),其中$ 1200 \ text {Baud} $和两个频率$ f_ {mark} = 1200 \ text {Hz} $和$ f_ {space} = 2200 \ text {Hz } $。我在Ruby中玩了一些,我的第一个实现只是模仿了一个经典的非相干解调器,到目前为止,它仍然可以正常工作。

问题是,我正在尝试将其移植到性能出色的移动平台上一个问题,我目前的解决方案太慢了。我发现了许多方法可以在软件中解调AFSK:


滑动DFT(FFT)
滑动Görtzel滤波器
锁相环
零交叉
/>
会走什么路?可供选择的选项太多。我相信还有更多选择。也许有比我上面提到的解决方案更好的解决方案?有人甚至有我的代码示例吗?我担心的是


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

任何建议和提示都将不胜感激!

评论

我认为您可能会卖空您定位的移动设备的功能。请记住,现代设备是时钟速度超过1 GHz的多核处理器。使用FSK解调器处理<10 ksps信号应该不会出现性能问题。但是,您的现有方法(在我看来像标记/空间过滤)不应不能在现代移动平台上实时运行的任何理由都没有。甚至更复杂的基于PLL的方法也应该适合您的处理范围。我会介绍一下您现有的代码。

通过FSK调制解调器的实际案例研究一下我的课程和DSP教学。

#1 楼

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

另外,我建议您将2200 Hz更改为2400 Hz。朴素的1200/2200 Hz方案实施会产生不连续性,如下图所示约三分之二,其中2200 Hz转换为1200 Hz。



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

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



评论


$ \ begingroup $
吉姆,谢谢您的详细回答!我的调制器实际上执行CPFSK,因此不连续性不是问题。我故意选择1200和2200 Hz,因为谐波的重叠不如1200的倍数多。或者我在这里错了吗? PLL听起来很棒,但是我真的不知道如何实现它们。您是否知道关于软件PLL的任何有用资料?
$ \ endgroup $
–帕特里克·奥斯奇(Patrick Oscity)
2012年8月21日在18:22

$ \ begingroup $
@Patrick不,您是正确的,1200和2400 Hz会有重叠的谐波。但是,在零交叉的情况下,我认为谐波并不重要。而且,恐怕我不知道有关PLL的良好在线资源。
$ \ endgroup $
– Jim Clay
2012年8月21日在18:52

$ \ begingroup $
这是不正确的。 AFSK 1200紧随Bell 202,并说音调应该是1200和2200。不间断不应在发射机端发生。检出开放源代码AFSK 1200调制器,通过跟踪每个音调的相位增量来完成调制:如果tone == LOW,则last_phase + = ph_low,否则last_phase + = ph_high endif; next_sample = sin(last_phase);
$ \ endgroup $
–vz0
16-10-6在14:57

#2 楼

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

由于符号中信号的相位未知,所以解决方案是在复杂域中工作:与其乘以正弦波,不如乘以复数指数。这意味着分别将$ \ sin $和$ \ cos $相乘,然后对它们进行积分,然后计算出绝对值(平方)。

所得的幅度与信号相位和输出完全无关。 SNR非常好。

评论


$ \ begingroup $
这正是我之前尝试过的,也就是所谓的“经典非相干解调器”。也许我的实现是错误的,但是我担心它会由于处理缓慢而遭受缓冲区溢出的困扰。不管怎么说,还是要谢谢你!
$ \ endgroup $
–帕特里克·奥斯奇(Patrick Oscity)
2012年8月21日在18:17

#3 楼

对于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时执行的代码块中再次调用此函数。 br />
这是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;
}