1 // ----------------------------------------------------------------------------
2 // digiscope.cxx, Miniature Oscilloscope/Phasescope Widget
3 //
4 // Copyright (C) 2006-2009
5 //		Dave Freese, W1HKJ
6 // Copyright (C) 2008
7 //		Stelios Bounanos, M0GLD
8 //
9 // This file is part of fldigi.  Adapted from code contained in gmfsk source code
10 // distribution.
11 //  gmfsk Copyright (C) 2001, 2002, 2003
12 //  Tomi Manninen (oh2bns@sral.fi)
13 //  Copyright (C) 2004
14 //  Lawrence Glaister (ve7it@shaw.ca)
15 //
16 // Fldigi is free software: you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation, either version 3 of the License, or
19 // (at your option) any later version.
20 //
21 // Fldigi is distributed in the hope that it will be useful,
22 // but WITHOUT ANY WARRANTY; without even the implied warranty of
23 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 // GNU General Public License for more details.
25 //
26 // You should have received a copy of the GNU General Public License
27 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
28 // ----------------------------------------------------------------------------
29 
30 #include <config.h>
31 
32 #include <iostream>
33 #include <cmath>
34 #include <cstring>
35 
36 #include <FL/Fl.H>
37 #include <FL/fl_draw.H>
38 
39 #include "digiscope.h"
40 #include "modem.h"
41 #include "trx.h"
42 #include "fl_digi.h"
43 #include "qrunner.h"
44 
45 
Digiscope(int X,int Y,int W,int H,const char * label)46 Digiscope::Digiscope (int X, int Y, int W, int H, const char *label) :
47 	Fl_Widget (X, Y, W, H, label) {
48 	_phase = _quality = _flo = _fhi = _amp = 0.0;
49 	box(FL_DOWN_BOX);
50 	vidbuf = new unsigned char[ 3 * (W-4) * (H-4)];
51 	vidline = new unsigned char[ 3 * (W-4)];
52 	memset(vidbuf, 0, 3*(W-4)*(H-4) * sizeof(unsigned char));
53 	memset(vidline, 0, 3 * (W-4) * sizeof(unsigned char));
54 	_highlight = false;
55 	_len = MAX_LEN;
56 	_zptr = 0;
57 	for (int i = 0; i < NUM_GRIDS; i++)
58 		_x[i] = _y[i] = 0;
59 	_y_user1 = _y_user2 = -1;
60 	_x_user1 = _x_user2 = -1;
61 	_x_graticule = _y_graticule = false;
62 
63 	phase_mode = PHASE1;
64 }
65 
~Digiscope()66 Digiscope::~Digiscope()
67 {
68 	if (vidbuf) delete [] vidbuf;
69 	if (vidline) delete [] vidline;
70 }
71 
video(double * data,int len,bool dir)72 void Digiscope::video(double *data, int len , bool dir)
73 {
74 	if (active_modem->HistoryON()) return;
75 
76 	if (data == NULL || len == 0)
77 		return;
78 
79 	int W = w() - 4;
80 	int H = h() - 4;
81 	for (int i = 0; i < W; i++)
82 		vidline[3*i] = vidline[3*i+1] =
83 		vidline[3*i+2] = (unsigned char)(data[i * len / W]);
84 	vidline[3*W/2] = 255;
85 	vidline[3*W/2+1] = 0;
86 	vidline[3*W/2+2] = 0;
87 	if (dir) {
88 		if (linecnt == H) {
89 			linecnt--;
90 			unsigned char *p = &vidbuf[3*W];
91 			memmove (vidbuf, p, 3*(W * (H-1))*sizeof(unsigned char));
92 			memcpy (&vidbuf[3*W*(H-1)], vidline, 3*W * sizeof (unsigned char));
93 		}
94 		else
95 			memcpy (&vidbuf[3*W*linecnt], vidline, 3*W * sizeof(unsigned char));
96 		linecnt++;
97 	} else {
98 		unsigned char *p = &vidbuf[3*W];
99 		memmove (p, vidbuf, 3 * (W * (H-1)) * sizeof(unsigned char));
100 		memcpy(vidbuf, vidline, 3 * W * sizeof(unsigned char));
101 	}
102 
103 	REQ_DROP(&Digiscope::redraw, this);
104 }
105 
zdata(cmplx * zarray,int len)106 void Digiscope::zdata(cmplx *zarray, int len )
107 {
108 	if (active_modem->HistoryON()) return;
109 
110 	if (zarray == NULL || len == 0)
111 		return;
112 
113 	for (int i = 0; i < len; i++) {
114 		_zdata[_zptr++] = zarray[i];
115 		if (_zptr == MAX_ZLEN) _zptr = 0;
116 	}
117 	REQ_DROP(&Digiscope::redraw, this);
118 }
119 
data(double * data,int len,bool scale)120 void Digiscope::data(double *data, int len, bool scale)
121 {
122 	if (active_modem->HistoryON()) return;
123 
124 	if (data == 0) {
125 		memset(_buf, 0, MAX_LEN * sizeof(*_buf));
126     	REQ_DROP(&Digiscope::redraw, this);
127 		return;
128 	}
129 	if (len == 0)
130 		return;
131 	if (len > MAX_LEN) _len = MAX_LEN;
132 	else _len = len;
133 	memcpy(_buf, data, len * sizeof(double));
134 
135 	if (scale) {
136 		double max = 1E-6;
137 		double min = 1E6;
138 		for (int i = 0; i < _len; i++) {
139 			max = MAX(max, _buf[i]);
140 			min = MIN(min, _buf[i]);
141 		}
142 		if (max == min) max *= 1.001;
143 		for (int i = 0; i < _len; i++)
144 			_buf[i] = (_buf[i] - min) / (max - min);
145 	}
146 	REQ_DROP(&Digiscope::redraw, this);
147 }
148 
phase(double ph,double ql,bool hl)149 void Digiscope::phase(double ph, double ql, bool hl)
150 {
151 	if (active_modem->HistoryON()) return;
152 
153 	_phase = ph;
154 	_quality = ql;
155 	_highlight = hl;
156 	REQ_DROP(&Digiscope::redraw, this);
157 }
158 
rtty(double flo,double fhi,double amp)159 void Digiscope::rtty(double flo, double fhi, double amp)
160 {
161 	if (active_modem->HistoryON()) return;
162 
163 	_flo = flo;
164 	_fhi = fhi;
165 	_amp = amp;
166 	REQ_DROP(&Digiscope::redraw, this);
167 }
168 
169 
mode(scope_mode md)170 void Digiscope::mode(scope_mode md)
171 {
172 	if (md == PHASE) {
173 		if (phase_mode >= PHASE1 && phase_mode <= PHASE3)
174 			md = phase_mode;
175 		else
176 			md = phase_mode = PHASE1;
177 	}
178 	int W = w() - 4;
179 	int H = h() - 4;
180 	_mode = md;
181 	memset(_buf, 0, MAX_LEN * sizeof(double));
182 	linecnt = 0;
183 	memset(vidbuf, 0, 3*W*H * sizeof(unsigned char));
184 	memset(vidline, 0, 3 * W * sizeof(unsigned char));
185 	vidline[3*W/2] = 255;
186 	vidline[3*W/2+1] = 0;
187 	vidline[3*W/2+2] = 0;
188 	for (int i = 0; i < H; i++)
189 		memcpy(&vidbuf[3*W*i], vidline, 3*W*sizeof(unsigned char) );
190 	REQ_DROP(&Digiscope::redraw, this);
191 }
192 
draw_phase()193 void Digiscope::draw_phase()
194 {
195 	// max number of shown vectors is first dimension
196 	static double pvecstack[8][2];
197 	static const size_t psz = sizeof(pvecstack)/sizeof(*pvecstack);
198 	static unsigned pszn = 0;
199 
200 	fl_clip(x()+2,y()+2,w()-4,h()-4);
201 	fl_color(FL_BLACK);
202 	fl_rectf(x()+2,y()+2,w()-4,h()-4);
203 	fl_push_matrix();
204 	fl_translate(x() + w() / 2.0, y() + w() / 2.0);
205 	fl_scale( 0.9*w()/2, -0.9*w()/2);
206 	fl_color(FL_WHITE);
207 	fl_circle( 0.0, 0.0, 1.0);
208 	fl_begin_line();
209 		fl_vertex(-1.0, 0.0);
210 		fl_vertex(-0.9, 0.0);
211 	fl_end_line();
212 	fl_begin_line();
213 		fl_vertex(1.0, 0.0);
214 		fl_vertex(0.9, 0.0);
215 	fl_end_line();
216 	fl_begin_line();
217 		fl_vertex(0.0, -1.0);
218 		fl_vertex(0.0, -0.9);
219 	fl_end_line();
220 	fl_begin_line();
221 		fl_vertex(0.0, 1.0);
222 		fl_vertex(0.0, 0.9);
223 	fl_end_line();
224 
225 	if (_highlight) {
226 		if (_mode > PHASE1) {
227 			if (pszn == psz - 1)
228 				memmove(pvecstack, pvecstack + 1, (psz - 1) * sizeof(*pvecstack));
229 			else
230 				pszn++;
231 			pvecstack[pszn][0] = _phase;
232 			pvecstack[pszn][1] = _quality;
233 
234 			// draw the stack in progressively brighter green
235 			for (unsigned i = 0; i <= pszn; i++) {
236 //				fl_color(fl_color_average(FL_GREEN, FL_BLACK, 1.0 - 0.8 * (n-i)/ pszn));
237 				fl_color(fl_color_average(FL_GREEN, FL_BLACK, 0.2 + 0.8 * i / pszn));
238 				fl_begin_line();
239 				fl_vertex(0.0, 0.0);
240 				if (_mode == PHASE3) // scale length by quality
241 					fl_vertex(0.9 * cos(pvecstack[i][0] - M_PI / 2) * pvecstack[i][1],
242 						  0.9 * sin(pvecstack[i][0] - M_PI / 2) * pvecstack[i][1]);
243 				else
244 					fl_vertex(0.9 * cos(pvecstack[i][0] - M_PI / 2),
245 						  0.9 * sin(pvecstack[i][0] - M_PI / 2));
246 				fl_end_line();
247 			}
248 		}
249 		else { // original style
250 			fl_color(FL_GREEN);
251 			fl_begin_line();
252                         fl_vertex(0.0, 0.0);
253                         fl_vertex(0.9 * cos(_phase - M_PI / 2), 0.9 * sin( _phase - M_PI / 2));
254 			fl_end_line();
255 		}
256 	} else {
257 		fl_color(FL_GREEN);
258 		fl_circle( 0.0, 0.0, 0.1);
259 	}
260 	fl_pop_matrix();
261 	fl_pop_clip();
262 }
263 
draw_scope()264 void Digiscope::draw_scope()
265 {
266 	int npts, np;
267 	fl_clip(x()+2,y()+2,w()-4,h()-4);
268 	fl_color(FL_BLACK);
269 	fl_rectf(x()+2,y()+2,w()-4,h()-4);
270 	fl_push_matrix();
271 	npts = MIN(w(), _len);
272 	npts = MAX(1, npts);
273 	fl_translate(x()+2, y() + h() - 2);
274 	fl_scale ((w()-4), - (h() - 4));
275 	fl_color(FL_GREEN);
276 	fl_begin_line();
277 	for (int i = 0; i < npts; i++) {
278 		np = i * _len / npts;
279 		np = np < MAX_LEN ? np : MAX_LEN - 1;
280 		fl_vertex( (double)i / npts, _buf[np] );
281 	}
282 	fl_end_line();
283 
284 // x & y axis'
285 	for (int i = 0; i < NUM_GRIDS; i++) {
286 		if (_x[i]) {
287 			fl_color(FL_WHITE);
288 			fl_begin_line();
289 			fl_vertex(_x[i], 0.0);
290 			fl_vertex(_x[i], 1.0);
291 			fl_end_line();
292 		}
293 		if (_y[i]) {
294 			fl_color(FL_WHITE);
295 			fl_begin_line();
296 			fl_vertex(0.0, _y[i]);
297 			fl_vertex(1.0, _y[i]);
298 			fl_end_line();
299 		}
300 	}
301 
302 	if (_x_graticule) {
303 		if (_y_user1 > 0 && _y_user1 < 1.0) {
304 			fl_color(FL_CYAN);
305 			fl_begin_line();
306 			fl_vertex(0.0, 1.0 - _y_user1);
307 			fl_vertex(1.0, 1.0 - _y_user1);
308 			fl_end_line();
309 		}
310 		if (_y_user2 > 0 && _y_user2 < 1.0) {
311 			fl_color(FL_MAGENTA);
312 			fl_begin_line();
313 			fl_vertex(0.0, 1.0 - _y_user2);
314 			fl_vertex(1.0, 1.0 - _y_user2);
315 			fl_end_line();
316 		}
317 	}
318 
319 	if (_y_graticule) {
320 		if (_x_user1 > 0 && _x_user1 < 1.0) {
321 			fl_color(FL_CYAN);
322 			fl_begin_line();
323 			fl_vertex(_x_user1, 0.0);
324 			fl_vertex(_x_user1, 1.0);
325 			fl_end_line();
326 		}
327 		if (_x_user2 > 0 && _x_user2 < 1.0) {
328 			fl_color(FL_MAGENTA);
329 			fl_begin_line();
330 			fl_vertex(_x_user2, 0.0);
331 			fl_vertex(_x_user2, 1.0);
332 			fl_end_line();
333 		}
334 	}
335 
336 	fl_pop_matrix();
337 	fl_pop_clip();
338 }
339 
340 
draw_xy()341 void Digiscope::draw_xy()
342 {
343 	fl_clip(x()+2,y()+2,w()-4,h()-4);
344 	fl_color(FL_BLACK);
345 	fl_rectf(x()+2,y()+2,w()-4,h()-4);
346 	fl_push_matrix();
347 	fl_translate(x() + w() / 2.0, y() + w() / 2.0);
348 	fl_scale( w()/2.0, -w()/2.0);
349 // x & y axis
350 	fl_color(FL_LIGHT1);
351 	fl_begin_line();
352 		fl_vertex(-0.6, 0.0);
353 		fl_vertex(-1.0, 0.0);
354 	fl_end_line();
355 	fl_begin_line();
356 		fl_vertex(0.6, 0.0);
357 		fl_vertex(1.0, 0.0);
358 	fl_end_line();
359 	fl_begin_line();
360 		fl_vertex(0.0, -0.6);
361 		fl_vertex(0.0, -1.0);
362 	fl_end_line();
363 	fl_begin_line();
364 		fl_vertex(0.0, 0.6);
365 		fl_vertex(0.0, 1.0);
366 	fl_end_line();
367 // data
368 	int W = w() / 2;
369 	int H = h() / 2;
370 	int X = x();
371 	int Y = y();
372 	int xp, yp, xp1, yp1;
373 	int j = _zptr;
374 	if (++j == MAX_ZLEN) j = 0;
375 	xp = X + (int)((_zdata[j].real() + 1.0) * W);
376 	yp = Y + (int)((_zdata[j].imag() + 1.0) * H);
377 
378 	fl_color(fl_rgb_color(0, 230,0));
379 	for (int i = 0; i <  MAX_ZLEN; i++ ) {
380 		if (++j == MAX_ZLEN) j = 0;
381 		xp1 = X + (int)((_zdata[j].real() + 1.0) * W);
382 		yp1 = Y + (int)((_zdata[j].imag() + 1.0) * H);
383 		fl_line(xp, yp, xp1, yp1);
384 		xp = xp1; yp = yp1;
385 	}
386 
387 	fl_pop_matrix();
388 	fl_pop_clip();
389 }
390 
draw_rtty()391 void Digiscope::draw_rtty()
392 {
393 	int npts, np;
394 	fl_clip(x()+2,y()+2,w()-4,h()-4);
395 	fl_color(FL_BLACK);
396 	fl_rectf(x()+2,y()+2,w()-4,h()-4);
397 	fl_push_matrix();
398 	npts = MIN(w(), _len);
399 	npts = MAX(1, npts);
400 	fl_translate(x()+2, y() + h() - 2);
401 	fl_scale ((w()-4), - (h() - 4));
402 	fl_color(FL_YELLOW);
403 	fl_begin_line();
404 		fl_vertex( 0.0, 0.9);
405 		fl_vertex( 1.0, 0.9);
406 	fl_end_line();
407 	fl_begin_line();
408 		fl_vertex( 0.0, 0.1);
409 		fl_vertex( 1.0, 0.1);
410 	fl_end_line();
411 	fl_color(FL_WHITE);
412 	fl_begin_line();
413 		fl_vertex( 0.0, 0.5);
414 		fl_vertex( 1.0, 0.5);
415 	fl_end_line();
416 	fl_color(FL_GREEN);
417 	fl_begin_line();
418 	double value = 0.0;
419 	for (int i = 0; i < npts; i++) {
420 		np = round(1.0 * i * _len / npts);
421 		if (np >= MAX_LEN) np = MAX_LEN - 1;
422 		value = _buf[np];
423 		fl_vertex( 1.0 * i / (npts - 1), 0.5 + 0.75 * value );
424 	}
425 	fl_end_line();
426 	fl_pop_matrix();
427 	fl_pop_clip();
428 }
429 
draw_video()430 void Digiscope::draw_video()
431 {
432 	fl_draw_image(
433 		vidbuf,
434 		x() + 2, y() + 2,
435 		w() - 4, h() - 4);
436 }
437 
draw()438 void Digiscope::draw()
439 {
440 	draw_box();
441 	if (_mode == WWV || _mode == DOMWF)
442 		draw_video();
443 	else {
444 		switch (_mode) {
445 			case SCOPE :	draw_scope(); break;
446 			case PHASE1:
447 			case PHASE2:
448 			case PHASE3:	draw_phase(); break;
449 			case RTTY :		draw_rtty(); break;
450 			case XHAIRS :	draw_xy(); break;
451 			case DOMDATA :	draw_scope(); break;
452 			case BLANK :
453 			default:
454 				fl_clip(x()+2,y()+2,w()-4,h()-4);
455 				fl_color(FL_BLACK);
456 				fl_rectf(x()+2,y()+2,w()-4,h()-4);
457 				fl_push_matrix();
458 				fl_pop_matrix();
459 				fl_pop_clip();
460 				break;
461 		}
462 	}
463 }
464 
handle(int event)465 int Digiscope::handle(int event)
466 {
467 	if (!Fl::event_inside(this))
468 		return 0;
469 
470 	switch (event) {
471 	case FL_RELEASE:
472 		switch (_mode) {
473 		case PHASE1: case PHASE2:
474 			_mode = (scope_mode)((int)_mode + 1);
475 			phase_mode = _mode;
476 			redraw();
477 			break;
478 		case PHASE3:
479 			_mode = PHASE1;
480 			phase_mode = _mode;
481 			redraw();
482 			break;
483 		case RTTY:
484 			_mode = XHAIRS;
485 			redraw();
486 			break;
487 		case XHAIRS:
488 			_mode = RTTY;
489 			redraw();
490 			break;
491 		case DOMDATA:
492 			_mode = DOMWF;
493 			redraw();
494 			break;
495 		case DOMWF:
496 			_mode = DOMDATA;
497 			redraw();
498 			break;
499 		case WWV:
500 			event = Fl::event_button();
501 			if (event == FL_LEFT_MOUSE)
502 				wwv_modem->set1(Fl::event_x() - x(), w());
503 			else if (event == FL_RIGHT_MOUSE)
504 				wwv_modem->set2(Fl::event_x() - x(), Fl::event_y() - y());
505 			break;
506 		default:
507 			break;
508 		}
509 		return 1;
510 	case FL_MOUSEWHEEL:
511 		if ((event = Fl::event_dy()) || (event = Fl::event_dx()))
512 			wf->handle_mouse_wheel(waterfall::WF_AFC_BW, event);
513 		break;
514 	default:
515 		break;
516 	}
517 
518 	return 1;
519 }
520 
resize(int x,int y,int w,int h)521 void Digiscope::resize(int x, int y, int w, int h)
522 {
523 	delete [] vidbuf;
524 	delete [] vidline;
525 	vidbuf = new unsigned char[ 3 * (w-4) * (h-4)];
526 	vidline = new unsigned char[ 3 * (w-4)];
527 	memset(vidbuf, 0, 3*(w-4)*(h-4) * sizeof(unsigned char));
528 	memset(vidline, 0, 3*(w-4) * sizeof(unsigned char));
529 
530 	Fl_Widget::resize(x, y, w, h);
531 }
532