无论是下采样还是过采样,您都试图在非采样时间点上重建信号......因此您必须做出一些假设。
采样定理告诉您,如果您对一个信号进行采样,并且知道它没有超过一半采样频率的频率分量,您就可以在整个时序周期内连续且完全地恢复该信号。有一种方法可以使用sinc() 函数重构信号(这是sin(x)/x)
sinc()(实际上是sin(M_PI/Sampling_period*x)/M_PI/x)是一个具有以下属性的函数:
-
x == 0.0 的值为 1,x == k*Sampling_period 和 k == 0, +-1, +-2, ... 的值为 0
- 它没有超过从
Sampling_period 派生的sample_frequency 一半的频率分量。
因此,如果您将函数 F_x(x) = Y[k]*sinc(x/Sampling_period - k) 的总和视为等于位置 k 处的采样值和其他采样值处的 0 的 sinc 函数,并对样本中的所有 k 求和,您将得到最佳连续函数,其特性是在超过一半采样频率的频率上没有分量,并且具有与您的样本集相同的值。
也就是说,您可以在任何您喜欢的位置重新采样此函数,从而获得重新采样数据的最佳方式。
到目前为止,这是一种复杂的数据重采样方式,(它也存在非因果关系的问题,因此无法实时实现)并且您过去使用过几种方法来简化插值。您必须为每个采样点构造所有 sinc 函数并将它们加在一起。然后,您必须将结果函数重新采样到新的采样点并给出结果。
接下来是刚刚描述的插值方法的一个例子。它接受一些输入数据(in_sz 样本)并使用前面描述的方法输出插值数据(我认为极值一致,这使得N+1 样本等于N+1 样本,这使得(in_sz - 1)/(out_sz - 1) 的计算有些复杂在代码中(如果要进行简单的N samples -> M samples 转换,请更改为in_sz/out_sz:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
/* normalized sinc function */
double sinc(double x)
{
x *= M_PI;
if (x == 0.0) return 1.0;
return sin(x)/x;
} /* sinc */
/* interpolate a function made of in samples at point x */
double sinc_approx(double in[], size_t in_sz, double x)
{
int i;
double res = 0.0;
for (i = 0; i < in_sz; i++)
res += in[i] * sinc(x - i);
return res;
} /* sinc_approx */
/* do the actual resampling. Change (in_sz - 1)/(out_sz - 1) if you
* don't want the initial and final samples coincide, as is done here.
*/
void resample_sinc(
double in[],
size_t in_sz,
double out[],
size_t out_sz)
{
int i;
double dx = (double) (in_sz-1) / (out_sz-1);
for (i = 0; i < out_sz; i++)
out[i] = sinc_approx(in, in_sz, i*dx);
}
/* test case */
int main()
{
double in[] = {
0.0, 1.0, 0.5, 0.2, 0.1, 0.0,
};
const size_t in_sz = sizeof in / sizeof in[0];
const size_t out_sz = 5;
double out[out_sz];
int i;
for (i = 0; i < in_sz; i++)
printf("in[%d] = %.6f\n", i, in[i]);
resample_sinc(in, in_sz, out, out_sz);
for (i = 0; i < out_sz; i++)
printf("out[%.6f] = %.6f\n", (double) i * (in_sz-1)/(out_sz-1), out[i]);
return EXIT_SUCCESS;
} /* main */