【问题标题】:FSK Demodulation - Parsing Japanese EWS DataFSK 解调 - 解析日本 EWS 数据
【发布时间】:2013-12-23 11:50:21
【问题描述】:

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

在日本有一种叫做“紧急警报广播系统”的东西。激活后是这样的:http://www.youtube.com/watch?v=9hjlYvp9Pxs

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

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

关于信号的事实:

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

到目前为止我做了什么:

  1. 编写一个简单的 RIFF 解析器,它接受 8 位单声道 WAV 文件并允许我从中获取样本。我已经测试过了,它可以工作。
  2. 一个循环需要 15.625 毫秒的样本,并且:
    1. 使用 RMS 寻找两秒钟的静默时间
    2. 使用 Goertzel 算法来确定信号是 1024Hz 还是 640Hz

我遇到的问题:

  • 0s 和 1s 在循环期间被吞下,具体取决于测试数据。
    • 考虑到信号的清晰度(YouTube 到 MP3 翻录),这不应该发生。
    • 如果我在 Audacity 中生成重复的 01 序列 30 次,我的程序将选取大约 10 个 01 对,而不是 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;
}

音频处理器只是一个类:

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;
}

感谢阅读。我希望你能帮助我。

【问题讨论】:

  • 因此,您使用的数据是从使用有损压缩的数据转换而来的,然后上传到 YouTube。然后你把它翻录成 MP3,导致更多的有损压缩。然后你把它转换成 WAV,它必须用一些东西来填补那些有损的空白。很有可能你没有得到准确的 1024 Hz。如果您在音频编辑器中检查信号看起来是否正确?我怀疑你需要做一些近似匹配,正如史密斯先生在他的回答中提到的那样。

标签: c# algorithm audio signal-processing frequency-analysis


【解决方案1】:

在重写了示例解析循环和静默检测部分后,我现在已经完成了大约 90% 的工作。我的实现中有两个主要问题。首先是静音检测器过于急切,所以我将其从处理每毫秒的样本更改为每半毫秒的样本。这让我完全了解了 FSK 数据。

下一个问题是,我当时天真地认为我可以让解调器查看 15.625 毫秒的样本,因为它通过 WAV 文件自行工作。事实证明,虽然这对于前 90 位左右的效果很好,但最终音调会比预期的更长或更短,并且解调器会不同步。当前代码找到并纠正了这种时序不匹配的 13 位。特别容易受到这种影响的是信号从标记变为空间的位置,反之亦然。

猜猜“analog”一词包含“anal”是有原因的。它是。我真的希望我能更多地了解信号理论和数字信号处理。 :(

我是如何发现这一切的:我导入了 MP3 并使用 Audacity 将其修剪为 FSK 部分。然后我让 Audacity 为每一位生成标签。之后,我根据标签突出显示了位。

【讨论】:

    【解决方案2】:

    我会这样做(高级描述)

    1. 通过FFT 运行您的信号
    2. 寻找大约 640Hz+1024Hz 的稳定峰值(我会说至少 +/- 10Hz)
    3. 如果信号在大约 10 毫秒内保持稳定(稳定的意思是大约 95% 的样本在 640Hz+/-10Hz(或 1024Hz+/-10Hz)的同一范围内),则将其作为音调检测。使用此检测还可以同步您的计时器,告诉您何时期待下一个音调。

    【讨论】:

      猜你喜欢
      • 2016-06-16
      • 2016-08-22
      • 2011-04-12
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多