假设我们需要产生一个很长的谐波信号,理想情况下是无限长的。乍一看,该解决方案似乎微不足道:
示例1:
float t = 0;
while (runned)
{
float v = sinf(w * t);
t += dt;
}
不幸的是,这是无效的解决方案。对于t >> dt
,由于浮动精度有限,将获得错误的值。幸运的是,我们可以想到sin(2*PI* n + x) = sin(x)
,其中n-任意整数值,因此修改示例并不难获得“无限”的模拟量
样本2:
float t = 0;
float tau = 2 * M_PI / w;
while (runned)
{
float v = sinf(w * t);
t += dt;
if (t > tau) t -= tau;
}
对于一个物理模拟,我需要获取一个无限信号,它是谐波信号的总和,如下所示:
示例3:
float getSignal(float x)
{
float ret = 0;
for (int i = 0; i < modNum; i++)
ret += sin(w[i] * x);
return ret;
}
float t = 0;
while (runned)
{
float v = getSignal(t);
t += dt;
}
在这种形式下,由于Sample1的类似原因,代码对于大t
无法正常工作。问题是-如何获得Sample3算法的“无限”实现?我认为该解决方案应该看起来像Sample2。一个非常重要的注意事项-一般来说, w [i]是任意的而不是谐波,也就是说,所有频率都不是某个基本频率的倍数,因此我找不到通用的tau
。不允许使用精度更高的类型(double,long double)。
感谢您的建议!
答案 0 :(得分:1)
您可以选择一个任意的tau
,并在从t
中减去每个mod时为每个mod存储相位提醒(如评论中@Damien所述)。
另外,将时间表示为t = dt * it
,其中it
是整数可以提高数值稳定性(我认为)。
也许是这样的:
int ndt = 1000; // accumulate phase every 1000 steps for example
float tau = dt * ndt;
std::vector<float> phases(modNum, 0.0f);
int it = 0;
float t = 0.0f;
while (runned)
{
t = dt * it;
float v = 0.0f;
for (int i = 0; i < modNum; i++)
{
v += sinf(w[i] * t + phases[i]);
}
if (++it >= ndt)
{
it = 0;
for (int i = 0; i < modNum; ++i)
{
phases[i] = fmod(w[i] * tau + phases[i], 2 * M_PI);
}
}
}