C# FSK解调-解析日本EWS数据

C# FSK解调-解析日本EWS数据,c#,algorithm,audio,signal-processing,frequency-analysis,C#,Algorithm,Audio,Signal Processing,Frequency Analysis,【这不是重复。类似的问题是关于人们控制源数据的场景。我没有。】 在日本,有一种叫做“紧急警报广播系统”的东西。当它被激活时,看起来是这样的: 在上面的视频中,大约在2:37,发送FSK调制信号。我想解析这个信号;i、 e.给定一个包含信号的WAV文件,我希望最终得到一个包含0和1的StringBuilder,以便以后处理它们。我有二进制数据和所有数据的规范,但问题是我对音频编程一无所知( 这只是一个爱好项目,但我迷上了。电视和广播制作者可以接收到这个信号,让他们的设备对它做出反应,所以不会那么难

【这不是重复。类似的问题是关于人们控制源数据的场景。我没有。】

在日本,有一种叫做“紧急警报广播系统”的东西。当它被激活时,看起来是这样的:

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

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

有关信号的事实:

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

  • 编写一个简单的RIFF解析器,它接受8位mono WAV文件,并允许我从中获取样本。我已经测试过了,它可以正常工作
  • 采集15.625ms样本的回路,以及:
  • 使用RMS查找两秒钟的静默
  • 使用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();
    双[]样本=新双[(int)(样本数*16.625D)];
    int index=0,readTo=/*当前偏移量+RIFF subChunk2Size*/;
    二进制读取器br=/*在PCM数据的开始处*/;
    while(br.BaseStream.Position150)
    {
    //到达EWS广播的结尾。
    解析(ews_bits.ToString());
    /*…重置状态;返回寻找静默*/
    }
    去做;
    }
    /******信号不是沉默******/
    if(静默\u计数>120&&state==ParserState.SearchingSilence)
    state=ParserState.Decoding;
    if(state==ParserState.Decoding)
    {
    音频处理器。解码(参考样本、采样器、参考ews_位);
    bool continue_decoding=/*检查前20位是否有签名*;
    如果(继续解码)转到完成;
    //如果我们到了这里,我们正在解码一个垃圾信号。
    state=ParserState.SearchingSilence;
    }
    /*还不够安静*/
    沉默计数=0;
    完成:
    指数=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;
    }
    
    publicstaticvoid解码(ref-double[]样本,int-sampleRate,ref-StringBuilder比特持有者)
    {
    双频640=戈尔特泽尔震级(参考样品,640,取样器);
    double freq_1024=Goertzel震级(参考样本,1024,采样器);
    中频(频率640>频率1024)
    比特持有人。追加(“0”);
    其他的
    比特持有人。附加(“1”);
    }
    公共静电干扰(参考双[]样本)
    {
    //功率均方根=sqrt(和(x^2)/N)
    双和=0;
    for(int i=0;i
    谢谢阅读。我希望你能帮助我。

    这是我将如何做的(高级描述)

  • 把你的信号通过一个电话
  • 在640Hz+1024Hz处寻找稳定的峰值(我会说至少+/-10Hz)
  • 如果信号稳定约10毫秒(稳定是指约95%的样本在640Hz+/-10Hz(或1024Hz+/-10Hz)的相同范围内),则将其作为音调检测。使用此检测也可同步计时器,告知您何时预期下一个音调
  • 我知道了
    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;
    }