dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
spectrogramplotdata.cpp
Go to the documentation of this file.
1 
12 /*
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  * for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, see <http://www.gnu.org/licenses/>
25  */
26 
27 #include <QDebug>
28 #include <math.h>
29 
34 #include "scopegadgetwidget.h"
35 
36 #include "qwt/src/qwt.h"
37 #include "qwt/src/qwt_color_map.h"
38 #include "qwt/src/qwt_matrix_raster_data.h"
39 #include "qwt/src/qwt_plot_spectrogram.h"
40 #include "qwt/src/qwt_scale_draw.h"
41 #include "qwt/src/qwt_scale_widget.h"
42 
43 #define PI 3.1415926535897932384626433832795
44 
53 SpectrogramData::SpectrogramData(QString uavObject, QString uavField, double samplingFrequency,
54  unsigned int windowWidth, double timeHorizon)
55  : Plot3dData(uavObject, uavField)
56  , spectrogram(nullptr)
57  , rasterData(nullptr)
58  , fft_object(nullptr)
59 {
60  this->samplingFrequency = samplingFrequency;
61  this->timeHorizon = timeHorizon;
62  autoscaleValueUpdated = 0;
63 
64  // Create raster data
65  rasterData = new QwtMatrixRasterData();
66 
67  if (mathFunction == "FFT") {
68  fft_object = new ffft::FFTReal<double>(windowWidth);
69  windowWidth /= 2;
70  }
71 
72  this->windowWidth = windowWidth;
73 
74  rasterData->setValueMatrix(*zDataHistory, windowWidth);
75 
76  // Set the ranges for the plot
77  resetAxisRanges();
78  plotData.clear();
79  lastInstanceIndex =
80  -1; // To keep track of missing instances. We assume communications keep packet order
81 }
82 
84 {
85  xMaximum = val;
86 
87  resetAxisRanges();
88 }
89 
91 {
92  yMaximum = val;
93 
94  resetAxisRanges();
95 }
96 
98 {
99  zMaximum = val;
100 
101  resetAxisRanges();
102 }
103 
104 void SpectrogramData::resetAxisRanges()
105 {
106  rasterData->setInterval(Qt::XAxis, QwtInterval(xMinimum, xMaximum));
107  rasterData->setInterval(Qt::YAxis, QwtInterval(yMinimum, yMaximum));
108  rasterData->setInterval(Qt::ZAxis, QwtInterval(0, zMaximum));
109 }
110 
115 void SpectrogramData::plotNewData(PlotData *plot3dData, ScopeConfig *scopeConfig,
116  ScopeGadgetWidget *scopeGadgetWidget)
117 {
118  Q_UNUSED(plot3dData);
119 
120  removeStaleData();
121 
122  // Check for new data
123  if (readAndResetUpdatedFlag() == true) {
124  // Plot new data
125  rasterData->setValueMatrix(*zDataHistory, windowWidth);
126 
127  // Check autoscale. (For some reason, QwtSpectrogram doesn't support autoscale)
128  if (zMaximum == 0) {
129  double newVal = readAndResetAutoscaleValue();
130  if (newVal != 0) {
131  rightAxis->setColorMap(
132  QwtInterval(0, newVal),
133  new ColorMap(
134  (dynamic_cast<SpectrogramScopeConfig *>(scopeConfig))->getColorMap()));
135  scopeGadgetWidget->setAxisScale(QwtPlot::yRight, 0, newVal);
136  }
137  }
138  }
139 }
140 
147 {
148  QDateTime NOW =
149  QDateTime::currentDateTime(); // TODO: Upgrade this to show UAVO time and not system time
150 
151  // Check to make sure it's the correct UAVO
152  if (uavObjectName == multiObj->getName()) {
153 
154  // Only run on UAVOs that have multiple instances
155  if (multiObj->isSingleInstance()) {
156  return false;
157  }
158 
159  // Instantiate object manager
160  UAVObjectManager *objManager;
161 
162  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
163  Q_ASSERT(pm != NULL);
164  objManager = pm->getObject<UAVObjectManager>();
165  Q_ASSERT(objManager != NULL);
166 
167  // Get list of object instances
168  QVector<UAVObject *> list = objManager->getObjectInstancesVector(multiObj->getName());
169 
170  uint16_t newWindowWidth =
171  list.size() * list.front()->getField(uavFieldName)->getNumElements();
172 
173  /* Check if the instance has a samples field as this will override the windowWidth
174  * Field can be used in objects that have dynamic size
175  * like the case of the Vibration Analysis modeule
176  */
177  QList<UAVObjectField *> fieldList = multiObj->getFields();
178  foreach (UAVObjectField *field, fieldList) {
179  if (field->getType() == UAVObjectField::INT16 && field->getName() == "samples") {
180  newWindowWidth = field->getValue().toDouble();
181  break;
182  }
183  }
184 
185  uint16_t valuesToProcess = newWindowWidth; // Store the number of samples expected
186 
187  // Can happen when changing the FFTP Window Width
188  if (mathFunction == "FFT") {
189  if (!((valuesToProcess != 0) && ((valuesToProcess & (valuesToProcess - 1)) == 0))) {
190  return false;
191  }
192  newWindowWidth /= 2; // FFT Output is half
193  }
194 
195  // Check that there is a full window worth of data. While GCS is starting up, the size of
196  // multiple instance UAVOs is 1, so it's possible for spurious data to come in before
197  // the flight controller board has had time to initialize the UAVO size.
198 
199  if (newWindowWidth != windowWidth) {
200  windowWidth = newWindowWidth;
201  clearPlots();
202 
203  plotData.clear();
204  rasterData->setValueMatrix(*zDataHistory, windowWidth);
205 
206  qDebug() << "Spectrogram width adjusted to " << windowWidth;
207  }
208 
209  UAVObjectField *multiField = multiObj->getField(uavFieldName);
210  Q_ASSERT(multiField);
211  if (multiField) {
212 
213  // Get the field of interest
214  foreach (UAVObject *obj, list) {
215  UAVObjectField *field = obj->getField(uavFieldName);
216  int numElements = field->getNumElements();
217 
218  double scale = 1;
219  QList<UAVObjectField *> fieldList = obj->getFields();
220  foreach (UAVObjectField *field, fieldList) {
221  // Check if the instance has a scale field
222  if (field->getType() == UAVObjectField::FLOAT32
223  && field->getName() == "scale") {
224  scale = field->getValue().toDouble();
225  break;
226  }
227 
228  // Check if data is ordered. If not, just discard everything
229  if (field->getType() == UAVObjectField::INT16 && field->getName() == "index") {
230  int currentIndex = field->getValue().toDouble();
231  if (currentIndex != (lastInstanceIndex + 1)) {
232  fprintf(stderr, "Out of order index. Got %d expected %d\n",
233  currentIndex, lastInstanceIndex + 1);
234  plotData.clear();
235  lastInstanceIndex = -1; // Next index will be 0
236  return false;
237  }
238 
239  lastInstanceIndex++;
240  }
241  }
242 
243  for (int i = 0; i < numElements; i++) {
244  double currentValue =
245  field->getValue(i).toDouble() / scale; // Get the value and scale it
246 
247  // Normally some math would go here, modifying currentValue before appending it
248  // to values
249  // .
250  // .
251  // .
252 
253  // Last step, assign value to vector
254  plotData += currentValue;
255  }
256 
257  // Check if we got enough values
258  // The object instance can temporarily have more values than required
259  if (plotData.size() == valuesToProcess) {
260  break;
261  }
262  }
263 
264  // If some instances are still missing
265  if (plotData.size() != valuesToProcess) {
266  return false;
267  }
268 
269  // Check if the FFT needs to be calculated
270  // Because this function is optional we will calculate the FFT and then
271  // update the original vector. This will allow using the same code
272  // to display the information.
273  if (mathFunction == "FFT") {
274 
275  // Check if the fft_object was already created or needs to be updated
276  // May happen if settings change after the spectrogram was created
277  if (fft_object == NULL || fft_object->get_length() != valuesToProcess) {
278  if (fft_object != NULL)
279  delete fft_object;
280 
281  fft_object = new ffft::FFTReal<double>(valuesToProcess);
282  }
283 
284  // Hanning Window
285  for (int i = 0; i < valuesToProcess; i++) {
286  plotData[i] *= pow(sin(PI * i / (valuesToProcess - 1)), 2);
287  }
288 
289  QVector<double> fftout(valuesToProcess);
290 
291  fft_object->do_fft(&fftout[0], plotData.data()); // Do FFT
292  plotData.clear(); // Clear vector
293 
294  // Lets get the magnitude and scale it.
295  // mag = X * sqrt(re^2 + im^2)/n
296  // X (4.2) is chosen so that the magnitude presented is similar to the acceleration
297  // registered
298  // although this is not 100% correct, it helps users understanding the spectrogram.
299  for (unsigned int i = 0; i < valuesToProcess / 2; i++) {
300  plotData << 4.2
301  * sqrt(pow(fftout[i], 2) + pow(fftout[valuesToProcess / 2 + i], 2))
302  / valuesToProcess;
303  }
304  }
305 
306  // Apply autoscale if enabled
307  if (zMaximum == 0) {
308  for (unsigned int i = 0; i < windowWidth; i++) {
309  // See if autoscale is turned on and if the value exceeds the maximum for the
310  // scope.
311  if (plotData[i] > rasterData->interval(Qt::ZAxis).maxValue()) {
312  // Change scope maximum and color depth
313  rasterData->setInterval(Qt::ZAxis, QwtInterval(0, plotData[i]));
314  autoscaleValueUpdated = plotData[i];
315  }
316  }
317  }
318 
319  timeDataHistory->append(NOW.toTime_t() + NOW.time().msec() / 1000.0);
320  while (timeDataHistory->back() - timeDataHistory->front() > timeHorizon) {
321  timeDataHistory->pop_front();
322  zDataHistory->remove(0, fminl(windowWidth, zDataHistory->size()));
323  }
324 
325  *zDataHistory << plotData;
326  plotData.clear();
327  lastInstanceIndex = -1; // Next index will be 0
328 
329  return true;
330  }
331  }
332 
333  return false;
334 }
335 
340 {
341  spectrogram->detach();
342 
343  // Don't delete raster data, this is done by the spectrogram's destructor
344  /* delete rasterData; */
345 
346  // Delete spectrogram (also deletes raster data)
347  delete spectrogram;
348  delete spectrogramData;
349 }
350 
355 {
356  timeDataHistory->clear();
357  zDataHistory->clear();
358 
359  resetAxisRanges();
360 }
double zMaximum
Definition: plotdata3d.h:67
QVector< UAVObject * > getObjectInstancesVector(const QString &name)
void do_fft(DataType f[], const DataType x[]) const
Definition: FFTReal.hpp:143
The ColorMap class Defines a program-wide colormap.
Definition: plotdata.h:118
SpectrogramData(QString uavObject, QString uavField, double samplingFrequency, unsigned int windowWidth, double timeHorizon)
SpectrogramData.
QVector< double > * timeDataHistory
Definition: plotdata3d.h:49
bool append(UAVObject *obj)
Append new data to the plot.
QString uavFieldName
Definition: plotdata.h:100
Core plugin system that manages the plugins, their life cycle and their registered objects...
Definition: pluginmanager.h:53
QVariant getValue(int index=0) const
const double PI
Definition: def.h:28
int getNumElements() const
long get_length() const
Definition: FFTReal.hpp:119
for i
Definition: OPPlots.m:140
QwtScaleWidget * rightAxis
Definition: plotdata.h:87
virtual void setXMaximum(double val)
QVector< double > * zDataHistory
Definition: plotdata3d.h:48
void clearPlots()
SpectrogramScopeConfig::clearPlots Clear all plot data.
The Plot3dData class Base class that keeps the data for each curve in the plot.
Definition: plotdata3d.h:39
double yMinimum
Definition: plotdata.h:96
double xMinimum
Definition: plotdata.h:94
virtual void plotNewData(PlotData *, ScopeConfig *, ScopeGadgetWidget *)
SpectrogramScopeConfig::plotNewData Update plot with new data.
numElements
bool isSingleInstance()
Definition: uavobject.cpp:123
UAVObjectField * getField(const QString &name)
Definition: uavobject.cpp:236
QString getName() const
The ScopeConfig class The parent class for scope configuration classes data sources.
Definition: scopesconfig.h:56
double yMaximum
Definition: plotdata.h:97
QList< UAVObjectField * > getFields()
Definition: uavobject.cpp:196
virtual void setYMaximum(double val)
double readAndResetAutoscaleValue()
readAndResetAutoscaleFlag reads the flag value and resets it
virtual void setZMaximum(double val)
double xMaximum
Definition: plotdata.h:95
QString getName()
Definition: uavobject.cpp:131
virtual void deletePlots(PlotData *)
SpectrogramScopeConfig::deletePlots Delete all plot data.
Scope Plugin Gadget Widget.
QString mathFunction
Definition: plotdata.h:106
QString uavObjectName
Definition: plotdata.h:99
fprintf('\n\n ***dRonin log parser ***\n\n')
virtual bool readAndResetUpdatedFlag()
Definition: plotdata3d.h:58
FieldType getType() const
virtual void removeStaleData()
Removes the old data from the buffer.