1 /* -*- c++ -*- */
2 /*
3 * Copyright 2012,2014 Free Software Foundation, Inc.
4 *
5 * This file is part of GNU Radio
6 *
7 * GNU Radio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
11 *
12 * GNU Radio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Radio; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include <gnuradio/qtgui/waterfalldisplayform.h>
24
25 #include <QColorDialog>
26 #include <QMessageBox>
27
28 #include <cmath>
29 #include <iostream>
30
WaterfallDisplayForm(int nplots,QWidget * parent)31 WaterfallDisplayForm::WaterfallDisplayForm(int nplots, QWidget* parent)
32 : DisplayForm(nplots, parent)
33 {
34 d_int_validator = new QIntValidator(this);
35 d_int_validator->setBottom(0);
36
37 d_layout = new QGridLayout(this);
38 d_display_plot = new WaterfallDisplayPlot(nplots, this);
39 d_layout->addWidget(d_display_plot, 0, 0);
40 setLayout(d_layout);
41
42 d_center_freq = 0;
43 d_samp_rate = 0;
44
45 d_fftsize = 1024;
46 d_fftavg = 1.0;
47
48 d_min_val = 1000;
49 d_max_val = -1000;
50
51 d_clicked = false;
52 d_clicked_freq = 0;
53 d_time_per_fft = 0;
54 // We don't use the normal menus that are part of the displayform.
55 // Clear them out to get rid of their resources.
56 for (int i = 0; i < nplots; i++) {
57 d_lines_menu[i]->clear();
58 }
59 d_line_title_act.clear();
60 d_line_color_menu.clear();
61 d_line_width_menu.clear();
62 d_line_style_menu.clear();
63 d_line_marker_menu.clear();
64 d_marker_alpha_menu.clear();
65
66 // Now create our own menus
67 for (int i = 0; i < nplots; i++) {
68 ColorMapMenu* colormap = new ColorMapMenu(i, this);
69 connect(
70 colormap,
71 SIGNAL(whichTrigger(unsigned int, const int, const QColor&, const QColor&)),
72 this,
73 SLOT(setColorMap(unsigned int, const int, const QColor&, const QColor&)));
74 d_lines_menu[i]->addMenu(colormap);
75
76 d_marker_alpha_menu.push_back(new MarkerAlphaMenu(i, this));
77 connect(d_marker_alpha_menu[i],
78 SIGNAL(whichTrigger(unsigned int, unsigned int)),
79 this,
80 SLOT(setAlpha(unsigned int, unsigned int)));
81 d_lines_menu[i]->addMenu(d_marker_alpha_menu[i]);
82 }
83
84 // One scales once when clicked, so no on/off toggling
85 d_autoscale_act->setText(tr("Auto Scale"));
86 d_autoscale_act->setCheckable(false);
87
88 d_sizemenu = new FFTSizeMenu(this);
89 d_avgmenu = new FFTAverageMenu(this);
90 d_winmenu = new FFTWindowMenu(this);
91 d_menu->addMenu(d_sizemenu);
92 d_menu->addMenu(d_avgmenu);
93 d_menu->addMenu(d_winmenu);
94 connect(d_sizemenu, SIGNAL(whichTrigger(int)), this, SLOT(setFFTSize(const int)));
95 connect(
96 d_avgmenu, SIGNAL(whichTrigger(float)), this, SLOT(setFFTAverage(const float)));
97 connect(d_winmenu,
98 SIGNAL(whichTrigger(gr::filter::firdes::win_type)),
99 this,
100 SLOT(setFFTWindowType(const gr::filter::firdes::win_type)));
101
102 PopupMenu* maxintmenu = new PopupMenu("Int. Max", this);
103 d_menu->addAction(maxintmenu);
104 connect(
105 maxintmenu, SIGNAL(whichTrigger(QString)), this, SLOT(setMaxIntensity(QString)));
106
107 PopupMenu* minintmenu = new PopupMenu("Int. Min", this);
108 d_menu->addAction(minintmenu);
109 connect(
110 minintmenu, SIGNAL(whichTrigger(QString)), this, SLOT(setMinIntensity(QString)));
111
112 Reset();
113
114 connect(d_display_plot,
115 SIGNAL(plotPointSelected(const QPointF)),
116 this,
117 SLOT(onPlotPointSelected(const QPointF)));
118 }
119
~WaterfallDisplayForm()120 WaterfallDisplayForm::~WaterfallDisplayForm()
121 {
122 // Qt deletes children when parent is deleted
123
124 // Don't worry about deleting Display Plots - they are deleted when parents are
125 // deleted
126 delete d_int_validator;
127 }
128
getPlot()129 WaterfallDisplayPlot* WaterfallDisplayForm::getPlot()
130 {
131 return ((WaterfallDisplayPlot*)d_display_plot);
132 }
133
newData(const QEvent * updateEvent)134 void WaterfallDisplayForm::newData(const QEvent* updateEvent)
135 {
136 WaterfallUpdateEvent* event = (WaterfallUpdateEvent*)updateEvent;
137 const std::vector<double*> dataPoints = event->getPoints();
138 const uint64_t numDataPoints = event->getNumDataPoints();
139 const gr::high_res_timer_type dataTimestamp = event->getDataTimestamp();
140
141 for (size_t i = 0; i < dataPoints.size(); i++) {
142 double* min_val =
143 std::min_element(&dataPoints[i][0], &dataPoints[i][numDataPoints - 1]);
144 double* max_val =
145 std::max_element(&dataPoints[i][0], &dataPoints[i][numDataPoints - 1]);
146 if (*min_val < d_min_val)
147 d_min_val = *min_val;
148 if (*max_val > d_max_val)
149 d_max_val = *max_val;
150 }
151
152 getPlot()->plotNewData(dataPoints, numDataPoints, d_time_per_fft, dataTimestamp, 0);
153 }
154
customEvent(QEvent * e)155 void WaterfallDisplayForm::customEvent(QEvent* e)
156 {
157 if (e->type() == WaterfallUpdateEvent::Type()) {
158 newData(e);
159 } else if (e->type() == SpectrumFrequencyRangeEventType) {
160 SetFreqEvent* fevent = (SetFreqEvent*)e;
161 setFrequencyRange(fevent->getCenterFrequency(), fevent->getBandwidth());
162 }
163 }
164
getFFTSize() const165 int WaterfallDisplayForm::getFFTSize() const { return d_fftsize; }
166
getFFTAverage() const167 float WaterfallDisplayForm::getFFTAverage() const { return d_fftavg; }
168
getFFTWindowType() const169 gr::filter::firdes::win_type WaterfallDisplayForm::getFFTWindowType() const
170 {
171 return d_fftwintype;
172 }
173
getColorMap(unsigned int which)174 int WaterfallDisplayForm::getColorMap(unsigned int which)
175 {
176 return getPlot()->getIntensityColorMapType(which);
177 }
178
getAlpha(unsigned int which)179 int WaterfallDisplayForm::getAlpha(unsigned int which)
180 {
181 return getPlot()->getAlpha(which);
182 }
183
getMinIntensity(unsigned int which)184 double WaterfallDisplayForm::getMinIntensity(unsigned int which)
185 {
186 return getPlot()->getMinIntensity(which);
187 }
188
getMaxIntensity(unsigned int which)189 double WaterfallDisplayForm::getMaxIntensity(unsigned int which)
190 {
191 return getPlot()->getMaxIntensity(which);
192 }
193
setSampleRate(const QString & samprate)194 void WaterfallDisplayForm::setSampleRate(const QString& samprate)
195 {
196 setFrequencyRange(d_center_freq, samprate.toDouble());
197 }
198
setFFTSize(const int newsize)199 void WaterfallDisplayForm::setFFTSize(const int newsize)
200 {
201 d_fftsize = newsize;
202 d_sizemenu->getActionFromSize(newsize)->setChecked(true);
203 getPlot()->replot();
204 }
205
setFFTAverage(const float newavg)206 void WaterfallDisplayForm::setFFTAverage(const float newavg)
207 {
208 d_fftavg = newavg;
209 d_avgmenu->getActionFromAvg(newavg)->setChecked(true);
210 getPlot()->replot();
211 }
212
setFFTWindowType(const gr::filter::firdes::win_type newwin)213 void WaterfallDisplayForm::setFFTWindowType(const gr::filter::firdes::win_type newwin)
214 {
215 d_fftwintype = newwin;
216 d_winmenu->getActionFromWindow(newwin)->setChecked(true);
217 getPlot()->replot();
218 }
219
setFrequencyRange(const double centerfreq,const double bandwidth)220 void WaterfallDisplayForm::setFrequencyRange(const double centerfreq,
221 const double bandwidth)
222 {
223 std::string strunits[4] = { "Hz", "kHz", "MHz", "GHz" };
224 double units10 = floor(log10(bandwidth));
225 double units3 = std::max(floor(units10 / 3.0), 0.0);
226 d_units = pow(10, (units10 - fmod(units10, 3.0)));
227 int iunit = static_cast<int>(units3);
228
229 d_center_freq = centerfreq;
230 d_samp_rate = bandwidth;
231
232 getPlot()->setFrequencyRange(centerfreq, bandwidth, d_units, strunits[iunit]);
233 getPlot()->replot();
234 }
235
setColorMap(unsigned int which,const int newType,const QColor lowColor,const QColor highColor)236 void WaterfallDisplayForm::setColorMap(unsigned int which,
237 const int newType,
238 const QColor lowColor,
239 const QColor highColor)
240 {
241 getPlot()->setIntensityColorMapType(which, newType, lowColor, highColor);
242 getPlot()->replot();
243 }
244
setAlpha(unsigned int which,unsigned int alpha)245 void WaterfallDisplayForm::setAlpha(unsigned int which, unsigned int alpha)
246 {
247 getPlot()->setAlpha(which, alpha);
248 getPlot()->replot();
249 }
250
setIntensityRange(const double minIntensity,const double maxIntensity)251 void WaterfallDisplayForm::setIntensityRange(const double minIntensity,
252 const double maxIntensity)
253 {
254 // reset max and min values
255 d_min_val = 1000;
256 d_max_val = -1000;
257
258 d_cur_min_val = minIntensity;
259 d_cur_max_val = maxIntensity;
260 getPlot()->setIntensityRange(minIntensity, maxIntensity);
261 getPlot()->replot();
262 }
263
setMaxIntensity(const QString & m)264 void WaterfallDisplayForm::setMaxIntensity(const QString& m)
265 {
266 double new_max = m.toDouble();
267 if (new_max > d_cur_min_val)
268 setIntensityRange(d_cur_min_val, new_max);
269 }
270
setMinIntensity(const QString & m)271 void WaterfallDisplayForm::setMinIntensity(const QString& m)
272 {
273 double new_min = m.toDouble();
274 if (new_min < d_cur_max_val)
275 setIntensityRange(new_min, d_cur_max_val);
276 }
277
autoScale(bool en)278 void WaterfallDisplayForm::autoScale(bool en)
279 {
280 double min_int = d_min_val - 5;
281 double max_int = d_max_val + 10;
282
283 setIntensityRange(min_int, max_int);
284 }
285
clearData()286 void WaterfallDisplayForm::clearData() { getPlot()->clearData(); }
287
onPlotPointSelected(const QPointF p)288 void WaterfallDisplayForm::onPlotPointSelected(const QPointF p)
289 {
290 d_clicked = true;
291 d_clicked_freq = d_units * p.x();
292 }
293
checkClicked()294 bool WaterfallDisplayForm::checkClicked()
295 {
296 if (d_clicked) {
297 d_clicked = false;
298 return true;
299 } else {
300 return false;
301 }
302 }
303
setTimeTitle(const std::string title)304 void WaterfallDisplayForm::setTimeTitle(const std::string title)
305 {
306 getPlot()->setAxisTitle(QwtPlot::yLeft, title.c_str());
307 }
308
getClickedFreq() const309 float WaterfallDisplayForm::getClickedFreq() const { return d_clicked_freq; }
310
setPlotPosHalf(bool half)311 void WaterfallDisplayForm::setPlotPosHalf(bool half)
312 {
313 getPlot()->setPlotPosHalf(half);
314 getPlot()->replot();
315 }
316
setTimePerFFT(double t)317 void WaterfallDisplayForm::setTimePerFFT(double t) { d_time_per_fft = t; }
318
getTimePerFFT()319 double WaterfallDisplayForm::getTimePerFFT() { return d_time_per_fft; }
320 // Override displayform SetUpdateTime() to set FFT time
setUpdateTime(double t)321 void WaterfallDisplayForm::setUpdateTime(double t)
322 {
323 d_update_time = t;
324 // Assume times are equal unless explicitly told by setTimePerFFT()
325 // This is the case when plotting using gr_spectrogram_plot
326 d_time_per_fft = t;
327 }
328