FSK解调 - 解析日本EWS数据

aaa*_*aaa 8 c# algorithm audio signal-processing frequency-analysis

【这不是重复的.类似的问题涉及人们可以控制源数据的情况.我不.】

在日本有一种叫做"紧急警告广播系统"的东西.激活时看起来像这样:http://www.youtube.com/watch?v = 9hjlYvp9Pxs

在上面的视频中,在2:37左右,发送FSK调制信号.我想解析这个信号; 即,给定一个包含信号的WAV文件,我想最终得到一个包含0和1的StringBuilder,以便稍后处理它们.我有二进制数据的规范和所有,但问题是我对音频编程一无所知.:(

这只是一个爱好项目,但我迷上了.电视和广播制造商可以接收到这个信号并让他们的设备做出反应,所以它不会那么难,对吧?:(

有关信号的事实:

  • 标记音为1024Hz,停止音为640Hz
  • 每个音调长15.625ms
  • 信号开始前和结束后2秒暂停(可能用于检测目的)

到目前为止我做了什么:

  1. 编写一个简单的RIFF解析器,接受8位单声道WAV文件,并允许我从中获取样本.我测试了它,它的工作原理.
  2. 需要15.625ms样本的循环和:
    1. 使用RMS寻找两秒钟的沉默
    2. 使用Goertzel算法确定信号是1024Hz还是640Hz

我遇到的问题:

  • 根据测试数据,在循环期间吞下0和1.
    • 鉴于信号的清晰度(YouTube-to-MP3 rip),这不应该发生.
    • 如果我在Audacity中生成重复的01序列30次,我的程序将获取01对中的大约10对,而不是30对
  • 有时交换0和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;
}
Run Code Online (Sandbox Code Playgroud)

音频处理器只是一个类:

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;
}
Run Code Online (Sandbox Code Playgroud)

谢谢阅读.我希望你能帮助我.

MrS*_*h42 2

这就是我要做的(高级描述)

  1. 通过FFT运行信号
  2. 寻找大约 640Hz+1024Hz 处的稳定峰值(我想说至少 +/- 10Hz)
  3. 如果信号稳定约 10 毫秒(稳定是指大约 95% 的样本处于相同范围 640Hz+/-10Hz(或 1024Hz+/-10Hz),则将其视为音调的检测。也可使用此检测来同步您的计时器,告诉您何时期待下一个提示音。