问题是,我正在尝试将其移植到性能出色的移动平台上一个问题,我目前的解决方案太慢了。我发现了许多方法可以在软件中解调AFSK:
滑动DFT(FFT)
滑动Görtzel滤波器
锁相环
零交叉
/>
会走什么路?可供选择的选项太多。我相信还有更多选择。也许有比我上面提到的解决方案更好的解决方案?有人甚至有我的代码示例吗?我担心的是
性能(应在移动平台上运行,例如iOS或Android设备)
稳定性(应能够处理一些噪音)
任何建议和提示都将不胜感激!
#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;
}
评论
我认为您可能会卖空您定位的移动设备的功能。请记住,现代设备是时钟速度超过1 GHz的多核处理器。使用FSK解调器处理<10 ksps信号应该不会出现性能问题。但是,您的现有方法(在我看来像标记/空间过滤)不应不能在现代移动平台上实时运行的任何理由都没有。甚至更复杂的基于PLL的方法也应该适合您的处理范围。我会介绍一下您现有的代码。通过FSK调制解调器的实际案例研究一下我的课程和DSP教学。