0
votes

I have decomposed some time series data using a custom FFT implementation. By design my FFT implementation gives me a set of cos and sine waves that I can then sum together to regenerate the original signal. This works well without issue, so I know that the extracted sine and cos waves are correct in terms of amplitude, period and phase.

The data I am using has 1024 samples which gives me the properties of 512 cos waves and 512 sine waves (eg the amplitude, phase and period data for each wave).

To save on data storage I am trying to find/understand the mathematical relationship between the amplitudes of the waves. Instead of having to save every amplitude for every sine and cos wave I would like to simply save some coefficients that I can later use to rebuild the amplitudes in code.

FFT Sine Waves with Amplitudes

From the above image you can see that there is a set of Power curve coefficients that roughly fit the amplitude data, however for my use case this is not accurate enough.

As I have all the source data along with the generated properties of each wave, is there a simple formula that I can use or a transform I can perform to generate the amplitudes in code after I have performed the FFT? I know that the amplitudes are related to the real and imaginary values however I cannot store all the real and imaginary values either due to space requirements.

As an example of how I am saving this issue for the period data, I have found that the period of each wave is simply Math.Power(waveIndex, -1). So for the wave periods I do not have to store the data, I can simply regenerate in code.

I cannot currently find a relationship between the amplitudes within the sine wave or even a relationship between cos and sine amplitudes, however the theory and math behind FFT is beyond me so I am hoping that there is a simply formula or concept I can implement.

Following the replies I have added the below code that I use to get the sine and cos wave values, this code snippet may help those replying.

internal void GetSineAndCosWavesBasic(double[] outReal, double[] outImag, int numWaves, out double[,] sineValues, out double[,] cosValues)
    {
        // the real and imag values from Cooley-Tukey decimation-in-time radix-2 FFT are passed in
        // and we want to generate the cos and sine values for each sample  for each wave

        var length = outReal.Length;
        var lengthDouble = (double)length;
        var halfLength = lengthDouble / 2.0;

        sineValues = new double[numWaves, length];
        cosValues = new double[numWaves, length];

        var Pi2 = 2 * Math.PI;

        for (var waveIdx = 0; waveIdx < numWaves; waveIdx++)
        {
            for (var sampleIdx = 0; sampleIdx < length; sampleIdx++)
            {
                // first value case and middle value case
                var reX = outReal[waveIdx] / halfLength;
                if (sampleIdx == 0)
                {
                    reX = outReal[waveIdx] / lengthDouble;
                }
                else if (sampleIdx == halfLength)
                {
                    reX = outReal[waveIdx] / lengthDouble;
                }

                // precompute the value that gets sine/cos applied 
                var tmp = (Pi2 * waveIdx * sampleIdx) / lengthDouble;

                // get the instant cos and sine values
                var valueCos = Math.Cos(tmp) * reX;
                var valueSin = Math.Sin(tmp) * (-outImag[waveIdx] / halfLength);

                // update the sine and cos values for this wave for this sample
                cosValues[waveIdx, sampleIdx] = valueCos;
                sineValues[waveIdx, sampleIdx] = valueSin;
            }
        }
    }

And the below is how I get the magnitude and phase values, although I do not currently use those anywhere.

    internal void CalculateMagAndPhaseBasic(double[] outReal, double[] outImag, out double[] mag, out double[] phase)
    {
        // the real and imag values from Cooley-Tukey decimation-in-time radix-2 FFT are passed in
        // and we want to generate the magnitude and phase values

        var length = outReal.Length;

        mag = new double[(length / 2) +1];
        phase = new double[(length / 2) + 1];

        for (var i = 0; i <= length / 2; i++)
        {
            mag[i] = Math.Pow((outReal[i] * outReal[i]) + (outImag[i] * outImag[i]), 0.5);
            phase[i] = Math.Atan2(outImag[i], outReal[i]);
        }
    }
2

2 Answers

0
votes

Actually the fft just returns you complex coefficients S(w)=a+jb

For an N point fft, abs(S(w)) * 2/N will be (close to) the amplitude of the sinusoidal component at frequency w.

This assumes that the sinusoidal component has a frequency close to the center of the fft bin, otherwise the power will be "split" between two adjacent bins.

And that the frequency you're interested in is present through all the fft window.

0
votes

The output of an FFT has the same number of degrees of freedom as the input. There is no simple formula (other than the FFT itself) that relates the FFT results to just each other, as all of the FFT outputs can change if any of the FFT inputs changes.

The relationship between the sine and cosine of each FFT complex bin result is related to the phase of the sinusoidal input component at that frequency (of the bin center), circularly relative to the start and end. If the phase changes, so can both the sine and cosine component. See: atan2()