Быстрые синусоидальные и косинусные C ++ альтернативы для обработки сигналов в реальном времени - PullRequest
0 голосов
/ 01 марта 2019

Мне нужно реализовать синхронный квадратурный детектор в реальном времени.Детектор получает поток входных данных (от 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;
   }
};

Моя проблема в том, что вычисления sin и cos занимают слишком много времени.Мне посоветовали использовать предварительно вычисленные таблицы sin и cos, но доступные частоты дискретизации АЦП не кратны w, поэтому существует проблема сшивания фрагментов.Существуют ли быстрые альтернативы для расчетов sin и cos?Буду благодарен за любые советы о том, как улучшить производительность этого кода.

UPD К сожалению, я ошибся в коде, убрав фильтрацию вызовов, код потерял свое значение,Спасибо Эрик Постпищил.

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Если вы можете использовать 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);
    }
};
0 голосов
/ 01 марта 2019

Я знаю решение, которое подойдет вам.Напомним школьную формулу синуса и косинуса для суммы углов:

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

Предположим, что 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)

Нам нужно вычислить значения 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) cos(wt), но я использовал его, и он работал хорошо.

...