简体   繁体   English

用于实时信号处理的快速C ++正弦和余弦替代方案

[英]Fast C++ sine and cosine alternatives for real-time signal processing

I need to implement a real-time synchronous quadrature detector. 我需要实现一个实时同步正交检波器。 The detector receives a stream of input data (from PCI ADC) and returns the amplitude of the harmonics w . 检测器接收输入数据流(来自PCI ADC)并返回谐波w的幅度。 There is simpified C++ code: 有简化的C ++代码:

double LowFreqFilter::process(double in)
{
   avg = avg * a + in * (1 - a);
   return avg;
}


class QuadroDetect
{
   double wt;
   const double wdt;

   LowFreqFilter lf1;
   LowFreqFilter lf2;

   QuadroDetect(const double w, const double dt) : wt(0), wdt(w * dt)
   {}

   inline double process(const double in)
   {
      double f1 = lf1.process(in * sin(wt));
      double f2 = lf2.process(in * cos(wt));
      double out = sqrt(f1 * f1 + f2 * f2);
      wt += wdt;
      return out;
   }
};

My problem is that sin and cos calculating takes too much time. 我的问题是sincos计算需要太多的时间。 I was advised to use a pre-calculated sin and cos table, but available ADC sampling frequencies is not multiple of w , so there is fragments stitching problem. 我被建议使用预先计算的sincos表,但可用的ADC采样频率不是w倍数,因此存在片段拼接问题。 Are there any fast alternatives for sin and cos calculations? 是否有任何快速的替代品sincos计算? I would be grateful for any advice on how to improve the performance of this code. 如果有关如何提高此代码性能的任何建议,我将不胜感激。

UPD Unfortunately, I was wrong in the code, removing the filtering calls, the code has lost its meaning. UPD不幸的是,我在代码中错了,删除了过滤调用,代码已经失去了意义。 Thanks Eric Postpischil. 谢谢Eric Postpischil。

I know a solution that can suit you. 我知道一个适合你的解决方案。 Recall the school formula of sine and cosine for the sum of angles: 回想一下角度总和的正弦和余弦的学校公式:

sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b)
cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b)

Suppose that wdt is a small increment of the wt angle, then we get the recursive calculation formula for the sin and cos for next time: 假设wdtwt角的一个小增量,那么我们得到下一次sincos的递归计算公式:

sin(wt + wdt) = sin(wt) * cos(wdt) + cos(wt) * sin(wdt)
cos(wt + wdt) = cos(wt) * cos(wdt) - sin(wt) * sin(wdt)

We need to calculate the sin(wdt) and cos(wdt) values only once. 我们只需计算一次sin(wdt)cos(wdt)值。 For other computations we need only addition and multiplication operations. 对于其他计算,我们只需要加法和乘法运算。 Recursion can be continued from any time moment, so we can replace the values with exactly calculated time by time to avoid indefinitely error accumulation. 递归可以从任何时间开始继续,因此我们可以用精确计算的时间替换值,以避免无限期的错误累积。

There is final code: 有最终代码:

class QuadroDetect
{
   const double sinwdt;
   const double coswdt;
   const double wdt;

   double sinwt = 0;
   double coswt = 1;
   double wt = 0;

   QuadroDetect(double w, double dt) :
      sinwdt(sin(w * dt)),
      coswdt(cos(w * dt)),
      wdt(w * dt)
   {}

   inline double process(const double in)
   {
      double f1 = in * sinwt;
      double f2 = in * coswt;
      double out = sqrt(f1 * f1 + f2 * f2);

      double tmp = sinwt;
      sinwt = sinwt * coswdt + coswt * sinwdt;
      coswt = coswt * coswdt - tmp * sinwdt;

      // Recalculate sinwt and coswt to avoid indefinitely error accumulation
      if (wt > 2 * M_PI)
      {
         wt -= 2 * M_PI;
         sinwt = sin(wt);
         coswt = cos(wt);
      }

      wt += wdt;
      return out;
   }
};

Please note that such recursive calculations provides less accurate results than sin(wt) cos(wt) , but I used it and it worked well. 请注意,这种递归计算提供的结果不如sin(wt) cos(wt) ,但我使用它并且效果很好。

If you can use std::complex the implementation becomes much simpler. 如果你可以使用std :: complex,那么实现就会变得简单得多。 Technical its the same solution as from @Dmytro Dadyka as complex numbers are working this way. 技术与@Dmytro Dadyka的解决方案相同,因为复杂的数字正在以这种方式工作。 If the optimiser works well it should be run the same time. 如果优化器运行良好,它应该在同一时间运行。

class QuadroDetect
{
public:
    std::complex<double> wt;
    std::complex <double> wdt;

    LowFreqFilter lf1;
    LowFreqFilter lf2;

    QuadroDetect(const double w, const double dt)
    :   wt(1.0, 0.0)
    ,   wdt(std::polar(1.0, w * dt))
    {
    }

    inline double process(const double in)
    {
        auto f = in * wt;
        f.imag(lf1.process(f.imag()));
        f.real(lf2.process(f.real()));
        wt *= wdt;
        return std::abs(f);
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM