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

Sta*_*dov 6 c++ optimization trigonometry signal-processing

我需要实现一个实时同步正交检波器.检测器接收输入数据流(来自PCI ADC)并返回谐波的幅度w.有简化的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;
   }
};
Run Code Online (Sandbox Code Playgroud)

我的问题是sincos计算花费太多时间.我被建议使用预先计算sincos表格,但可用的ADC采样频率不是倍数w,因此存在片段拼接问题.有没有快速的替代方案sincos计算方法?如果有关如何提高此代码性能的任何建议,我将不胜感激.

UPD 不幸的是,我在代码中错了,删除了过滤调用,代码已经失去了意义.谢谢Eric Postpischil.

Dmy*_*yka 7

我知道一个适合你的解决方案.回想一下角度总和的正弦和余弦的学校公式:

sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b)
cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b)
Run Code Online (Sandbox Code Playgroud)

假设wdt是小增量wt的角度,那么我们得到的递推计算公式为sin,并cos为下一次:

sin(wt + wdt) = sin(wt) * cos(wdt) + cos(wt) * sin(wdt)
cos(wt + wdt) = cos(wt) * cos(wdt) - sin(wt) * sin(wdt)
Run Code Online (Sandbox Code Playgroud)

我们只需计算一次sin(wdt)cos(wdt)值.对于其他计算,我们只需要加法和乘法运算.递归可以从任何时间开始继续,因此我们可以用精确计算的时间替换值,以避免无限期的错误累积.

有最终代码:

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;
   }
};
Run Code Online (Sandbox Code Playgroud)

请注意,这种递归计算提供的结果不如准确sin(wt) cos(wt),但我使用它并且效果很好.

  • 根据任务的不同,您可能需要偶尔重新计算sin和cos,因此错误不会无限累积. (4认同)
  • 重新计算不正确.'wt'只能在ist'2*M_PI'时设置为0.所以'wt - = 2*M_PI更合适.它应该在'sinwt'和'coswt'重新计算之前设置. (2认同)