1 // ----------------------------------------------------------------------------
2 // rtty.cxx -- RTTY modem
3 //
4 // Copyright (C) 2012
5 // Dave Freese, W1HKJ
6 // Stefan Fendt, DL1SMF
7 //
8 // This file is part of fldigi.
9 //
10 // This code bears some resemblance to code contained in gmfsk from which
11 // it originated. Much has been changed, but credit should still be
12 // given to Tomi Manninen (oh2bns@sral.fi), who so graciously distributed
13 // his gmfsk modem under the GPL.
14 //
15 // Fldigi is free software: you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation, either version 3 of the License, or
18 // (at your option) any later version.
19 //
20 // Fldigi is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
27 // ----------------------------------------------------------------------------
28
29 #include <config.h>
30 #include <iostream>
31 #include <fstream>
32
33 using namespace std;
34
35 //#include "rtty.h"
36 #include "view_rtty.h"
37 #include "fl_digi.h"
38 #include "digiscope.h"
39 #include "misc.h"
40 #include "waterfall.h"
41 #include "confdialog.h"
42 #include "configuration.h"
43 #include "status.h"
44 #include "digiscope.h"
45 #include "trx.h"
46 #include "debug.h"
47 #include "synop.h"
48 #include "main.h"
49 #include "modem.h"
50
51 #include "threads.h"
52
53 #include "rtty.h"
54
55 #define FILTER_DEBUG 0
56
57 #define SHAPER_BAUD 150
58
59 //void start_fsk_thread();
60 //void stop_fsk_thread();
61
62 //=====================================================================
63 // Baudot support
64 //=====================================================================
65
66 static char letters[32] = {
67 '\0', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
68 '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
69 'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
70 'O', 'B', 'G', ' ', 'M', 'X', 'V', ' '
71 };
72
73 /*
74 * U.S. version of the figures case.
75 */
76 static char figures[32] = {
77 '\0', '3', '\n', '-', ' ', '\a', '8', '7',
78 '\r', '$', '4', '\'', ',', '!', ':', '(',
79 '5', '"', ')', '2', '#', '6', '0', '1',
80 '9', '?', '&', ' ', '.', '/', ';', ' '
81 };
82
83 int dspcnt = 0;
84
85 static char msg1[20];
86
87 const double rtty::SHIFT[] = {23, 85, 160, 170, 182, 200, 240, 350, 425, 850};
88 // FILTLEN must be same size as BAUD
89 const double rtty::BAUD[] = {45, 45.45, 50, 56, 75, 100, 110, 150, 200, 300};
90 const int rtty::FILTLEN[] = { 512, 512, 512, 512, 512, 512, 512, 256, 128, 64};
91 const int rtty::BITS[] = {5, 7, 8};
92 const int rtty::numshifts = (int)(sizeof(SHIFT) / sizeof(*SHIFT));
93 const int rtty::numbauds = (int)(sizeof(BAUD) / sizeof(*BAUD));
94
tx_init()95 void rtty::tx_init()
96 {
97 phaseacc = 0;
98 preamble = true;
99 videoText();
100
101 symbols = 0;
102 acc_symbols = 0;
103 ovhd_symbols = 0;
104 }
105
106 // Customizes output of Synop decoded data.
107 struct rtty_callback : public synop_callback {
108 // Callback for writing decoded synop messages.
printrtty_callback109 void print(const char * str, size_t nb, bool bold ) const {
110 // Could choose: FTextBase::CTRL,XMIT,RECV
111 int style = bold ? FTextBase::XMIT : FTextBase::RECV;
112 for( size_t i = 0; i < nb; ++i ) {
113 unsigned char c = str[i];
114 put_rx_char(progdefaults.rx_lowercase ? tolower(c) : c, style );
115 }
116 }
117 // Should we log new Synop messages to the current Adif log file ?
log_adifrtty_callback118 bool log_adif(void) const { return progdefaults.SynopAdifDecoding ;}
119 // Should we log new Synop messages to KML file ?
log_kmlrtty_callback120 bool log_kml(void) const { return progdefaults.SynopKmlDecoding ;}
121
interleavedrtty_callback122 bool interleaved(void) const { return progdefaults.SynopInterleaved ;}
123 };
124
rx_init()125 void rtty::rx_init()
126 {
127 rxstate = RTTY_RX_STATE_IDLE;
128 rxmode = LETTERS;
129 phaseacc = 0;
130 FSKphaseacc = 0;
131
132 for (int i = 0; i < MAXBITS; i++ ) bit_buf[i] = 0.0;
133
134 mark_phase = 0;
135 space_phase = 0;
136 xy_phase = 0.0;
137
138 mark_mag = 0;
139 space_mag = 0;
140 mark_env = 0;
141 space_env = 0;
142
143 inp_ptr = 0;
144
145 lastchar = 0;
146
147 // Synop file is reloaded each time we enter this modem. Ideally do that when the file is changed.
148 static bool wmo_loaded = false ;
149 if( wmo_loaded == false ) {
150 wmo_loaded = true ;
151 SynopDB::Init(PKGDATADIR);
152 }
153 /// Used by weather reports decoding.
154 synop::setup<rtty_callback>();
155 synop::instance()->init();
156 }
157
init()158 void rtty::init()
159 {
160 bool wfrev = wf->Reverse();
161 bool wfsb = wf->USB();
162 // Probably not necessary because similar to modem::set_reverse
163 reverse = wfrev ^ !wfsb;
164 stopflag = false;
165
166 if (progdefaults.StartAtSweetSpot)
167 set_freq(progdefaults.RTTYsweetspot);
168 else if (progStatus.carrier != 0) {
169 set_freq(progStatus.carrier);
170 #if !BENCHMARK_MODE
171 progStatus.carrier = 0;
172 #endif
173 } else
174 set_freq(wf->Carrier());
175
176 rx_init();
177 put_MODEstatus(mode);
178 if ((rtty_baud - (int)rtty_baud) == 0)
179 snprintf(msg1, sizeof(msg1), "%-3.0f/%-4.0f", rtty_baud, rtty_shift);
180 else
181 snprintf(msg1, sizeof(msg1), "%-4.2f/%-4.0f", rtty_baud, rtty_shift);
182 put_Status1(msg1);
183 if (progdefaults.PreferXhairScope)
184 set_scope_mode(Digiscope::XHAIRS);
185 else
186 set_scope_mode(Digiscope::RTTY);
187 for (int i = 0; i < MAXPIPE; i++) mark_history[i] = space_history[i] = cmplx(0,0);
188
189 lastchar = 0;
190 }
191
~rtty()192 rtty::~rtty()
193 {
194 if (rttyviewer) delete rttyviewer;
195
196 if (mark_filt) delete mark_filt;
197 if (space_filt) delete space_filt;
198 if (pipe) delete [] pipe;
199 if (dsppipe) delete [] dsppipe;
200 if (bits) delete bits;
201 delete m_Osc1;
202 delete m_Osc2;
203 delete m_SymShaper1;
204 delete m_SymShaper2;
205
206 // EXPERIMENTAL FSK DTR/RTS
207 // stop_fsk_thread();
208 }
209
reset_filters()210 void rtty::reset_filters()
211 {
212 delete mark_filt;
213 mark_filt = new fftfilt(rtty_baud/samplerate, filter_length);
214 mark_filt->rtty_filter(rtty_baud/samplerate);
215 delete space_filt;
216 space_filt = new fftfilt(rtty_baud/samplerate, filter_length);
217 space_filt->rtty_filter(rtty_baud/samplerate);
218 }
219
restart()220 void rtty::restart()
221 {
222 double stl;
223
224 rtty_shift = shift = (progdefaults.rtty_shift < numshifts ?
225 SHIFT[progdefaults.rtty_shift] : progdefaults.rtty_custom_shift);
226 if (progdefaults.rtty_baud > numbauds - 1) progdefaults.rtty_baud = numbauds - 1;
227 rtty_baud = BAUD[progdefaults.rtty_baud];
228 filter_length = FILTLEN[progdefaults.rtty_baud];
229
230 nbits = rtty_bits = BITS[progdefaults.rtty_bits];
231 if (rtty_bits == 5)
232 rtty_parity = RTTY_PARITY_NONE;
233 else
234 switch (progdefaults.rtty_parity) {
235 case 0 : rtty_parity = RTTY_PARITY_NONE; break;
236 case 1 : rtty_parity = RTTY_PARITY_EVEN; break;
237 case 2 : rtty_parity = RTTY_PARITY_ODD; break;
238 case 3 : rtty_parity = RTTY_PARITY_ZERO; break;
239 case 4 : rtty_parity = RTTY_PARITY_ONE; break;
240 default : rtty_parity = RTTY_PARITY_NONE; break;
241 }
242 // (exists below already) rtty_stop = progdefaults.rtty_stop;
243
244 txmode = LETTERS;
245 rxmode = LETTERS;
246 symbollen = (int) (samplerate / rtty_baud + 0.5);
247 // part of an EXPERIMENTAL FSK implementation
248 // bitlen = 1000.0 / rtty_baud; // bit length in milliseconds
249 set_bandwidth(shift);
250
251 rtty_BW = progdefaults.RTTY_BW = rtty_baud * 2;
252
253 wf->redraw_marker();
254
255 reset_filters();
256
257 if (bits)
258 bits->setLength(symbollen / 8);//2);
259 else
260 bits = new Cmovavg(symbollen / 8);//2);
261 mark_noise = space_noise = 0;
262 bit = nubit = true;
263
264 // stop length = 1, 1.5 or 2 bits
265 rtty_stop = progdefaults.rtty_stop;
266 if (rtty_stop == 0) stl = 1.0;
267 else if (rtty_stop == 1) stl = 1.5;
268 else stl = 2.0;
269 stoplen = (int) (stl * samplerate / rtty_baud + 0.5);
270 freqerr = 0.0;
271 pipeptr = 0;
272
273 for (int i = 0; i < MAXBITS; i++ ) bit_buf[i] = 0.0;
274
275 metric = 0.0;
276
277 if ((rtty_baud - (int)rtty_baud) == 0)
278 snprintf(msg1, sizeof(msg1), "%-3.0f/%-4.0f", rtty_baud, rtty_shift);
279 else
280 snprintf(msg1, sizeof(msg1), "%-4.2f/%-4.0f", rtty_baud, rtty_shift);
281 put_Status1(msg1);
282 put_MODEstatus(mode);
283 for (int i = 0; i < MAXPIPE; i++)
284 QI[i] = cmplx(0.0, 0.0);
285 sigpwr = 0.0;
286 noisepwr = 0.0;
287 sigsearch = 0;
288 dspcnt = 2*(nbits + 2);
289
290 clear_zdata = true;
291
292 // restart symbol-rtty_shaper
293 m_SymShaper1->Preset(rtty_baud, samplerate);
294 m_SymShaper2->Preset(rtty_baud, samplerate);
295
296 mark_phase = 0;
297 space_phase = 0;
298 xy_phase = 0.0;
299
300 mark_mag = 0;
301 space_mag = 0;
302 mark_env = 0;
303 space_env = 0;
304
305 inp_ptr = 0;
306
307 for (int i = 0; i < MAXPIPE; i++) mark_history[i] = space_history[i] = cmplx(0,0);
308
309 // if (::rttyviewer) ::rttyviewer->restart();
310 if (rttyviewer) rttyviewer->restart();
311
312 progStatus.rtty_filter_changed = false;
313
314 }
315
rtty(trx_mode tty_mode)316 rtty::rtty(trx_mode tty_mode)
317 {
318 cap |= CAP_AFC | CAP_REV;
319
320 mode = tty_mode;
321
322 samplerate = RTTY_SampleRate;
323
324 mark_filt = (fftfilt *)0;
325 space_filt = (fftfilt *)0;
326
327 bits = (Cmovavg *)0;
328
329 pipe = new double[MAXPIPE];
330 dsppipe = new double [MAXPIPE];
331
332 // if (::rttyviewer == 0) ::rttyviewer = new view_rtty(mode);
333
334 rttyviewer = new view_rtty(mode);
335
336 m_Osc1 = new Oscillator( samplerate );
337 m_Osc2 = new Oscillator( samplerate );
338
339 m_SymShaper1 = new SymbolShaper( 45, samplerate );
340 m_SymShaper2 = new SymbolShaper( 45, samplerate );
341
342 restart();
343
344 // EXPERIMENT FSK on DTR/RTS
345 // start_fsk_thread();
346 }
347
Update_syncscope()348 void rtty::Update_syncscope()
349 {
350 int j;
351 for (int i = 0; i < symbollen; i++) {
352 j = pipeptr - i;
353 if (j < 0) j += symbollen;
354 dsppipe[i] = pipe[j];
355 }
356 set_scope(dsppipe, symbollen, false);
357 }
358
Clear_syncscope()359 void rtty::Clear_syncscope()
360 {
361 set_scope(0, 0, false);
362 }
363
mixer(double & phase,double f,cmplx in)364 cmplx rtty::mixer(double &phase, double f, cmplx in)
365 {
366 cmplx z = cmplx( cos(phase), sin(phase)) * in;
367
368 phase -= TWOPI * f / samplerate;
369 if (phase < -TWOPI) phase += TWOPI;
370
371 return z;
372 }
373
Bit_reverse(unsigned char in,int n)374 unsigned char rtty::Bit_reverse(unsigned char in, int n)
375 {
376 unsigned char out = 0;
377
378 for (int i = 0; i < n; i++)
379 out = (out << 1) | ((in >> i) & 1);
380
381 return out;
382 }
383
rparity(int c)384 static int rparity(int c)
385 {
386 int w = c;
387 int p = 0;
388 while (w) {
389 p += (w & 1);
390 w >>= 1;
391 }
392 return p & 1;
393 }
394
rttyparity(unsigned int c,int nbits)395 int rttyparity(unsigned int c, int nbits)
396 {
397 c &= (1 << nbits) - 1;
398
399 switch (progdefaults.rtty_parity) {
400 default:
401 case rtty::RTTY_PARITY_NONE:
402 return 0;
403
404 case rtty::RTTY_PARITY_ODD:
405 return rparity(c);
406
407 case rtty::RTTY_PARITY_EVEN:
408 return !rparity(c);
409
410 case rtty::RTTY_PARITY_ZERO:
411 return 0;
412
413 case rtty::RTTY_PARITY_ONE:
414 return 1;
415 }
416 }
417
decode_char()418 int rtty::decode_char()
419 {
420 unsigned int parbit, par, data;
421
422 parbit = (rxdata >> nbits) & 1;
423 par = rttyparity(rxdata, nbits);
424
425 if (rtty_parity != RTTY_PARITY_NONE && parbit != par)
426 return 0;
427
428 data = rxdata & ((1 << nbits) - 1);
429
430 if (nbits == 5)
431 return baudot_dec(data);
432
433 return data;
434 }
435
is_mark_space(int & correction)436 bool rtty::is_mark_space( int &correction)
437 {
438 correction = 0;
439 // test for rough bit position
440 if (bit_buf[0] && !bit_buf[symbollen-1]) {
441 // test for mark/space straddle point
442 for (int i = 0; i < symbollen; i++)
443 correction += bit_buf[i];
444 if (abs(symbollen/2 - correction) < 6) // too small & bad signals are not decoded
445 return true;
446 }
447 return false;
448 }
449
is_mark()450 bool rtty::is_mark()
451 {
452 return bit_buf[symbollen / 2];
453 }
454
rx(bool bit)455 bool rtty::rx(bool bit) // original modified for probability test
456 {
457 bool flag = false;
458 unsigned char c = 0;
459 int correction;
460
461 for (int i = 1; i < symbollen; i++) bit_buf[i-1] = bit_buf[i];
462 bit_buf[symbollen - 1] = bit;
463
464 switch (rxstate) {
465 case RTTY_RX_STATE_IDLE:
466 if ( is_mark_space(correction)) {
467 rxstate = RTTY_RX_STATE_START;
468 counter = correction;
469 }
470 break;
471 case RTTY_RX_STATE_START:
472 if (--counter == 0) {
473 if (!is_mark()) {
474 rxstate = RTTY_RX_STATE_DATA;
475 counter = symbollen;
476 bitcntr = 0;
477 rxdata = 0;
478 } else {
479 rxstate = RTTY_RX_STATE_IDLE;
480 }
481 }
482 break;
483 case RTTY_RX_STATE_DATA:
484 if (--counter == 0) {
485 rxdata |= is_mark() << bitcntr++;
486 counter = symbollen;
487 }
488 if (bitcntr == nbits + (rtty_parity != RTTY_PARITY_NONE ? 1 : 0))
489 rxstate = RTTY_RX_STATE_STOP;
490 break;
491 case RTTY_RX_STATE_STOP:
492 if (--counter == 0) {
493 if (is_mark()) {
494 if ((metric >= progStatus.sldrSquelchValue && progStatus.sqlonoff) || !progStatus.sqlonoff) {
495 c = decode_char();
496 if( progdefaults.SynopAdifDecoding || progdefaults.SynopKmlDecoding ) {
497 if (c != 0 && c != '\r') {
498 synop::instance()->add(c);
499 } else {
500 if( synop::instance()->enabled() )
501 synop::instance()->flush(false);
502 put_rx_char(c);
503 }
504 } else if ( c != 0 ) {
505 // supress <CR><CR> and <LF><LF> sequences
506 // these were observed during the RTTY contest 2/9/2013
507 if (c == '\r' && lastchar == '\r');
508 else if (c == '\n' && lastchar == '\n');
509 else
510 put_rx_char(progdefaults.rx_lowercase ? tolower(c) : c);
511 lastchar = c;
512 }
513 flag = true;
514 }
515 }
516 rxstate = RTTY_RX_STATE_IDLE;
517 }
518 break;
519 default : break;
520 }
521
522 return flag;
523 }
524
525 char snrmsg[80];
Metric()526 void rtty::Metric()
527 {
528 double delta = rtty_baud/8.0;
529 double np = wf->powerDensity(frequency, delta) * 3000 / delta;
530 double sp =
531 wf->powerDensity(frequency - shift/2, delta) +
532 wf->powerDensity(frequency + shift/2, delta) + 1e-10;
533 double snr = 0;
534
535 sigpwr = decayavg( sigpwr, sp, sp > sigpwr ? 2 : 8);
536 noisepwr = decayavg( noisepwr, np, 16 );
537 snr = 10*log10(sigpwr / noisepwr);
538
539 snprintf(snrmsg, sizeof(snrmsg), "s/n %-3.0f dB", snr);
540 put_Status2(snrmsg);
541 metric = CLAMP((3000 / delta) * (sigpwr/noisepwr), 0.0, 100.0);
542 display_metric(metric);
543 }
544
searchDown()545 void rtty::searchDown()
546 {
547 double srchfreq = frequency - shift -100;
548 double minfreq = shift * 2 + 100;
549 double spwrlo, spwrhi, npwr;
550 while (srchfreq > minfreq) {
551 spwrlo = wf->powerDensity(srchfreq - shift/2, 2*rtty_baud);
552 spwrhi = wf->powerDensity(srchfreq + shift/2, 2*rtty_baud);
553 npwr = wf->powerDensity(srchfreq + shift, 2*rtty_baud) + 1e-10;
554 if ((spwrlo / npwr > 10.0) && (spwrhi / npwr > 10.0)) {
555 frequency = srchfreq;
556 set_freq(frequency);
557 sigsearch = SIGSEARCH;
558 break;
559 }
560 srchfreq -= 5.0;
561 }
562 }
563
searchUp()564 void rtty::searchUp()
565 {
566 double srchfreq = frequency + shift +100;
567 double maxfreq = IMAGE_WIDTH - shift * 2 - 100;
568 double spwrhi, spwrlo, npwr;
569 while (srchfreq < maxfreq) {
570 spwrlo = wf->powerDensity(srchfreq - shift/2, 2*rtty_baud);
571 spwrhi = wf->powerDensity(srchfreq + shift/2, 2*rtty_baud);
572 npwr = wf->powerDensity(srchfreq - shift, 2*rtty_baud) + 1e-10;
573 if ((spwrlo / npwr > 10.0) && (spwrhi / npwr > 10.0)) {
574 frequency = srchfreq;
575 set_freq(frequency);
576 sigsearch = SIGSEARCH;
577 break;
578 }
579 srchfreq += 5.0;
580 }
581 }
582
583 #if FILTER_DEBUG == 1
584 int snum = 0;
585 int mnum = 0;
586 #define ook(sp) \
587 { \
588 value = sin(2.0*M_PI*( \
589 (((sp / symbollen) % 2 == 0) ? (frequency + shift/2.0) : (frequency - shift/2.0))\
590 /samplerate)*sp); \
591 }
592
593 std::fstream ook_signal("ook_signal.csv", std::ios::out );
594 #endif
595
rx_process(const double * buf,int len)596 int rtty::rx_process(const double *buf, int len)
597 {
598 const double *buffer = buf;
599 int length = len;
600 static int showxy = symbollen;
601
602 cmplx z, zmark, zspace, *zp_mark, *zp_space;
603
604 int n_out = 0;
605 static int bitcount = 5 * nbits * symbollen;
606
607 if ( !progdefaults.report_when_visible ||
608 dlgViewer->visible() || progStatus.show_channels )
609 if (!bHistory && rttyviewer) rttyviewer->rx_process(buf, len);
610
611 if (progStatus.rtty_filter_changed) {
612 progStatus.rtty_filter_changed = false;
613 reset_filters();
614 }
615 {
616 reverse = wf->Reverse() ^ !wf->USB();
617 }
618
619 Metric();
620 #if FILTER_DEBUG == 1
621 double value;
622 #endif
623 while (length-- > 0) {
624
625 // Create analytic signal from sound card input samples
626
627 #if FILTER_DEBUG == 1
628 if (snum < 2 * filter_length) {
629 frequency = 1000.0;
630 ook(snum);
631 z = cmplx(value, value);
632 ook_signal << snum << "," << z.real() << ",";
633 // snum++;
634 } else {
635 z = cmplx(*buffer, *buffer);
636 }
637 #else
638 z = cmplx(*buffer, *buffer);
639 #endif
640 buffer++;
641
642 // Mix it with the audio carrier frequency to create two baseband signals
643 // mark and space are separated and processed independently
644 // lowpass Windowed Sinc - Overlap-Add convolution filters.
645 // The two fftfilt's are the same size and processed in sync
646 // therefore the mark and space filters will concurrently have the
647 // same size outputs available for further processing
648
649 zmark = mixer(mark_phase, frequency + shift/2.0, z);
650 mark_filt->run(zmark, &zp_mark);
651
652 zspace = mixer(space_phase, frequency - shift/2.0, z);
653 n_out = space_filt->run(zspace, &zp_space);
654 #if FILTER_DEBUG == 1
655 if (snum < 2 * filter_length) {
656 ook_signal << abs(zmark) <<"\n";
657 snum++;
658 }
659 #endif
660 for (int i = 0; i < n_out; i++) {
661
662 mark_mag = abs(zp_mark[i]);
663 mark_env = decayavg (mark_env, mark_mag,
664 (mark_mag > mark_env) ? symbollen / 4 : symbollen * 16);
665 mark_noise = decayavg (mark_noise, mark_mag,
666 (mark_mag < mark_noise) ? symbollen / 4 : symbollen * 48);
667 space_mag = abs(zp_space[i]);
668 space_env = decayavg (space_env, space_mag,
669 (space_mag > space_env) ? symbollen / 4 : symbollen * 16);
670 space_noise = decayavg (space_noise, space_mag,
671 (space_mag < space_noise) ? symbollen / 4 : symbollen * 48);
672 #if FILTER_DEBUG == 1
673 if (mnum < 2 * filter_length)
674 ook_signal << ",,," << mnum++ + filter_length / 2 << "," << mark_mag << "," << space_mag << "\n";
675 #endif
676 noise_floor = min(space_noise, mark_noise);
677
678 // clipped if clipped decoder selected
679 double mclipped = 0, sclipped = 0;
680 mclipped = mark_mag > mark_env ? mark_env : mark_mag;
681 sclipped = space_mag > space_env ? space_env : space_mag;
682 if (mclipped < noise_floor) mclipped = noise_floor;
683 if (sclipped < noise_floor) sclipped = noise_floor;
684
685 switch (progdefaults.rtty_cwi) {
686 case 1 : // mark only decode
687 space_env = sclipped = noise_floor;
688 break;
689 case 2: // space only decode
690 mark_env = mclipped = noise_floor;
691 default : ;
692 }
693
694 // double v0, v1, v2, v3, v4, v5;
695 double v3;
696
697 // no ATC
698 // v0 = mark_mag - space_mag;
699 // Linear ATC
700 // v1 = mark_mag - space_mag - 0.5 * (mark_env - space_env);
701 // Clipped ATC
702 // v2 = (mclipped - noise_floor) - (sclipped - noise_floor) - 0.5 * (
703 // (mark_env - noise_floor) - (space_env - noise_floor));
704 // Optimal ATC
705 v3 = (mclipped - noise_floor) * (mark_env - noise_floor) -
706 (sclipped - noise_floor) * (space_env - noise_floor) - 0.25 * (
707 (mark_env - noise_floor) * (mark_env - noise_floor) -
708 (space_env - noise_floor) * (space_env - noise_floor));
709 // Kahn Squarer with Linear ATC
710 // v4 = (mark_mag - noise_floor) * (mark_mag - noise_floor) -
711 // (space_mag - noise_floor) * (space_mag - noise_floor) - 0.25 * (
712 // (mark_env - noise_floor) * (mark_env - noise_floor) -
713 // (space_env - noise_floor) * (space_env - noise_floor));
714 // Kahn Squarer with Clipped ATC
715 // v5 = (mclipped - noise_floor) * (mclipped - noise_floor) -
716 // (sclipped - noise_floor) * (sclipped - noise_floor) - 0.25 * (
717 // (mark_env - noise_floor) * (mark_env - noise_floor) -
718 // (space_env - noise_floor) * (space_env - noise_floor));
719 // switch (progdefaults.rtty_demodulator) {
720 // switch (2) { // Optimal ATC
721 // case 0: // linear ATC
722 // bit = v1 > 0;
723 // break;
724 // case 1: // clipped ATC
725 // bit = v2 > 0;
726 // break;
727 // case 2: // optimal ATC
728 bit = v3 > 0;
729 // break;
730 // case 3: // Kahn linear ATC
731 // bit = v4 > 0;
732 // break;
733 // case 4: // Kahn clipped
734 // bit = v5 > 0;
735 // break;
736 // case 5: // No ATC
737 // default :
738 // bit = v0 > 0;
739 // }
740
741 // XY scope signal generation
742
743 if (progdefaults.true_scope) {
744 //----------------------------------------------------------------------
745 // "true" scope implementation------------------------------------------
746 //----------------------------------------------------------------------
747
748 // get the baseband-signal and...
749 xy = cmplx(
750 zp_mark[i].real() * cos(xy_phase) + zp_mark[i].imag() * sin(xy_phase),
751 zp_space[i].real() * cos(xy_phase) + zp_space[i].imag() * sin(xy_phase) );
752
753 // if mark-tone has a higher magnitude than the space-tone,
754 // further reduce the scope's space-amplitude and vice versa
755 // this makes the scope looking a little bit nicer, too...
756 // aka: less noisy...
757 if( abs(zp_mark[i]) > abs(zp_space[i]) ) {
758 // note ox x complex lib does not support xy.real(double) or xy.imag(double)
759 xy = cmplx( xy.real(),
760 xy.imag() * abs(zp_space[i])/abs(zp_mark[i]) );
761 // xy.imag() *= abs(zp_space[i])/abs(zp_mark[i]);
762 } else {
763 xy = cmplx( xy.real() / ( abs(zp_space[i])/abs(zp_mark[i]) ),
764 xy.imag() );
765 // xy.real() /= abs(zp_space[i])/abs(zp_mark[i]);
766 }
767
768 // now normalize the scope
769 double const norm = 1.3*(abs(zp_mark [i]) + abs(zp_space[i]));
770 xy /= norm;
771
772 } else {
773 //----------------------------------------------------------------------
774 // "ortho" scope implementation-----------------------------------------
775 //----------------------------------------------------------------------
776 // get magnitude of the baseband-signal
777 if (bit)
778 xy = cmplx( mark_mag * cos(xy_phase), space_noise * sin(xy_phase) / 2.0);
779 else
780 xy = cmplx( mark_noise * cos(xy_phase) / 2.0, space_mag * sin(xy_phase));
781 // now normalize the scope
782 double const norm = (mark_env + space_env);
783 xy /= norm;
784 }
785
786 // Rotate the scope x-y iaw frequency error. Old scopes were not capable
787 // of this, but it should be very handy, so... who cares of realism anyways?
788 double const rotate = 8 * TWOPI * freqerr / rtty_shift;
789 xy = xy * cmplx(cos(rotate), sin(rotate));
790
791 QI[inp_ptr] = xy;
792
793 // shift it to 128Hz(!) and not to it's original position.
794 // this makes it more pretty and does not remove it's other
795 // qualities. Reason is that this is a fraction of the used
796 // block-size.
797 xy_phase += (TWOPI * (128.0 / samplerate));
798 // end XY signal generation
799
800 mark_history[inp_ptr] = zp_mark[i];
801 space_history[inp_ptr] = zp_space[i];
802
803 inp_ptr = (inp_ptr + 1) % MAXPIPE;
804
805 if (dspcnt && (--dspcnt % (nbits + 2) == 0)) {
806 pipe[pipeptr] = bit - 0.5; //testbit - 0.5;
807 pipeptr = (pipeptr + 1) % symbollen;
808 }
809
810 // detect TTY signal transitions
811 // rx(...) returns true if valid TTY bit stream detected
812 // either character or idle signal
813 if ( rx( reverse ? !bit : bit ) ) {
814 dspcnt = symbollen * (nbits + 2);
815 if (!bHighSpeed) Update_syncscope();
816 clear_zdata = true;
817 bitcount = 5 * nbits * symbollen;
818 if (sigsearch) sigsearch--;
819 int mp0 = inp_ptr - 2;
820 int mp1 = mp0 + 1;
821 if (mp0 < 0) mp0 += MAXPIPE;
822 if (mp1 < 0) mp1 += MAXPIPE;
823 double ferr = (TWOPI * samplerate / rtty_baud) *
824 (!reverse ?
825 arg(conj(mark_history[mp1]) * mark_history[mp0]) :
826 arg(conj(space_history[mp1]) * space_history[mp0]));
827 if (fabs(ferr) > rtty_baud / 2) ferr = 0;
828 freqerr = decayavg ( freqerr, ferr / 8,
829 progdefaults.rtty_afcspeed == 0 ? 8 :
830 progdefaults.rtty_afcspeed == 1 ? 4 : 1 );
831 if (progStatus.afconoff &&
832 (metric > progStatus.sldrSquelchValue || !progStatus.sqlonoff))
833 set_freq(frequency - freqerr);
834 } else
835 if (bitcount) --bitcount;
836 }
837 if (!bHighSpeed) {
838 if (!bitcount) {
839 if (clear_zdata) {
840 clear_zdata = false;
841 Clear_syncscope();
842 for (int i = 0; i < MAXPIPE; i++)
843 QI[i] = cmplx(0.0, 0.0);
844 }
845 }
846 if (!--showxy) {
847 set_zdata(QI, MAXPIPE);
848 showxy = symbollen;
849 }
850 }
851 }
852 return 0;
853 }
854
855 //=====================================================================
856 // RTTY transmit
857 //=====================================================================
858 //double freq1;
859 double maxamp = 0;
860
nco(double freq)861 double rtty::nco(double freq)
862 {
863 phaseacc += TWOPI * freq / samplerate;
864
865 if (phaseacc > TWOPI) phaseacc -= TWOPI;
866
867 return cos(phaseacc);
868 }
869
FSKnco()870 double rtty::FSKnco()
871 {
872 FSKphaseacc += TWOPI * 1000 / samplerate;
873
874 if (FSKphaseacc > TWOPI) FSKphaseacc -= TWOPI;
875
876 return sin(FSKphaseacc);
877
878 }
879
880 extern Cserial CW_KEYLINE_serial;
881 extern bool CW_KEYLINE_isopen;
882
send_symbol(int symbol,int len)883 void rtty::send_symbol(int symbol, int len)
884 {
885 if (reverse) symbol = !symbol;
886
887 acc_symbols += len;
888
889 if (!progStatus.shaped_rtty) {
890 double freq;
891
892 if (symbol)
893 freq = get_txfreq_woffset() + shift / 2.0;
894 else
895 freq = get_txfreq_woffset() - shift / 2.0;
896
897 for (int i = 0; i < len; i++) {
898 outbuf[i] = nco(freq);
899 if (symbol)
900 FSKbuf[i] = FSKnco();
901 else
902 FSKbuf[i] = 0.0;
903 }
904 } else {
905 double const freq1 = get_txfreq_woffset() + shift / 2.0;
906 double const freq2 = get_txfreq_woffset() - shift / 2.0;
907 double mark = 0, space = 0;
908 double signal = 0;
909
910 if (maxamp == 0) {
911 int sym = 0;
912 for (int j = 0; j < 100; j++) {
913 if (sym) sym = 0;
914 else sym = 1;
915 for( int i = 0; i < 3*len; ++i ) {
916 mark = m_SymShaper1->Update( sym) * m_Osc1->Update( freq1 );
917 space = m_SymShaper2->Update(!sym) * m_Osc2->Update( freq2 );
918 signal = mark + space;
919
920 if (maxamp < fabs(signal)) maxamp = fabs(signal);
921 }
922 }
923 }
924
925 for( int i = 0; i < len; ++i ) {
926 mark = m_SymShaper1->Update( symbol) * m_Osc1->Update( freq1 );
927 space = m_SymShaper2->Update(!symbol) * m_Osc2->Update( freq2 );
928 signal = mark + space;
929
930 if (maxamp < fabs(signal)) {
931 maxamp = fabs(signal);
932 }
933
934 outbuf[i] = maxamp ? (signal / maxamp) : 0.0;
935
936 if (symbol)
937 FSKbuf[i] = FSKnco();
938 else
939 FSKbuf[i] = 0.0 * FSKnco();
940 }
941 }
942
943 if (progdefaults.PseudoFSK)
944 ModulateStereo(outbuf, FSKbuf, symbollen);
945 else
946 ModulateXmtr(outbuf, symbollen);
947
948 }
949
send_stop()950 void rtty::send_stop()
951 {
952 if (!progStatus.shaped_rtty) {
953 double freq;
954 bool invert = reverse;
955
956 if (invert)
957 freq = get_txfreq_woffset() - shift / 2.0;
958 else
959 freq = get_txfreq_woffset() + shift / 2.0;
960
961 for (int i = 0; i < stoplen; i++) {
962 outbuf[i] = nco(freq);
963 if (invert)
964 FSKbuf[i] = 0.0;
965 else
966 FSKbuf[i] = FSKnco();
967 }
968 } else {
969 double const freq1 = get_txfreq_woffset() + shift / 2.0;
970 double const freq2 = get_txfreq_woffset() - shift / 2.0;
971 double mark = 0, space = 0, signal = 0;
972 bool symbol = true;
973
974 if (reverse)
975 symbol = !symbol;
976
977 for( int i = 0; i < stoplen; ++i ) {
978 mark = m_SymShaper1->Update( symbol)*m_Osc1->Update( freq1 );
979 space = m_SymShaper2->Update(!symbol)*m_Osc2->Update( freq2 );
980 signal = mark + space;
981
982 if (maxamp < fabs(signal))
983 maxamp = fabs(signal);
984 outbuf[i] = maxamp ? (signal / maxamp) : 0.0;
985
986 if (reverse)
987 FSKbuf[i] = 0.0;
988 else
989 FSKbuf[i] = FSKnco();
990 }
991 }
992
993 if (progdefaults.PseudoFSK)
994 ModulateStereo(outbuf, FSKbuf, stoplen);
995 else
996 ModulateXmtr(outbuf, stoplen);
997
998 }
999
flush_stream()1000 void rtty::flush_stream()
1001 {
1002 double const freq1 = get_txfreq_woffset() + shift / 2.0;
1003 double const freq2 = get_txfreq_woffset() - shift / 2.0;
1004 double mark = 0, space = 0, signal = 0;
1005
1006 for( int i = 0; i < symbollen * 6; ++i ) {
1007 mark = m_SymShaper1->Update(0)*m_Osc1->Update( freq1 );
1008 space = m_SymShaper2->Update(0)*m_Osc2->Update( freq2 );
1009 signal = mark + space;
1010
1011 if (maxamp < fabs(signal)) maxamp = fabs(signal);
1012
1013 outbuf[i] = maxamp ? (signal / maxamp) : 0.0;
1014
1015 FSKbuf[i] = 0.0;
1016 }
1017
1018 sig_stop = true;
1019 if (progdefaults.PseudoFSK)
1020 ModulateStereo(outbuf, FSKbuf, symbollen * 6);
1021 else
1022 ModulateXmtr(outbuf, symbollen * 6);
1023
1024 }
1025
send_char(int c)1026 void rtty::send_char(int c)
1027 {
1028 // experimental code that fails to perform well on Windows, poorly on OS-X
1029 // and perfectly on Linux!
1030 // if (progdefaults.useFSK && (CW_KEYLINE_isopen || progdefaults.CW_KEYLINE_on_cat_port)) {
1031 // send_FSK(c);
1032 // }
1033
1034 int i;
1035 if (nbits == 5) {
1036 if (c == LETTERS)
1037 c = 0x1F;
1038 if (c == FIGURES)
1039 c = 0x1B;
1040 }
1041
1042 // start bit
1043 send_symbol(0, symbollen);
1044 // data bits
1045 for (i = 0; i < nbits; i++) {
1046 send_symbol((c >> i) & 1, symbollen);
1047 }
1048 // parity bit
1049 if (rtty_parity != RTTY_PARITY_NONE)
1050 send_symbol(rttyparity(c, nbits), symbollen);
1051 // stop bit(s)
1052 send_stop();
1053
1054 if (nbits == 5) {
1055 if (c == 0x1F || c == 0x1B)
1056 return;
1057 if (txmode == LETTERS)
1058 c = letters[c];
1059 else
1060 c = figures[c];
1061 if (c)
1062 put_echo_char(progdefaults.rx_lowercase ? tolower(c) : c);
1063 }
1064 else
1065 put_echo_char(c);
1066
1067 }
1068
send_idle()1069 void rtty::send_idle()
1070 {
1071 if (nbits == 5) {
1072 send_char(LETTERS);
1073 txmode = LETTERS;
1074 } else
1075 send_char(0);
1076 }
1077
1078 static int line_char_count = 0;
1079 // 1 start, 5 data, 1.5/2.0 stopbits
1080 #define wait_one_byte(baud, stopbits) \
1081 MilliSleep( (int)((6 + (stopbits))*1000.0 / (baud)));
1082
tx_process()1083 int rtty::tx_process()
1084 {
1085 modem::tx_process();
1086
1087 int c = get_tx_char();
1088
1089 if (progStatus.nanoFSK_online) {
1090 if (preamble) {
1091 start_deadman();
1092 sig_start = true;
1093 for (int i = 0; i < progdefaults.TTY_LTRS; i++)
1094 nano_send_char(-1);
1095 preamble = false;
1096 nano_send_char(-1);
1097 nano_send_char(-1);
1098 }
1099
1100 if (c == GET_TX_CHAR_ETX || stopflag) {
1101 stopflag = false;
1102 stop_deadman();
1103 return -1;
1104 }
1105 // send idle character if c == -1
1106 // nanoIO does not return an idle character in it's buffer so fldigi
1107 // must insert a suitable time delay to account for the idle
1108 if (c == GET_TX_CHAR_NODATA) {
1109 wait_one_byte(
1110 progdefaults.nanoIO_baud,
1111 1.5);
1112 return 0;
1113 }
1114 nano_send_char(c);
1115 put_echo_char(c);
1116 return 0;
1117 }
1118
1119 if (use_Nav) {
1120 if (preamble) {
1121 start_deadman();
1122 for (int i = 0; i < progdefaults.TTY_LTRS; i++)
1123 Nav_send_char(-1);
1124 preamble = false;
1125 }
1126
1127 if (c == GET_TX_CHAR_ETX || stopflag) {
1128 stopflag = false;
1129 stop_deadman();
1130 return -1;
1131 }
1132 if (c == GET_TX_CHAR_NODATA) {
1133 Nav_send_char(-1);
1134 return 0;
1135 }
1136 Nav_send_char(c);
1137 put_echo_char(c);
1138 return 0;
1139 }
1140
1141 if (progStatus.WK_online && progStatus.WKFSK_mode) {
1142 if (preamble) {
1143 start_deadman();
1144 WKFSK_send_char('[');
1145 preamble = false;
1146 }
1147
1148 if (c == GET_TX_CHAR_ETX || stopflag) {
1149 if (stopflag) WKFSK_send_char('\\');
1150 WKFSK_send_char(']');
1151 stop_deadman();
1152 stopflag = false;
1153 return -1;
1154 }
1155 // send idle character if c == -1
1156 // must insert a suitable time delay to account for the idle
1157 if (c == GET_TX_CHAR_NODATA) {
1158 wait_one_byte(
1159 (progStatus.WKFSK_baud == 0 ? 45.45 :
1160 progStatus.WKFSK_baud == 1 ? 50.0 :
1161 progStatus.WKFSK_baud == 2 ? 75.0 : 100.0),
1162 (progStatus.WKFSK_stopbits == 0 ? 2.0 : 1.5));
1163 return 0;
1164 }
1165 else {
1166 WKFSK_send_char(c);
1167 put_echo_char(c);
1168 wait_one_byte(
1169 (progStatus.WKFSK_baud == 0 ? 45.45 :
1170 progStatus.WKFSK_baud == 1 ? 50.0 :
1171 progStatus.WKFSK_baud == 2 ? 75.0 : 100.0),
1172 (progStatus.WKFSK_stopbits == 0 ? 2.0 : 1.5));
1173 }
1174 return 0;
1175 }
1176
1177 if (preamble) {
1178 sig_start = true;
1179 for (int i = 0; i < progdefaults.TTY_LTRS; i++)
1180 send_char(LETTERS);
1181 preamble = false;
1182 }
1183
1184 // TX buffer empty
1185 if (c == GET_TX_CHAR_ETX || stopflag) {
1186 stopflag = false;
1187 line_char_count = 0;
1188 if (nbits != 5) {
1189 if (progdefaults.rtty_crcrlf) send_char('\r');
1190 send_char('\r');
1191 send_char('\n');
1192 } else {
1193 if (progdefaults.rtty_crcrlf) send_char(0x08);
1194 send_char(0x08);
1195 send_char(0x02);
1196 }
1197 flush_stream();
1198 return -1;
1199 }
1200 // send idle character if c == -1
1201 if (c == GET_TX_CHAR_NODATA) {
1202 send_idle();
1203 return 0;
1204 }
1205
1206 // if NOT Baudot
1207 if (nbits != 5) {
1208 acc_symbols = 0;
1209 send_char(c);
1210 xmt_samples = char_samples = acc_symbols;
1211 return 0;
1212 }
1213
1214 if (isalpha(c) || isdigit(c) || isblank(c) || ispunct(c)) {
1215 ++line_char_count;
1216 }
1217
1218 if (progdefaults.rtty_autocrlf && (c != '\n' && c != '\r') &&
1219 (line_char_count == progdefaults.rtty_autocount ||
1220 (line_char_count > progdefaults.rtty_autocount - 5 && c == ' '))) {
1221 line_char_count = 0;
1222 if (progdefaults.rtty_crcrlf)
1223 send_char(0x08); // CR-CR-LF triplet
1224 send_char(0x08);
1225 send_char(0x02);
1226 if (c == ' ')
1227 return 0;
1228 }
1229 if (c == '\r') {
1230 line_char_count = 0;
1231 send_char(0x08);
1232 return 0;
1233 }
1234 if (c == '\n') {
1235 line_char_count = 0;
1236 if (progdefaults.rtty_crcrlf)
1237 send_char(0x08); // CR-CR-LF triplet
1238 send_char(0x02);
1239 return 0;
1240 }
1241
1242
1243 /* unshift-on-space */
1244 if (c == ' ') {
1245 if (progdefaults.UOStx) {
1246 send_char(LETTERS);
1247 send_char(0x04); // coded value for a space
1248 txmode = LETTERS;
1249 } else
1250 send_char(0x04);
1251 return 0;
1252 }
1253
1254 if ((c = baudot_enc(c)) < 0)
1255 return 0;
1256
1257 // switch case if necessary
1258
1259 if ((c & 0x300) != txmode) {
1260 if (txmode == FIGURES) {
1261 send_char(LETTERS);
1262 txmode = LETTERS;
1263 } else {
1264 send_char(FIGURES);
1265 txmode = FIGURES;
1266 }
1267 }
1268 ///
1269 acc_symbols = 0;
1270 send_char(c & 0x1F);
1271 xmt_samples = char_samples = acc_symbols;
1272
1273 return 0;
1274 }
1275
baudot_enc(unsigned char data)1276 int rtty::baudot_enc(unsigned char data)
1277 {
1278 int i, c, mode;
1279
1280 mode = 0;
1281 c = -1;
1282
1283 if (islower(data))
1284 data = toupper(data);
1285
1286 for (i = 0; i < 32; i++) {
1287 if (data == letters[i]) {
1288 mode |= LETTERS;
1289 c = i;
1290 }
1291 if (data == figures[i]) {
1292 mode |= FIGURES;
1293 c = i;
1294 }
1295 if (c != -1)
1296 return (mode | c);
1297 }
1298
1299 return -1;
1300 }
1301
baudot_dec(unsigned char data)1302 char rtty::baudot_dec(unsigned char data)
1303 {
1304 int out = 0;
1305
1306 switch (data) {
1307 case 0x1F: /* letters */
1308 rxmode = LETTERS;
1309 break;
1310 case 0x1B: /* figures */
1311 rxmode = FIGURES;
1312 break;
1313 case 0x04: /* unshift-on-space */
1314 if (progdefaults.UOSrx)
1315 rxmode = LETTERS;
1316 return ' ';
1317 break;
1318 default:
1319 if (rxmode == LETTERS)
1320 out = letters[data];
1321 else
1322 out = figures[data];
1323 break;
1324 }
1325
1326 return out;
1327 }
1328
1329 // ---------------------------------------------------------------------
1330 // TTY output on DTR/RTS signal lines
1331 //----------------------------------------------------------------------
1332 // experimental code that performs perfect on Linux, OK on OS-X and
1333 // fails on Windows-10 !!!
1334 /*
1335 static pthread_t fsk_pthread;
1336 static pthread_cond_t fsk_cond;
1337 static pthread_mutex_t fsk_mutex = PTHREAD_MUTEX_INITIALIZER;
1338
1339 static bool fsk_thread_running = false;
1340 static bool fsk_terminate_flag = false;
1341
1342 #define fskbit(bit, len) {if (progdefaults.FSK_keyline == 2) ser->SetDTR(bit);\
1343 else ser->SetRTS(bit);\
1344 MilliSleep((len));}
1345
1346 static int fsk_ch;
1347 static int fsk_nbits;
1348 static int fsk_parity;
1349 static bool fsk_sending = false;
1350
1351 void send_fsk(int c)
1352 {
1353 fsk_sending = true;
1354 int bitlen = 1000.0 / rtty::BAUD[progdefaults.rtty_baud];
1355 int stoplen = bitlen * (progdefaults.rtty_stop == 0 ? 1.0 : progdefaults.rtty_stop == 1 ? 1.5 : 2.0);
1356
1357 Cserial *ser = &CW_KEYLINE_serial;
1358 if (progdefaults.CW_KEYLINE_on_cat_port)
1359 ser = &rigio;
1360
1361 if (fsk_nbits == 5) {
1362 if (c == LETTERS) c = 0x1F;
1363 else if (c == FIGURES) c = 0x1B;
1364 }
1365
1366 // start bit
1367 fskbit(1, bitlen);
1368 // data bits
1369 for (int i = 0; i < fsk_nbits; i++)
1370 fskbit(!((c >> i) & 1), bitlen);
1371
1372 // parity bit
1373 if (fsk_parity != rtty::RTTY_PARITY_NONE)
1374 fskbit(!rttyparity(c, fsk_nbits), bitlen);
1375
1376 // stop bits
1377 fskbit(0, stoplen);
1378 fsk_sending = false;
1379 }
1380
1381 static void * fsk_loop(void *args)
1382 {
1383 // SET_THREAD_ID(AUDIO_ALERT_TID);
1384
1385 fsk_thread_running = true;
1386 fsk_terminate_flag = false;
1387
1388 while(1) {
1389 pthread_mutex_lock(&fsk_mutex);
1390 pthread_cond_wait(&fsk_cond, &fsk_mutex);
1391 pthread_mutex_unlock(&fsk_mutex);
1392
1393 if (fsk_terminate_flag)
1394 break;
1395 send_fsk(fsk_ch);
1396 }
1397 return (void *)0;
1398 }
1399
1400 void stop_fsk_thread(void)
1401 {
1402 if(!fsk_thread_running) return;
1403
1404 fsk_terminate_flag = true;
1405 pthread_cond_signal(&fsk_cond);
1406
1407 MilliSleep(10);
1408
1409 pthread_join(fsk_pthread, NULL);
1410
1411 pthread_mutex_destroy(&fsk_mutex);
1412 pthread_cond_destroy(&fsk_cond);
1413
1414 memset((void *) &fsk_pthread, 0, sizeof(fsk_pthread));
1415 memset((void *) &fsk_mutex, 0, sizeof(fsk_mutex));
1416
1417 fsk_thread_running = false;
1418 fsk_terminate_flag = false;
1419 }
1420
1421 void start_fsk_thread(void)
1422 {
1423 if (fsk_thread_running) return;
1424
1425 memset((void *) &fsk_pthread, 0, sizeof(fsk_pthread));
1426 memset((void *) &fsk_mutex, 0, sizeof(fsk_mutex));
1427 memset((void *) &fsk_cond, 0, sizeof(fsk_cond));
1428
1429 if(pthread_cond_init(&fsk_cond, NULL)) {
1430 LOG_ERROR("Alert thread create fail (pthread_cond_init)");
1431 return;
1432 }
1433
1434 if(pthread_mutex_init(&fsk_mutex, NULL)) {
1435 LOG_ERROR("AUDIO_ALERT thread create fail (pthread_mutex_init)");
1436 return;
1437 }
1438
1439 if (pthread_create(&fsk_pthread, NULL, fsk_loop, NULL) < 0) {
1440 pthread_mutex_destroy(&fsk_mutex);
1441 LOG_ERROR("AUDIO_ALERT thread create fail (pthread_create)");
1442 }
1443
1444 LOG_VERBOSE("started audio fsk thread");
1445
1446 MilliSleep(10); // Give the CPU time to set 'fsk_thread_running'
1447 }
1448
1449 void rtty::send_FSK(int c)
1450 {
1451 if (!fsk_thread_running)
1452 start_fsk_thread();
1453
1454 int count = 100;
1455 while (fsk_sending) {
1456 MilliSleep(1);
1457 if (--count <= 0) return;
1458 }
1459 fsk_ch = c;
1460 fsk_nbits = nbits;
1461 fsk_parity = rtty_parity;
1462
1463 pthread_cond_signal(&fsk_cond);
1464 }
1465 */
1466 //======================================================================
1467 // methods for class Oscillator and class SymbolShaper
1468 //======================================================================
1469
Oscillator(double samplerate)1470 Oscillator::Oscillator( double samplerate )
1471 {
1472 m_phase = 0;
1473 m_samplerate = samplerate;
1474 // std::cerr << "samplerate for Oscillator:"<<m_samplerate<<"\n";
1475 }
1476
Update(double frequency)1477 double Oscillator::Update( double frequency )
1478 {
1479 m_phase += frequency/m_samplerate * TWOPI;
1480 if ( m_phase > TWOPI ) m_phase -= TWOPI;
1481
1482 return ( sin( m_phase ) );
1483 }
1484
SymbolShaper(double baud,double sr)1485 SymbolShaper::SymbolShaper(double baud, double sr)
1486 {
1487 m_sinc_table = 0;
1488 Preset( baud, sr );
1489 }
1490
~SymbolShaper()1491 SymbolShaper::~SymbolShaper()
1492 {
1493 delete [] m_sinc_table;
1494 }
1495
reset()1496 void SymbolShaper::reset()
1497 {
1498 m_State = false;
1499 m_Accumulator = 0.0;
1500 m_Counter0 = 1024;
1501 m_Counter1 = 1024;
1502 m_Counter2 = 1024;
1503 m_Counter3 = 1024;
1504 m_Counter4 = 1024;
1505 m_Counter5 = 1024;
1506 m_Factor0 = 0.0;
1507 m_Factor1 = 0.0;
1508 m_Factor2 = 0.0;
1509 m_Factor3 = 0.0;
1510 m_Factor4 = 0.0;
1511 m_Factor5 = 0.0;
1512 }
1513
Preset(double baud,double sr)1514 void SymbolShaper::Preset(double baud, double sr)
1515 {
1516 double baud_rate = baud;
1517 double sample_rate = sr;
1518
1519 LOG_INFO("Shaper::reset( %f, %f )", baud_rate, sample_rate);
1520
1521 // calculate new table-size for six integrators ----------------------
1522
1523 m_table_size = sample_rate / baud_rate * 5.49;
1524 LOG_INFO("Shaper::m_table_size = %d", m_table_size);
1525
1526 // kill old sinc-table and get memory for the new one -----------------
1527
1528 if (m_sinc_table)
1529 delete [] m_sinc_table;
1530 m_sinc_table = new double[m_table_size];
1531
1532 // set up the new sinc-table based on the new parameters --------------
1533
1534 long double sum = 0.0;
1535
1536 for( int x=0; x<m_table_size; ++x ) {
1537 int const offset = m_table_size/2;
1538 double wfactor = 1.0 / 1.568; // optimal
1539 // symbol-length in samples if wmultiple = 1.0
1540 double const T = wfactor * sample_rate / (baud_rate*2.0);
1541 // symbol-time relative to zero
1542 double const t = (x-offset);
1543
1544 m_sinc_table[x] = rcos( t, T, 1.0 );
1545
1546 // calculate integral
1547 sum += m_sinc_table[x];
1548 }
1549
1550 // scale the values in the table so that the integral over it is as
1551 // exactly 1.0000000 as we can do this...
1552
1553 for( int x=0; x<m_table_size; ++x ) {
1554 m_sinc_table[x] *= 1.0 / sum;
1555 }
1556
1557 // reset internal states
1558 reset();
1559 maxamp = 0;
1560 }
1561
Update(bool state)1562 double SymbolShaper::Update( bool state )
1563 {
1564 if( m_State != state ) {
1565 m_State = state;
1566 if( m_Counter0 >= m_table_size ) {
1567 m_Counter0 = 0;
1568 m_Factor0 = (state)? +1.0 : -1.0;
1569 } else if( m_Counter1 >= m_table_size ) {
1570 m_Counter1 = 0;
1571 m_Factor1 = (state)? +1.0 : -1.0;
1572 } else if( m_Counter2 >= m_table_size ) {
1573 m_Counter2 = 0;
1574 m_Factor2 = (state)? +1.0 : -1.0;
1575 } else if( m_Counter3 >= m_table_size ) {
1576 m_Counter3 = 0;
1577 m_Factor3 = (state)? +1.0 : -1.0;
1578 } else if( m_Counter4 >= m_table_size ) {
1579 m_Counter4 = 0;
1580 m_Factor4 = (state)? +1.0 : -1.0;
1581 } else if( m_Counter5 >= m_table_size ) {
1582 m_Counter5 = 0;
1583 m_Factor5 = (state)? +1.0 : -1.0;
1584 }
1585 }
1586
1587 if( m_Counter0 < m_table_size )
1588 m_Accumulator += m_Factor0 * m_sinc_table[m_Counter0++];
1589
1590 if( m_Counter1 < m_table_size )
1591 m_Accumulator += m_Factor1 * m_sinc_table[m_Counter1++];
1592
1593 if( m_Counter2 < m_table_size )
1594 m_Accumulator += m_Factor2 * m_sinc_table[m_Counter2++];
1595
1596 if( m_Counter3 < m_table_size )
1597 m_Accumulator += m_Factor3 * m_sinc_table[m_Counter3++];
1598
1599 if( m_Counter4 < m_table_size )
1600 m_Accumulator += m_Factor4 * m_sinc_table[m_Counter4++];
1601
1602 if( m_Counter5 < m_table_size )
1603 m_Accumulator += m_Factor5 * m_sinc_table[m_Counter5++];
1604
1605 return ( m_Accumulator / sqrt(2) );
1606 }
1607
print_sinc_table()1608 void SymbolShaper::print_sinc_table()
1609 {
1610 for (int i = 0; i < 1024; i++) printf("%f\n", m_SincTable[i]);
1611 }
1612
1613