如何在 Prometheus/Micrometer 指标中记录二维 FFT 数据?

问题描述 投票:0回答:1

我想用几部安卓手机做远程音频检测器,需要采集几部不同手机的数据,用Prometheus/Grafana集中分析。

我有一个 Android 应用程序(使用 Noise)成功录制音频,并对其进行分析(使用 Kiss FFT)以计算每个频率的幅度。

噪声以

FloatArray
的形式返回 FFT,Android 应用程序使用它来将音频频率可视化为热图和直方图。

import android.media.AudioRecord
import com.paramsen.noise.Noise
import com.paramsen.noise.sample.R
import com.paramsen.noise.sample.source.AudioSource

/** Subscribe to microphone updates */
private fun start(recorder: AudioRecord) {

  val src = AudioSource(recorder).stream()
  val noise = Noise.real(SAMPLE_SIZE)

  //FFT Views
  disposable.add(src.observeOn(Schedulers.newThread())
    .map { 
      // compute the FFT
      noise.fft(it, FloatArray(SAMPLE_SIZE + 2)) 
    }
    .subscribe({ fft: FloatArray ->
      // forward the FFT data to the visualisers
      fftHeatMapView.onFFT(fft)
      fftBandView.onFFT(fft)
    }, { e -> Log.e(TAG, e.message ?: "null") })
  )
}

const val RATE_HZ = 44100
const val SAMPLE_SIZE = 4096

FFT 数据按预期呈现(或多或少 - 我认为窗口函数需要调整)。

下一步是尝试将FFT数据转发给Prometheus。我为 FFT 数据创建了一个新的处理程序,并创建了一个 Prometheus 直方图指标。

object FFTMetrics : FFTView {

  private val registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)

  private val fftDist = DistributionSummary
    .builder("fft.dist")
    .description("FFT distribution")
    .baseUnit("hertz")
    //.scale(100.0)
    .register(registry)

  override fun onFFT(fft: FloatArray) {
    // TODO
  }
}

然而,我唯一可用的选择是记录一次读数,在这种情况下这是不正确的。 FFT 数组的每个浮点值代表记录频率的magnitude。该数组是二维的,其中索引代表赫兹,而千分尺指标仅允许记录一维值。

override fun onFFT(fft: FloatArray) {
  fft.forEach { magnitude: Float ->
    // this is incorrect:
    summary.record(magnitude.toDouble())
  }
}

我想做的是多次增加单个桶

override fun onFFT(fft: FloatArray) {
  fft.forEachIndexed { i: Int, magnitude: Float ->
    val hz = computeHertzForBin(i)
    // I want to record multiple readings per bin:
    summary.record(hz, magnitude)
  }
}

我曾尝试用 for 循环实现等价物,但它速度慢且笨拙,并且不必要地需要在数字类型之间进行转换。

override fun onFFT(fft: FloatArray) {
  fft.forEachIndexed { i, magnitude ->
    val hz = computeHertzForBin(i)
    (0..magnitude.toLong()).forEach { j ->
      summary.record(hz.toDouble())
    }
  }
}

是否有更好的方法在 Micrometer 或 Prometheus 中记录二维数据?

版本

我在用

  • 科特林 1.6.21
  • 安卓 7.3.1
  • 千分尺 1.10.4
kotlin histogram prometheus fft micrometer
1个回答
0
投票

直接回答你的问题,普罗米修斯不接受多维值。为此,您应该使用标签。在你的例子中,在我看来 Hz 将是一个标签,而幅度将是它的值。例如,假设您的指标称为

signal_magnitude
,我认为您是在提议:

signal_magnitude = 10KHz, 10.11

(表示频率 10KHz 的幅度为 10.11 - 我发明的所有任意数字组成示例)

由于频率集合已知且有限,适合作为标签:

signal_magnitude{frequency=100000} = 10.11

在这种情况下,您的导出器会向 Prometheus 提供一组指标,例如:

signal_magnitude{frequency=10000} = 10.11
signal_magnitude{frequency=20000} = 5.0
signal_magnitude{frequency=30000} = 3.76
signal_magnitude{frequency=40000} = 18.27
signal_magnitude{frequency=50000} = 8.943

为了分析这一点,可以使用

frequency
标签作为热图图表的维度。

请注意,重要的是要考虑一个标签可以采用多少个不同的值,这将表示您的数据库的基数。由于度量名称和标签集的每种不同组合都会增加基数。这就是为什么标签值应该是有限的。

现在,根据其他想法,Prometheus 是否适合您的应用程序?

由于 Prometheus 每 N 秒抓取一次您提供给它的指标,如果您在不到 N 秒内多次更新指标值,您将丢失数据点。即使你愿意通过 Pushgateway 推送指标,它们也会在那里被覆盖并被 Prometheus 服务器刮掉一次。

此外,Prometheus 不允许回填,这意味着如果您正在后期处理此音频文件,则无法指定过去的数据点。数据点的时间戳由 Prometheus 服务器的抓取时间给出。

如果您需要“每个时间点的每个数据点”都在数据库中并且知道时间(抓取间隔)由 Prometheus 服务器(而不是您的应用程序)控制并且错过抓取意味着丢失数据点,我会考虑另一个此任务的时间序列数据库。也许 InfluxDB 更适合这种场景。

© www.soinside.com 2019 - 2024. All rights reserved.