1 // ----------------------------------------------------------------------------
2 // waterfall.cxx - Waterfall Spectrum Analyzer Widget
3 //
4 // Copyright (C) 2006-2010
5 //		Dave Freese, W1HKJ
6 // Copyright (C) 2007-2010
7 //		Stelios Bounanos, M0GLD
8 //
9 // This file is part of fldigi.
10 //
11 // Fldigi is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // Fldigi is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
23 // ----------------------------------------------------------------------------
24 
25 //#define USE_BLACKMAN
26 //#define USE_HAMMING
27 //#define USE_HANNING
28 
29 #include <config.h>
30 
31 #include <sstream>
32 #include <vector>
33 #include <queue>
34 #include <algorithm>
35 #include <map>
36 
37 #include <FL/Fl.H>
38 #include <FL/fl_draw.H>
39 #include <FL/Fl_Widget.H>
40 #include <FL/Fl_Repeat_Button.H>
41 #include <FL/Fl_Light_Button.H>
42 #include <FL/Fl_Menu_Button.H>
43 #include <FL/Fl_Group.H>
44 #include <FL/Fl_Box.H>
45 #include <FL/Fl_Counter.H>
46 #include <FL/Enumerations.H>
47 
48 #include "fl_digi.h"
49 #include "trx.h"
50 #include "misc.h"
51 #include "waterfall.h"
52 #include "main.h"
53 #include "modem.h"
54 #include "qrunner.h"
55 #include "threads.h"
56 
57 #if USE_HAMLIB
58 	#include "hamlib.h"
59 #endif
60 #include "rigio.h"
61 
62 #include "fldigi-config.h"
63 #include "configuration.h"
64 #include "status.h"
65 #include "Viewer.h"
66 #include "macros.h"
67 #include "arq_io.h"
68 #include "confdialog.h"
69 #include "flmisc.h"
70 #include "gettext.h"
71 #include "rtty.h"
72 #include "flslider2.h"
73 #include "debug.h"
74 #include "rigsupport.h"
75 #include "xmlrpc.h"
76 #include "psm/psm.h"
77 #include "kiss_io.h"
78 #include "fmt_dialog.h"
79 
80 #include "spectrum_viewer.h"
81 
82 using namespace std;
83 
84 //pthread_mutex_t draw_mutex = PTHREAD_MUTEX_INITIALIZER;
85 
86 #define bwFFT		30
87 #define cwRef		50
88 #define bwX1		25
89 #define bwMov		18
90 #define bwRate		45
91 #define cwCnt		92
92 #define bwQsy		32
93 #define bwXmtLock	32
94 #define bwRev		32
95 #define bwMem		40
96 #define bwXmtRcv	40
97 #define wSpace		1
98 
99 #define bwdths	(wSpace + bwFFT + wSpace + cwRef + wSpace + cwRef + wSpace + bwX1 + \
100 				wSpace + 3*bwMov + wSpace + bwRate + wSpace + \
101 				cwCnt + wSpace + bwQsy + wSpace + bwMem + wSpace + \
102 				bwXmtLock + wSpace + bwRev + wSpace + bwXmtRcv + wSpace)
103 
104 extern modem *active_modem;
105 
106 static	RGB RGByellow	= {254,254,0};
107 //static	RGB RGBgreen	= {0,254,0};
108 //static	RGB RGBdkgreen	= {0,128,0};
109 //static	RGB RGBblue		= {0,0,255};
110 static	RGB RGBred		= {254,0,0};
111 //static	RGB RGBwhite	= {254,254,254};
112 //static	RGB RGBblack	= {0,0,0};
113 //static RGB RGBmagenta = {196,0,196};
114 //static RGB RGBblack   = {0,0,0};
115 
116 // RGBI is a structure consisting of the values RED, GREEN, BLUE, INTENSITY
117 // each value can range from 0 (extinguished) to 255 (full on)
118 // the INTENSITY value is used for the grayscale waterfall display
119 
120 RGBI	mag2RGBI[256];
121 RGB		palette[9];
122 
123 short int *tmp_fft_db;
124 
125 static pthread_mutex_t waterfall_mutex = PTHREAD_MUTEX_INITIALIZER;
126 
WFdisp(int x0,int y0,int w0,int h0,char * lbl)127 WFdisp::WFdisp (int x0, int y0, int w0, int h0, char *lbl) :
128 			  Fl_Widget(x0,y0,w0,h0,"") {
129 	disp_width = w();
130 	if (disp_width > progdefaults.HighFreqCutoff/4)
131 		disp_width = progdefaults.HighFreqCutoff/4;
132 	scale_width = IMAGE_WIDTH * 2;
133 	image_height = h() - WFTEXT - WFSCALE - WFMARKER;
134 	image_area      = IMAGE_WIDTH * image_height;
135 	sig_image_area  = IMAGE_WIDTH * h();
136 	RGBsize			= sizeof(RGB);
137 	RGBwidth		= RGBsize * scale_width;
138 	fft_img			= new RGBI[image_area];
139 	markerimage		= new RGB[scale_width * WFMARKER];
140 	scaleimage		= new uchar[scale_width * WFSCALE];
141 	scline			= new uchar[scale_width];
142 	fft_sig_img 	= new uchar[image_area];
143 	sig_img			= new uchar[sig_image_area];
144 	pwr				= new wf_fft_type[IMAGE_WIDTH];
145 	fft_db			= new short int[image_area];
146 	tmp_fft_db		= new short int[image_area];
147 	circbuff		= new wf_fft_type[WF_FFTLEN];
148 	wfbuf			= new wf_cpx_type[WF_FFTLEN];
149 	wfft			= new g_fft<wf_fft_type>(WF_FFTLEN);
150 	fftwindow		= new wf_fft_type[WF_FFTLEN];
151 	setPrefilter(progdefaults.wfPreFilter);
152 
153 	memset(circbuff, 0, WF_FFTLEN * sizeof(double));
154 
155 	mag = 1;
156 	step = 4;
157 	offset = 0;
158 	sigoffset = 0;
159 	ampspan = 75;
160 //	reflevel = -10;
161 	initmaps();
162 	bandwidth = 32;
163 	RGBmarker = RGBred;
164 	RGBcursor = RGByellow;
165 	RGBInotch.I = progdefaults.notchRGBI.I;
166 	RGBInotch.R = progdefaults.notchRGBI.R;
167 	RGBInotch.G = progdefaults.notchRGBI.G;
168 	RGBInotch.B = progdefaults.notchRGBI.B;
169 	mode = WATERFALL;
170 	centercarrier = false;
171 	overload = false;
172 	rfc = 0L;
173 	usb = true;
174 	wfspeed = NORMAL;
175 	wfspdcnt = 0;
176 	dispcnt = 1.0 * WF_BLOCKSIZE / WF_SAMPLERATE;
177 	dispdec = 1.0 * WF_BLOCKSIZE / WF_SAMPLERATE;
178 	wantcursor = false;
179 	cursormoved = false;
180 	for (int i = 0; i < IMAGE_WIDTH; i++)
181 		pwr[i] = 0.0;
182 
183 	carrier(1500);
184 
185 	oldcarrier = newcarrier = 0;
186 	tmp_carrier = false;
187 	ptrCB = 0;
188 	ptrFFTbuff = 0;
189 
190 	for (int i = 0; i < 256; i++)
191 		mag2RGBI[i].I = mag2RGBI[i].R = mag2RGBI[i].G = mag2RGBI[i].B = 0;
192 
193 	int error = 0;
194 
195 // use fastest sync converter
196 //	SRC_SINC_BEST_QUALITY		= 0,
197 //	SRC_SINC_MEDIUM_QUALITY		= 1,
198 //	SRC_SINC_FASTEST			= 2,
199 //	SRC_ZERO_ORDER_HOLD			= 3,
200 //	SRC_LINEAR					= 4,
201 
202 	src_state = src_new(2, 1, &error);
203 	if (error) {
204 		LOG_ERROR("src_new error %d: %s", error, src_strerror(error));
205 		abort();
206 	}
207 
208 	error = src_reset(src_state);
209 	if (error)
210 		LOG_ERROR("src_reset error %d: %s", error, src_strerror(error));
211 
212 	src_data.end_of_input = 0;
213 	src_data.src_ratio = 0.0;
214 	genptr = 0;
215 }
216 
~WFdisp()217 WFdisp::~WFdisp() {
218 	delete wfft;
219 	delete [] fft_img;
220 	delete [] scaleimage;
221 	delete [] markerimage;
222 	delete [] fft_sig_img;
223 	delete [] sig_img;
224 	delete [] pwr;
225 	delete [] scline;
226 	delete [] fft_db;
227 	delete [] tmp_fft_db;
228 }
229 
initMarkers()230 void WFdisp::initMarkers() {
231 	memset(markerimage, 224, RGBwidth * WFMARKER);
232 }
233 
234 // draw a marker of specified width and colour centred at freq and clrM
makeMarker_(int width,const RGB * color,int freq,const RGB * clrMin,RGB * clrM,const RGB * clrMax)235 inline void WFdisp::makeMarker_(int width, const RGB* color, int freq, const RGB* clrMin, RGB* clrM, const RGB* clrMax)
236 {
237 	if (!active_modem) return;
238 	trx_mode marker_mode = active_modem->get_mode();
239 
240 	if (marker_mode == MODE_RTTY) {
241 	// rtty has two bandwidth indicators on the waterfall
242 	// upper and lower frequency
243 		int shift = static_cast<int>(
244 			(progdefaults.rtty_shift < rtty::numshifts ?
245 				rtty::SHIFT[progdefaults.rtty_shift] :
246 				progdefaults.rtty_custom_shift));
247 		int bw_limit_hi = (int)(shift / 2 + progdefaults.RTTY_BW / 2.0);
248 		int bw_limit_lo = (int)(shift / 2 - progdefaults.RTTY_BW / 2.0);
249 		int bw_freq = static_cast<int>(freq + 0.5);
250 		int bw_lower1 = -bw_limit_hi;
251 		int bw_upper1 = -bw_limit_lo;
252 		int bw_lower2 = bw_limit_lo;
253 		int bw_upper2 = bw_limit_hi;
254 		if (bw_lower1 + bw_freq < 0)
255 			bw_lower1 -= bw_lower1 + bw_freq;
256 		if (bw_upper1 + bw_freq < 0)
257 			bw_lower2 -= bw_lower2 + bw_freq;
258 		if (bw_upper2 + bw_freq > scale_width)
259 			bw_upper2 -= bw_upper2 + bw_freq - scale_width;
260 		if (bw_lower2 + bw_freq > scale_width)
261 			bw_lower2 -= bw_lower2 + bw_freq - scale_width;
262 	// draw it
263 		RGB* clrPos;
264 		for (int y = 0; y < WFMARKER - 2; y++) {
265 			for (int x = bw_lower1; x < bw_upper1; x++) {
266 				clrPos = clrM + x + y * scale_width;
267 				if (clrPos > clrMin && clrPos < clrMax)
268 					*clrPos = *color;
269 			}
270 			for (int x = bw_lower2; x < bw_upper2; x++) {
271 				clrPos = clrM + x + y * scale_width;
272 				if (clrPos > clrMin && clrPos < clrMax)
273 					*clrPos = *color;
274 			}
275 		}
276 		return;
277 	}
278 
279 	int bw_lower = -width, bw_upper = width;
280 
281 	if (marker_mode >= MODE_MT63_500S && marker_mode <= MODE_MT63_2000L)
282 			bw_upper = (int)(width * 31 / 32);
283 
284 	if (marker_mode == MODE_FSQ || marker_mode == MODE_IFKP)
285 			bw_upper = (int)(width * 32 / 33);
286 
287 	if (bw_lower + static_cast<int>(freq+0.5) < 0)
288 		bw_lower -= bw_lower + static_cast<int>(freq+0.5);
289 
290 	if (bw_upper + static_cast<int>(freq+0.5) > scale_width)
291 		bw_upper -= bw_upper + static_cast<int>(freq+0.5) - scale_width;
292 
293 	// draw it
294 	RGB* clrPos;
295 	for (int y = 0; y < WFMARKER - 2; y++) {
296 		for (int x = bw_lower; x < bw_upper; x++) {
297 			clrPos = clrM + x + y * scale_width;
298 			if (clrPos > clrMin && clrPos < clrMax)
299 				*clrPos = *color;
300 		}
301 	}
302 }
303 
make_fmt_marker()304 void WFdisp::make_fmt_marker ()
305 {
306 	RGB unk_trk_color, ref_trk_color;
307 
308 	memset(markerimage + scale_width, 0, RGBwidth * (WFMARKER - 2));
309 
310 	Fl::get_color(progdefaults.FMT_unk_color, unk_trk_color.R, unk_trk_color.G, unk_trk_color.B);
311 
312 	Fl::get_color(progdefaults.FMT_ref_color, ref_trk_color.R, ref_trk_color.G, ref_trk_color.B);
313 
314 	int fmt_bw = progdefaults.FMT_filter;
315 
316 	RGB *mrkr_U = markerimage + scale_width + int(round(cnt_unk_freq->value()));
317 	RGB *mrkr_R = markerimage + scale_width + int(round(cnt_ref_freq->value()));
318 
319 	// draw marker
320 
321 	for (int y = 0; y < WFMARKER - 2; y++) {
322 		for (int x = -fmt_bw; x <= fmt_bw; x++) {
323 			*(mrkr_U + x + y * scale_width) = unk_trk_color;
324 			*(mrkr_R + x + y * scale_width) = ref_trk_color;
325 		}
326 	}
327 
328 }
329 
makeMarker()330 void WFdisp::makeMarker()
331 {
332 	if (unlikely(!active_modem))
333 		return;
334 
335 	int mode = active_modem->get_mode();
336 	RGB *clrMin, *clrMax, *clrM;
337 	int marker_width = bandwidth;
338 
339 	if (mode == MODE_FMT) {
340 		make_fmt_marker ();
341 		marker_width = progdefaults.FMT_filter;
342 	} else {
343 		clrMin = markerimage + scale_width;
344 		clrMax = clrMin + (WFMARKER - 2) * scale_width;
345 		memset(clrMin, 0, RGBwidth * (WFMARKER - 2));
346 		clrM = clrMin + (int)((double)carrierfreq + 0.5);
347 
348 		if (mode >= MODE_PSK_FIRST && mode <= MODE_PSK_LAST)
349 			marker_width += mailserver ? progdefaults.ServerOffset :
350 				progdefaults.SearchRange;
351 		else if (mode >= MODE_FELDHELL && mode <= MODE_HELL80)
352 			marker_width = (int)progdefaults.HELL_BW;
353 		else if (mode == MODE_RTTY)
354 			marker_width = static_cast<int>((progdefaults.rtty_shift < rtty::numshifts ?
355 					rtty::SHIFT[progdefaults.rtty_shift] :
356 					progdefaults.rtty_custom_shift));
357 		marker_width = (int)(marker_width / 2.0 + 1);
358 
359 		RGBmarker.R = progdefaults.bwTrackRGBI.R;
360 		RGBmarker.G = progdefaults.bwTrackRGBI.G;
361 		RGBmarker.B = progdefaults.bwTrackRGBI.B;
362 
363 		makeMarker_(marker_width, &RGBmarker, carrierfreq, clrMin, clrM, clrMax);
364 
365 		if (unlikely(active_modem->freqlocked() || mode == MODE_FSQ)) {
366 			int txfreq = static_cast<int>(active_modem->get_txfreq());
367 			adjust_color_inv(RGBmarker.R, RGBmarker.G, RGBmarker.B, FL_BLACK, FL_RED);
368 			makeMarker_( static_cast<int>(bandwidth / 2.0 + 1),
369 						&RGBmarker, txfreq,
370 						clrMin, clrMin + (int)((double)txfreq + 0.5), clrMax);
371 		}
372 	}
373 
374 	if (!wantcursor) return;
375 
376 	if (cursorpos > progdefaults.HighFreqCutoff - bandwidth / 2 / step)
377 		cursorpos = progdefaults.HighFreqCutoff - bandwidth / 2 / step;
378 	if (cursorpos >= (progdefaults.HighFreqCutoff - offset - bandwidth/2)/step)
379 		cursorpos = (progdefaults.HighFreqCutoff - offset - bandwidth/2)/step;
380 	if (cursorpos < (progdefaults.LowFreqCutoff + bandwidth / 2) / step)
381 		cursorpos = (progdefaults.LowFreqCutoff + bandwidth / 2) / step;
382 
383 // Create the cursor marker
384 	double xp = offset + step * cursorpos;
385 	if (xp < bandwidth / 2.0 || xp > (progdefaults.HighFreqCutoff - bandwidth / 2.0))
386 		return;
387 	clrM = markerimage + scale_width + (int)(xp + 0.5);
388 	RGBcursor.R = progdefaults.cursorLineRGBI.R;
389 	RGBcursor.G = progdefaults.cursorLineRGBI.G;
390 	RGBcursor.B = progdefaults.cursorLineRGBI.B;
391 
392 	int bw_lo = marker_width;
393 	int bw_hi = marker_width;
394 	if (mode >= MODE_MT63_500S && mode <= MODE_MT63_2000L)
395 		bw_hi = bw_hi * 31 / 32;
396 	if (mode == MODE_FSQ || mode == MODE_IFKP) bw_hi = bw_hi * 32 / 33;
397 
398 	for (int y = 0; y < WFMARKER - 2; y++) {
399 		int incr = y * scale_width;
400 		int msize = (WFMARKER - 2 - y)*RGBsize*step/4;
401 		for (int m = -step; m < step; m++)
402 			*(clrM + incr + m) = RGBcursor;
403 
404 		if (xp - (bw_lo + msize) > 0)
405 			for (int i = bw_lo - msize; i <= bw_lo + msize; i++)
406 				*(clrM - i + incr) = RGBcursor;
407 
408 		if (xp + (bw_hi + msize) < scale_width)
409 			for (int i = bw_hi - msize; i <= bw_hi + msize; i++)
410 				*(clrM + i + incr) = RGBcursor;
411 	}
412 }
413 
makeScale()414 void WFdisp::makeScale() {
415 	uchar *gmap = scaleimage;
416 	memset(scline, 0, scale_width);
417 
418 	for (int tic = 500; tic < scale_width; tic += 500) {
419 		scline[tic] = 255;
420 		for (int ticn = 1; ticn < step; ticn++)
421 			if (tic + ticn < scale_width) scline[tic + ticn] = 255;
422 	}
423 
424 	for (int i = 0; i < WFSCALE - 5; i++) {
425 		memcpy(gmap, scline, scale_width);
426 		gmap += (scale_width);
427 	}
428 
429 	for (int tic = 100; tic < scale_width ; tic += 100) {
430 		scline[tic] = 255;
431 		for (int ticn = 1; ticn < step; ticn++)
432 			if (tic + ticn < scale_width) scline[tic + ticn] = 255;
433 	}
434 	for (int i = 0; i < 5; i++) {
435 		memcpy(gmap, scline, scale_width);
436 		gmap += (scale_width);
437 	}
438 }
439 
setcolors()440 void WFdisp::setcolors() {
441 	double di;
442 	int r, g, b;
443 	for (int i = 0; i < 256; i++) {
444 		di = sqrt((double)i / 256.0);
445 		mag2RGBI[i].I = (uchar)(200*di);
446 	}
447 	for (int n = 0; n < 8; n++) {
448 		for (int i = 0; i < 32; i++) {
449 			r = palette[n].R + (int)(1.0 * i * (palette[n+1].R - palette[n].R) / 32.0);
450 			g = palette[n].G + (int)(1.0 * i * (palette[n+1].G - palette[n].G) / 32.0);
451 			b = palette[n].B + (int)(1.0 * i * (palette[n+1].B - palette[n].B) / 32.0);
452 			mag2RGBI[i + 32*n].R = r;
453 			mag2RGBI[i + 32*n].G = g;
454 			mag2RGBI[i + 32*n].B = b;
455 		}
456 	}
457 }
458 
459 
initmaps()460 void WFdisp::initmaps() {
461 	for (int i = 0; i < image_area; i++) fft_db[i] = tmp_fft_db[i] = log2disp(-1000);
462 
463 	memset (fft_img, 0, image_area * sizeof(RGBI) );
464 	memset (scaleimage, 0, scale_width * WFSCALE);
465 	memset (markerimage, 0, RGBwidth * WFMARKER);
466 	memset (fft_sig_img, 0, image_area);
467 	memset (sig_img, 0, sig_image_area);
468 
469 	memset (mag2RGBI, 0, sizeof(mag2RGBI));
470 	initMarkers();
471 	makeScale();
472 	setcolors();
473 }
474 
peakFreq(int f0,int delta)475 int WFdisp::peakFreq(int f0, int delta)
476 {
477 	guard_lock waterfall_lock(&waterfall_mutex);
478 
479 	double threshold = 0.0;
480 	int f1, fmin =	(int)((f0 - delta)),
481 		f2, fmax =	(int)((f0 + delta));
482 	f1 = fmin; f2 = fmax;
483 	if (fmin < 0 || fmax > IMAGE_WIDTH) return f0;
484 	for (int f = fmin; f <= fmax; f++)
485 		threshold += pwr[f];
486 	threshold /= delta;
487 	for (int f = fmin; f <= fmax; f++)
488 		if (pwr[f] > threshold) {
489 			f2 = f;
490 		}
491 	for (int f = fmax; f >= fmin; f--)
492 		if (pwr[f] > threshold) {
493 			f1 = f;
494 		}
495 	return (f1 + f2) / 2;
496 }
497 
powerDensity(double f0,double bw)498 double WFdisp::powerDensity(double f0, double bw)
499 {
500 	double pwrdensity = 0.0;
501 	int flower = (int)((f0 - bw/2)),
502 		fupper = (int)((f0 + bw/2));
503 	if (flower < 0 || fupper > IMAGE_WIDTH)
504 		return 0.0;
505 	{
506 		guard_lock waterfall_lock(&waterfall_mutex);
507 		for (int i = flower; i <= fupper; i++)
508 			pwrdensity += pwr[i];
509 	}
510 	return pwrdensity/(bw+1);
511 }
512 
513 /// Frequency of the maximum power for a given bandwidth. Used for AFC.
powerDensityMaximum(int bw_nb,const int (* bw)[2]) const514 double WFdisp::powerDensityMaximum(int bw_nb, const int (*bw)[2]) const
515 {
516 	if (bw_nb < 1) return carrierfreq;
517 
518 	int fmax[bw_nb];
519 	double pnbw[bw_nb];
520 	int f_lowest = carrierfreq;
521 	int f_highest = carrierfreq;
522 	double max_pwr = 0;
523 	for (int i = 0; i < bw_nb; i++) {
524 		fmax[i] = carrierfreq;
525 		pnbw[i] = 0;
526 	}
527 
528 	{
529 		guard_lock waterfall_lock(&waterfall_mutex);
530 		for (int i = 0; i < bw_nb; i++) {
531 			f_lowest = carrierfreq + bw[i][0];
532 			if (f_lowest <= 0) f_lowest = 0;
533 			f_highest = carrierfreq + bw[i][1];
534 			if (f_highest > IMAGE_WIDTH) f_highest = IMAGE_WIDTH;
535 			max_pwr = 0;
536 			pnbw[i] = 0;
537 			for (int n = f_lowest; n < f_highest; n++) {
538 				if (pwr[n] > max_pwr) {
539 					max_pwr = pwr[n];
540 					fmax[i] = n;
541 				}
542 				pnbw[i] += pwr[n];
543 			}
544 			if (pnbw[i] == 0) return carrierfreq;
545 		}
546 	}
547 	int fmid = 0;
548 	double total_pwr = 0;
549 	for (int i = 0; i < bw_nb; i++) total_pwr += pnbw[i];
550 
551 	for (int i = 0; i < bw_nb; i++) fmid += fmax[i] * pnbw[i] / total_pwr;
552 
553 	return fmid;
554 }
555 
setPrefilter(int v)556 void WFdisp::setPrefilter(int v)
557 {
558 	switch (v) {
559 	case WF_FFT_RECTANGULAR: RectWindow(fftwindow, WF_FFTLEN); break;
560 	case WF_FFT_BLACKMAN: BlackmanWindow(fftwindow, WF_FFTLEN); break;
561 	case WF_FFT_HAMMING: HammingWindow(fftwindow, WF_FFTLEN); break;
562 	case WF_FFT_HANNING: HanningWindow(fftwindow, WF_FFTLEN); break;
563 	case WF_FFT_TRIANGULAR: TriangularWindow(fftwindow, WF_FFTLEN); break;
564 	}
565 //	BlackmanWindow(fftwindow, WF_FFTLEN);
566 	prefilter = v;
567 }
568 
log2disp(int v)569 int WFdisp::log2disp(int v)
570 {
571 	double val = -255.0 * v / ampspan;
572 	if (val < 0) return 255;
573 	if (val > 255 ) return 0;
574 	return (int)(255 - val);
575 }
576 
processFFT()577 void WFdisp::processFFT() {
578 	if (prefilter != progdefaults.wfPreFilter)
579 		setPrefilter(progdefaults.wfPreFilter);
580 
581 	wf_fft_type scale = WF_FFTLEN / 8000.0;
582 
583 	if ((wfspeed != PAUSE) && ((dispcnt -= dispdec) <= 0)) {
584 		static const int log2disp100 = log2disp(-100);
585 		double vscale = 2.0 / WF_FFTLEN;
586 
587 		for (int i = 0; i < WF_FFTLEN; i++) wfbuf[i] = 0;
588 
589 		void *pv = static_cast<void*>(wfbuf);
590 		wf_fft_type *pbuf = static_cast<wf_fft_type*>(pv);
591 
592 		int latency = progdefaults.wf_latency;
593 		if (latency < 1) latency = 1;
594 		if (latency > 16) latency = 16;
595 		int nsamples = WF_FFTLEN * latency / 16;
596 		vscale *= sqrt(16.0 / latency);
597 		for (int i = 0; i < nsamples; i++)
598 			pbuf[i] = fftwindow[i * 16 / latency] * circbuff[i] * vscale;
599 
600 		for (int i = 0; i < WF_FFTLEN; i++)
601 			pbuf[i] = fftwindow[i] * circbuff[i] * vscale;
602 
603 		wfft->RealFFT(wfbuf);
604 
605 		memset(pwr, 0, progdefaults.LowFreqCutoff * sizeof(wf_fft_type));
606 		memset(&fft_db[ptrFFTbuff * IMAGE_WIDTH],
607 				log2disp100,
608 				progdefaults.LowFreqCutoff * sizeof(*fft_db));
609 
610 		int n = 0;
611 		for (int i = progdefaults.LowFreqCutoff + 1; i < IMAGE_WIDTH; i++) {
612 			n = round(scale * i);
613 			pwr[i] = norm(wfbuf[n]);
614 			int ffth = round(10.0 * log10(pwr[i] + 1e-10) );
615 			fft_db[ptrFFTbuff * IMAGE_WIDTH + i] = log2disp(ffth);
616 		}
617 
618 		ptrFFTbuff--;
619 		if (ptrFFTbuff < 0) ptrFFTbuff += image_height;
620 
621 		for (int i = 0; i < image_height; i++) {
622 			int j = (i + 1 + ptrFFTbuff) % image_height;
623 			memmove( (void *)(tmp_fft_db + i * IMAGE_WIDTH),
624 					 (void *)(fft_db + j * IMAGE_WIDTH),
625 					 IMAGE_WIDTH * sizeof(short int));
626 		}
627 
628 		dispdec = 1.0 * WF_BLOCKSIZE / WF_SAMPLERATE;
629 		dispcnt = 1.0 * WF_BLOCKSIZE / WF_SAMPLERATE; // FAST
630 		if (wfspeed == NORMAL) dispcnt *= NORMAL;
631 		if (wfspeed == SLOW) dispcnt *= progdefaults.drop_speed;
632 	}
633 	redraw();
634 }
635 
process_analog(wf_fft_type * sig,int len)636 void WFdisp::process_analog (wf_fft_type *sig, int len) {
637 	int h1, h2, h3;
638 	int sigy, sigpixel, ynext, graylevel;
639 	h1 = h()/8 - 1;
640 	h2 = h()/2 - 1;
641 	h3 = h()*7/8 + 1;
642 	graylevel = 220;
643 // clear the signal display area
644 	sigy = 0;
645 	sigpixel = IMAGE_WIDTH*h2;
646 	memset (sig_img, 0, sig_image_area);
647 	memset (&sig_img[h1*IMAGE_WIDTH], 160, IMAGE_WIDTH);
648 	memset (&sig_img[h2*IMAGE_WIDTH], 255, IMAGE_WIDTH);
649 	memset (&sig_img[h3*IMAGE_WIDTH], 160, IMAGE_WIDTH);
650 	int cbc = ptrCB;
651 	for (int c = 0; c < IMAGE_WIDTH; c++) {
652 		ynext = (int)(h2 * sig[cbc]);
653 		if (ynext < -h2) ynext = -h2;
654 		if (ynext > h2) ynext = h2;
655 		cbc = (cbc + 1) % (WF_FFTLEN);
656 		for (; sigy < ynext; sigy++) sig_img[sigpixel -= IMAGE_WIDTH] = graylevel;
657 		for (; sigy > ynext; sigy--) sig_img[sigpixel += IMAGE_WIDTH] = graylevel;
658 		sig_img[sigpixel++] = graylevel;
659 	}
660 	redraw();
661 }
662 
663 //----------------------------------------------------------------------
664 // queue audio_blocks used to separate audio stream process timing
665 // from GUI processing and timing
666 // mutex guards the audio_blocks data
667 //----------------------------------------------------------------------
668 
669 extern state_t trx_state;
670 static pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
671 struct AUDIO_BLOCK {
672 	wf_fft_type sig[WF_BLOCKSIZE];
673 };
674 bool clear_audio_blocks;
675 
676 queue<AUDIO_BLOCK> audio_blocks;
677 
sig_data(double * sig,int len)678 void WFdisp::sig_data( double *sig, int len )
679 {
680 	double src_ratio = 1.0 * WF_SAMPLERATE / active_modem->get_samplerate();
681 
682 	if (audio_blocks.size() > 32) { //8) {
683 		clear_audio_blocks = true;
684 		LOG_ERROR("%s", "audio_blocks overflow");
685 		return;
686 	}
687 	if ((len * src_ratio) > WF_BLOCKSIZE * 2) {
688 		LOG_ERROR("%s", "len * src_ratio > WFBLOCKSIZE * 2");
689 		return;
690 	}
691 
692 	AUDIO_BLOCK audio_block;
693 
694 	buf = insamples;
695 	srclen = len;
696 	int error;
697 
698 	if (src_data.src_ratio != src_ratio) {
699 		src_data.src_ratio = src_ratio;
700 		src_set_ratio(src_state, src_data.src_ratio);
701 		LOG_INFO("Waterfall sample rate ratio %f", src_ratio);
702 	}
703 	for (int n = 0; n < len; n++) insamples[n] = sig[n];
704 
705 	while (srclen > 0) {
706 		src_data.data_in = insamples;
707 		src_data.input_frames = srclen;
708 		src_data.data_out = &outsamples[genptr];
709 		src_data.output_frames = WF_BLOCKSIZE * 2 - genptr;
710 		src_data.input_frames_used = 0;
711 
712 		if (unlikely(error = src_process(src_state, &src_data))) {
713 			LOG_ERROR("src_process error %d: %s", error, src_strerror(error));
714 			return;
715 		}
716 		size_t gend = src_data.output_frames_gen;
717 		size_t used = src_data.input_frames_used;
718 
719 		genptr	+= gend;
720 		buf		+= used;
721 		srclen	-= used;
722 
723 		while (genptr >= WF_BLOCKSIZE) {
724 			for (int n = 0; n < WF_BLOCKSIZE; n++) audio_block.sig[n] = outsamples[n];
725 			{
726 				guard_lock data_lock (&data_mutex);
727 				audio_blocks.push(audio_block);
728 			}
729 			for (int n = 0; n < WF_BLOCKSIZE; n++) outsamples[n] = outsamples[n+WF_BLOCKSIZE];
730 			genptr -= WF_BLOCKSIZE;
731 		}
732 	}
733 	return;
734 }
735 
736 // this method must be called from main thread
737 
handle_sig_data()738 void WFdisp::handle_sig_data()
739 {
740 	ENSURE_THREAD(FLMAIN_TID);
741 
742 	double gain = pow(10, progdefaults.wfRefLevel / -20.0);
743 	AUDIO_BLOCK current;
744 	while (1) {//!audio_blocks.empty()) {
745 		if (clear_audio_blocks) {
746 			guard_lock data_lock(&data_mutex);
747 			while ( !audio_blocks.empty() ) audio_blocks.pop();
748 			clear_audio_blocks = false;
749 		}
750 		if (audio_blocks.empty())
751 			return;
752 
753 		for (int n = 0; n < WF_FFTLEN - WF_BLOCKSIZE; n++)
754 			circbuff[n] = circbuff[n + WF_BLOCKSIZE];
755 		{
756 // this block guarded by data_mutex
757 			guard_lock data_lock(&data_mutex);
758 			current = audio_blocks.front();
759 			audio_blocks.pop();
760 		}
761 		for (int n = 0; n < WF_BLOCKSIZE; n++)
762 			circbuff[n + WF_FFTLEN - WF_BLOCKSIZE - 1] = current.sig[n];
763 
764 		overload = false;
765 		double overval = 0, peak = 0.0;
766 		for (int i = WF_FFTLEN - WF_BLOCKSIZE; i < WF_FFTLEN; i++) {
767 			overval = fabs(circbuff[i]);
768 			if (overval > peak) peak = overval;
769 			circbuff[i] *= gain;
770 		}
771 
772 		if (mode == SCOPE)
773 			process_analog(circbuff, WF_FFTLEN);
774 		else
775 			processFFT();
776 
777 		put_WARNstatus(peak);
778 
779 		static char szFrequency[14];
780 		if (active_modem && rfc != 0) {
781 			int offset = 0;
782 			double afreq = active_modem->get_txfreq();
783 			trx_mode mode = active_modem->get_mode();
784 			string testmode = qso_opMODE->value();
785 
786 			bool xcvr_useFSK = ((testmode.find("RTTY") != string::npos) ||
787 								(testmode.find("FSK") != string::npos) ||
788 								((testmode.find("DATA") != string::npos) &&
789 								 (use_nanoIO || progdefaults.PseudoFSK)) );
790 			usb = !ModeIsLSB(testmode);
791 			if ((testmode.find("DATA") != string::npos) && xcvr_useFSK)
792 				usb = !usb;
793 
794 			if (mode == MODE_RTTY && progdefaults.useMARKfreq && !xcvr_useFSK) {
795 				offset = (progdefaults.rtty_shift < rtty::numshifts ?
796 					rtty::SHIFT[progdefaults.rtty_shift] :
797 					progdefaults.rtty_custom_shift);
798 				offset /= 2;
799 				if (active_modem->get_reverse()) offset *= -1;
800 			}
801 			if (testmode.find("CW") != string::npos)
802 				afreq = 0;
803 			if (xcvr_useFSK)
804 				afreq = 0;
805 			if (mode == MODE_ANALYSIS) {
806 				dfreq = 0;
807 			} else {
808 				if (usb)
809 					dfreq = rfc + afreq + offset;
810 				else
811 					dfreq = rfc - afreq - offset;
812 			}
813 			snprintf(szFrequency, sizeof(szFrequency), "%-.3f", dfreq / 1000.0);
814 		} else {
815 			dfreq = active_modem->get_txfreq();
816 			snprintf(szFrequency, sizeof(szFrequency), "%-.0f", dfreq);
817 		}
818 		inpFreq->value(szFrequency);
819 
820 	}
821 }
822 
823 // Check the display offset & limit to 0 to max IMAGE_WIDTH displayed
checkoffset()824 void WFdisp::checkoffset() {
825 	if (mode == SCOPE) {
826 		if (sigoffset < 0)
827 			sigoffset = 0;
828 		if (sigoffset > (IMAGE_WIDTH - disp_width))
829 			sigoffset = IMAGE_WIDTH - disp_width;
830 	} else {
831 		if (offset > (int)(progdefaults.HighFreqCutoff - step * disp_width))
832 			offset = (int)(progdefaults.HighFreqCutoff - step * disp_width);
833 		if (offset < 0)
834 			offset = 0;
835 	}
836 }
837 
setOffset(int v)838 void WFdisp::setOffset(int v) {
839 	offset = v;
840 	checkoffset();
841 }
842 
slew(int dir)843 void WFdisp::slew(int dir) {
844 	if (mode == SCOPE)
845 		sigoffset += dir;
846 	else
847 		offset += dir;
848 	checkoffset();
849 }
850 
movetocenter()851 void WFdisp::movetocenter() {
852 	if (mode == SCOPE) {
853 		sigoffset = IMAGE_WIDTH / 2;
854 	} else if (active_modem->get_mode() == MODE_FMT) {
855 		if (progdefaults.fmt_center_on_unknown)
856 			offset = cnt_unk_freq->value() - (disp_width * step / 2);
857 		else if (progdefaults.fmt_center_on_reference)
858 			offset = cnt_ref_freq->value() - (disp_width * step / 2);
859 		else if (progdefaults.fmt_center_on_median)
860 			offset = (cnt_unk_freq->value() + cnt_ref_freq->value())/2 -
861 					 (disp_width * step / 2);
862 		else
863 			offset = progdefaults.PSKsweetspot - (disp_width * step / 2);
864 	} else
865 		offset = carrierfreq - (disp_width * step / 2);
866 	checkoffset();
867 }
868 
carrier(int cf)869 void WFdisp::carrier(int cf) {
870 	if (cf >= bandwidth / 2 && cf < (IMAGE_WIDTH - bandwidth / 2)) {
871 		carrierfreq = cf;
872 		makeMarker();
873 	}
874 }
875 
carrier()876 int WFdisp::carrier() {
877 	return carrierfreq;
878 }
879 
checkWidth()880 void WFdisp::checkWidth()
881 {
882 	disp_width = w();
883 	if (mag == MAG_1) step = 4;
884 	if (mag == MAG_1 && disp_width > progdefaults.HighFreqCutoff/4)
885 		disp_width = progdefaults.HighFreqCutoff/4;
886 	if (mag == MAG_2) step = 2;
887 	if (mag == MAG_2 && disp_width > progdefaults.HighFreqCutoff/2)
888 		disp_width = progdefaults.HighFreqCutoff/2;
889 	if (mag == MAG_4) step = 1;
890 }
891 
checkMag()892 int WFdisp::checkMag()
893 {
894 	checkWidth();
895 	makeScale();
896 	return mag;
897 }
898 
setMag(int m)899 int WFdisp::setMag(int m)
900 {
901 	int mid = offset + (disp_width * step / 2);
902 	mag = m;
903 	checkMag();
904 	if (centercarrier || Fl::event_shift()) {
905 		offset = mid - (disp_width * step / 2);
906 	}
907 	else {
908 		movetocenter();
909 	}
910 	return mag;
911 }
912 
wfmag()913 int WFdisp::wfmag() {
914 	int mid = offset + (disp_width * step / 2);
915 	if (mag == MAG_1) mag = MAG_2;
916 	else if (mag == MAG_2) mag = MAG_4;
917 	else mag = MAG_1;
918 	checkMag();
919 	if (centercarrier || Fl::event_shift()) {
920 		offset = mid - (disp_width * step / 2);
921 	}
922 	else {
923 		movetocenter();
924 	}
925 	return mag;
926 }
927 
928 
drawScale()929 void WFdisp::drawScale() {
930 	int fw = 60, xoff;
931 	static char szFreq[20];
932 	double fr;
933 	uchar *pixmap;
934 
935 	if (progdefaults.wf_audioscale) {
936 		pixmap = (scaleimage + (int)offset);
937 		fl_draw_image_mono(
938 			pixmap,
939 			x(), y() + WFTEXT,
940 			w(), WFSCALE,
941 			step, scale_width);
942 
943 		fl_color(0xFFFFFF00);
944 		fl_font(progdefaults.WaterfallFontnbr, progdefaults.WaterfallFontsize);
945 
946 		for (int i = 1; ; i++) {
947 			fr = 500.0 * i;
948 			snprintf(szFreq, sizeof(szFreq), "%7.0f", fr);
949 			fw = (int)fl_width(szFreq);
950 			xoff = (int) (( (1000.0/step) * i - fw) / 2.0 - offset /step );
951 			if (xoff > 0 && xoff < w() - fw)
952 				fl_draw(szFreq, x() + xoff, y() + 10 );
953 			if (xoff > w() - fw) break;
954 		}
955 		return;
956 	}
957 
958 	int mdoffset = 0;
959 	string testmode = qso_opMODE->value();
960 
961 	bool xcvr_useFSK = ((testmode.find("RTTY") != string::npos) ||
962 						(testmode.find("FSK") != string::npos) ||
963 						((testmode.find("DATA") != string::npos) &&
964 						 (use_nanoIO ||progdefaults.PseudoFSK)) );
965 
966 	usb = !ModeIsLSB(testmode);
967 	if ((testmode.find("DATA") != string::npos) && xcvr_useFSK)
968 		usb = !usb;
969 
970 	if (testmode.find("CW") != string::npos)
971 		mdoffset = progdefaults.CWsweetspot;
972 
973 	if (xcvr_useFSK) {
974 		if (usb) mdoffset = progdefaults.xcvr_FSK_MARK + rtty::SHIFT[progdefaults.rtty_baud] * 2;
975 		else mdoffset = progdefaults.xcvr_FSK_MARK;
976 	}
977 
978 	if (usb)
979 		pixmap = (scaleimage +  (int)(((rfc - mdoffset) % 1000 + offset)) );
980 	else
981 		pixmap = (scaleimage + (int)((1000 - (rfc + mdoffset) % 1000 + offset)));
982 
983 	fl_draw_image_mono(
984 		pixmap,
985 		x(), y() + WFTEXT,
986 		w(), WFSCALE,
987 		step, scale_width);
988 
989 	fl_color(0xFFFFFF00);
990 	fl_font(progdefaults.WaterfallFontnbr, progdefaults.WaterfallFontsize);
991 
992 	for (int i = 1; ; i++) {
993 		if (usb)
994 			fr = (rfc - mdoffset - (rfc - mdoffset) % 500 + 500 * i)/1000.0;
995 		else
996 			fr = (rfc + mdoffset - (rfc + mdoffset) % 500 - 500 * i + 500)/1000.0;
997 
998 		snprintf(szFreq, sizeof(szFreq), "%7.1f", fr);
999 		fw = (int)fl_width(szFreq);
1000 		if (usb)
1001 			xoff = (int) ( ( (1000.0/step) * i - fw) / 2.0 -
1002 							(offset + (rfc - mdoffset) % 500) / step );
1003 		else
1004 			xoff = (int) ( ( (1000.0/step) * i - fw) / 2.0 -
1005 							(offset + 500 - (rfc + mdoffset) % 500) / step );
1006 		if (xoff > 0 && xoff < w() - fw)
1007 			fl_draw(szFreq, x() + xoff, y() + 10 );
1008 		if (xoff > w() - fw) break;
1009 	}
1010 }
1011 
drawMarker()1012 void WFdisp::drawMarker() {
1013 	if (mode == SCOPE) return;
1014 	int msize = RGBsize * scale_width;
1015 	int psize = scale_width * WFMARKER;
1016 	uchar *pixmap = (uchar *)(markerimage + (int)(offset));
1017 	uchar map[msize];
1018 	memset(map, 0, sizeof(map));
1019 	int y1 = y() + WFSCALE + WFTEXT;
1020 	for (int yp = 0; yp < WFMARKER; yp++) {
1021 		for (int xp = 0; xp < scale_width; xp++) {
1022 			if ((RGBsize * xp + 2 < msize) &&
1023 				(RGBsize * (yp * scale_width + xp * step + 2) < psize)) {
1024 				map[RGBsize * xp] = pixmap[RGBsize * (yp * scale_width + xp * step)];
1025 				map[RGBsize * xp + 1] = pixmap[RGBsize * (yp * scale_width + xp * step) + 1];
1026 				map[RGBsize * xp + 2] = pixmap[RGBsize * (yp * scale_width + xp * step) + 2];
1027 			}
1028 		}
1029 		fl_draw_image((const uchar *)map, x(), y1 + yp, w(), 1, RGBsize, 0);
1030 	}
1031 	return;
1032 }
1033 
update_waterfall()1034 void WFdisp::update_waterfall() {
1035 // transfer the fft history data into the WF image
1036 	short int * __restrict__ p1, * __restrict__ p2;
1037 	RGBI * __restrict__ p3, * __restrict__ p4;
1038 	p1 = tmp_fft_db + offset + step/2;
1039 	p2 = p1;
1040 	p3 = fft_img;
1041 	p4 = p3;
1042 
1043 	short*  __restrict__ limit = tmp_fft_db + image_area - step + 1;
1044 
1045 #define UPD_LOOP( Step, Operation ) \
1046 case Step: for (int row = 0; row < image_height; row++) { \
1047 		p2 = p1; \
1048 		p4 = p3; \
1049 		for ( const short *  __restrict__ last_p2 = std::min( p2 + Step * disp_width, limit +1 ); p2 < last_p2; p2 += Step ) { \
1050 			*(p4++) = mag2RGBI[ Operation ]; \
1051 		} \
1052 		p1 += IMAGE_WIDTH; \
1053 		p3 += disp_width; \
1054 	}; break
1055 
1056 	if (progdefaults.WFaveraging) {
1057 		switch(step) {
1058 			UPD_LOOP( 4, (*p2 + *(p2+1) + *(p2+2) + *(p2-1) + *(p2-1))/5 );
1059 			UPD_LOOP( 2, (*p2 + *(p2+1) + *(p2-1))/3 );
1060 			UPD_LOOP( 1, *p2 );
1061 			default:;
1062 		}
1063 	} else {
1064 		switch(step) {
1065 			UPD_LOOP( 4, MAX( MAX( MAX ( MAX ( *p2, *(p2+1) ), *(p2+2) ), *(p2-2) ), *(p2-1) ) );
1066 			UPD_LOOP( 2, MAX( MAX( *p2, *(p2+1) ), *(p2-1) ) );
1067 			UPD_LOOP( 1, *p2 );
1068 			default:;
1069 		}
1070 	}
1071 #undef UPD_LOOP
1072 
1073 	if (active_modem && progdefaults.UseBWTracks) {
1074 		trx_mode mode = active_modem->get_mode();
1075 		if (mode == MODE_FMT) {
1076 			int bw =progdefaults.FMT_filter;
1077 			int trk = int(round(cnt_unk_freq->value()));
1078 			RGBI  *pos1 = fft_img + (trk - offset - bw) / step;
1079 			RGBI  *pos2 = fft_img + (trk - offset + bw) / step;
1080 			RGBI unk_trk_color, ref_trk_color;
1081 
1082 			Fl::get_color(progdefaults.FMT_unk_color, unk_trk_color.R, unk_trk_color.G, unk_trk_color.B);
1083 			Fl::get_color(progdefaults.FMT_ref_color, ref_trk_color.R, ref_trk_color.G, ref_trk_color.B);
1084 
1085 			if (likely(pos1 >= fft_img && pos2 < fft_img + disp_width)) {
1086 				if (progdefaults.UseWideTracks) {
1087 					for (int y = 0; y < image_height; y ++) {
1088 						*(pos1 + 1) = *pos1 = unk_trk_color;
1089 						*(pos2 - 1) = *pos2 = unk_trk_color;
1090 						pos1 += disp_width;
1091 						pos2 += disp_width;
1092 					}
1093 				} else {
1094 					for (int y = 0; y < image_height; y ++) {
1095 						*pos1 = unk_trk_color;
1096 						*pos2 = unk_trk_color;
1097 						pos1 += disp_width;
1098 						pos2 += disp_width;
1099 					}
1100 				}
1101 			}
1102 
1103 			trk = int(round(cnt_ref_freq->value()));
1104 			pos1 = fft_img + (trk - offset - bw) / step;
1105 			pos2 = fft_img + (trk - offset + bw) / step;
1106 
1107 			if (likely(pos1 >= fft_img && pos2 < fft_img + disp_width)) {
1108 				if (progdefaults.UseWideTracks) {
1109 					for (int y = 0; y < image_height; y ++) {
1110 						*(pos1 + 1) = *pos1 = ref_trk_color;
1111 						*(pos2 - 1) = *pos2 = ref_trk_color;
1112 						pos1 += disp_width;
1113 						pos2 += disp_width;
1114 					}
1115 				} else {
1116 					for (int y = 0; y < image_height; y ++) {
1117 						*pos1 = ref_trk_color;
1118 						*pos2 = ref_trk_color;
1119 						pos1 += disp_width;
1120 						pos2 += disp_width;
1121 					}
1122 				}
1123 			}
1124 
1125 		} else {
1126 
1127 			int bw_lo = bandwidth / 2;
1128 			int bw_hi = bandwidth / 2;
1129 			trx_mode mode = active_modem->get_mode();
1130 			if (mode >= MODE_MT63_500S && mode <= MODE_MT63_2000L)
1131 				bw_hi = bw_hi * 31 / 32;
1132 			if (mode == MODE_FSQ || mode == MODE_IFKP) {
1133 				bw_hi = bw_lo = 69 * bandwidth / 100;
1134 			}
1135 			RGBI  *pos1 = fft_img + (carrierfreq - offset - bw_lo) / step;
1136 			RGBI  *pos2 = fft_img + (carrierfreq - offset + bw_hi) / step;
1137 			if (unlikely(pos2 == fft_img + disp_width))
1138 				pos2--;
1139 			if (likely(pos1 >= fft_img && pos2 < fft_img + disp_width)) {
1140 				RGBI rgbi1, rgbi2 ;
1141 
1142 				if (mode == MODE_RTTY && progdefaults.useMARKfreq) {
1143 					if (active_modem->get_reverse()) {
1144 						rgbi1 = progdefaults.rttymarkRGBI;
1145 						rgbi2 = progdefaults.bwTrackRGBI;
1146 					} else {
1147 						rgbi1 = progdefaults.bwTrackRGBI;
1148 						rgbi2 = progdefaults.rttymarkRGBI;
1149 					}
1150 				} else {
1151 					rgbi1 = progdefaults.bwTrackRGBI;
1152 					rgbi2 = progdefaults.bwTrackRGBI;
1153 				}
1154 				if (progdefaults.UseWideTracks) {
1155 					for (int y = 0; y < image_height; y ++) {
1156 						*(pos1 + 1) = *pos1 = rgbi1;
1157 						*(pos2 - 1) = *pos2 = rgbi2;
1158 						pos1 += disp_width;
1159 						pos2 += disp_width;
1160 					}
1161 				} else {
1162 					for (int y = 0; y < image_height; y ++) {
1163 						*pos1 = rgbi1;
1164 						*pos2 = rgbi2;
1165 						pos1 += disp_width;
1166 						pos2 += disp_width;
1167 					}
1168 				}
1169 			}
1170 		}
1171 	}
1172 
1173 // draw notch
1174 	if ((notch_frequency > 1) && (notch_frequency < progdefaults.HighFreqCutoff - 1)) {
1175 		RGBInotch.I = progdefaults.notchRGBI.I;
1176 		RGBInotch.R = progdefaults.notchRGBI.R;
1177 		RGBInotch.G = progdefaults.notchRGBI.G;
1178 		RGBInotch.B = progdefaults.notchRGBI.B;
1179 		RGBI  *notch = fft_img + (notch_frequency - offset) / step;
1180 		int dash = 0;
1181 		for (int y = 0; y < image_height; y++) {
1182 			dash = (dash + 1) % 6;
1183 			if (dash == 0 || dash == 1 || dash == 2)
1184 				*(notch-1) = *notch = *(notch+1) = RGBInotch;
1185 			notch += disp_width;
1186 		}
1187 	}
1188 
1189 	if (progdefaults.show_psm_btn &&
1190 		progStatus.kpsql_enabled &&
1191 		(trx_state == STATE_RX))
1192 		signal_psm();
1193 }
1194 
drawcolorWF()1195 void WFdisp::drawcolorWF() {
1196 	uchar *pixmap = (uchar *)fft_img;
1197 
1198 	update_waterfall();
1199 
1200 	if (active_modem && wantcursor &&
1201 		(progdefaults.UseCursorLines || progdefaults.UseCursorCenterLine) ) {
1202 		trx_mode mode = active_modem->get_mode();
1203 
1204 		int bw_lo = bandwidth / 2;
1205 		int bw_hi = bandwidth / 2;
1206 		if (mode >= MODE_MT63_500S && mode <= MODE_MT63_2000L)
1207 			bw_hi = bw_hi * 31 / 32;
1208 		if (mode == MODE_FSQ || mode == MODE_IFKP) bw_hi = bw_hi * 32 / 33;
1209 		if (mode == MODE_FMT) {
1210 			bw_lo = bw_hi = progdefaults.FMT_filter;
1211 		}
1212 		RGBI  *pos0 = (fft_img + cursorpos);
1213 		RGBI  *pos1 = (fft_img + cursorpos - bw_lo/step);
1214 		RGBI  *pos2 = (fft_img + cursorpos + bw_hi/step);
1215 		if (pos1 >= fft_img && pos2 < fft_img + disp_width) {
1216 			for (int y = 0; y < image_height; y ++) {
1217 				if (progdefaults.UseCursorLines) {
1218 					*pos1 = *pos2 = progdefaults.cursorLineRGBI;
1219 					if (progdefaults.UseWideCursor)
1220 						*(pos1 + 1) = *(pos2 - 1) = *pos1;
1221 				}
1222 				if (progdefaults.UseCursorCenterLine) {
1223 					*pos0 = progdefaults.cursorCenterRGBI;
1224 					if (progdefaults.UseWideCenter)
1225 						*(pos0 - 1) = *(pos0 + 1) = *pos0;
1226 				}
1227 				pos0 += disp_width;
1228 				pos1 += disp_width;
1229 				pos2 += disp_width;
1230 			}
1231 		}
1232 
1233 	}
1234 
1235 	fl_color(FL_BLACK);
1236 	fl_rectf(x(), y(), w(), WFSCALE + WFMARKER + WFTEXT);
1237 	fl_color(fl_rgb_color(palette[0].R, palette[0].G, palette[0].B));
1238 	fl_rectf(x(), y() + WFSCALE + WFMARKER + WFTEXT, w(), image_height);
1239 	fl_draw_image(
1240 		pixmap, x(), y() + WFSCALE + WFMARKER + WFTEXT,
1241 		disp_width, image_height,
1242 		sizeof(RGBI), disp_width * sizeof(RGBI) );
1243 	drawScale();
1244 }
1245 
drawspectrum()1246 void WFdisp::drawspectrum() {
1247 	int sig;
1248 	long offset_idx = 0;
1249 	long ynext,
1250 		h1 = image_height - 1,
1251 		ffty = 0,
1252 		fftpixel = IMAGE_WIDTH * h1,
1253 		graylevel = 220;
1254 	uchar *pixmap = (uchar *)fft_sig_img + offset / step;
1255 
1256 	memset (fft_sig_img, 0, image_area);
1257 
1258 	fftpixel /= step;
1259 	for (int c = 0; c < IMAGE_WIDTH; c += step) {
1260 		sig = tmp_fft_db[c];
1261 		if (step == 1)
1262 			sig = tmp_fft_db[c];
1263 		else if (step == 2)
1264 			sig = MAX(tmp_fft_db[c], tmp_fft_db[c+1]);
1265 		else
1266 			sig = MAX( MAX ( MAX ( tmp_fft_db[c], tmp_fft_db[c+1] ), tmp_fft_db[c+2] ), tmp_fft_db[c+3]);
1267 		ynext = h1 * sig / 256;
1268 		offset_idx = (IMAGE_WIDTH/step);
1269 		while ((ffty < ynext)) {
1270 			fft_sig_img[fftpixel -= offset_idx] = graylevel;
1271 			ffty++;
1272 			if (fftpixel < offset_idx) {
1273 				cout << "corrupt index 1\n";
1274 				break;
1275 			}
1276 		}
1277 		while ((ffty > ynext)) {
1278 			fft_sig_img[fftpixel += offset_idx] = graylevel;
1279 			ffty--;
1280 			if (fftpixel >= (image_area - 1)) {
1281 				cout << "corrupt index 2\n";
1282 				break;
1283 			}
1284 		}
1285 		if (fftpixel >= 0 && fftpixel <= image_area)
1286 			fft_sig_img[fftpixel++] = graylevel;
1287 		else
1288 			cout << "fft_sig_image index out of bounds: " << fftpixel << endl;
1289 	}
1290 
1291 	if (progdefaults.UseBWTracks) {
1292 		if (active_modem == fmt_modem) {
1293 			uchar  *pos1;
1294 			uchar  *pos2;
1295 			int trk1 = int(round(cnt_unk_freq->value()));
1296 			int trk2 = int(round(cnt_ref_freq->value()));
1297 			int bw = int(round(progdefaults.FMT_filter));
1298 			pos1 = pixmap + (trk1 - offset - bw) / step;
1299 			pos2 = pixmap + (trk1 - offset + bw) / step;
1300 			if (pos1 >= pixmap &&
1301 				pos2 < pixmap + disp_width)
1302 				for (int y = 0; y < image_height; y ++) {
1303 					*pos1 = *pos2 = 255;
1304 					if (progdefaults.UseWideTracks) {
1305 						*(pos1 + 1) = *(pos2 - 1) = 255;
1306 					}
1307 					pos1 += IMAGE_WIDTH/step;
1308 					pos2 += IMAGE_WIDTH/step;
1309 				}
1310 			pos1 = pixmap + (trk2 - offset - bw) / step;
1311 			pos2 = pixmap + (trk2 - offset + bw) / step;
1312 			if (pos1 >= pixmap &&
1313 				pos2 < pixmap + disp_width) {
1314 				for (int y = 0; y < image_height; y ++) {
1315 					*pos1 = *pos2 = 255;
1316 					if (progdefaults.UseWideTracks) {
1317 						*(pos1 + 1) = *(pos2 - 1) = 255;
1318 					}
1319 				pos1 += IMAGE_WIDTH/step;
1320 				pos2 += IMAGE_WIDTH/step;
1321 				}
1322 			}
1323 		} else {
1324 			uchar  *pos1 = pixmap + (carrierfreq - offset - bandwidth/2) / step;
1325 			uchar  *pos2 = pixmap + (carrierfreq - offset + bandwidth/2) / step;
1326 			if (pos1 >= pixmap &&
1327 				pos2 < pixmap + disp_width) {
1328 				for (int y = 0; y < image_height; y ++) {
1329 					*pos1 = *pos2 = 255;
1330 					if (progdefaults.UseWideTracks) {
1331 						*(pos1 + 1) = 255;
1332 						*(pos2 - 1) = 255;
1333 					}
1334 					pos1 += IMAGE_WIDTH/step;
1335 					pos2 += IMAGE_WIDTH/step;
1336 				}
1337 			}
1338 		}
1339 	}
1340 
1341 	if (active_modem && wantcursor &&
1342 		(progdefaults.UseCursorLines || progdefaults.UseCursorCenterLine)) {
1343 		trx_mode mode = active_modem->get_mode();
1344 		int bw_lo = bandwidth / 2;
1345 		int bw_hi = bandwidth / 2;
1346 		if (mode >= MODE_MT63_500S && mode <= MODE_MT63_2000L)
1347 			bw_hi = bw_hi * 31 / 32;
1348 		if (mode == MODE_FSQ || mode == MODE_IFKP) bw_hi = bw_hi * 32 / 33;
1349 		uchar  *pos0 = pixmap + cursorpos;
1350 		uchar  *pos1 = (pixmap + cursorpos - bw_lo/step);
1351 		uchar  *pos2 = (pixmap + cursorpos + bw_hi/step);
1352 		for (int y = 0; y < h1; y ++) {
1353 			if (progdefaults.UseCursorLines) {
1354 				*pos1 = *pos2 = 255;
1355 				if (progdefaults.UseWideCursor)
1356 					*(pos1 + 1) = *(pos2 - 1) = *pos1;
1357 			}
1358 			if (progdefaults.UseCursorCenterLine) {
1359 				*pos0 = 255;
1360 				if (progdefaults.UseWideCenter) *(pos0-1) = *(pos0+1) = *(pos0);
1361 			}
1362 			pos0 += IMAGE_WIDTH/step;
1363 			pos1 += IMAGE_WIDTH/step;
1364 			pos2 += IMAGE_WIDTH/step;
1365 		}
1366 	}
1367 
1368 // draw notch
1369 	if ((notch_frequency > 1) && (notch_frequency < progdefaults.HighFreqCutoff - 1)) {
1370 		uchar  *notch = pixmap + (notch_frequency - offset) / step;
1371 		int dash = 0;
1372 		for (int y = 0; y < image_height; y++) {
1373 			dash = (dash + 1) % 6;
1374 			if (dash == 0 || dash == 1 || dash == 2)
1375 				*(notch-1) = *notch = *(notch+1) = 255;
1376 			notch += IMAGE_WIDTH/step;
1377 		}
1378 	}
1379 
1380 	fl_color(FL_BLACK);
1381 	fl_rectf(x(), y(), w(), WFSCALE + WFMARKER + WFTEXT + image_height);
1382 
1383 	fl_draw_image_mono(
1384 		pixmap,
1385 		x(), y() + WFSCALE + WFMARKER + WFTEXT,
1386 		disp_width, image_height,
1387 		1, IMAGE_WIDTH / step);
1388 	drawScale();
1389 }
1390 
drawsignal()1391 void WFdisp::drawsignal() {
1392 	uchar *pixmap = (uchar *)(sig_img + sigoffset);
1393 
1394 	fl_color(FL_BLACK);
1395 	fl_rectf(x() + disp_width, y(), w() - disp_width, h());
1396 	fl_draw_image_mono(pixmap, x(), y(), disp_width, h(), 1, IMAGE_WIDTH);
1397 }
1398 
draw()1399 void WFdisp::draw() {
1400 
1401 	checkoffset();
1402 	checkWidth();
1403 
1404 	if (progdefaults.show_psm_btn && progStatus.kpsql_enabled) {
1405 		drawcolorWF();
1406 		drawMarker();
1407 		return;
1408 	}
1409 
1410 	switch (mode) {
1411 	case SPECTRUM :
1412 		drawspectrum();
1413 		drawMarker();
1414 		break;
1415 	case SCOPE :
1416 		drawsignal();
1417 		break;
1418 	case WATERFALL :
1419 	default:
1420 		drawcolorWF();
1421 		drawMarker();
1422 	}
1423 }
1424 
1425 //=======================================================================
1426 // waterfall
1427 //=======================================================================
1428 
x1_cb(Fl_Widget * w,void * v)1429 void x1_cb(Fl_Widget *w, void* v) {
1430 	waterfall *wf = (waterfall *)w->parent();
1431 	int m = wf->wfdisp->wfmag();
1432 	if (m == MAG_1) w->label("x1");
1433 	if (m == MAG_2) w->label("x2");
1434 	if (m == MAG_4) w->label("x4");
1435 	restoreFocus();
1436 }
1437 
slew_left(Fl_Widget * w,void * v)1438 void slew_left(Fl_Widget *w, void * v) {
1439 	waterfall *wf = (waterfall *)w->parent();
1440 	wf->wfdisp->slew(-100);
1441 	restoreFocus();
1442 }
1443 
slew_right(Fl_Widget * w,void * v)1444 void slew_right(Fl_Widget *w, void * v) {
1445 	waterfall *wf = (waterfall *)w->parent();
1446 	wf->wfdisp->slew(100);
1447 	restoreFocus();
1448 }
1449 
1450 
center_cb(Fl_Widget * w,void * v)1451 void center_cb(Fl_Widget *w, void *v) {
1452 	waterfall *wf = (waterfall *)w->parent();
1453 	wf->wfdisp->movetocenter();
1454 	restoreFocus();
1455 }
1456 
killMacroTimer()1457 void killMacroTimer()
1458 {
1459 	stopMacroTimer();
1460 }
1461 
carrier_cb(Fl_Widget * w,void * v)1462 void carrier_cb(Fl_Widget *w, void *v) {
1463 	Fl_Counter *cntr = (Fl_Counter *)w;
1464 	waterfall *wf = (waterfall *)w->parent();
1465 	int selfreq = (int) cntr->value();
1466 	if (selfreq > progdefaults.HighFreqCutoff) selfreq = progdefaults.HighFreqCutoff - wf->wfdisp->Bandwidth() / 2;
1467 	killMacroTimer();
1468 	if (active_modem)
1469 		active_modem->set_freq(selfreq);
1470 	wf->wfdisp->carrier(selfreq);
1471 	restoreFocus();
1472 }
1473 
do_qsy(bool dir)1474 void do_qsy(bool dir)
1475 {
1476 	if (!active_modem) return;
1477 	static vector<qrg_mode_t> qsy_stack;
1478 	qrg_mode_t m;
1479 
1480 	wf->xmtlock->value(0);
1481 	wf->xmtlock->do_callback();
1482 
1483 	if (dir) {
1484 // store
1485 		m.rfcarrier = wf->rfcarrier();
1486 		int wfc = m.carrier = active_modem->get_freq();
1487 		qsy_stack.push_back(m);
1488 		m.rmode = qso_opMODE->value();
1489 		trx_mode md = active_modem->get_mode();
1490 
1491 		string testmode = qso_opMODE->value();
1492 		bool xcvr_useFSK = ((testmode.find("RTTY") != string::npos) ||
1493 							(testmode.find("FSK") != string::npos) ||
1494 							((testmode.find("DATA") != string::npos) &&
1495 							 (use_nanoIO)) );
1496 
1497 // qsy to the sweet spot frequency that is the center of the PBF in the rig
1498 		switch (md) {
1499 			case MODE_CW:
1500 				m.carrier = progdefaults.CWsweetspot;
1501 				break;
1502 			case MODE_RTTY:
1503 				if (xcvr_useFSK) {
1504 					// qsy operates on change in audio center track
1505 					m.carrier = progdefaults.xcvr_FSK_MARK + rtty::SHIFT[progdefaults.rtty_shift]/2;
1506 				} else
1507 					m.carrier = progdefaults.RTTYsweetspot;
1508 				break;
1509 			case MODE_FMT:
1510 				if (progdefaults.fmt_center_on_unknown)
1511 					m.carrier = cnt_unk_freq->value();
1512 				else if (progdefaults.fmt_center_on_reference)
1513 					m.carrier = cnt_ref_freq->value();
1514 				else if (progdefaults.fmt_center_on_median)
1515 					m.carrier = (cnt_unk_freq->value() + cnt_ref_freq->value())/2;
1516 				else
1517 					m.carrier = progdefaults.PSKsweetspot;
1518 				break;
1519 			default:
1520 				m.carrier = progdefaults.PSKsweetspot;
1521 				break;
1522 		}
1523 		if (m.rmode.find("CW") != string::npos) {
1524 			if (wf->USB())
1525 				m.rfcarrier += (wfc - m.carrier);
1526 			else
1527 				m.rfcarrier -= (wfc - m.carrier);
1528 		} else if ( (md == MODE_RTTY) && xcvr_useFSK ) {
1529 			if (wf->USB()) {
1530 				m.rfcarrier += (wfc - m.carrier);
1531 			} else {
1532 				m.rfcarrier -= (wfc - m.carrier);
1533 			}
1534 		} else {
1535 			if (wf->USB())
1536 				m.rfcarrier += (wf->carrier() - m.carrier);
1537 			else
1538 				m.rfcarrier -= (wf->carrier() - m.carrier);
1539 		}
1540 	}
1541 	else { // qsy to top of stack
1542 		if (qsy_stack.size()) {
1543 			m = qsy_stack.back();
1544 			qsy_stack.pop_back();
1545 		}
1546 	}
1547 
1548 	if (m.carrier > 0)
1549 		qsy(m.rfcarrier, m.carrier);
1550 }
1551 
qsy_cb(Fl_Widget * w,void * v)1552 void qsy_cb(Fl_Widget *w, void *v)
1553 {
1554 	if (Fl::event_button() != FL_RIGHT_MOUSE)
1555 		do_qsy(true);
1556 	else
1557 		do_qsy(false);
1558 	restoreFocus();
1559 }
1560 
rate_cb(Fl_Widget * w,void * v)1561 void rate_cb(Fl_Widget *w, void *v) {
1562 	waterfall* wf = static_cast<waterfall*>(w->parent());
1563 	WFspeed new_speed;
1564 
1565 	switch (wf->wfdisp->Speed()) {
1566 	case SLOW:
1567 		new_speed = NORMAL;
1568 		break;
1569 	case NORMAL: default:
1570 		new_speed = FAST;
1571 		break;
1572 	case FAST:
1573 		new_speed = PAUSE;
1574 		break;
1575 	case PAUSE:
1576 		new_speed = SLOW;
1577 		break;
1578 	}
1579 
1580 	wf->Speed(new_speed);
1581 	restoreFocus();
1582 }
1583 
1584 //extern void reset_xmlchars();
1585 
xmtrcv_cb(Fl_Widget * w,void * vi)1586 void xmtrcv_cb(Fl_Widget *w, void *vi)
1587 {
1588 	if (!active_modem) return;
1589 	Fl_Light_Button *b = (Fl_Light_Button *)w;
1590 	int v = b->value();
1591 	if (!(active_modem->get_cap() & modem::CAP_TX)) {
1592 		b->value(0);
1593 		restoreFocus();		return;
1594 	}
1595 	if (v == 1) {
1596 		killMacroTimer();
1597 		active_modem->set_stopflag(false);
1598 
1599 		if (progdefaults.show_psm_btn && progStatus.kpsql_enabled)
1600 			set_xmtrcv_selection_color_pending();
1601 		trx_transmit();
1602 	} else {
1603 		if (progdefaults.show_psm_btn && progStatus.kpsql_enabled) {
1604 			psm_transmit_ended(PSM_ABORT);
1605 			xmtrcv_selection_color(progdefaults.XmtColor);
1606 		}
1607 
1608 		if (btnTune->value()) {
1609 			btnTune->value(0);
1610 			btnTune->do_callback();
1611 		}
1612 		else {
1613 			TransmitText->clear();
1614 			if (active_modem->get_mode() == MODE_FSQ && fsq_tx_text)
1615 				fsq_tx_text->clear();
1616 			else if (active_modem->get_mode() == MODE_IFKP && ifkp_tx_text)
1617 				ifkp_tx_text->clear();
1618 
1619 			if (arq_text_available)
1620 				AbortARQ();
1621 
1622 			if(xmltest_char_available)
1623 				reset_xmlchars();
1624 
1625 			if(kiss_text_available)
1626 				flush_kiss_tx_buffer();
1627 
1628 			if (progStatus.timer) {
1629 				progStatus.timer = 0;
1630 			}
1631 
1632 			queue_reset();
1633 			active_modem->set_stopflag(true);
1634 		}
1635 	}
1636 	restoreFocus();
1637 }
1638 
xmtlock_cb(Fl_Widget * w,void * vi)1639 void xmtlock_cb(Fl_Widget *w, void *vi)
1640 {
1641 	if (!active_modem) return;
1642 	Fl_Light_Button *b = (Fl_Light_Button *)w;
1643 	int v = b->value();
1644 	active_modem->set_freqlock(v ? true : false );
1645 	restoreFocus();
1646 }
1647 
set_XmtRcvBtn(bool val)1648 void waterfall::set_XmtRcvBtn(bool val)
1649 {
1650 	xmtrcv->value(val);
1651 	if (!val && btnTune->value()) {
1652 		btnTune->value(0);
1653 		btnTune->labelcolor(FL_FOREGROUND_COLOR);
1654 	}
1655 }
1656 
set_wf_mode(void)1657 void set_wf_mode(void)
1658 {
1659    static const char* names[NUM_WF_MODES] = { "WF", "FFT", "SIG" };
1660    int m = 0;
1661 
1662    if (progdefaults.show_psm_btn && progStatus.kpsql_enabled) {
1663 	  if(wf->wfdisp->Mode() == WATERFALL) {
1664 		 return;
1665 	  }
1666 	  m = WATERFALL;
1667    } else {
1668 	  m = wf->wfdisp->Mode() + (Fl::event_button() == FL_LEFT_MOUSE ? 1 : -1);
1669    }
1670 
1671 	m = WCLAMP(m, WATERFALL, NUM_WF_MODES-1);
1672 
1673 	if (m == SCOPE)
1674 		wf->x1->deactivate();
1675 	else
1676 		wf->x1->activate();
1677 
1678 	wf->wfdisp->Mode(static_cast<WFmode>(m));
1679 	wf->mode->label(names[m]);
1680 	restoreFocus();
1681 
1682 }
1683 
mode_cb(Fl_Widget * w,void *)1684 void mode_cb(Fl_Widget* w, void*)
1685 {
1686 	set_wf_mode();
1687 }
1688 
reflevel_cb(Fl_Widget * w,void * v)1689 void reflevel_cb(Fl_Widget *w, void *v) {
1690 	waterfall *wf = (waterfall *)w->parent();
1691 	double val = wf->wfRefLevel->value();
1692 	progdefaults.wfRefLevel = val;
1693 	restoreFocus();
1694 }
1695 
ampspan_cb(Fl_Widget * w,void * v)1696 void ampspan_cb(Fl_Widget *w, void *v) {
1697 	waterfall *wf = (waterfall *)w->parent();
1698 	double val = wf->wfAmpSpan->value();
1699 	wf->wfdisp->Ampspan(val);
1700 	progdefaults.wfAmpSpan = val;
1701 	restoreFocus();
1702 }
1703 
btnRev_cb(Fl_Widget * w,void * v)1704 void btnRev_cb(Fl_Widget *w, void *v)
1705 {
1706 	if (!active_modem) return;
1707 	waterfall *wf = (waterfall *)w->parent();
1708 	Fl_Light_Button *b = (Fl_Light_Button *)w;
1709 	wf->Reverse(b->value());
1710 	active_modem->set_reverse(wf->Reverse());
1711 	progdefaults.rtty_reverse = b->value();
1712 	progdefaults.changed = true;
1713 	restoreFocus();
1714 }
1715 
btnMem_cb(Fl_Widget *,void * menu_event)1716 void btnMem_cb(Fl_Widget *, void *menu_event)
1717 {
1718 	if (!active_modem) return;
1719 	static std::vector<qrg_mode_t> qrg_list;
1720 	enum { SELECT, APPEND, REPLACE, REMOVE, CLEAR };
1721 	int op = SELECT, elem = 0;
1722 
1723 	if (menu_event) { // event on popup menu
1724 		elem = wf->mbtnMem->value();
1725 
1726 		switch (Fl::event_button()) {
1727 			case FL_MIDDLE_MOUSE:
1728 				op = REPLACE;
1729 				break;
1730 			case FL_LEFT_MOUSE: case FL_RIGHT_MOUSE: default:
1731 				op = (Fl::event_state() & FL_SHIFT) ? REMOVE : SELECT;
1732 				break;
1733 		}
1734 	}
1735 	else { // button press
1736 		switch (Fl::event_button()) {
1737 			case FL_RIGHT_MOUSE:
1738 				return;
1739 			case FL_MIDDLE_MOUSE: // select last
1740 				if ((elem = qrg_list.size() - 1) < 0)
1741 					return;
1742 				op = SELECT;
1743 				break;
1744 			case FL_LEFT_MOUSE: default:
1745 				op = (Fl::event_state() & FL_SHIFT) ? CLEAR : APPEND;
1746 				break;
1747 			}
1748 	}
1749 
1750 	qrg_mode_t m;
1751 	switch (op) {
1752 		case SELECT:
1753 			m = qrg_list[elem];
1754 			if (active_modem != *mode_info[m.mode].modem) {
1755 				init_modem_sync(m.mode);
1756 			}
1757 			if (m.rfcarrier && m.rfcarrier != wf->rfcarrier())
1758 				qsy(m.rfcarrier, m.carrier);
1759 			else
1760 				active_modem->set_freq(m.carrier);
1761 			break;
1762 		case REMOVE:
1763 			wf->mbtnMem->remove(elem);
1764 			qrg_list.erase(qrg_list.begin() + elem);
1765 			break;
1766 		case CLEAR:
1767 			wf->mbtnMem->clear();
1768 			qrg_list.clear();
1769 			break;
1770 		case APPEND: case REPLACE:
1771 			m.rfcarrier = wf->rfcarrier();
1772 			m.carrier = active_modem->get_freq();
1773 			m.mode = active_modem->get_mode();
1774 			if (op == APPEND) {
1775 				if (find(qrg_list.begin(), qrg_list.end(), m) == qrg_list.end())
1776 					qrg_list.push_back(m);
1777 			else
1778 				break;
1779 			}
1780 			else
1781 				qrg_list[elem] = m;
1782 // write the menu item text
1783 			{
1784 				ostringstream o;
1785 				o << mode_info[m.mode].name << " @@ ";
1786 				if (m.rfcarrier > 0) { // write 1000s separators
1787 					char s[20], *p = s + sizeof(s) - 1;
1788 					int i = 0;
1789 
1790 					*p = '\0';
1791 					do {
1792 						if (i % 3 == 0 && i)
1793 							*--p = '.';
1794 						*--p = '0' + m.rfcarrier % 10;
1795 						++i;
1796 					} while ((m.rfcarrier /= 10) && p > s);
1797 
1798 					o << p << (wf->USB() ? " + " : " - ");
1799 				}
1800 				o << m.carrier;
1801 				if (op == APPEND) {
1802 					wf->mbtnMem->add(o.str().c_str());
1803 				} else {
1804 					wf->mbtnMem->replace(elem, o.str().c_str());
1805 				}
1806 			}
1807 			break;
1808 	}
1809 
1810 	restoreFocus();
1811 }
1812 
opmode()1813 void waterfall::opmode() {
1814 	if (!active_modem) return;
1815 	int val = (int)active_modem->get_bandwidth();
1816 
1817 	wfdisp->carrier((int)CLAMP(
1818 		wfdisp->carrier(),
1819 		progdefaults.LowFreqCutoff + val / 2,
1820 		progdefaults.HighFreqCutoff - val / 2));
1821 
1822 	wfdisp->Bandwidth( val );
1823 	wfcarrier->range(progdefaults.LowFreqCutoff + val/2, progdefaults.HighFreqCutoff - val/2);
1824 }
1825 
carrier(int f)1826 void waterfall::carrier(int f) {
1827 	wfdisp->carrier(f);
1828 	wfcarrier->value(f);
1829 	wfcarrier->damage(FL_DAMAGE_ALL);
1830 }
1831 
Speed()1832 int waterfall::Speed() {
1833 	return (int)wfdisp->Speed();
1834 }
1835 
Speed(int rate)1836 void waterfall::Speed(int rate)
1837 {
1838 	WFspeed speed = static_cast<WFspeed>(rate);
1839 	wfdisp->Speed(speed);
1840 
1841 	const char* label;
1842 	switch (speed) {
1843 	case SLOW:
1844 		label = "SLOW";
1845 		break;
1846 	case NORMAL: default:
1847 		label = "NORM";
1848 		break;
1849 	case FAST:
1850 		label = "FAST";
1851 		break;
1852 	case PAUSE:
1853 		label = "PAUSE";
1854 		break;
1855 	}
1856 
1857 	wfrate->label(label);
1858 	wfrate->redraw_label();
1859 }
1860 
Mag()1861 int waterfall::Mag() {
1862 	return wfdisp->Mag();
1863 }
1864 
Mag(int m)1865 void waterfall::Mag(int m) {
1866 	wfdisp->Mag(m);
1867 	if (m == MAG_1) x1->label("x1");
1868 	if (m == MAG_2) x1->label("x2");
1869 	if (m == MAG_4) x1->label("x4");
1870 	x1->redraw_label();
1871 }
1872 
Offset()1873 int waterfall::Offset() {
1874 	return wfdisp->Offset();
1875 }
1876 
Offset(int v)1877 void waterfall::Offset(int v) {
1878 	wfdisp->Offset(v);
1879 }
1880 
Carrier()1881 int waterfall::Carrier()
1882 {
1883 	return wfdisp->carrier();
1884 }
1885 
Carrier(int f)1886 void waterfall::Carrier(int f)
1887 {
1888 	if (active_modem) active_modem->set_freq(f);
1889 }
1890 
rfcarrier(long long cf)1891 void waterfall::rfcarrier(long long cf) {
1892 	wfdisp->rfcarrier(cf);
1893 }
1894 
rfcarrier()1895 long long waterfall::rfcarrier() {
1896 	return wfdisp->rfcarrier();
1897 }
1898 
setRefLevel()1899 void waterfall::setRefLevel() {
1900 	wfRefLevel->value(progdefaults.wfRefLevel);
1901 }
1902 
setAmpSpan()1903 void waterfall::setAmpSpan() {
1904 	wfAmpSpan->value(progdefaults.wfAmpSpan);
1905 	wfdisp->Ampspan(progdefaults.wfAmpSpan);
1906 }
1907 
USB(bool b)1908 void waterfall::USB(bool b) {
1909 	if (wfdisp->USB() == b)
1910 		return;
1911 	wfdisp->USB(b);
1912 	if (active_modem) active_modem->set_reverse(reverse);
1913 	REQ(&viewer_redraw);
1914 }
1915 
USB()1916 bool waterfall::USB() {
1917 	return wfdisp->USB();
1918 }
1919 
show_scope(bool on)1920 void waterfall::show_scope(bool on)
1921 {
1922 	if (on) {
1923 		wfscope->show();
1924 		wfscope->position(wf->x() + wf->w() - wf_dim - BEZEL, wf->y());
1925 		wfdisp->size( wf->w() - 2 * BEZEL - wf_dim, wf_dim - 2 * BEZEL);
1926 		rs1->init_sizes();
1927 	} else {
1928 		wfscope->hide();
1929 		wfscope->position(wf->x() + wf->w(), wf->y());
1930 		wfdisp->size( wf->w() - 2 * BEZEL, wf_dim - 2 * BEZEL);
1931 		rs1->init_sizes();
1932 	}
1933 	wfscope->redraw();
1934 }
1935 
waterfall(int x0,int y0,int w0,int h0,char * lbl)1936 waterfall::waterfall(int x0, int y0, int w0, int h0, char *lbl) :
1937 	Fl_Group(x0,y0,w0,h0,lbl) {
1938 	int xpos;
1939 	float ratio;
1940 	ratio = w0 * 1.0 / bwdths;
1941 
1942 	wf_dim = h() - BTN_HEIGHT - 4;
1943 
1944 	buttonrow = h() + y() - BTN_HEIGHT - 1;
1945 
1946 	rs1 = new Fl_Group(x(), y(), w(), wf_dim);
1947 		rs1->box(FL_DOWN_BOX);
1948 		wfdisp = new WFdisp(
1949 			x() + BEZEL,
1950 			y() + BEZEL,
1951 			w() - 2 * BEZEL,
1952 			wf_dim - 2 * BEZEL);
1953 		wfscope = new Digiscope (x() + w(), y(), wf_dim, wf_dim);
1954 		rs1->resizable(wfdisp);
1955 	rs1->end();
1956 	wfscope->hide();
1957 
1958 	xpos = x() + wSpace;
1959 
1960 	mode = new Fl_Button(xpos, buttonrow, (int)(bwFFT*ratio), BTN_HEIGHT, "WF");
1961 	mode->callback(mode_cb, 0);
1962 	mode->tooltip(_("Waterfall / FFT / Scope"));
1963 
1964 	xpos = xpos + (int)(bwFFT*ratio) + wSpace;
1965 	wfRefLevel = new Fl_Counter2(xpos, buttonrow, (int)(cwRef*ratio), BTN_HEIGHT );
1966 	wfRefLevel->callback(reflevel_cb, 0);
1967 	wfRefLevel->step(1.0);
1968 	wfRefLevel->precision(0);
1969 	wfRefLevel->range(-80.0, 0.0);//(-40.0, 0.0);
1970 	wfRefLevel->value(0.0);//(-20.0);
1971 	wfRefLevel->tooltip(_("Upper signal level (dB)"));
1972 	wfRefLevel->type(FL_SIMPLE_COUNTER);
1973 
1974 	xpos = xpos + (int)(cwRef*ratio) + wSpace;
1975 	wfAmpSpan = new Fl_Counter2(xpos, buttonrow, (int)(cwRef*ratio), BTN_HEIGHT );
1976 	wfAmpSpan->callback(ampspan_cb, 0);
1977 	wfAmpSpan->step(1.0);
1978 	wfAmpSpan->precision(0);
1979 	wfAmpSpan->range(6.0, 90.0);
1980 	wfAmpSpan->value(70.0);
1981 	wfdisp->Ampspan(70.0);
1982 	wfAmpSpan->tooltip(_("Signal range (dB)"));
1983 	wfAmpSpan->type(FL_SIMPLE_COUNTER);
1984 
1985 	xpos = xpos + (int)(cwRef*ratio) + wSpace;
1986 	x1 = new Fl_Button(xpos, buttonrow, (int)(bwX1*ratio), BTN_HEIGHT, "x1");
1987 	x1->callback(x1_cb, 0);
1988 	x1->tooltip(_("Change waterfall scale"));
1989 
1990 	xpos = xpos + (int)(bwX1*ratio) + wSpace;
1991 	left = new Fl_Repeat_Button(xpos, buttonrow, (int)(bwMov*ratio), BTN_HEIGHT, "@<");
1992 	left->callback(slew_left, 0);
1993 	left->tooltip(_("Slew display lower in frequency"));
1994 
1995 	xpos = xpos + (int)(bwMov*ratio);
1996 	center = new Fl_Button(xpos, buttonrow, (int)(bwMov*ratio), BTN_HEIGHT, "@||");
1997 	center->callback(center_cb, 0);
1998 	center->tooltip(_("Center display on signal"));
1999 
2000 	xpos = xpos + (int)(bwMov*ratio);
2001 	right = new Fl_Repeat_Button(xpos, buttonrow, (int)(bwMov*ratio), BTN_HEIGHT, "@>");
2002 	right->callback(slew_right, 0);
2003 	right->tooltip(_("Slew display higher in frequency"));
2004 
2005 	xpos = xpos + (int)(bwMov*ratio) + wSpace;
2006 	wfrate = new Fl_Button(xpos, buttonrow, (int)(bwRate*ratio), BTN_HEIGHT, "Norm");
2007 	wfrate->callback(rate_cb, 0);
2008 	wfrate->tooltip(_("Waterfall drop speed"));
2009 
2010 	xpos = xpos + (int)(bwRate*ratio) + wSpace;
2011 	wfcarrier = new Fl_Counter2(xpos, buttonrow, (int)(cwCnt*ratio), BTN_HEIGHT );
2012 	wfcarrier->callback(carrier_cb, 0);
2013 	wfcarrier->step(1.0);
2014 	wfcarrier->lstep(10.0);
2015 	wfcarrier->precision(0);
2016 	wfcarrier->range(16.0, progdefaults.HighFreqCutoff - 16.0);
2017 	wfcarrier->value(wfdisp->carrier());
2018 	wfcarrier->tooltip(_("Adjust cursor frequency"));
2019 
2020 	xpos = xpos + (int)(cwCnt*ratio) + wSpace;
2021 	qsy = new Fl_Button(xpos, buttonrow, (int)(bwQsy*ratio), BTN_HEIGHT, "QSY");
2022 	qsy->callback(qsy_cb, 0);
2023 	qsy->tooltip(_("Center in passband\nRight click to undo"));
2024 	qsy->deactivate();
2025 
2026 	xpos = xpos + (int)(bwQsy*ratio) + wSpace;
2027 	btnMem = new Fl_Button(xpos, buttonrow, (int)(bwMem*ratio), BTN_HEIGHT, "Store");
2028 	btnMem->callback(btnMem_cb, 0);
2029 	btnMem->tooltip(_("Store mode and frequency\nRight click for list"));
2030 	mbtnMem = new Fl_Menu_Button(btnMem->x(), btnMem->y(), btnMem->w(), btnMem->h(), 0);
2031 	mbtnMem->callback(btnMem->callback(), mbtnMem);
2032 	mbtnMem->type(Fl_Menu_Button::POPUP3);
2033 
2034 	xpos = xpos + (int)(bwMem*ratio) + wSpace;
2035 	xmtlock = new Fl_Light_Button(xpos, buttonrow, (int)(bwXmtLock*ratio), BTN_HEIGHT, "Lk");
2036 	xmtlock->callback(xmtlock_cb, 0);
2037 	xmtlock->value(0);
2038 	xmtlock->selection_color(progdefaults.LkColor);
2039 	xmtlock->tooltip(_("Lock transmit frequency"));
2040 
2041 	/// We save this flag which is used by rtty decoding.
2042 	xpos = xpos + (int)(bwXmtLock*ratio) + wSpace;
2043 	btnRev = new Fl_Light_Button(xpos, buttonrow, (int)(bwRev*ratio), BTN_HEIGHT, "Rv");
2044 	btnRev->callback(btnRev_cb, 0);
2045 	reverse = progdefaults.rtty_reverse;
2046 	btnRev->value(reverse);
2047 	btnRev->selection_color(progdefaults.RevColor);
2048 	btnRev->tooltip(_("Reverse"));
2049 
2050 	xpos = w() - (int)(bwXmtRcv*ratio) - wSpace;
2051 	xmtrcv = new Fl_Light_Button(xpos, buttonrow, (int)(bwXmtRcv*ratio) - BEZEL, BTN_HEIGHT, "T/R");
2052 	xmtrcv->callback(xmtrcv_cb, 0);
2053 	xmtrcv->selection_color(progdefaults.XmtColor);
2054 	xmtrcv->value(0);
2055 	xmtrcv->tooltip(_("Transmit/Receive"));
2056 	end();
2057 }
2058 
UI_select(bool on)2059 void waterfall::UI_select(bool on) {
2060 	if (on) {
2061 		if (!progdefaults.WF_UIrev)
2062 			btnRev->hide();
2063 		else
2064 			btnRev->show();
2065 		if (!progdefaults.WF_UIwfcarrier)
2066 			wfcarrier->hide(); else wfcarrier->show();
2067 		if (!progdefaults.WF_UIwfreflevel)
2068 			wfRefLevel->hide(); else wfRefLevel->show();
2069 		if (!progdefaults.WF_UIwfampspan)
2070 			wfAmpSpan->hide(); else wfAmpSpan->show();
2071 		if (!progdefaults.WF_UIxmtlock)
2072 			xmtlock->hide(); else xmtlock->show();
2073 		if (!progdefaults.WF_UIqsy)
2074 			qsy->hide(); else qsy->show();
2075 		if (!progdefaults.WF_UIwfmode)
2076 			mode->hide(); else mode->show();
2077 		if (!progdefaults.WF_UIx1)
2078 			x1->hide(); else x1->show();
2079 		if (!progdefaults.WF_UIwfshift) {
2080 			left->hide();
2081 			center->hide();
2082 			right->hide();
2083 		} else {
2084 			left->show();
2085 			center->show();
2086 			right->show();
2087 		}
2088 		if (!progdefaults.WF_UIwfdrop)
2089 			wfrate->hide(); else wfrate->show();
2090 		if (!progdefaults.WF_UIwfstore) {
2091 			btnMem->hide();
2092 			mbtnMem->hide();
2093 		} else {
2094 			btnMem->show();
2095 			mbtnMem->show();
2096 		}
2097 //if (noUI) xmtrcv->hide();
2098 	} else {
2099 //		btnRev->show();
2100 		if (!progdefaults.WF_UIrev)
2101 			btnRev->hide();
2102 		else btnRev->show();
2103 		wfcarrier->show();
2104 		wfRefLevel->show();
2105 		wfAmpSpan->show();
2106 		xmtlock->show();
2107 		qsy->show();
2108 		mode->show();
2109 		x1->show();
2110 		left->show();
2111 		center->show();
2112 		right->show();
2113 		wfrate->show();
2114 		btnMem->show();
2115 		mbtnMem->show();
2116 	}
2117 	btnRev->redraw();
2118 	wfcarrier->redraw();
2119 	wfRefLevel->redraw();
2120 	wfAmpSpan->redraw();
2121 	xmtlock->redraw();
2122 	qsy->redraw();
2123 	mode->redraw();
2124 	x1->redraw();
2125 	left->redraw();
2126 	center->redraw();
2127 	right->redraw();
2128 	wfrate->redraw();
2129 	btnMem->redraw();
2130 	mbtnMem->redraw();
2131 }
2132 
handle(int event)2133 int waterfall::handle(int event)
2134 {
2135 	if (event != FL_MOUSEWHEEL || Fl::event_inside(wfdisp))
2136 		return Fl_Group::handle(event);
2137 
2138 	int d;
2139 	if ( !((d = Fl::event_dy()) || (d = Fl::event_dx())) )
2140 		return 1;
2141 
2142 	// this does not belong here, but we don't have access to this widget's
2143 	// handle method (or its parent's)
2144 	if (active_modem && Fl::event_inside(MODEstatus)) {
2145 		trx_mode mode = active_modem->get_mode();
2146 		for (;;) {
2147 			mode = WCLAMP(mode + d, 0, NUM_MODES - 1);
2148 			if ((mode >= NUM_RXTX_MODES && mode < NUM_MODES) ||
2149 				progdefaults.visible_modes.test(mode))
2150 				break;
2151 		}
2152 		init_modem(mode);
2153 		return 1;
2154 	}
2155 	// as above; handle wheel events for the macro bar
2156 	extern void altmacro_cb(Fl_Widget *w, void *v);
2157 	if (progdefaults.macro_wheel) {
2158 		if (progdefaults.mbar_scheme > MACRO_SINGLE_BAR_MAX) {
2159 			if (Fl::event_inside(macroFrame2)) {
2160 				altmacro_cb(btnAltMacros2, reinterpret_cast<void *>(d));
2161 				return 1;
2162 			}
2163 		} else {
2164 			if (Fl::event_inside(macroFrame1)) {
2165 				altmacro_cb(btnAltMacros1, reinterpret_cast<void *>(d));
2166 				return 1;
2167 			}
2168 		}
2169 	}
2170 
2171 	return Fl_Group::handle(event);
2172 }
2173 
2174 static Fl_Cursor cursor = FL_CURSOR_DEFAULT;
2175 
hide_cursor(void * w)2176 static void hide_cursor(void *w)
2177 {
2178 	if (cursor != FL_CURSOR_NONE)
2179 		reinterpret_cast<Fl_Widget *>(w)->window()->cursor(cursor = FL_CURSOR_NONE);
2180 }
2181 
insert_text(bool check)2182 void waterfall::insert_text(bool check)
2183 {
2184 	if (active_modem && check) {
2185 		qrg_mode_t m;
2186 		m.rfcarrier = wf->rfcarrier();
2187 		m.carrier = active_modem->get_freq();
2188 		m.mode = active_modem->get_mode();
2189 		extern qrg_mode_t last_marked_qrg;
2190 		if (last_marked_qrg.mode == m.mode && last_marked_qrg.rfcarrier == m.rfcarrier &&
2191 			abs(last_marked_qrg.carrier - m.carrier) <= 16)
2192 			return;
2193 		last_marked_qrg = m;
2194 	}
2195 
2196 	string::size_type i;
2197 	if ((i = progdefaults.WaterfallClickText.find("<FREQ>")) != string::npos) {
2198 		string s = progdefaults.WaterfallClickText;
2199 		s[i] = '\0';
2200 		ReceiveText->addstr(s);
2201 		note_qrg(false);
2202 //		ReceiveText->addstr(s);
2203 //		ReceiveText->addstr(s.c_str() + i + strlen("<FREQ>"));
2204 	}
2205 	else
2206 		ReceiveText->addstr(progdefaults.WaterfallClickText, FTextView::SKIP);
2207 }
2208 
find_signal_text(void)2209 static void find_signal_text(void)
2210 {
2211 	if (!active_modem) return;
2212 	int freq = active_modem->get_freq();
2213 	trx_mode mode = active_modem->get_mode();
2214 
2215 	extern map<string, qrg_mode_t> qrg_marks;
2216 	map<string, qrg_mode_t>::const_iterator i;
2217 	for (i = qrg_marks.begin(); i != qrg_marks.end(); ++i)
2218 		if (i->second.mode == mode && abs(i->second.carrier - freq) <= 20)
2219 			break;
2220 	if (i != qrg_marks.end()) {
2221 		// Search backward from the current text cursor position, then
2222 		// try the other direction
2223 		int pos = ReceiveText->insert_position();
2224 		if (ReceiveText->buffer()->search_backward(pos, i->first.c_str(), &pos, 1) ||
2225 			ReceiveText->buffer()->search_forward(pos, i->first.c_str(), &pos, 1)) {
2226 			ReceiveText->insert_position(pos);
2227 			ReceiveText->show_insert_position();
2228 		}
2229 	}
2230 }
2231 
handle(int event)2232 int WFdisp::handle(int event)
2233 {
2234 	static int pxpos, push;
2235 	if (!(event == FL_LEAVE || Fl::event_inside(this))) {
2236 		if (event == FL_RELEASE)
2237 			push = 0;
2238 		return 0;
2239 	}
2240 
2241 	if (trx_state != STATE_RX)
2242 		return 1;
2243 
2244 	int xpos = Fl::event_x() - x();
2245 	int ypos = Fl::event_y() - y();
2246 	int eb;
2247 
2248 	if (active_modem == fmt_modem) {
2249 		int nuf = cursorFreq(xpos);
2250 		if ((Fl::event_state() & (FL_SHIFT)) == FL_SHIFT) {
2251 			set_unk_freq_value(nuf);
2252 		} else if ((Fl::event_state() & (FL_CTRL)) == FL_CTRL) {
2253 			set_ref_freq_value(nuf);
2254 		}
2255 		return 1;
2256 	}
2257 
2258 	switch (event) {
2259 	case FL_MOVE:
2260 		if (progdefaults.WaterfallQSY && ypos < WFTEXT + WFSCALE) {
2261 			Fl::remove_timeout(hide_cursor, this);
2262 			if (cursor != FL_CURSOR_WE)
2263 				window()->cursor(cursor = FL_CURSOR_WE);
2264 			if (wantcursor) {
2265 				wantcursor = false;
2266 				makeMarker();
2267 			}
2268 			break;
2269 		}
2270 		if (cursor != FL_CURSOR_DEFAULT)
2271 			window()->cursor(cursor = FL_CURSOR_DEFAULT);
2272 		if (!Fl::has_timeout(hide_cursor, this))
2273 			Fl::add_timeout(1, hide_cursor, this);
2274 		wantcursor = true;
2275 		cursorpos = xpos;
2276 		makeMarker();
2277 		break;
2278 	case FL_DRAG: case FL_PUSH:
2279 		killMacroTimer();
2280 
2281 		switch (eb = Fl::event_button()) {
2282 		case FL_RIGHT_MOUSE:
2283 			wantcursor = false;
2284 			if (event == FL_PUSH) {
2285 				tmp_carrier = true;
2286 				oldcarrier = carrier();
2287 				if (progdefaults.WaterfallHistoryDefault)
2288 					bHistory = true;
2289 			}
2290 			goto lrclick;
2291 		case FL_LEFT_MOUSE:
2292 			if ((Fl::event_state() & (FL_ALT | FL_CTRL)) == (FL_ALT | FL_CTRL)) {
2293 				if (notch_frequency)
2294 					notch_off();
2295 				else
2296 					notch_on(cursorFreq(xpos));
2297 				return 1;
2298 			}
2299 			if (event == FL_PUSH) {
2300 				push = ypos;
2301 				pxpos = xpos;
2302 				if (Fl::event_clicks())
2303 					return 1;
2304 			}
2305 			if (progdefaults.WaterfallQSY && push < WFTEXT + WFSCALE) {
2306 				long long newrfc = (pxpos - xpos) * step;
2307 				if (!USB())
2308 					newrfc = -newrfc;
2309 				newrfc += rfcarrier();
2310 				qsy(newrfc, active_modem ? active_modem->get_freq() : 1500);
2311 				pxpos = xpos;
2312 				return 1;
2313 			}
2314 		lrclick:
2315 			if (Fl::event_state() & FL_CTRL) {
2316 				if (event == FL_DRAG)
2317 					break;
2318 				if (!progdefaults.WaterfallHistoryDefault)
2319 					bHistory = true;
2320 				if (eb == FL_LEFT_MOUSE) {
2321 					   restoreFocus();
2322 					   break;
2323 				}
2324 			}
2325 			if (progdefaults.WaterfallHistoryDefault)
2326 				bHistory = true;
2327 			newcarrier = cursorFreq(xpos);
2328 			if (active_modem) {
2329 				newcarrier = (int)CLAMP(
2330 					newcarrier,
2331 					progdefaults.LowFreqCutoff + active_modem->get_bandwidth() / 2,
2332 					progdefaults.HighFreqCutoff - active_modem->get_bandwidth() / 2);
2333 				active_modem->set_freq(newcarrier);
2334 				viewer_paste_freq(newcarrier);
2335 				if (!(Fl::event_state() & FL_SHIFT))
2336 					active_modem->set_sigsearch(SIGSEARCH);
2337 			}
2338 			restoreFocus();
2339 			break;
2340 		case FL_MIDDLE_MOUSE:
2341 			if (event == FL_DRAG)
2342 				break;
2343 			btnAFC->value(!btnAFC->value());
2344 			btnAFC->do_callback();
2345 		}
2346 		break;
2347 	case FL_RELEASE:
2348 		switch (eb = Fl::event_button()) {
2349 		case FL_RIGHT_MOUSE:
2350 			tmp_carrier = false;
2351 			if (active_modem) active_modem->set_freq(oldcarrier);
2352 			restoreFocus();
2353 			// fall through
2354 		case FL_LEFT_MOUSE:
2355 			push = 0;
2356 			oldcarrier = newcarrier;
2357 			if (eb != FL_LEFT_MOUSE || !ReceiveText->visible())
2358 				break;
2359 			if (eb == FL_LEFT_MOUSE)
2360 				recenter_spectrum_viewer();
2361 			if (!(Fl::event_state() & (FL_CTRL | FL_META | FL_ALT | FL_SHIFT))) {
2362 				if (Fl::event_clicks() == 1)
2363 					note_qrg(true, "\n", "\n");
2364 				else
2365 					if (progdefaults.WaterfallClickInsert)
2366 						wf->insert_text(true);
2367 			}
2368 			else if (Fl::event_state() & (FL_META | FL_ALT))
2369 				find_signal_text();
2370 			break;
2371 		}
2372 		break;
2373 
2374 	case FL_MOUSEWHEEL:
2375 	{
2376 		killMacroTimer();
2377 
2378 		int d;
2379 		if ( !((d = Fl::event_dy()) || (d = Fl::event_dx())) )
2380 			break;
2381 		int state = Fl::event_state();
2382 		if (state & FL_CTRL)
2383 			wf->handle_mouse_wheel(waterfall::WF_AFC_BW, d);
2384 		else if (state & (FL_META | FL_ALT))
2385 			wf->handle_mouse_wheel(waterfall::WF_SIGNAL_SEARCH, d);
2386 		else if (state & FL_SHIFT)
2387 			wf->handle_mouse_wheel(waterfall::WF_SQUELCH, d);
2388 		else {
2389 			if (progdefaults.WaterfallQSY && Fl::event_inside(x(), y(), w(), WFTEXT+WFSCALE+WFMARKER))
2390 				qsy(wf->rfcarrier() - 500*d);
2391 			else
2392 				wf->handle_mouse_wheel(progdefaults.WaterfallWheelAction, d);
2393 		}
2394 		return handle(FL_MOVE);
2395 	}
2396 	case FL_SHORTCUT:
2397 		if (Fl::event_inside(this))
2398 			take_focus();
2399 		break;
2400 	case FL_KEYBOARD:
2401 	{
2402 		killMacroTimer();
2403 
2404 		int d = (Fl::event_state() & FL_CTRL) ? 10 : 1;
2405 		int k = Fl::event_key();
2406 		switch (k) {
2407 		case FL_Left: case FL_Right:
2408 			if (k == FL_Left)
2409 				d = -d;
2410 			if (active_modem) {
2411 				oldcarrier = newcarrier = (int)CLAMP(
2412 					carrier() + d,
2413 					progdefaults.LowFreqCutoff + active_modem->get_bandwidth() / 2,
2414 					progdefaults.HighFreqCutoff - active_modem->get_bandwidth() / 2);
2415 				active_modem->set_freq(newcarrier);
2416 			}
2417 			break;
2418 		case FL_Tab:
2419 			restoreFocus();
2420 			break;
2421 		default:
2422 			restoreFocus();
2423 			return TransmitText->handle(event);
2424 		}
2425 		break;
2426 	}
2427 	case FL_KEYUP:
2428 	{
2429 		if (Fl::event_inside(this)) {
2430 			int k = Fl::event_key();
2431 			if (k == FL_Shift_L || k == FL_Shift_R || k == FL_Control_L ||
2432 				k == FL_Control_R || k == FL_Meta_L || k == FL_Meta_R ||
2433 				k == FL_Alt_L || k == FL_Alt_R)
2434 				restoreFocus();
2435 		}
2436 		break;
2437 	}
2438 
2439 	case FL_LEAVE:
2440 		Fl::remove_timeout(hide_cursor, this);
2441 		if (cursor != FL_CURSOR_DEFAULT)
2442 			window()->cursor(cursor = FL_CURSOR_DEFAULT);
2443 		wantcursor = false;
2444 		makeMarker();
2445 		break;
2446 
2447 	}
2448 
2449 	return 1;
2450 }
2451 
handle_mouse_wheel(int what,int d)2452 void waterfall::handle_mouse_wheel(int what, int d)
2453 {
2454 	if (d == 0)
2455 		return;
2456 
2457 	Fl_Valuator *val = 0;
2458 	const char* msg_fmt = 0, *msg_label = 0;
2459 
2460 	switch (what) {
2461 	case WF_NOP:
2462 		return;
2463 	case WF_AFC_BW:
2464 	{
2465 		if (active_modem) {
2466 			trx_mode m = active_modem->get_mode();
2467 			if (m >= MODE_PSK_FIRST && m <= MODE_PSK_LAST) {
2468 				val = mailserver ? cntServerOffset : cntSearchRange;
2469 				msg_label = "Srch Rng";
2470 			}
2471 			else if (m >= MODE_HELL_FIRST && m <= MODE_HELL_LAST) {
2472 				val = sldrHellBW;
2473 				msg_label = "BW";
2474 			}
2475 			else if (m == MODE_CW) {
2476 				val = sldrCWbandwidth;
2477 				msg_label = "BW";
2478 			}
2479 			else
2480 				return;
2481 			msg_fmt = "%s: %2.0f Hz";
2482 		}
2483 		break;
2484 	}
2485 	case WF_SIGNAL_SEARCH:
2486 		if (d > 0) {
2487 			if (active_modem) active_modem->searchDown();
2488 		} else {
2489 			if (active_modem) active_modem->searchUp();
2490 		}
2491 		return;
2492 	case WF_SQUELCH:
2493 		val = sldrSquelch;
2494 		d = -d;
2495 		msg_fmt = "%s = %2.0f %%";
2496 		msg_label = "Squelch";
2497 		break;
2498 	case WF_CARRIER:
2499 		val = wfcarrier;
2500 		break;
2501 	case WF_MODEM:
2502 		init_modem(d > 0 ? MODE_NEXT : MODE_PREV);
2503 		return;
2504 	case WF_SCROLL:
2505 		(d > 0 ? right : left)->do_callback();
2506 		return;
2507 	}
2508 
2509 	val->value(val->clamp(val->increment(val->value(), -d)));
2510 	bool changed_save = progdefaults.changed;
2511 	val->do_callback();
2512 	progdefaults.changed = changed_save;
2513 	if (val == cntServerOffset || val == cntSearchRange) {
2514 		if (active_modem) active_modem->set_sigsearch(SIGSEARCH);
2515 	} else if (val == sldrSquelch) { // sldrSquelch gives focus to TransmitText
2516 		take_focus();
2517 	}
2518 	if (msg_fmt) {
2519 		char msg[60];
2520 		snprintf(msg, sizeof(msg), msg_fmt, msg_label, val->value());
2521 		put_status(msg, 2.0);
2522 	}
2523 }
2524 
2525 const char* waterfall::wf_wheel_action[] = {
2526 	_("None"), _("AFC range or BW"),
2527 	_("Signal search"), _("Squelch level"),
2528 	_("Modem carrier"), _("Modem"), _("Scroll")
2529 };
2530