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