Search code examples
mathsignal-processingfftfrequency-analysisdft

How can I transfer a discrete signal from the time domain into the frequency domain and back without losing data?


For a few weeks now, I have been trying to implement a DFT that takes an arbitrary set of bytes and treats them as a signal. It then transforms them to the frequency domain. Following that it transforms them back. It originally only attempted to use some of the components to reconstruct the original signal. When this failed, I tried using all of the components and it still failed.

I have been following Wikipedia's Equations as a guide for how to do this and my code seems to match the equations given (in my mind) given this code:

DFT:

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

IDFT:

for (int n = 0; n < data.length; n++) {
    doubleValue[n] = 0;
    for (int k = 0; k < freqUsed.length; k++) {
        double val = (2.0 * Math.PI * n * k / data.length);
        doubleValue[n] = freq_real[k] * Math.cos(val) - freq_imag[k] * Math.sin(val);
    }
    time_real[n] = (byte) (Math.floor(doubleValue[n]));
}

Can anybody help me identify what the problem is?

I asked a previous question that is about the same project, but it was worded terribly and editing may have caused more confusion, not less. Also, although that question may have been answered, I still have more to figure out. That can be found here


Solution

  • At least three things are wrong:

    First, you are not summing over all frequencies in your IDFT. That's a big, big problem, basically equivalent to only taking the IDFT of one discrete frequency instead of the entire frequency domain data. Second, you have a sign flipped in your IDFT.

    Change line 5 of snippet 2 to

        doubleValue[n] += freq_real[k] * Math.cos(val) + freq_imag[k] * Math.sin(val);
    

    and make sure you initialize doubleValue to zeros.

    Third, you'll want to add a normalization step;

    Change line 7 of snippet 2 to

    time_real[n] = (byte) (Math.floor(doubleValue[n] / data.length))
    

    Fourth, for your own ease, test this floating point algorithm using floating point inputs and outputs before you truncate to an integral data type and don't assume that you'll get precisely correct answers doing a round trip on integral data-- floating point error is very real.

    It might also help to grab someone else's implementation of the DFT and IDFT and compare the behavior with your implementation on some very simple inputs to catch other errors. Since the DFT is linear algebra, you may get it less than perfectly correct and still see qualitatively okay-seeming answers.