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