我需要实现一个实时同步正交检波器。检测器接收来自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。
答案 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
角的一小部分增量,那么我们下次获得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)
准确,但是我使用了它并且效果很好。
答案 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);
}
};