1 // ----------------------------------------------------------------------------
2 // rtty.cxx -- RTTY modem
3 //
4 // Copyright (C) 2006-2010
5 // Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi. Adapted from code contained in gmfsk source code
8 // distribution.
9 //
10 // Fldigi is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Fldigi is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
22 // ----------------------------------------------------------------------------
23
24 #include <config.h>
25 #include <iostream>
26 using namespace std;
27
28 //#include "rtty.h"
29 #include "view_rtty.h"
30 #include "fl_digi.h"
31 #include "digiscope.h"
32 #include "misc.h"
33 #include "waterfall.h"
34 #include "confdialog.h"
35 #include "configuration.h"
36 #include "status.h"
37 #include "digiscope.h"
38 #include "Viewer.h"
39 #include "qrunner.h"
40
41 //=====================================================================
42 // Baudot support
43 //=====================================================================
44
45 static char letters[32] = {
46 '\0', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
47 '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
48 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
49 'O', 'B', 'G', ' ', 'M', 'X', 'V', ' '
50 };
51
52 // U.S. version of the figures case.
53 static char figures[32] = {
54 '\0', '3', '\n', '-', ' ', '\a', '8', '7',
55 '\r', '$', '4', '\'', ',', '!', ':', '(',
56 '5', '"', ')', '2', '#', '6', '0', '1',
57 '9', '?', '&', ' ', '.', '/', ';', ' '
58 };
59
60 const double view_rtty::SHIFT[] = {23, 85, 160, 170, 182, 200, 240, 350, 425, 850};
61 // FILTLEN must be same size as BAUD
62 const double view_rtty::BAUD[] = {45, 45.45, 50, 56, 75, 100, 110, 150, 200, 300};
63 const int view_rtty::FILTLEN[] = { 512, 512, 512, 512, 512, 512, 512, 256, 128, 64};
64 const int view_rtty::BITS[] = {5, 7, 8};
65 const int view_rtty::numshifts = (int)(sizeof(SHIFT) / sizeof(*SHIFT));
66 const int view_rtty::numbauds = (int)(sizeof(BAUD) / sizeof(*BAUD));
67
rx_init()68 void view_rtty::rx_init()
69 {
70 for (int ch = 0; ch < progdefaults.VIEWERchannels; ch++) {
71 channel[ch].state = IDLE;
72 channel[ch].rxstate = RTTY_RX_STATE_IDLE;
73 channel[ch].rxmode = LETTERS;
74 channel[ch].phaseacc = 0;
75 channel[ch].timeout = 0;
76 channel[ch].frequency = NULLFREQ;
77 channel[ch].poserr = channel[ch].negerr = 0.0;
78
79 channel[ch].mark_phase = 0;
80 channel[ch].space_phase = 0;
81 channel[ch].mark_mag = 0;
82 channel[ch].space_mag = 0;
83 channel[ch].mark_env = 0;
84 channel[ch].space_env = 0;
85
86 channel[ch].inp_ptr = 0;
87
88 for (int i = 0; i < VIEW_MAXPIPE; i++)
89 channel[ch].mark_history[i] =
90 channel[ch].space_history[i] = cmplx(0,0);
91 }
92 }
93
init()94 void view_rtty::init()
95 {
96 bool wfrev = wf->Reverse();
97 bool wfsb = wf->USB();
98 reverse = wfrev ^ !wfsb;
99 rx_init();
100 }
101
~view_rtty()102 view_rtty::~view_rtty()
103 {
104 for (int ch = 0; ch < MAX_CHANNELS; ch ++) {
105 if (channel[ch].mark_filt) delete channel[ch].mark_filt;
106 if (channel[ch].space_filt) delete channel[ch].space_filt;
107 if (channel[ch].bits) delete channel[ch].bits;
108 }
109 }
110
reset_filters(int ch)111 void view_rtty::reset_filters(int ch)
112 {
113 delete channel[ch].mark_filt;
114 channel[ch].mark_filt = new fftfilt(rtty_baud/samplerate, filter_length);
115 channel[ch].mark_filt->rtty_filter(rtty_baud/samplerate);
116 delete channel[ch].space_filt;
117 channel[ch].space_filt = new fftfilt(rtty_baud/samplerate, filter_length);
118 channel[ch].space_filt->rtty_filter(rtty_baud/samplerate);
119 }
120
restart()121 void view_rtty::restart()
122 {
123 double stl;
124
125 rtty_shift = shift = (progdefaults.rtty_shift < rtty::numshifts ?
126 SHIFT[progdefaults.rtty_shift] : progdefaults.rtty_custom_shift);
127 rtty_baud = BAUD[progdefaults.rtty_baud];
128 filter_length = FILTLEN[progdefaults.rtty_baud];
129
130 nbits = rtty_bits = BITS[progdefaults.rtty_bits];
131 if (rtty_bits == 5)
132 rtty_parity = RTTY_PARITY_NONE;
133 else
134 switch (progdefaults.rtty_parity) {
135 case 0 : rtty_parity = RTTY_PARITY_NONE; break;
136 case 1 : rtty_parity = RTTY_PARITY_EVEN; break;
137 case 2 : rtty_parity = RTTY_PARITY_ODD; break;
138 case 3 : rtty_parity = RTTY_PARITY_ZERO; break;
139 case 4 : rtty_parity = RTTY_PARITY_ONE; break;
140 default : rtty_parity = RTTY_PARITY_NONE; break;
141 }
142 rtty_stop = progdefaults.rtty_stop;
143
144
145 symbollen = (int) (samplerate / rtty_baud + 0.5);
146 bflen = symbollen/3;
147
148 set_bandwidth(shift);
149
150 rtty_BW = progdefaults.RTTY_BW;
151
152 bp_filt_lo = (shift/2.0 - rtty_BW/2.0) / samplerate;
153 if (bp_filt_lo < 0) bp_filt_lo = 0;
154 bp_filt_hi = (shift/2.0 + rtty_BW/2.0) / samplerate;
155
156 for (int ch = 0; ch < MAX_CHANNELS; ch ++) {
157
158 reset_filters(ch);
159
160 channel[ch].state = IDLE;
161 channel[ch].timeout = 0;
162 channel[ch].freqerr = 0.0;
163 channel[ch].metric = 0.0;
164 channel[ch].sigpwr = 0.0;
165 channel[ch].noisepwr = 0.0;
166 channel[ch].sigsearch = 0;
167 channel[ch].frequency = NULLFREQ;
168 channel[ch].counter = symbollen / 2;
169 channel[ch].mark_phase = 0;
170 channel[ch].space_phase = 0;
171 channel[ch].mark_mag = 0;
172 channel[ch].space_mag = 0;
173 channel[ch].mark_env = 0;
174 channel[ch].space_env = 0;
175 channel[ch].inp_ptr = 0;
176
177 if (channel[ch].bits)
178 channel[ch].bits->setLength(symbollen / 8);
179 else
180 channel[ch].bits = new Cmovavg(symbollen / 8);
181
182 channel[ch].mark_noise = channel[ch].space_noise = 0;
183 channel[ch].bit = channel[ch].nubit = true;
184
185 for (int i = 0; i < VIEW_RTTY_MAXBITS; i++) channel[ch].bit_buf[i] = 0.0;
186
187 for (int i = 0; i < VIEW_MAXPIPE; i++)
188 channel[ch].mark_history[i] = channel[ch].space_history[i] = cmplx(0,0);
189 }
190
191 // stop length = 1, 1.5 or 2 bits
192 rtty_stop = progdefaults.rtty_stop;
193 if (rtty_stop == 0) stl = 1.0;
194 else if (rtty_stop == 1) stl = 1.5;
195 else stl = 2.0;
196 stoplen = (int) (stl * samplerate / rtty_baud + 0.5);
197
198 rx_init();
199 }
200
view_rtty(trx_mode tty_mode)201 view_rtty::view_rtty(trx_mode tty_mode)
202 {
203 cap |= CAP_AFC | CAP_REV;
204
205 mode = tty_mode;
206
207 samplerate = RTTY_SampleRate;
208
209 for (int ch = 0; ch < MAX_CHANNELS; ch ++) {
210 channel[ch].mark_filt = (fftfilt *)0;
211 channel[ch].space_filt = (fftfilt *)0;
212 channel[ch].bits = (Cmovavg *)0;
213 }
214
215 restart();
216 }
217
mixer(double & phase,double f,cmplx in)218 cmplx view_rtty::mixer(double &phase, double f, cmplx in)
219 {
220 cmplx z = cmplx( cos(phase), sin(phase)) * in;;
221
222 phase -= TWOPI * f / samplerate;
223 if (phase < - TWOPI) phase += TWOPI;
224
225 return z;
226 }
227
228
bitreverse(unsigned char in,int n)229 unsigned char view_rtty::bitreverse(unsigned char in, int n)
230 {
231 unsigned char out = 0;
232
233 for (int i = 0; i < n; i++)
234 out = (out << 1) | ((in >> i) & 1);
235
236 return out;
237 }
238
rparity(int c)239 static int rparity(int c)
240 {
241 int w = c;
242 int p = 0;
243 while (w) {
244 p += (w & 1);
245 w >>= 1;
246 }
247 return p & 1;
248 }
249
rttyparity(unsigned int c)250 int view_rtty::rttyparity(unsigned int c)
251 {
252 c &= (1 << nbits) - 1;
253
254 switch (rtty_parity) {
255 default:
256 case RTTY_PARITY_NONE:
257 return 0;
258
259 case RTTY_PARITY_ODD:
260 return rparity(c);
261
262 case RTTY_PARITY_EVEN:
263 return !rparity(c);
264
265 case RTTY_PARITY_ZERO:
266 return 0;
267
268 case RTTY_PARITY_ONE:
269 return 1;
270 }
271 }
272
decode_char(int ch)273 int view_rtty::decode_char(int ch)
274 {
275 unsigned int parbit, par, data;
276
277 parbit = (channel[ch].rxdata >> nbits) & 1;
278 par = rttyparity(channel[ch].rxdata);
279
280 if (rtty_parity != RTTY_PARITY_NONE && parbit != par)
281 return 0;
282
283 data = channel[ch].rxdata & ((1 << nbits) - 1);
284
285 if (nbits == 5)
286 return baudot_dec(ch & 0x7F, data);
287
288 return data;
289 }
290
is_mark_space(int ch,int & correction)291 bool view_rtty::is_mark_space( int ch, int &correction)
292 {
293 correction = 0;
294 // test for rough bit position
295 if (channel[ch].bit_buf[0] && !channel[ch].bit_buf[symbollen-1]) {
296 // test for mark/space straddle point
297 for (int i = 0; i < symbollen; i++)
298 correction += channel[ch].bit_buf[i];
299 if (abs(symbollen/2 - correction) < 6) // too small & bad signals are not decoded
300 return true;
301 }
302 return false;
303 }
304
is_mark(int ch)305 bool view_rtty::is_mark(int ch)
306 {
307 return channel[ch].bit_buf[symbollen / 2];
308 }
309
rx(int ch,bool bit)310 bool view_rtty::rx(int ch, bool bit)
311 {
312 bool flag = false;
313 unsigned char c = 0;
314
315 int correction = 0;
316
317 for (int i = 1; i < symbollen; i++)
318 channel[ch].bit_buf[i-1] = channel[ch].bit_buf[i];
319 channel[ch].bit_buf[symbollen - 1] = bit;
320
321 switch (channel[ch].rxstate) {
322 case RTTY_RX_STATE_IDLE:
323 if ( is_mark_space(ch, correction)) {
324 channel[ch].rxstate = RTTY_RX_STATE_START;
325 channel[ch].counter = correction;
326 }
327 break;
328 case RTTY_RX_STATE_START:
329 if (--channel[ch].counter == 0) {
330 if (!is_mark(ch)) {
331 channel[ch].rxstate = RTTY_RX_STATE_DATA;
332 channel[ch].counter = symbollen;
333 channel[ch].bitcntr = 0;
334 channel[ch].rxdata = 0;
335 } else {
336 channel[ch].rxstate = RTTY_RX_STATE_IDLE;
337 }
338 }
339 break;
340 case RTTY_RX_STATE_DATA:
341 if (--channel[ch].counter == 0) {
342 channel[ch].rxdata |= is_mark(ch) << channel[ch].bitcntr++;
343 channel[ch].counter = symbollen;
344 }
345 if (channel[ch].bitcntr == nbits + (rtty_parity != RTTY_PARITY_NONE ? 1 : 0))
346 channel[ch].rxstate = RTTY_RX_STATE_STOP;
347 break;
348 case RTTY_RX_STATE_STOP:
349 if (--channel[ch].counter == 0) {
350 if (is_mark(ch)) {
351 if (channel[ch].metric > rtty_squelch) {
352 c = decode_char(ch);
353 // print this RTTY_CHANNEL
354 if ( c != 0 )
355 REQ(&viewaddchr, ch, (int)channel[ch].frequency, c, mode);
356 }
357 flag = true;
358 }
359 channel[ch].rxstate = RTTY_RX_STATE_IDLE;
360 }
361 break;
362 default : break;
363 }
364
365 return flag;
366 }
367
Metric(int ch)368 void view_rtty::Metric(int ch)
369 {
370 double delta = rtty_baud/2.0;
371 double np = wf->powerDensity(channel[ch].frequency, delta) * 3000 / delta;
372 double sp =
373 wf->powerDensity(channel[ch].frequency - shift/2, delta) +
374 wf->powerDensity(channel[ch].frequency + shift/2, delta) + 1e-10;
375
376 channel[ch].sigpwr = decayavg( channel[ch].sigpwr, sp, sp - channel[ch].sigpwr > 0 ? 2 : 16);
377
378 channel[ch].noisepwr = decayavg( channel[ch].noisepwr, np, 16 );
379
380 channel[ch].metric = CLAMP(channel[ch].sigpwr/channel[ch].noisepwr, 0.0, 100.0);
381
382 if (channel[ch].state == RCVNG)
383 if (channel[ch].metric < rtty_squelch) {
384 channel[ch].timeout = progdefaults.VIEWERtimeout * samplerate / WF_BLOCKSIZE;
385 channel[ch].state = WAITING;
386 }
387
388 if (channel[ch].timeout) {
389 channel[ch].timeout--;
390 if (!channel[ch].timeout) {
391 channel[ch].frequency = NULLFREQ;
392 channel[ch].metric = 0;
393 channel[ch].freqerr = 0;
394 channel[ch].state = IDLE;
395 REQ(&viewclearchannel, ch);
396 }
397 }
398 }
399
find_signals()400 void view_rtty::find_signals()
401 {
402 double spwrhi = 0.0, spwrlo = 0.0, npwr = 0.0;
403 double rtty_squelch = pow(10, progStatus.VIEWER_rttysquelch / 10.0);
404 for (int i = 0; i < progdefaults.VIEWERchannels; i++) {
405 if (channel[i].state != IDLE) continue;
406 int cf = progdefaults.LowFreqCutoff + 100 * i;
407 if (cf < shift) cf = shift;
408 double delta = rtty_baud / 8;
409 for (int chf = cf; chf < cf + 100 - rtty_baud / 4; chf += 5) {
410 spwrlo = wf->powerDensity(chf - shift/2, delta);
411 spwrhi = wf->powerDensity(chf + shift/2, delta);
412 npwr = (wf->powerDensity(chf, delta) * 3000 / rtty_baud) + 1e-10;
413 if ((spwrlo / npwr > rtty_squelch) && (spwrhi / npwr > rtty_squelch)) {
414 if (!i && (channel[i+1].state == SRCHG || channel[i+1].state == RCVNG)) break;
415 if ((i == (progdefaults.VIEWERchannels -2)) &&
416 (channel[i+1].state == SRCHG || channel[i+1].state == RCVNG)) break;
417 if (i && (channel[i-1].state == SRCHG || channel[i-1].state == RCVNG)) break;
418 if (i > 3 && (channel[i-2].state == SRCHG || channel[i-2].state == RCVNG)) break;
419 channel[i].frequency = chf;
420 channel[i].sigsearch = SIGSEARCH;
421 channel[i].state = SRCHG;
422 REQ(&viewaddchr, i, (int)channel[i].frequency, 0, mode);
423 break;
424 }
425 }
426 }
427 for (int i = 1; i < progdefaults.VIEWERchannels; i++ )
428 if (fabs(channel[i].frequency - channel[i-1].frequency) < rtty_baud/2)
429 clearch(i);
430 }
431
clearch(int ch)432 void view_rtty::clearch(int ch)
433 {
434 channel[ch].state = IDLE;
435 channel[ch].rxstate = RTTY_RX_STATE_IDLE;
436 channel[ch].rxmode = LETTERS;
437 channel[ch].phaseacc = 0;
438 channel[ch].frequency = NULLFREQ;
439 channel[ch].poserr = channel[ch].negerr = 0.0;
440 REQ( &viewclearchannel, ch);
441 }
442
clear()443 void view_rtty::clear()
444 {
445 for (int ch = 0; ch < progdefaults.VIEWERchannels; ch++) {
446 channel[ch].state = IDLE;
447 channel[ch].rxstate = RTTY_RX_STATE_IDLE;
448 channel[ch].rxmode = LETTERS;
449 channel[ch].phaseacc = 0;
450 channel[ch].frequency = NULLFREQ;
451 channel[ch].poserr = channel[ch].negerr = 0.0;
452 }
453 }
454
rx_process(const double * buf,int buflen)455 int view_rtty::rx_process(const double *buf, int buflen)
456 {
457 cmplx z, zmark, zspace, *zp_mark, *zp_space;
458 static bool bit = true;
459 int n = 0;
460
461 {
462 reverse = wf->Reverse() ^ !wf->USB();
463 }
464
465 rtty_squelch = pow(10, progStatus.VIEWER_rttysquelch / 10.0);
466
467 for (int ch = 0; ch < progdefaults.VIEWERchannels; ch++) {
468 if (channel[ch].state == IDLE)
469 continue;
470 if (channel[ch].sigsearch) {
471 channel[ch].sigsearch--;
472 if (!channel[ch].sigsearch)
473 channel[ch].state = RCVNG;
474 }
475
476 for (int len = 0; len < buflen; len++) {
477 z = cmplx(buf[len], buf[len]);
478
479 zmark = mixer(channel[ch].mark_phase, channel[ch].frequency + shift/2.0, z);
480 channel[ch].mark_filt->run(zmark, &zp_mark);
481
482 zspace = mixer(channel[ch].space_phase, channel[ch].frequency - shift/2.0, z);
483 n = channel[ch].space_filt->run(zspace, &zp_space);
484
485 // n loop
486 if (n) Metric(ch);
487
488 for (int i = 0; i < n; i++) {
489
490 channel[ch].mark_mag = abs(zp_mark[i]);
491 channel[ch].mark_env = decayavg (channel[ch].mark_env, channel[ch].mark_mag,
492 (channel[ch].mark_mag > channel[ch].mark_env) ? symbollen / 4 : symbollen * 16);
493 channel[ch].mark_noise = decayavg (channel[ch].mark_noise, channel[ch].mark_mag,
494 (channel[ch].mark_mag < channel[ch].mark_noise) ? symbollen / 4 : symbollen * 48);
495 channel[ch].space_mag = abs(zp_space[i]);
496 channel[ch].space_env = decayavg (channel[ch].space_env, channel[ch].space_mag,
497 (channel[ch].space_mag > channel[ch].space_env) ? symbollen / 4 : symbollen * 16);
498 channel[ch].space_noise = decayavg (channel[ch].space_noise, channel[ch].space_mag,
499 (channel[ch].space_mag < channel[ch].space_noise) ? symbollen / 4 : symbollen * 48);
500
501 channel[ch].noise_floor = min(channel[ch].space_noise, channel[ch].mark_noise);
502
503 // clipped if clipped decoder selected
504 double mclipped = 0, sclipped = 0;
505 mclipped = channel[ch].mark_mag > channel[ch].mark_env ?
506 channel[ch].mark_env : channel[ch].mark_mag;
507 sclipped = channel[ch].space_mag > channel[ch].space_env ?
508 channel[ch].space_env : channel[ch].space_mag;
509 if (mclipped < channel[ch].noise_floor) mclipped = channel[ch].noise_floor;
510 if (sclipped < channel[ch].noise_floor) sclipped = channel[ch].noise_floor;
511
512 // Optimal ATC
513 // int v = (((mclipped - channel[ch].noise_floor) * (channel[ch].mark_env - channel[ch].noise_floor) -
514 // (sclipped - channel[ch].noise_floor) * (channel[ch].space_env - channel[ch].noise_floor)) -
515 // 0.25 * ((channel[ch].mark_env - channel[ch].noise_floor) *
516 // (channel[ch].mark_env - channel[ch].noise_floor) -
517 // (channel[ch].space_env - channel[ch].noise_floor) *
518 // (channel[ch].space_env - channel[ch].noise_floor)));
519 // bit = (v > 0);
520 // Kahn Square Law demodulator
521 bit = norm(zp_mark[i]) >= norm(zp_space[i]);
522
523 channel[ch].mark_history[channel[ch].inp_ptr] = zp_mark[i];
524 channel[ch].space_history[channel[ch].inp_ptr] = zp_space[i];
525 channel[ch].inp_ptr = (channel[ch].inp_ptr + 1) % VIEW_MAXPIPE;
526
527 if (channel[ch].state == RCVNG && rx( ch, reverse ? !bit : bit ) ) {
528 if (channel[ch].sigsearch) channel[ch].sigsearch--;
529 int mp0 = channel[ch].inp_ptr - 2;
530 int mp1 = mp0 + 1;
531 if (mp0 < 0) mp0 += VIEW_MAXPIPE;
532 if (mp1 < 0) mp1 += VIEW_MAXPIPE;
533 double ferr = (TWOPI * samplerate / rtty_baud) *
534 (!reverse ?
535 arg(conj(channel[ch].mark_history[mp1]) * channel[ch].mark_history[mp0]) :
536 arg(conj(channel[ch].space_history[mp1]) * channel[ch].space_history[mp0]));
537 if (fabs(ferr) > rtty_baud / 2) ferr = 0;
538 channel[ch].freqerr = decayavg ( channel[ch].freqerr, ferr / 4,
539 progdefaults.rtty_afcspeed == 0 ? 8 :
540 progdefaults.rtty_afcspeed == 1 ? 4 : 1 );
541 if (channel[ch].metric > pow(10, progStatus.VIEWER_rttysquelch / 10.0))
542 channel[ch].frequency -= ferr;
543 }
544 }
545 }
546 }
547
548 find_signals();
549
550 return 0;
551 }
552
baudot_dec(int ch,unsigned char data)553 char view_rtty::baudot_dec(int ch, unsigned char data)
554 {
555 int out = 0;
556
557 switch (data) {
558 case 0x1F: /* letters */
559 channel[ch].rxmode = LETTERS;
560 break;
561 case 0x1B: /* figures */
562 channel[ch].rxmode = FIGURES;
563 break;
564 case 0x04: /* unshift-on-space */
565 if (progdefaults.UOSrx)
566 channel[ch].rxmode = LETTERS;
567 return ' ';
568 break;
569 default:
570 if (channel[ch].rxmode == LETTERS)
571 out = letters[data];
572 else
573 out = figures[data];
574 break;
575 }
576
577 return out;
578 }
579
580 //=====================================================================
581 // RTTY transmit
582 //=====================================================================
583
tx_process()584 int view_rtty::tx_process()
585 {
586 return 0;
587 }
588
589