forked from farrellf/Telemetry-Viewer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FrequencyDomainChart.java
171 lines (127 loc) · 5.64 KB
/
FrequencyDomainChart.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.SwingUtilities;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.LogAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* A line chart of a Fourier transform.
*/
@SuppressWarnings("serial")
public class FrequencyDomainChart extends PositionedChart {
Image chartImage;
public static ChartDescriptor getDescriptor() {
return new ChartDescriptor() {
@Override public String toString() { return "Frequency Domain Chart"; }
@Override public int getMinimumDuration() { return 10; }
@Override public int getDefaultDuration() { return 1000; }
@Override public int getMaximumDuration() { return Integer.MAX_VALUE; }
@Override public String[] getInputNames() { return new String[] {"Data"}; }
@Override public PositionedChart createChart(int x1, int y1, int x2, int y2, int chartDuration, Dataset[] chartInputs) {
return new FrequencyDomainChart(x1, y1, x2, y2, chartDuration, chartInputs);
}
};
}
@Override public String toString() {
return "Frequency Domain Chart";
}
public FrequencyDomainChart(int x1, int y1, int x2, int y2, int chartDuration, Dataset[] chartInputs) {
super(x1, y1, x2, y2, chartDuration, chartInputs);
// spawn a thread that draws the chart
Thread thread = new Thread(new Runnable() {
@Override public void run() {
while(true) {
long startTime = System.currentTimeMillis();
// draw the chart
int chartWidth = getWidth();
int chartHeight = getHeight();
if(chartWidth < 1) chartWidth = 1;
if(chartHeight < 1) chartHeight = 1;
String chartTitle = "";
String xAxisTitle = "Frequency (Hz)";
String yAxisTitle = "Power (W)";
BufferedImage image = getFrequencyDomainChart(chartTitle, xAxisTitle, yAxisTitle, chartWidth, chartHeight);
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
// free resources of old image
if(chartImage != null)
chartImage.flush();
// paint new image
chartImage = image;
repaint();
}
});
// end this thread if we are no longer visible (the user clicked the "Reset" button)
if(!isShowing())
break;
// wait if needed before drawing a new frame
long timeToNextFrame = startTime + Controller.getTargetFramePeriod() - System.currentTimeMillis();
if(timeToNextFrame <= 0)
continue;
else
try{ Thread.sleep(timeToNextFrame); } catch(Exception e) {}
}
}
});
String inputNames = "";
for(int i = 0; i < datasets.length; i++)
inputNames += datasets[i].name + ", ";
thread.setName(String.format("FrequencyDomainChart of: %s", inputNames));
thread.start();
}
private BufferedImage getFrequencyDomainChart(String chartTitle, String xAxisTitle, String yAxisTitle, int chartWidth, int chartHeight) {
int maxX = Controller.getSamplesCount(datasets) - 1;
int minX = maxX - duration;
if(minX < 0) minX = 0;
// bin size (in Hertz) is the reciprocal of the window size (in seconds)
// example: 500ms window -> 1/0.5 = 2 Hz bin size
double samplesPerSecond = Controller.getSampleRate();
double sampleCount = maxX - minX + 1;
double binSizeHz = 1.0 / (sampleCount / samplesPerSecond);
// maximum frequency range (in Hertz) is from 0 to the sample rate (in Hertz), divided by 2
// example: sampling at 1kHz -> 0 Hz to 1000/2 = 500 Hz
double maxFrequencyHz = samplesPerSecond / 2.0;
// calc the DFT, assuming the samples are in Volts, and assuming the load is a unit load (1 ohm)
XYSeries powerLevels = new XYSeries("DFT of " + datasets[0].name);
double realV;
double imaginaryV;
double powerW;
for(double frequencyHz = 0; frequencyHz <= maxFrequencyHz; frequencyHz += binSizeHz) {
realV = 0.0;
imaginaryV = 0.0;
for(int x = minX; x <= maxX; x++) {
double sample = datasets[0].get(x);
double timeSec = (double) x / samplesPerSecond;
realV += sample * Math.cos(2.0 * Math.PI * frequencyHz * timeSec);
imaginaryV += sample * Math.sin(2.0 * Math.PI * frequencyHz * timeSec);
}
realV /= sampleCount;
imaginaryV /= sampleCount;
powerW = (realV * realV) + (imaginaryV * imaginaryV);
powerW *= 2; // because DFT is from -Fs to +Fs
powerLevels.add(frequencyHz, powerW);
}
XYSeriesCollection seriesCollection = new XYSeriesCollection(powerLevels);
JFreeChart frequencyDomainChart = ChartFactory.createXYLineChart(chartTitle, xAxisTitle, yAxisTitle, seriesCollection);
// frequencyDomainChart.removeLegend();
frequencyDomainChart.getXYPlot().getRenderer().setSeriesPaint(0, datasets[0].color);
LogAxis verticalAxis = new LogAxis("Power (W)");
verticalAxis.setLabelFont(frequencyDomainChart.getXYPlot().getDomainAxis().getLabelFont());
verticalAxis.setTickLabelFont(frequencyDomainChart.getXYPlot().getDomainAxis().getTickLabelFont());
verticalAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
// verticalAxis.setRange(Math.pow(10.0, -8.0), Math.pow(10.0, 2.0));
frequencyDomainChart.getXYPlot().setRangeAxis(verticalAxis);
frequencyDomainChart.getXYPlot().getDomainAxis().setRange(0.0, maxFrequencyHz);
frequencyDomainChart.getTitle().setFont(Model.chartTitleFont);
return frequencyDomainChart.createBufferedImage(chartWidth, chartHeight);
}
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(chartImage != null)
g.drawImage(chartImage, 0, 0, null);
}
}