首页 > 解决方案 > 如何找到脉冲的频率?

问题描述

我目前面临关于脉冲频率检测的问题。我有一个程序以这种方式记录设备的网络活动。

"device": {
  "mac": "b8:27:eb:5c:27:13",
  "activity": [
    {
      "ip" : "224.0.0.251",
      "port" : "5353",
      "history" : [
        {
          "timestamp" : "2019-09-23T09:34:30.898836",
          "pktsSent" : 6,
          "pktsReceived" : 0,
          "duration" : "3.972347"
        },
        ...
      ]
    }
  ]    
}

我目前正在尝试实现的是使用其历史来检测活动的频率(如果它不是周期性的,则不检测)。想起我的工程学习课程,我想到了 FFT。这是我到目前为止在 Python 中的内容:

import numpy as np
import dateutil.parser as dateutil

class FrequencyAnalyzer:
  ...

  def analyze(self, device_mac, activity) -> int:

    if not 'history' in activity:
      raise FrequencyAnalyzerError("No history could be found for the device '%s' and its activity '%s:%s'" %(device_mac, activity['ip'], activity['port']))

    start = int(dateutil.parse(activity['history'][0]['timestamp']).timestamp())
    stop = int(dateutil.parse(activity['history'][-1]['timestamp']).timestamp())
    size = stop - start

    # Array of timestamps 
    timestamps = np.fromiter( [dateutil.parse(history['timestamp']).timestamp() for history in activity['history'] ], int)

    # Array of data, setting 1 if there was an activity at this timestamp, else 0
    data = np.fromiter( [ 1 if x in timestamps and x != start else 0 for x in range(start, stop)], int)

    # Framerate of 1Hz
    frate = 1

    fft = np.fft.fft(data)

    freqs = np.fft.fftfreq(len(fft))
    print(freqs.min(), freqs.max())

    # Find the peak in the coefficients
    idx = np.argmax(np.abs(fft))
    freq = freqs[idx]
    freq_in_hertz = abs(freq * frate)
    print("Freq in Hz = %s" %str(freq_in_hertz))

精度不是那么重要。现在我已将其固定为 1 秒,但也可能是 1 分钟。

在我正在测试的活动中,每 3 分钟和其他时间戳有一个 mDNS 活动。我想返回所有相关的频率,但现在我想得到 1 个正确的频率。

找到的频率始终为零。我记得使用 FFT 分析诸如音频之类的信号,但不适用于脉冲。不应该是同一个方信号(谐波分解)吗?

这是使用上述程序的我的信号的表示: 快速傅里叶变换

我想知道这是否是正确的方法。因为它应该在嵌入式系统中运行,所以我不希望使用像 SciPy 这样有点重的框架。

有什么建议吗?

标签: pythonnumpysignal-processingfft

解决方案


0-Hz 峰值是平均值。因为你的冲动只是积极的,所以平均值也是积极的。所以你应该忽略那个频率。

另一个问题是在执行 FFT 之前您没有对数据应用任何窗口函数。这将导致看起来非常嘈杂的频谱。窗函数将权衡光谱灵敏度(基本上,峰有多窄)以提高信噪比。一个有效且非常容易实现的窗口是Hann 函数

另一件需要注意的事情是,如果您希望能够检测一个周期为 1 秒的 1 Hz 信号,您需要在数十秒内执行 FFT 以获得该频率附近的合理频谱分辨率。如果你想检测每 3 分钟发生一次的事情,那么你应该对大约一小时的数据进行 FFT。

如果您认为 SciPy 对于您的嵌入式设备来说已经太重了,那么您应该考虑将您的程序移植到 C 或 C++,并完全摆脱 Python。另一个问题是您的日志显然是 JSON 格式,编码和解码可能需要相当长的时间,而且它不是最紧凑的格式。考虑使用像MessagePack这样的二进制格式,或者只是以pcap格式存储数据包日志并直接在峰值检测器程序中处理它。许多编程语言都有处理这些格式的库。


推荐阅读