Skip to content

Commit

Permalink
#32 Added filter to calculate enclosed area
Browse files Browse the repository at this point in the history
  • Loading branch information
chillibasket committed Jun 11, 2022
1 parent 78368a9 commit 544c9b3
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 15 deletions.
2 changes: 1 addition & 1 deletion ProcessingGrapher/CustomTable.pde
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand Down
5 changes: 3 additions & 2 deletions ProcessingGrapher/FileGraph.pde
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand Down Expand Up @@ -200,6 +200,7 @@ class FileGraph implements TabAPI {
if (newoutput.contains(".csv")) {
//outputfile = newoutput;
currentfile = newoutput;
xData = -1;
workerActive = true;
WorkerThread loadingThread = new WorkerThread();
loadingThread.loadFile();
Expand Down Expand Up @@ -233,7 +234,7 @@ class FileGraph implements TabAPI {
for (int i = 0; i < dataTable.getColumnCount(); i++) {

String columnTitle = dataTable.getColumnTitle(i);
if (columnTitle.contains("x:")) {
if (columnTitle.contains("x:") || columnTitle.contains("X:")) {
xData = i;
} else if (columnTitle.contains("l:")) {
columnTitle = split(columnTitle, ':')[1];
Expand Down
260 changes: 256 additions & 4 deletions ProcessingGrapher/Filters.pde
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand All @@ -29,6 +29,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import java.text.DecimalFormat;


class Filters {

// Filter list to be shown in the sidebar menu.
Expand All @@ -42,9 +45,12 @@ class Filters {
"Absolute Value: |x|",
"Squared: x^2",
"Derivative: Δx/dt",
"Integral: Σxdt"};
"Integral: Σxdt",
"h:Signal Analysis",
"Fourier Transform",
"Enclosed Area"};

String[] filterSlug = {"h", "avg", "tv", "lp", "hp", "h", "abs", "squ", "Δ/dt", "Σdt"};
String[] filterSlug = {"h", "avg", "tv", "lp", "hp", "h", "abs", "squ", "Δ/dt", "Σdt", "h", "fft", "ea"};

/**
* Default Constructor
Expand Down Expand Up @@ -150,6 +156,30 @@ class Filters {
}
break;
}

// Fast Fourier Transform
case 10:
case 11: {
alertMessage("Coming Soon\nThe FFT feature is still being developed!");
break;
}

// Enclosed Area Calculation
case 12: {
AreaCalculation areaCalc = new AreaCalculation();
double[] calculatedAreas = areaCalc.processCycles(xAxisData, signalData);

if (calculatedAreas == null || calculatedAreas.length < 1) {
alertMessage("Enclosed Area Calculation\nError: No cycles were found in the data\nTherefore there are no enclosed areas");
} else {
double averageArea = 0;
for (int i = 0; i < calculatedAreas.length; i++) averageArea += calculatedAreas[i];
averageArea /= calculatedAreas.length;
DecimalFormat format = new DecimalFormat("0.########");
alertMessage("Enclosed Area Calculation\nCycles Detected: " + calculatedAreas.length + "\nAverage Area: " + format.format(averageArea));
}
break;
}
}

return outputData;
Expand Down Expand Up @@ -388,7 +418,7 @@ class Filters {
* 1D Total Variation (TV) Noise Removal Algorithm
*
* @author Laurent Condat
* @copyright CeCILL Licence (compatible with GNU GPL v2)
* @copyright CeCILL Licence (compatible with GNU GPL v3)
* @note The algorithm is based on the original C code by
* Laurent Condat with minor adaptations to encapsulate
* it in a class and make it work in Java
Expand Down Expand Up @@ -528,4 +558,226 @@ class Filters {
}
}


/**
* Fast Fourier Transform (FFT) to analyse frequency response of a signal
*
* @author Numerical Recipes chapter on FFTs
* @copyright Apache Licence (compatible with GNU GPL v3)
*/
public class FastFourierTransform {

public double[] process(double[] signalData, double samplingFrequency, double[] amplitudeArray)
{
// Figure out length of FFT (it must be a factor of 2)
int signalLength = signalData.length;
int pt = signalLength;
pt--;
pt |= pt >> 1;
pt |= pt >> 2;
pt |= pt >> 4;
pt |= pt >> 8;
pt |= pt >> 16;
pt++;

// Generate the complex data array
double[] complexArray = new double[pt * 2];
for (int i = 0; i < pt * 2; i++) complexArray[i] = 0;
for (int i = 0; i < signalLength; i++) complexArray[i * 2] = signalData[i];

// Calculate the FFT
fft(complexArray, pt, 1);

// Get the amplitude of the complex number
amplitudeArray = new double[signalLength];
for (int i = 0; i < signalLength; i++) {
amplitudeArray[i] = java.lang.Math.sqrt((complexArray[i * 2] * complexArray[i * 2]) + (complexArray[i*2 + 1] * complexArray[i*2 + 1]));
}

// Generate the frequency axis
double[] frequencyAxis = new double[signalLength];
for (int i = 0; i < signalLength; i++) frequencyAxis[i] = i * samplingFrequency / pt;

return amplitudeArray;
}


public void fft(double[] data, int noOfSamples, int direction)
{
//variables for trigonometric recurrences
int i, j, n, m, mmax, istep;
double wr, wpr, wi, wpi, wtemp, tempr, tempi, theta;

// The complex array is real+complex so the array has a
// size n = 2* number of complex samples. The real part
// is the data[index] and the complex part is the data[index+1]
n = noOfSamples * 2;

// Binary inversion (real = even-indexes, complex = odd-indexes)
j = 0;

for (i = 0; i < n / 2; i += 2) {
if (j > i) {

// Swap the real part
tempr = data[j];
data[j] = data[i];
data[i] = tempr;

// Swap the complex part
tempr = data[j+1];
data[j+1] = data[i+1];
data[i+1] = tempr;

// Checks if the changes occurs in the first half
// and use the mirrored effect on the second half
if ((j / 2) < (n / 4)) {
// Swap the real part
tempr = data[(n - (i + 2))];
data[(n - (i + 2))] = data[(n - (j + 2))];
data[(n - (j + 2))] = tempr;

// Swap the complex part
tempr = data[(n - (i + 2)) + 1];
data[(n - (i + 2)) + 1] = data[(n - (j + 2)) + 1];
data[(n - (j + 2)) + 1] = tempr;
}
}

m = n / 2;

while (m >= 2 && j >= m) {
j -= m;
m = m / 2;
}
j += m;

print(i); print(" , "); println(j);
}

// Danielson-Lanzcos routine
mmax = 2;

// External loop
while (n > mmax) {
istep = mmax << 1;
theta = direction * (2 * PI / mmax);
wtemp = java.lang.Math.sin(0.5 * theta);
wpr = -2.0 * wtemp * wtemp;
wpi = java.lang.Math.sin(theta);
wr = 1.0;
wi = 0.0;

// Internal loops
for (m = 1; m < mmax; m += 2) {
for (i = m; i <= n; i += istep) {
j = i + mmax;
tempr = wr * data[j-1] - wi * data[j];
tempi = wr * data[j] + wi * data[j-1];
data[j-1] = data[i-1] - tempr;
data[j] = data[i] - tempi;
data[i-1] += tempr;
data[i] += tempi;
}
wr = (wtemp = wr) * wpr - wi * wpi + wr;
wi = wi * wpr + wtemp * wpi + wi;
}
mmax = istep;
}
}
}


/**
* Enclosed Area Calculation
*
* @author Simon Bluett
* @copyright GNU GPL-v3
*/
public class AreaCalculation {

/**
* Calculate the area of enclosed cycles/loops in the data
* @param[in] xData X-axis data
* @param[in] yData Y-axis data
*/
public double[] processCycles(double[] xData, double[] yData) {

// Detect cycles in the data, which correspond to enclosed areas
int[] cycleEnd = detectCycles(xData, yData);
double[] calculatedArea = null;
//println("CycleEnd.length = " + cycleEnd.length);

if ((cycleEnd != null) && (cycleEnd.length > 1)) {
calculatedArea = new double[0];

for (int i = 1; i < cycleEnd.length; i++) {

double currentArea = 0;

// Apply the discrete version of Green's Theorem (Pick's Theorem)
// Based on formula in DOI: 10.1117/1.JEI.26.6.063022
for (int j = cycleEnd[i - 1]; j < cycleEnd[i] - 1; j++) {
double areaSlice = ((xData[j] * yData[j+1]) - (yData[j] * xData[j + 1])) / 2.0;
currentArea += areaSlice;
//println(areaSlice);
}

// Ensure the cycle is closed
currentArea += ((xData[cycleEnd[i]-1] * yData[cycleEnd[i - 1]]) - (yData[cycleEnd[i] - 1] * xData[cycleEnd[i - 1]])) / 2.0;
currentArea = java.lang.Math.abs(currentArea);
calculatedArea = (double[])append(calculatedArea, currentArea);
//println(currentArea);
}
}

return calculatedArea;
}

/**
* Detect whether the data forms a closed loop
* @param[in] xData X-axis data
* @param[in] yData Y-axis data
* @return An array of the indices where one cycle ends and a new one starts
*/
public int[] detectCycles(double[] xData, double[] yData) {

if (xData == null || yData == null || (xData.length != yData.length) || xData.length < 3) return null;

int[] cycleEnd = { 0 };
double[] startPoint = { xData[0], yData[0] };
double avgStepDistance = distance(xData[0], yData[0], xData[1], yData[0]);

for (int i = 2; i < xData.length; i++) {
// Update the average distance between points
avgStepDistance = ((avgStepDistance * i) + distance(xData[i-1], yData[i-1], xData[i], yData[i])) / (double)(i + 1);

// If the current point is closer to the start point than half the
// average distance, then a full loop has probably been completed
if (distance(startPoint[0], startPoint[1], xData[i], yData[i]) < avgStepDistance * 0.5) {
cycleEnd = append(cycleEnd, i);
startPoint[0] = xData[i];
startPoint[1] = yData[i];
//println(i);

// Increment twice to prevent the next point from triggering a loop detection
i++;
}
}

return cycleEnd;
}

/**
* Calculate the distance between two coordinates
* @param[in] x1 X-value of point A
* @param[in] y1 Y-value of point A
* @param[in] x2 X-value of point B
* @param[in] y2 Y-value of point B
* @return The euclidean distance between two points
*/
private double distance(double x1, double y1, double x2, double y2) {
return java.lang.Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}
}
}
2 changes: 1 addition & 1 deletion ProcessingGrapher/Graph.pde
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand Down
2 changes: 1 addition & 1 deletion ProcessingGrapher/LiveGraph.pde
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand Down
8 changes: 4 additions & 4 deletions ProcessingGrapher/ProcessingGrapher.pde
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
* @website https://wired.chillibasket.com/processing-grapher/
*
* @copyright GNU General Public License v3
* @date 21st December 2021
* @version 1.3.5
* @date 11th June 2022
* @version 1.4.0
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand All @@ -31,7 +31,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

final String versionNumber = "1.3.5";
final String versionNumber = "1.4.0";

// Swing for input popups
import static javax.swing.JOptionPane.*;
Expand Down
2 changes: 1 addition & 1 deletion ProcessingGrapher/SerialMonitor.pde
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand Down
2 changes: 1 addition & 1 deletion ProcessingGrapher/Settings.pde
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* * * * * * * * * * * * * * * * * * * * * * */

/*
* Copyright (C) 2021 - Simon Bluett <[email protected]>
* Copyright (C) 2022 - Simon Bluett <[email protected]>
*
* This file is part of ProcessingGrapher
* <https://github.com/chillibasket/processing-grapher>
Expand Down

0 comments on commit 544c9b3

Please sign in to comment.