【问题标题】:How can I transfer a discrete set of data into the frequency domain and back (preferrably losslessly)如何将一组离散的数据传输到频域并返回(最好是无损的)
【发布时间】:2011-10-16 02:40:19
【问题描述】:

我想获取一个大小约为 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);
}

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

这是我用来重建信号的代码,我很确定这是非常错误的(它应该执行 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]));
}

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

【问题讨论】:

  • 如果我可以提供任何有助于解决问题的更多信息,请告诉我,我很乐意发布。
  • 我更改了我的代码,使其不再将值转换为域 [-1,1] 并保留它们 [-128,127] ...它似乎仍然无法正常工作。
  • 如果你的正弦波相对于它的对应频率窗口是相移的,或者它的周期不是任何频率窗口周期的乘积,那么你的 DFT 不包含用于重建的有效数据集,这是基本问题频域信号重构。要解决这个问题,您必须有足够多的样本,有时甚至需要在 DFT/IDFT 之前转换信号,以使您的 DFT/IDFT 与之同步......

标签: java signal-processing fft frequency-analysis dft


【解决方案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;
}

其中windows 定义为复数值数组。

【讨论】:

  • 有道理。那么我如何重建信号。前向变换当然是有帮助的,但这只是问题的一半。我想我可以从@Oli 的回答中弄清楚。不过感谢您的帮助。
  • 逆变换与正向变换非常相似,除了您有复杂的输入数据并且您将忽略输出数据的虚部(无论如何它应该接近于零)并使用输出的实部。最大的不同是您需要执行复数乘法。
  • 复数乘法,你的意思是@Oli在他的回答中的意思,对吗? [以下评论]
  • X_real[k] = SUM { x_real[n] * cos(2*pink/N) + x_imag[n] * sin(2*pink/N) }
  • 是的 - 我认为逆变换的至少一个旋转因子也有符号变化,但查找起来应该很容易。
【解决方案2】:

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

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

j 是 sqrt(-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

【讨论】:

  • 哇...好吧...首先,谢谢...其次,如何将 x[n] 分解为 x_real[n] 和 x_imag[n]?第三,x_real[n] 应该是一个单独的数组而不是 x_real[k]?或者不是?它似乎是递归定义的......
  • @Matt:啊,我在区分时域 (x) 和频域 (X)(这种大小写差异在信号处理方面相当普遍)。如果你的数据是真实的(比如录音什么的),那么x_real[n] = x[n]x_imag[n] = 0,那么所有带有x_imag的术语都会消失。
  • 我的数据似乎是随机的,因此我不确定我的数据是否只是真实的。我的意思是......我只有一组数据,所以我(认为)我可以把它当作真实的,但我不确定这是否有并发症。只有一个数组是否意味着我的数据是真实的?
  • 另外,我觉得我所有的数学信誉都因为对欧拉公式的贬低而付诸东流。哦,好吧,至少它正在修复,再次感谢。
  • 哦,还有...我的数据不是来自 [-1,1] 它是 [-128,127] 我需要缩小它吗?还是这样好吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 2010-10-14
  • 2016-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多