减少实时绘图数据的有效方法

Effective way of reducing data for real-time plot

本文关键字:有效 方法 数据 绘图 实时      更新时间:2023-10-16

我正在Windows窗体(VC++2010)中开发科学应用程序,它控制着相对较新的电子设备。我通过用C编写的附加的包装库来控制它。在初始设置所有参数后,这个应用程序会触发设备中的测量。然后,它以显著的速度向我的应用程序发送超过20万个int样本的巨大数据——假设它是每秒50个数据集。

现在,我需要使用Windows窗体图表实时绘制数据。将750个样本以大约30FPS的速率绘制在图表中是完美的。我遇到的问题在于在不损失绘图可靠性的情况下快速缩减数据库的算法。

我的想法(数据围绕值=127振荡):

  1. 只需选择第(200000/750)点即可选择750点

  2. 将数据分组并计算平均值

  3. 将数据分组,然后选择最大值或最小值(基于整体分组位置——如果大多数数据高于127——则选择最小值,否则选择最大值)。

考虑到我必须以实时速度绘制数据,并且绘制不应错过我们有任何重要信号(看起来像一种变窄的调制正弦波)的点,这些解决方案中的哪一个(如果有的话)是最好的?有什么更好的方法吗?

最后一个问题是:考虑到我总是有相同的收集数据缓冲区(设备只是不断地用新数据覆盖这个缓冲区),我是否应该考虑使用指向我巨大数据缓冲区的指针表或数据副本作为绘图数据?

这是我的第一篇帖子,所以如果帖子的风格有什么问题,请通知我。

我开发了一个应用程序,可以从16个通道读取256Hz(256个样本/秒)的数据,并将其显示在16个不同的图表中。实时绘制所有数据的最佳方式是使用单独的线程来更新绘图。以下是可能对您也有用的解决方案(c#中)。

读取新数据时,数据将存储在列表或数组中。由于它是实时数据,时间戳也在这里生成。使用所获取数据的采样率:timeStamp=timeStamp+sampleIdx/sampleRate;

public void OnDataRead(object source, EEGEventArgs e)
{
if ((e.rawData.Length > 0) && (!_shouldStop))
{
lock (_bufferRawData)
{
for (int sampleIdx = 0; sampleIdx < e.rawData.Length; sampleIdx++)
{
// Append data
_bufferRawData.Add(e.rawData[sampleIdx]);
// Calculate corresponding timestamp
secondsToAdd = (float) sampleIdx/e.sampleRate;
// Append corresponding timestamp
_bufferXValues.Add( e.timeStamp.AddSeconds(secondsToAdd));
}
}

然后,创建一个每N毫秒睡眠一次的线程(100毫秒适合我显示2秒的数据,但如果我想显示10秒,我需要将线程的睡眠时间增加到500毫秒)

//Create thread
//define a thread to add values into chart
ThreadStart addDataThreadObj = new ThreadStart(AddDataThreadLoop);
_addDataRunner = new Thread(addDataThreadObj);
addDataDel += new AddDataDelegate(AddData);
//Start thread
_addDataRunner.Start();

最后,更新图表并使线程每N毫秒休眠一次

private void AddDataThreadLoop()
{
while (!_shouldStop)
{
chChannels[1].Invoke(addDataDel);
// Sleeep thread for 100ms
Thread.Sleep(100); 
}
}

数据将每100ms 添加到图表中

private void AddData()
{
// Copy data stored in lists to arrays
float[] rawData;
DateTime[] xValues;
if (_bufferRawData.Count > 0)
{
// Copy buffered data in thread-safe manner
lock (_bufferRawData)
{
rawData = _bufferRawData.ToArray();
_bufferRawData.Clear();
xValues = _bufferXValues.ToArray();
_bufferXValues.Clear();
}
for (int sampleIdx = 0; sampleIdx < rawData.Length; sampleIdx++)
{
foreach (Series ptSeries in chChannels[channelIdx].Series)
// Add new datapoint to the corresponding chart (x, y, chartIndex, seriesIndex)
AddNewPoint(xValues[sampleIdx], rawData[sampleIdx], ptSeries);
}
}
}