ソフトウェアでAFSK信号を復調する方法


14

オーディオチャネル(スピーカー/マイク)を介して、あるデバイスから別のデバイスにバイナリデータを送信しようとしています。パケットラジオのように、と2つの周波数および AFSK(Audio Frequency Shift Keying)を使用し。私はRubyで少し遊んでみましたが、私の最初の実装は古典的なインコヒーレントな復調器を単純に模倣したもので、今のところうまくいきます。1200 ボーfmark=1200 Hzfspace=2200 Hz

問題は、パフォーマンスを懸念している現在のソリューションが遅すぎるモバイルプラットフォームにこれを移植しようとしていることです。ソフトウェアでAFSKを復調する方法は多数あります。

  • スライド式DFT(FFT)
  • スライド式ゲルツェルフィルター
  • フェーズロックループ
  • ゼロクロッシング

どのように行くのでしょうか?選択できるオプションが多すぎます。さらに多くのオプションが利用可能であると確信しています。おそらく、上記で名前を挙げたものよりも優れたソリューションが存在するのでしょうか?誰かが私のためにコード例を持っていますか?心配です

  • パフォーマンス(iOSまたはAndroidデバイスなどのモバイルプラットフォームで実行する必要があります)
  • 安定性(ある程度のノイズを処理できるはずです)

提案やヒントは大歓迎です!


3
おそらくあなたはあなたがターゲットにしているモバイルデバイスの能力を空売りしていると思います。最新のデバイスは、クロック速度が1 GHzを超えるマルチコアプロセッサであることを忘れないでください。FSK復調器で<10 kspsの信号を処理しても、パフォーマンスの問題は発生しません。しかし、既存のアプローチ(マーク/スペースフィルタリングのように聞こえます)が最新のモバイルプラットフォームでリアルタイムに実行できない理由はありません。より洗練されたPLLベースのアプローチでさえ、処理エンベロープに快適に収まるはずです。既存のコードを少しプロファイルします。
ジェイソンR

回答:


9

位相ロックループを使用すると、復調器のビット誤り率(BER)に関して最高のパフォーマンスが得られると思います。ただし、高速にする必要があります。まだ十分に機能する高速アルゴリズムに対する最善の策は、ゼロクロッシングだと思います。

補足として、2200 Hzを2400 Hzに変更することをお勧めします。1200/2200 Hzスキームの単純な実装では、下のプロットの約3分の2に見られるように、2200 Hzが1200 Hzに移行する不連続性が生じます。

1200 Hzおよび2200 Hz

使用している帯域幅を最小限に抑え、信号を歪ませる不連続性を避けるために、位相を連続させる必要があります。ただし、トランスミッターの位相を連続的にした場合でも、2200 Hzのシンボルは、位相が異なるために常に同じ数のゼロ交差を持たないという問題があります。通常、それらには4つのゼロクロッシングがありますが、時には3つあります。一方、1200 Hzのシンボルは、ボーレートがFSK周波数に均等に分割されるため、常に2つのゼロ交差があります。

2200 Hzを2400 Hzに変更することにより、これらの問題の両方を解決できます。次に、シンボルは常に0度で開始および終了し(したがって、自動的に位相が連続します)、常に同じ数のゼロクロッシング2と4を持ちます。

1200 Hzおよび2400 Hz


ちょっとジム、あなたの詳細な答えをありがとう!私の変調器は実際にCPFSKを実行するため、不連続性は問題になりません。高調波が1200の倍数ほど重ならないため、意図的に1200および2200 Hzを選択しました。または、ここで間違っていますか?PLLは素晴らしいサウンドですが、私はそれらをどのように実装するのか本当に分かりません。ソフトウェアPLLに関する良い情報源を知っていますか?
パトリックオスシティ

@Patrickいいえ、1200 Hzと2400 Hzに倍音が重複することは正しいです。ただし、ゼロクロッシングのコンテキストでは、高調波は重要ではないと思います。そしていや、PLLについての良いオンライン情報源を知らないのではないかと思う。
ジム・クレイ

これは正しくありません。AFSK 1200はベル202に準拠しており、トーンは1200と2200である必要があります。送信側で不連続が発生することはありません。オープンソースのAFSK 1200変調器を確認してください。変調は、すべてのトーンの位相増分を追跡することで行われます。next_sample = sin(last_phase);
vz0

5

1200 Hzおよび2200 Hzの相関レシーバーを使用してAFSK(Bell 202標準)用のデコーダーを作成しましたが、非常に良い結果が得られました。

cos

結果として得られる振幅は、信号の位相からまったく独立しており、出力SNRは非常に良好です。


これはまさに私が以前に試したものであり、「古典的な非干渉性復調器」と呼んでいるものです。実装に誤りがあるかもしれませんが、処理が遅いためにバッファオーバーフローが発生するのではないかと心配しています。とにかくありがとう!
パトリックオスシティ

0

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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.