psambit9791 / jdsp

A Java Library for Digital Signal Processing
https://jdsp.dev
MIT License
247 stars 43 forks source link

Plotting output is inconsistent with peak detection indices #44

Closed LobsterMan123 closed 2 years ago

LobsterMan123 commented 2 years ago

Hello,

I plotted the data in the file named "Freq_ANGLE_PER_NUM_29_theta.txt" (shown below) Freq_angles_plot

and then I obtained the DFT for that same data and plotted it (shown below) Freq_DFT_plot

I also stored this DFT output data in the file named "Freq_theta_Num_29_FrequencyValues.txt" (I included index/predictibly increasing numbers as part of this data, but they are not provided from the JDSP code).

I then wanted to find out where the peaks were and so I used the following code on the DFT version of my data: FindPeak dftFp = new FindPeak(xOut); Peak middleMan = dftFp.detectPeaks(); int[] peaks = middleMan.getPeaks(); //System.out.println(peaks.length + "\n"); System.out.println("Residue: " + counter);

The results from this peak-determining code indicated that the very first peak/maximum was located at the x-axis value of '1' when the plot by contrast seems to show that the very first peak is instead located the x-axis value of '2.'

I stored the location results of all peaks in the file named "Freq_theta_Num_29AllTransformPeaks.txt"

Am I reading/interpreting the output data incorrectly (all data files referenced to in this message are attached Freq_ANGLE_PER_NUM_29_theta.txt

Freq_theta_Num_29_FrequencyValues.txt

Freq_theta_Num_29AllTransformPeaks.txt )? Why does the plot seem to indicate the peak is at one position, but the peak-detecting analysis with the library declares the peak is at another?

Thanks.

LM

psambit9791 commented 2 years ago

I just checked the "Freq_theta_Num_29_FrequencyValues.txt" file and I can see that the point at index 1 is ,in fact, the peak. Just a quick note, in MATLAB indexing starts from 1, in Java/Python it starts from 0.

0|6343.6353132
1|15120.646817172741
2|2314.137785381897
3|207.12337507802246

Quite clearly, index 1 is the peak in the neighbourhood. I am not sure what the issue is here. Could you please clarify?

LobsterMan123 commented 2 years ago

Yes, my reason for asking the question is because if you look closely at the graph/plot, the very first data point (6343.6353132) - which is not a peak - does not intersect on the built-in plot grid line at '0' but instead appears to be at '1.' This would make the second data point (15120.646817172741) - which is the actual peak - thus located above '2' not '1.' The plot image is remarkably high-res and zooming into the region clearly shows that the very first data point (6343.6353132) is not positioned at '0.' This is why I was confused, as I was consulting the plot initially. If this is just a quirk of the plotting utility, that is fine, I'll just plot the data with Excel or something, but I wanted to make sure I wasn't interpreting things incorrectly. Thanks.

LM

psambit9791 commented 2 years ago

Can you please add the whole code (including the plotting code) here?

LobsterMan123 commented 2 years ago

Certainly. Below is the code for obtaining the DFT and how I saved the results to a file:

_Fourier ft = new FastFourier(XArray); ft.transform(); boolean onlyPositive = true; double[] xOut = ft.getMagnitude(onlyPositive); // Print frequency spectrum - only positive values try { PrintWriter writer = new PrintWriter(cwd + File.separator + "AngleNum" + counter + "_FrequencyValues.txt", "UTF-8"); for (int b = 0; b < xOut.length; b++) { writer.println(b + "|" + xOut[b]); } // END 'for' writer.close(); } // END 'try' catch (FileNotFoundException ex) { System.out.println("Unable to open the file"); } // END 'catch' catch (IOException ex) { System.out.println("Cannot read the file"); } // END 'catch'

Below is the code for detecting peaks and storing them in a file:

    FindPeak dftFp = new FindPeak(xOut);
    Peak middleMan = dftFp.detectPeaks();
    int[] peaks = middleMan.getPeaks();
    //System.out.println(peaks.length + "\n");
    System.out.println("Residue: " + counter);

    try {
            PrintWriter writer3 = new PrintWriter(cwd + File.separator + "Angle_Num_" + counter + "AllTransformPeaks.txt", "UTF-8");
            for (i = 0; i < peaks.length; i++) {
                    writer3.println(peaks[i]);
            }
            writer3.close();
    }
    catch (FileNotFoundException ex) {
            System.out.println("Unable to open the file");
    } // END 'catch'
    catch (IOException ex) {
            System.out.println("Cannot read the file");
    } // END 'catch'

