1

我想获取一个大小约为 70-80k 的字节数组,并将它们从时域转换为频域(可能使用 DFT)。到目前为止,我一直在关注 wiki 并获得了此代码。

for (int k = 0; k < windows.length; k++) {
        double imag = 0.0;
        double real = 0.0;
        for (int n = 0; n < data.length; n++) {
            double val = (data[n])
                    * Math.exp(-2.0 * Math.PI * n * k / data.length)
                    / 128;
            imag += Math.cos(val);
            real += Math.sin(val);
        }
        windows[k] = Math.sqrt(imag * imag + real
                * real);
}

据我所知,这可以找到每个频率窗口/箱的幅度。然后我穿过窗户,找到震级最高的那一个。我在该频率上添加了一个标志,以便在重建信号时使用。我检查重建的信号是否与我的原始数据集匹配。如果它没有找到下一个最高频率窗口,并在重建信号时标记它。

这是我用来重建信号的代码,我很确定这是非常错误的(它应该执行 IDFT):

for (int n = 0; n < data.length; n++) {
        double imag = 0.0;
        double real = 0.0;
        sinValue[n] = 0;
        for (int k = 0; k < freqUsed.length; k++) {
            if (freqUsed[k]) {
                double val = (windows[k] * Math.exp(2.0 * Math.PI * n
                        * k / data.length));
                imag += Math.cos(val);
                real += Math.sin(val);
            }
        }
        sinValue[n] = imag* imag + real * real;
        sinValue[n] /= data.length;
        newData[n] = (byte) (127 * sinValue[n]);
}

freqUsed 是一个布尔数组,用于标记在重建信号时是否应使用频率窗口。

无论如何,这是出现的问题:

  1. 即使使用了所有频率窗口,也不会重建信号。这可能是由于...
  2. 有时 Math.exp() 的值太高,因此返回无穷大。这使得难以获得准确的计算。
  3. 虽然我一直关注 wiki 作为指南,但很难判断我的数据是否有意义。这使得测试和识别问题变得困难。

脱离问题:

我对此很陌生,并不完全了解所有内容。因此,感谢任何帮助或见解。感谢您花时间阅读所有内容,并提前感谢您提供的任何帮助。任何帮助真的会很好,即使你认为我这样做是最糟糕的可怕方式,我想知道。再次感谢。

-

编辑:

所以我更新了我的代码,如下所示:

for (int k = 0; k < windows.length; k++) {
        double imag = 0.0;
        double real = 0.0;
        for (int n = 0; n < data.length; n++) {
            double val = (-2.0 * Math.PI * n * k / data.length);
            imag += data[n]*-Math.sin(val);
            real += data[n]*Math.cos(val);
        }
        windows[k] = Math.sqrt(imag * imag + real
                * real);
}

对于原始变换和:

for (int n = 0; n < data.length; n++) {
    double imag = 0.0;
    double real = 0.0;
    sinValue[n] = 0;
    for (int k = 0; k < freqUsed.length; k++) {
        if (freqUsed[k]) {
            double val = (2.0 * Math.PI * n
                    * k / data.length);
            imag += windows[k]*-Math.sin(val);
            real += windows[k]*Math.cos(val);
        }
    }
    sinValue[n] = Math.sqrt(imag* imag + real * real);
    sinValue[n] /= data.length;
    newData[n] = (byte) (Math.floor(sinValue[n]));
}

为逆变换。虽然我仍然担心它不能正常工作。我生成了一个包含单个正弦波的数组,它甚至无法分解/重建它。关于我缺少什么的任何见解?

4

2 回答 2

3

是的,您的代码(用于 DFT 和 IDFT)已损坏。您对如何使用指数的问题感到困惑。DFT 可以写成:

       N-1
X[k] = SUM { x[n] . exp(-j * 2 * pi * n * k / N) }
       n=0

jsqrt(-1)在哪里。这可以表示为:

       N-1
X[k] = SUM {   (x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N))
       n=0  +j.(x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N)) }

这又可以分为:

            N-1
X_real[k] = SUM { x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N) }
            n=0

            N-1
X_imag[k] = SUM { x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N) }
            n=0

如果您的输入数据是真实的,这将简化为:

            N-1
X_real[k] = SUM { x[n] * cos(2*pi*n*k/N) }
            n=0

            N-1
X_imag[k] = SUM { x[n] * -sin(2*pi*n*k/N) }
            n=0

因此,总而言之,您不需要同时使用expcos/sin

于 2011-07-29T16:36:23.777 回答
1

除了@Oli 正确提出的观点外,您还对时域和频域之间的转换存在根本性的误解。您的真实输入信号成为频域中的复数信号。应该把它的大小转换回时域(如果做得正确,这实际上会给你时域自相关,但这不是你想要的)。如果您希望能够重建时域信号,那么您必须保持复频域信号原样(即分离实部/虚部)并执行复数到实数 IDFT 以返回时域。

例如,您的正向变换应如下所示:

for (int k = 0; k < windows.length; k++) {
        double imag = 0.0;
        double real = 0.0;
        for (int n = 0; n < data.length; n++) {
            double val = (-2.0 * Math.PI * n * k / data.length);
            imag += data[n]*-Math.sin(val);
            real += data[n]*Math.cos(val);
        }
        windows[k].real = real;
        windows[k].imag = image;
}

wherewindows被定义为一个复数值数组。

于 2011-07-31T08:35:08.813 回答