1 // ----------------------------------------------------------------------------
2 // spectrum_viewer.cxx -- spectrum dialog
3 //
4 // Copyright (C) 2017
5 // Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22
23 #include <FL/Fl_Double_Window.H>
24 #include <FL/Fl_Group.H>
25 #include <FL/Fl_Counter.H>
26 #include <FL/Fl_Button.H>
27 #include <FL/Fl_Menu_Bar.H>
28 #include <FL/Fl_Output.H>
29 #include <FL/Fl_Box.H>
30
31 #include <iostream>
32
33 #include "configuration.h"
34 #include "status.h"
35
36 #include "spectrum.h"
37 #include "spectrum_viewer.h"
38 #include "fft-monitor.h"
39 #include "gettext.h"
40 #include "modem.h"
41 #include "trx.h"
42
43 Fl_Double_Window *spectrum_viewer = (Fl_Double_Window *)0;
44
45 spectrum *fftscope = (spectrum *)0;
46
47 Fl_Group *fftmon_mnuFrame = (Fl_Group *)0;
48 Fl_Group *group1 = (Fl_Group *)0;
49 Fl_Group *group2 = (Fl_Group *)0;
50 Fl_Counter *fftviewer_scans = (Fl_Counter *)0;
51 Fl_Counter *fftviewer_range = (Fl_Counter *)0;
52 Fl_Counter *fftviewer_maxdb = (Fl_Counter *)0;
53 Fl_Counter *fftviewer_fcenter = (Fl_Counter *)0;
54 Fl_Counter *fftviewer_frng = (Fl_Counter *)0;
55 Fl_Button *fftviewer_reset = (Fl_Button *)0;
56 Fl_Button *fftviewer_goto = (Fl_Button *)0;
57 Fl_Group *g1 = (Fl_Group *)0;
58 Fl_Group *g3 = (Fl_Group *)0;
59 Fl_Group *g2 = (Fl_Group *)0;
60
61 Fl_Menu_Bar *fftmon_mnu_bar = (Fl_Menu_Bar *)0;
62 Fl_Output *values = (Fl_Output *)0;
63 Fl_Output *db_diffs = (Fl_Output *)0;
64 Fl_Output *f_diffs = (Fl_Output *)0;
65 Fl_Button *pause_button = (Fl_Button *)0;
66 Fl_Box *annunciator = (Fl_Box *)0;
67
68 fftmon *fft_modem = (fftmon *)0;
69
cb_fftviewer_scans(Fl_Counter * w,void * d)70 void cb_fftviewer_scans(Fl_Counter *w, void *d)
71 {
72 progdefaults.fftviewer_scans = w->value();
73 fft_modem->restart();
74 }
75
cb_fftviewer_range(Fl_Counter * w,void * d)76 void cb_fftviewer_range(Fl_Counter *w, void *d)
77 {
78 progdefaults.fftviewer_range = w->value();
79 }
80
cb_fftviewer_maxdb(Fl_Counter * w,void * d)81 void cb_fftviewer_maxdb(Fl_Counter *w, void *d)
82 {
83 progdefaults.fftviewer_maxdb = w->value();
84 }
85
check_frng(int fr)86 void check_frng(int fr)
87 {
88 int fc = progdefaults.fftviewer_fcenter;
89 if (fc - fr/2 < 0) fr = 2*fc;
90 if (fc + fr/2 > 4000) fr = 2*(4000 - fc);
91 progdefaults.fftviewer_frng = fr;
92 fftviewer_frng->value(fr);
93 fftviewer_frng->redraw();
94 }
95
cb_fftviewer_fcenter(Fl_Counter * w,void * d)96 void cb_fftviewer_fcenter(Fl_Counter *w, void *d)
97 {
98 progdefaults.fftviewer_fcenter = w->value();
99 int fr = progdefaults.fftviewer_frng;
100 check_frng(fr);
101 }
102
cb_fftviewer_frng(Fl_Counter * w,void * d)103 void cb_fftviewer_frng(Fl_Counter *w, void *d)
104 {
105 progdefaults.fftviewer_frng = w->value();
106 int fr = progdefaults.fftviewer_frng;
107 check_frng(fr);
108 }
109
cb_fftviewer_reset(Fl_Button * b,void * d)110 void cb_fftviewer_reset(Fl_Button *b, void *d)
111 {
112 progdefaults.fftviewer_fcenter = 2000;
113 progdefaults.fftviewer_frng = 4000;
114 fftviewer_fcenter->value(2000);
115 fftviewer_frng->value(4000);
116 fftviewer_fcenter->redraw();
117 fftviewer_frng->redraw();
118
119 if (progdefaults.wf_spectrum_dbvals) {
120 progdefaults.fftviewer_range = progdefaults.wfAmpSpan;
121 progdefaults.fftviewer_maxdb = progdefaults.wfRefLevel;
122 fftviewer_maxdb->value(progdefaults.fftviewer_maxdb);
123 fftviewer_maxdb->redraw();
124 fftviewer_range->value(progdefaults.fftviewer_range);
125 fftviewer_range->redraw();
126 }
127 }
128
cb_pause_button(Fl_Button * b,void * d)129 void cb_pause_button(Fl_Button *b, void *d)
130 {
131 fftscope->paused( !fftscope->paused() );
132 }
133
cb_fftviewer_goto(Fl_Button * b,void * d)134 void cb_fftviewer_goto(Fl_Button *b, void *d)
135 {
136 progdefaults.fftviewer_fcenter = active_modem->get_freq();
137 int fr = progdefaults.fftviewer_frng;
138 if (progdefaults.wf_spectrum_modem_scale)
139 fr = progdefaults.wf_spectrum_scale_factor * active_modem->get_bandwidth();
140 check_frng(fr);
141 fftviewer_fcenter->value(progdefaults.fftviewer_fcenter);
142 fftviewer_fcenter->redraw();
143
144 if (progdefaults.wf_spectrum_dbvals) {
145 progdefaults.fftviewer_range = progdefaults.wfAmpSpan;
146 progdefaults.fftviewer_maxdb = progdefaults.wfRefLevel;
147 fftviewer_maxdb->value(progdefaults.fftviewer_maxdb);
148 fftviewer_maxdb->redraw();
149 fftviewer_range->value(progdefaults.fftviewer_range);
150 fftviewer_range->redraw();
151 }
152 }
153
cb_spectrum_viewer(Fl_Double_Window * w,void *)154 void cb_spectrum_viewer(Fl_Double_Window *w, void *)
155 {
156 progStatus.svX = spectrum_viewer->x();
157 progStatus.svY = spectrum_viewer->y();
158 progStatus.svW = spectrum_viewer->w();
159 progStatus.svH = spectrum_viewer->h();
160 spectrum_viewer->hide();
161 return;
162 }
163
cb_mnu_fftmon_close(Fl_Menu_ *,void *)164 void cb_mnu_fftmon_close(Fl_Menu_*, void*)
165 {
166 cb_spectrum_viewer(0,0);
167 }
168
169 extern bool b_write_fftfile;
cb_mnu_fftmon_csv(Fl_Menu_ *,void *)170 void cb_mnu_fftmon_csv(Fl_Menu_*, void*)
171 {
172 if (!b_write_fftfile) b_write_fftfile = true;
173 }
174
cb_mnu_x_graticule(Fl_Menu_ *,void *)175 void cb_mnu_x_graticule(Fl_Menu_*, void*)
176 {
177 progStatus.x_graticule = true;
178 progStatus.y_graticule = false;
179 progStatus.xy_graticule = false;
180 fftscope->x_graticule(true);
181 fftscope->y_graticule(false);
182 }
183
cb_mnu_y_graticule(Fl_Menu_ *,void *)184 void cb_mnu_y_graticule(Fl_Menu_*, void*)
185 {
186 progStatus.y_graticule = true;
187 progStatus.x_graticule = false;
188 progStatus.xy_graticule = false;
189 fftscope->x_graticule(false);
190 fftscope->y_graticule(true);
191 }
192
cb_mnu_xy_graticules(Fl_Menu_ *,void *)193 void cb_mnu_xy_graticules(Fl_Menu_*, void*)
194 {
195 progStatus.xy_graticule = true;
196 progStatus.x_graticule = false;
197 progStatus.y_graticule = false;
198 fftscope->x_graticule(true);
199 fftscope->y_graticule(true);
200 }
201
202 Fl_Menu_Item fftmon_menu[] = {
203 {_("&Dialog"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0},
204 {_("&Graticule"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0},
205 { _("X graticule"), 0, (Fl_Callback*)cb_mnu_x_graticule, 0, FL_MENU_RADIO, FL_NORMAL_LABEL, 0, 14, 0},
206 { _("Y graticule"), 0, (Fl_Callback*)cb_mnu_y_graticule, 0, FL_MENU_RADIO, FL_NORMAL_LABEL, 0, 14, 0},
207 { _("X/Y graticules"), 0, (Fl_Callback*)cb_mnu_xy_graticules, 0, FL_MENU_RADIO, FL_NORMAL_LABEL, 0, 14, 0},
208 { 0 },
209 {_("&Save to CSV"), 0, (Fl_Callback*)cb_mnu_fftmon_csv, 0, FL_MENU_DIVIDER, FL_NORMAL_LABEL, 0, 14, 0},
210 {_("&Close"), 0, (Fl_Callback*)cb_mnu_fftmon_close, 0, 0, FL_NORMAL_LABEL, 0, 14, 0},
211 { 0 },
212 { 0 }
213 };
214
215 #define VALWIDTH 120
216 #define DIFFSWIDTH 80
217 #define PAUSEWIDTH 60
218 #define ANNUNWIDTH 140
219 #define WIDTHS (VALWIDTH + 2*DIFFSWIDTH + PAUSEWIDTH + ANNUNWIDTH)
220 #define STATUS_COLOR 0xfdf5e600
221
create_spectrum_viewer()222 void create_spectrum_viewer()
223 {
224 if (spectrum_viewer) return;
225
226 spectrum_viewer = new Fl_Double_Window(0, 0, 550, 400, "Spectrum Scope");
227 spectrum_viewer->xclass(PACKAGE_NAME);
228
229 fftmon_mnuFrame = new Fl_Group(0,0, spectrum_viewer->w(), 20);
230 fftmon_mnu_bar = new Fl_Menu_Bar(0, 0, fftmon_mnuFrame->w() - WIDTHS, 20);
231 fftmon_mnu_bar->menu(fftmon_menu);
232
233 pause_button = new Fl_Button(
234 fftmon_mnu_bar->x() + fftmon_mnu_bar->w(), 0,
235 PAUSEWIDTH, 20, "Running");
236 pause_button->callback((Fl_Callback*)cb_pause_button);
237
238 annunciator = new Fl_Box(
239 pause_button->x() + pause_button->w(), 0,
240 ANNUNWIDTH, 20, "");
241 annunciator->box(FL_DOWN_BOX);
242 annunciator->color(STATUS_COLOR);
243
244 values = new Fl_Output(
245 annunciator->x() + annunciator->w(), 0,
246 VALWIDTH, 20, "");
247 values->value("");
248 values->color(STATUS_COLOR);
249
250 db_diffs = new Fl_Output(
251 values->x() + values->w(), 0,
252 DIFFSWIDTH, 20, "");
253 db_diffs->value("");
254 db_diffs->color(STATUS_COLOR);
255
256 f_diffs = new Fl_Output(
257 db_diffs->x() + db_diffs->w(), 0,
258 DIFFSWIDTH, 20, "");
259 f_diffs->value("");
260 f_diffs->color(STATUS_COLOR);
261
262 fftmon_mnuFrame->resizable(fftmon_mnu_bar);
263 fftmon_mnuFrame->end();
264
265 group1 = new Fl_Group(0, 20, 550, 330);
266 fftscope = new spectrum (group1->x(), group1->y(), group1->w(), group1->h());
267 group1->resizable(fftscope);
268 group1->end();
269 group1->show();
270
271 group2 = new Fl_Group(0, 350, 550, 50);
272
273 g1 = new Fl_Group(0, 350, 548, 50, "");
274
275 fftviewer_scans = new Fl_Counter(5, 360, 100, 22, "# scans");
276 fftviewer_scans->minimum(1);
277 fftviewer_scans->maximum(500);
278 fftviewer_scans->step(1);
279 fftviewer_scans->lstep(10.0);
280 fftviewer_scans->value(50);
281 fftviewer_scans->callback((Fl_Callback*)cb_fftviewer_scans);
282 fftviewer_scans->value(progdefaults.fftviewer_scans);
283 fftviewer_scans->tooltip(_("each display point an average of past N fft values"));
284
285 fftviewer_range = new Fl_Counter(
286 5 + fftviewer_scans->x() + fftviewer_scans->w(), 360,
287 80, 22, "dB Range");
288 fftviewer_range->type(1);
289 fftviewer_range->minimum(20);
290 fftviewer_range->maximum(120);
291 fftviewer_range->step(10);
292 fftviewer_range->value(60);
293 fftviewer_range->callback((Fl_Callback*)cb_fftviewer_range);
294 fftviewer_range->value(progdefaults.fftviewer_range);
295 fftviewer_range->tooltip(_("range of dB scale"));
296
297 fftviewer_maxdb = new Fl_Counter(
298 5 + fftviewer_range->x() + fftviewer_range->w(), 360,
299 80, 22, "upper dB");
300 fftviewer_maxdb->type(1);
301 fftviewer_maxdb->minimum(-60);
302 fftviewer_maxdb->maximum(0);
303 fftviewer_maxdb->step(10);
304 fftviewer_maxdb->value(0);
305 fftviewer_maxdb->callback((Fl_Callback*)cb_fftviewer_maxdb);
306 fftviewer_maxdb->value(progdefaults.fftviewer_maxdb);
307 fftviewer_maxdb->tooltip(_("offset Db scale"));
308
309 int xp = 5 + fftviewer_maxdb->x() + fftviewer_maxdb->w();
310 g2 = new Fl_Group(
311 xp, 352,
312 g1->w() - 2 - xp, 46, "");
313 g2->box(FL_ENGRAVED_FRAME);
314
315 fftviewer_fcenter = new Fl_Counter(
316 5 + g2->x(), 358,
317 100, 22, "F-center");
318 fftviewer_fcenter->minimum(0);
319 fftviewer_fcenter->maximum(3950);
320 fftviewer_fcenter->step(1);
321 fftviewer_fcenter->lstep(10);
322 fftviewer_fcenter->value(0);
323 fftviewer_fcenter->callback((Fl_Callback*)cb_fftviewer_fcenter);
324 fftviewer_fcenter->value(progdefaults.fftviewer_fcenter);
325 fftviewer_fcenter->tooltip(_("center frequency"));
326
327 fftviewer_frng = new Fl_Counter(
328 5 + fftviewer_fcenter->x() + fftviewer_fcenter->w(), 358,
329 110, 22, "F-range");
330 fftviewer_frng->minimum(100);
331 fftviewer_frng->maximum(4000);
332 fftviewer_frng->step(20);
333 fftviewer_frng->lstep(100);
334 fftviewer_frng->value(4000);
335 fftviewer_frng->callback((Fl_Callback*)cb_fftviewer_frng);
336 fftviewer_frng->value(progdefaults.fftviewer_frng);
337 fftviewer_frng->tooltip(_("frequency range"));
338
339 fftviewer_reset = new Fl_Button(
340 5 + fftviewer_frng->x() + fftviewer_frng->w(), 354,
341 38, 20, "Reset");
342 fftviewer_reset->callback((Fl_Callback*)cb_fftviewer_reset);
343 fftviewer_reset->tooltip(_("Center = 2000, Range = 4000"));
344
345 fftviewer_goto = new Fl_Button(
346 5 + fftviewer_frng->x() + fftviewer_frng->w(), 376,
347 38, 20, "Goto");
348 fftviewer_goto->callback((Fl_Callback*)cb_fftviewer_goto);
349 fftviewer_goto->tooltip(_("Center - wf track\nRange = 10 * mode-bw"));
350
351 g2->end();
352
353 g1->end();
354
355 g3 = new Fl_Group(spectrum_viewer->w() - 2, 350, 2, 50, "");
356 g3->end();
357
358 group2->end();
359 group2->resizable(g3);
360
361 spectrum_viewer->resizable(group1);
362 spectrum_viewer->size_range(550, 400);
363 spectrum_viewer->end();
364 spectrum_viewer->callback((Fl_Callback *)cb_spectrum_viewer);
365
366 if (progStatus.x_graticule) {
367 fftmon_menu[2].setonly();
368 fftscope->x_graticule(true);
369 fftscope->y_graticule(false);
370 } else if (progStatus.y_graticule) {
371 fftmon_menu[3].setonly();
372 fftscope->x_graticule(false);
373 fftscope->y_graticule(true);
374 } else {
375 fftmon_menu[4].setonly();
376 fftscope->x_graticule(true);
377 fftscope->y_graticule(true);
378 }
379 }
380
381 //======================================================================
382
open_spectrum_viewer()383 void open_spectrum_viewer()
384 {
385 if (!spectrum_viewer) create_spectrum_viewer();
386 if (!fft_modem) {
387 progdefaults.fftviewer_fcenter = active_modem->get_freq();
388 fft_modem = new fftmon();
389 active_modem->set_freq(progdefaults.fftviewer_fcenter);
390 } else
391 progdefaults.fftviewer_fcenter = active_modem->get_freq();
392
393 int fr = progdefaults.fftviewer_frng;
394 if (progdefaults.wf_spectrum_modem_scale)
395 fr = progdefaults.wf_spectrum_scale_factor * active_modem->get_bandwidth();
396 check_frng(fr);
397 fftviewer_fcenter->value(progdefaults.fftviewer_fcenter);
398 fftviewer_fcenter->redraw();
399
400 if (progdefaults.wf_spectrum_dbvals) {
401 progdefaults.fftviewer_range = progdefaults.wfAmpSpan;
402 progdefaults.fftviewer_maxdb = progdefaults.wfRefLevel;
403 fftviewer_maxdb->value(progdefaults.fftviewer_maxdb);
404 fftviewer_maxdb->redraw();
405 fftviewer_range->value(progdefaults.fftviewer_range);
406 fftviewer_range->redraw();
407 }
408
409 spectrum_viewer->show();
410
411 spectrum_viewer->redraw();
412 spectrum_viewer->resize(progStatus.svX, progStatus.svY, progStatus.svW, progStatus.svH);
413
414 }
415
close_spectrum_viewer()416 void close_spectrum_viewer()
417 {
418 if (spectrum_viewer) {
419 spectrum_viewer->hide();
420 delete spectrum_viewer;
421 spectrum_viewer = 0;
422 }
423 if (fft_modem) {
424 delete fft_modem;
425 fft_modem = 0;
426 }
427 }
428
recenter_spectrum_viewer()429 void recenter_spectrum_viewer()
430 {
431 if (!spectrum_viewer) return;
432 if (!spectrum_viewer->visible()) return;
433 if (!progdefaults.wf_spectrum_center) return;
434 if (!fft_modem) return;
435
436 progdefaults.fftviewer_fcenter = active_modem->get_freq();
437 int fr = progdefaults.fftviewer_frng;
438 if (progdefaults.wf_spectrum_modem_scale)
439 fr = progdefaults.wf_spectrum_scale_factor * active_modem->get_bandwidth();
440 check_frng(fr);
441
442 fftviewer_fcenter->value(progdefaults.fftviewer_fcenter);
443 fftviewer_fcenter->redraw();
444
445 if (progdefaults.wf_spectrum_dbvals) {
446 progdefaults.fftviewer_range = progdefaults.wfAmpSpan;
447 progdefaults.fftviewer_maxdb = progdefaults.wfRefLevel;
448 fftviewer_maxdb->value(progdefaults.fftviewer_maxdb);
449 fftviewer_maxdb->redraw();
450 fftviewer_range->value(progdefaults.fftviewer_range);
451 fftviewer_range->redraw();
452 }
453
454 spectrum_viewer->redraw();
455 }
456