Now here is the code to plot the frequencies:

    width = 600; // original value was 600
    height = 500; // original value was 500
    title = "AngleNum " + counter + " Angle Transform";
    x_axis = "Frequency";
    y_axis = "Angle Magnitude";
    Plotting xFrequencies = new Plotting(width, height, title, x_axis, y_axis);
    xFrequencies.initialisePlot();

    xFrequencies.addSignal("AngleNum " + counter + " Angle1", xOut, false);
    xFrequencies.saveAsPNG("AngleNum_" + counter + "_AngleFrequencies.png");

I am iterating this code over many input files to be plotted and that is where the "counter" variable is from.

psambit9791 commented 2 years ago

So, this happens because by default, the X axis starts counting from 1 when we plot it. JDSP uses xchart as a dependency for plotting and by default, xchart starts counting points from 1. That's the reason, your plot starts from 1 instead of 0.

To resolve this, all you need to do is:

double[ ] bOut = UtilMethods.arange(0, xOut.length, 1.0);
xFrequencies.addSignal("AngleNum " + counter + " Angle1", bOut, xOut, false);
LobsterMan123 commented 2 years ago

Ok, thanks for explaining this. So as it turns out, for my data, my frequency interval was 1.875 MHz. Does this then mean that the data file I initially provided:

0|6343.6353132 1|15120.646817172741 2|2314.137785381897 3|207.12337507802246

should instead look like this?

0|6343.6353132 (1.875 1)|15120.646817172741 (1.875 2)|2314.137785381897 (1.875 * 3)|207.12337507802246

In other words, is the very first frequency data point output by JDSP's FFT always correspond to a frequency of '0?' Since the peak detection feature says the very first peak is at position '1' (from my initial data file) it sounds to me that the very first frequency data point provided by the FFT algorithm will indeed always correspond to a frequency of '0,' but I just want to verify with you that this assumption is correct. Thanks again.

LM

psambit9791 commented 2 years ago

Other than the sampling frequency, DFT bins are also dependent on the signal length, so it might not be the case. I would recommend using the getFFTFreq() function introduced in v2.0.0 to get the corresponding frequency bins.

LobsterMan123 commented 2 years ago

Ok, thanks, I'll check it out then.

LM

LobsterMan123 commented 2 years ago

I went ahead and implemented the getFFTFreq as follows:

    _Fourier ft = new FastFourier(XArray);
    ft.transform();
    boolean onlyPositive = true;
    double[] xOut = ft.getMagnitude(onlyPositive);
    double[] freqBins = ft.getFFTFreq(1, onlyPositive);
    try {
            PrintWriter writer2 = new PrintWriter(cwd + File.separator + "RealFreqBins.txt", "UTF-8");
            for (int w = 0; w < freqBins.length; w++) {
                    writer2.println(freqBins[w]);
            } // END 'for'

            writer2.close();
    } // END 'try

This yielded the 'freqBins' array, and therefore my output file, as having a total of 32769 bins/x-axis items.

The first 4 reported frequency bins are: 0.0, 1.52587890625E-5, 3.0517578125E-5, 4.57763671875E-5 and the very last reported frequency bin is 0.5.

My question is what are the units that JDSP is working with when I give it a '1' for my sampling frequency? Therefore, what are the units of the frequency bins that the getFFTFreq() is giving me as output that I listed above (i.e. 0.0, 1.52587890625E-5, 3.0517578125E-5, 4.57763671875E-5)? I provided a '1' to the 'getFFTFreq()' method for the sampling frequency because the sampling frequency for data collection that I used was collecting my time domain data every 1 nanosecond. Thanks.

LM

psambit9791 commented 2 years ago

When nothing is provided as the sampling frequency, the sampling frequency is set to 1 which represents the normalised frequency scale. To know more about normalised frequency, please use this link.

LobsterMan123 commented 2 years ago

Ok. So all I want is for the output results of the code I used above:

    double[] xOut = ft.getMagnitude(onlyPositive);
    double[] freqBins = ft.getFFTFreq(1000000000, onlyPositive); //I collected data every 1 billionth of a second

to give me output data, that if I were to plot it directly, would show me what frequencies in Hz are present in my raw data (since my supplied "Fs" value to JDSP was in Hz - it is indeed 1 billion Hz) - I assume any peaks in such a plot of "xOut" vs. "freqBins" indicate the frequencies present in the raw/collected data. Is the above code snippet sufficient to give me this if I were to plot its output ("xOut" vs. "freqBins") without additional manipulation?

LM

psambit9791 commented 2 years ago

Yes