8

【これは複製ではありません。同様の質問は、人々がソース データを制御できるシナリオに関するものです。しません】</p>

日本には「緊急警報放送システム」というものがあります。有効にすると次のようになります: http://www.youtube.com/watch?v=9hjlYvp9Pxs

上の動画では2:37あたりからFSK変調された信号が送られてきます。この信号を解析したい。つまり、信号を含む WAV ファイルが与えられた場合、後で処理するために 0 と 1 を含む StringBuilder を作成したいと考えています。バイナリ データなどの仕様は把握していますが、問題はオーディオ プログラミングについて何も知らないことです。:(

これは単なる趣味のプロジェクトですが、夢中になりました。テレビやラジオのメーカーは、この信号を受信して​​、家電製品に反応させることができるので、それほど難しいことではありませんよね? :(

信号に関する事実:

  • マークトーンは1024Hz、ストップトーンは640Hz
  • 各トーンの長さは 15.625ms
  • 信号の開始前と終了後に 2 秒間の一時停止 (おそらく検出のため)

私がこれまでにやったこと:

  1. 8 ビットのモノラル WAV ファイルを受け入れ、それらからサンプルを取得できる単純な RIFF パーサーを作成します。私はそれをテストしましたが、動作します。
  2. 15.625 ミリ秒のサンプルを取得するループ:
    1. RMS を使用して 2 秒間の無音を探します
    2. Goertzel アルゴリズムを使用して、信号が 1024Hz か 640Hz かを決定します。

私が抱えている問題:

  • テスト データによっては、ループ中に 0 と 1 が飲み込まれます。
    • 信号の明瞭さ (YouTube から MP3 へのリッピング) を考えると、それは起こらないはずです。
    • Audacity で繰り返し 01 シーケンスを 30 回生成すると、プログラムは 01 ペアのうち 30 ではなく約 10 を取得します。
  • 時々 0 と 1 が入れ替わる (上記の副作用?)
  • 1 つのテスト サウンド ファイルで動作するようにコードを微調整すると、他のテスト サウンド ファイルが動作しなくなります

私の質問:

  • FSK デコードがソフトウェアで適切に行われる方法について、概要を説明してくれる人はいますか?
  • 信号を 640Hz+1024Hz に制限し、他のすべてをミュートする何らかのフィルターを適用する必要がありますか?
  • タイミングを正しく保つための最良のアプローチは何ですか? 多分私はそれを間違っていますか?
  • この種のオーディオ処理に関する初心者向けの文献へのリンクはありますか? 私は本当に学び、これを機能させたいと思っています。

サンプルを読み取るコードは次のとおりです (簡略化)。

StringBuilder ews_bits = new StringBuilder();
double[] samples = new double[(int)(samplesPerMs * 16.625D)];
int index = 0, readTo = /* current offset + RIFF subChunk2Size */;
BinaryReader br = /* at start of PCM data */;

while (br.BaseStream.Position < readTo)
{
    switch (bitsPerSample / 8)
    {
        case 1: // 8bit
            samples[index++] = ((double)br.ReadByte() - 127.5D) / 256D;
            break;
        case 2: // 16bit
            samples[index++] = (double)br.ReadInt16() / 32768D;
            break;
    }

    if (index != samples.Length)
        continue;

    /****** The sample buffer is full and we must process it. ******/

    if (AudioProcessor.IsSilence(ref samples))
    {
        silence_count++;
        if (state == ParserState.Decoding && silence_count > 150)
        {
            // End of EWS broadcast reached.
            EwsSignalParser.Parse(ews_bits.ToString());

            /* ... reset state; go back looking for silence... */
        }
        goto Done;
    }

    /****** The signal was not silence. ******/

    if (silence_count > 120 && state == ParserState.SearchingSilence)
        state = ParserState.Decoding;

    if (state == ParserState.Decoding)
    {
        AudioProcessor.Decode(ref samples, sampleRate, ref ews_bits);

        bool continue_decoding = /* check first 20 bits for signature */;
        if (continue_decoding) goto Done;

        // If we get here, we were decoding a junk signal.
        state = ParserState.SearchingSilence;
    }

    /* Not enough silence yet */
    silence_count = 0;
Done:
    index = 0;
}

オーディオ プロセッサは、次のような単なるクラスです。

public static void Decode(ref double[] samples, int sampleRate, ref StringBuilder bitHolder)
{
    double freq_640 = GoertzelMagnitude(ref samples, 640, sampleRate);
    double freq_1024 = GoertzelMagnitude(ref samples, 1024, sampleRate);

    if (freq_640 > freq_1024)
        bitHolder.Append("0");
    else
        bitHolder.Append("1");
}

public static bool IsSilence(ref double[] samples)
{
    // power_RMS = sqrt(sum(x^2) / N)

    double sum = 0;

    for (int i = 0; i < samples.Length; i++)
        sum += samples[i] * samples[i];

    double power_RMS = Math.Sqrt(sum / samples.Length);

    return power_RMS < 0.01;
}


/// <remarks>http://www.embedded.com/design/embedded/4024443/The-Goertzel-Algorithm</remarks>
private static double GoertzelMagnitude(ref double[] samples, double targetFrequency, int sampleRate)
{
    double n = samples.Length;
    int k = (int)(0.5D + ((double)n * targetFrequency) / (double)sampleRate);
    double w = (2.0D * Math.PI / n) * k;
    double cosine = Math.Cos(w);
    double sine = Math.Sin(w);
    double coeff = 2.0D * cosine;

    double q0 = 0, q1 = 0, q2 = 0;

    for (int i = 0; i < samples.Length; i++)
    {
        double sample = samples[i];

        q0 = coeff * q1 - q2 + sample;
        q2 = q1;
        q1 = q0;
    }

    double magnitude = Math.Sqrt(q1 * q1 + q2 * q2 - q1 * q2 * coeff);

    return magnitude;
}

読んでくれてありがとう。あなたが私を助けてくれることを願っています。

4

2 に答える 2

2

これが私が行う方法です(高レベルの説明)

  1. 信号をFFTで実行する
  2. 約 640Hz+1024Hz で安定したピークを探します (少なくとも +/- 10Hz と言えます)
  3. 信号が約 10 ミリ秒間安定している場合 (安定とは、サンプルの約 95% が同じ範囲 640Hz+/-10Hz (または 1024Hz+/-10Hz) にあることを意味します) をトーンの検出と見なします。この検出を使用して、次のトーンをいつ期待するかを教えてくれるタイマーを同期させます。
于 2013-12-06T08:11:33.797 に答える