DCF77解码器与嘈杂信号对比

19

我已经几乎完成了我的开源DCF77解码器项目。起初,我注意到标准(Arduino)DCF77库在嘈杂的信号下表现非常差。尤其是当天线靠近计算机或我的洗衣机运行时,我从未能够从解码器中获取时间。

我的第一个尝试是向输入信号添加(数字)指数滤波器+触发器。

虽然这显着改善了情况,但仍不是真正的好。然后我开始阅读一些关于数字信号处理的标准书籍,特别是克劳德·埃尔伍德·香农的原始作品。我的结论是,适当的方法是根本不要“解码”信号,因为它(除了闰秒之外)是完全预先知道的。相反,更恰当的方法是将接收到的数据与本地合成的信号匹配,并确定正确的相位。这反过来会将有效带宽减少几个数量级,从而显著降低噪音。

相位检测意味着需要快速卷积。高效卷积的标准方法当然是快速傅里叶变换。但是我正在为Arduino / Atmega 328实现。因此,我只有2k RAM。因此,我开始堆叠匹配的锁相环滤波器,而不是使用FFT的直接方法。我在这里记录了不同的项目阶段:

  • 添加本地时钟以应对信号丢失
  • 使用本地合成信号以更快地重新锁定信号
  • 我在互联网上进行了广泛的搜索,但没有找到类似的方法。但我想知道是否存在类似(或更好的)实现。或者是否存在这种信号重建的研究。

    我不搜索的内容:设计优化代码以接近香农极限。我也不需要关于DCF77上超级PRNG代码的信息。我也不需要有关“匹配滤波器”的提示,因为我的当前实现是匹配滤波器的近似值。除非它们解决紧密的CPU和RAM约束问题,否则不需要针对特定Viterbi解码器或Trellis方法的特定提示。

    我正在寻找的内容:是否有其他用于解码像DCF77这样的信号的非平凡算法的描述/实现,在存在显着噪声的情况下具有有限的CPU和RAM? 也许在互联网出现之前的一些书籍或论文中?


    这不是我的专业领域,但您是否考虑过在双状态马尔可夫链上使用Viterbi算法替换低通滤波器和触发器? - David Eisenstat
    这似乎是我见过的最过度设计的时钟,但我非常喜欢它。一定是一个有趣的项目。等我有更多时间时,必须阅读你所有的博客文章。使用预测波形的卷积似乎接近于最优解。你有读过卡尔曼滤波吗?这与你所做的有些相似,大致的想法是模拟你观察到的系统,然后将模拟测量值与真实测量值进行比较,根据差异更新模型状态。 - Bas Swinckels
    关于维特比算法和卡尔曼滤波,你是正确的。这些都是可能进行调查的途径。但由于内存和CPU限制,我没有尝试过它们。如果有人在如此薄弱的CPU上尝试过这个,我会很愿意了解实现情况。关于过度设计:其中有一些奇怪的满足感。 "任何值得做的事都值得过度设计;)"完全知道信号的卷积方法也称为“最优滤波器”。唯一的问题是由于内存限制,我只能近似处理。 - Udo Klein
    转念一想,我不确定卡尔曼滤波是否适用,因为通常情况下你会测量一些连续的物理参数而不是包含数字调制的内容。研究GPS接收器的工作可能更有趣。它们进行某种卷积并通常包含一个小型微处理器。主要区别在于它们使用真实的PRN生成器,而在您的情况下,您本地重建了定时信号,这使得其略微不太随机。卷积部分应该是类似的。 - Bas Swinckels
    两状态维特比算法的资源消耗不应该比低通滤波器加触发器更大。 - David Eisenstat
    我不明白双状态维特比算法如何有所改进。这个递归意味着你需要更多的状态。 - Udo Klein
    2个回答

    2

    2
    你考虑过使用芯片匹配滤波器来执行卷积吗?

    http://en.wikipedia.org/wiki/Matched_filter

    它们几乎可以轻松实现,因为每个芯片/位周期都可以实现为一个加减延迟线(使用循环缓冲区)。
    对于一个未知序列(但已知频率)的方波(也适用于其他波形,但不太优化),可以实现一个简单的如下:
    // Filter class
    template <int samples_per_bit>
    class matchedFilter(
       public:
          // constructor
          matchedFilter() : acc(0) {};
    
          // destructor
          ~matchedFilter() {};
    
          int filterInput(int next_sample){
            int temp;
            temp = sample_buffer.insert(nextSample);
            temp -= next_sample;
            temp -= result_buffer.insert(temp);
            return temp;
          };
    
       private:
         int acc;
         CircularBuffer<samples_per_bit> sample_buffer;
         CircularBuffer<samples_per_bit> result_buffer;
    );
    
    // Circular buffer
    template <int length>
    class CircularBuffer(
       public:
          // constructor
          CircularBuffer() : element(0) {
             buffer.fill(0);
          };
          // destructor
          ~CircularBuffer(){};
    
          int insert(int new_element){
            int temp;
            temp = array[element_pos];
            array[element_pos] = new_element;
            element_pos += 1;
            if (element_pos == length){
               element_pos = 0;
            };
            return temp;
          }
    
       private:
          std::array<int, length> buffer;
          int element_pos;
    );
    

    正如你所看到的,从资源方面来说,这是相对微不足道的。如果你想要特定的波形,你可以将它们级联在一起以获得更长的相关性。

    我已经在实现一个递归匹配滤波器栈。我知道这种方法。然而,你所建议的直接方法在内存限制紧张的情况下会失败。使用你的方法,对于2k的采样,最多只能采样1000s,这比我目前能够做到的更糟糕。 - Udo Klein
    我对您如何构建匹配滤波器以在不存储1000s数据的情况下处理更多数据感兴趣。如果需要1000s,您的信噪比一定很糟糕。这是40dB的增益,对吧? - OllieB
    整个实验的目的是尽可能地提高信噪比。有关详细信息,请阅读我的博客。我已经记录了一切。关键是数据的开始始终在第二个开始。因此,我可以匹配这个,然后递归处理其余部分。但是,我认为1000s是数字增益30 dB而不是40 dB。 - Udo Klein
    然而,由于我只能近似匹配滤波器,因此我无法获得30dB。但是我可以采样更长时间,从而获得更好的增益。 - Udo Klein

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接