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