[英]Fast C++ sine and cosine alternatives for real-time 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;
}
};
我的問題是sin
和cos
計算需要太多的時間。 我被建議使用預先計算的sin
和cos
表,但可用的ADC采樣頻率不是w
倍數,因此存在片段拼接問題。 是否有任何快速的替代品sin
和cos
計算? 如果有關如何提高此代碼性能的任何建議,我將不勝感激。
UPD不幸的是,我在代碼中錯了,刪除了過濾調用,代碼已經失去了意義。 謝謝Eric Postpischil。
我知道一個適合你的解決方案。 回想一下角度總和的正弦和余弦的學校公式:
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)
,但我使用它並且效果很好。
如果你可以使用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);
}
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.