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

时间:2019-03-01 11:43:16

标签: c++ optimization signal-processing sin cos

我需要实现一个实时同步正交检波器。检测器接收来自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;
   }
};

我的问题是sincos的计算需要太多时间。建议我使用预先计算的sincos表,但是可用的ADC采样频率不是w的倍数,因此存在碎片拼接问题。 sincos的计算是否有其他快速选择?对于有关如何提高此代码性能的任何建议,我将不胜感激。

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

2 个答案:

答案 0 :(得分:7)

我知道一个适合您的解决方案。回忆一下正弦和余弦的学校公式,求出角度之和:

<template>
  <div class="notifications" v-if="show">
    <v-layout>
      <v-flex xs12 sm6 offset-sm3>
        <v-card flat color="green">

          <v-card-title primary-title>
            <div>
              <h3 class="headline">Neu Benutzer angelegt</h3>
              <div> {{ card_text }} </div>
            </div>
          </v-card-title>

          <v-card-actions>
            <div class="close"><v-btn @click="removeMessage(2)">Ok</v-btn></div>
          </v-card-actions>
        </v-card>
      </v-flex>
    </v-layout>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        card_text: 'Success!',
        show:true;
      }
    },
    methods: {
      removeMessage(seconds) {
         setTimeout(()=> this.show = false, seconds * 1000);
      },
    },
  };

</script>

假设sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b) cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b) wdt角的一小部分增量,那么我们下次获得wtsin的递归计算公式:

cos

我们只需要计算一次sin(wt + wdt) = sin(wt) * cos(wdt) + cos(wt) * sin(wdt) cos(wt + wdt) = cos(wt) * cos(wdt) - sin(wt) * sin(wdt) 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; } }; sin(wt)准确,但是我使用了它并且效果很好。

答案 1 :(得分:3)

如果可以使用std :: complex,则实现将变得更加简单。技术方面,它与@Dmytro Dadyka的解决方案相同,因为复数正在以这种方式工作。如果优化程序运行良好,则应同时运行。

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);
    }
};