1 // ----------------------------------------------------------------------------
2 //
3 // dominoex.cxx -- DominoEX modem
4 //
5 // Copyright (C) 2008-20012
6 // David Freese <w1hkj@w1hkj.com>
7 // Hamish Moffatt <hamish@debian.org>
8 // John Phelps <kl4yfd@gmail.com>
9 //
10 // based on code in gmfsk
11 //
12 // This file is part of fldigi.
13 //
14 // Fldigi is free software: you can redistribute it and/or modify
15 // it under the terms of the GNU General Public License as published by
16 // the Free Software Foundation, either version 3 of the License, or
17 // (at your option) any later version.
18 //
19 // Fldigi is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
26 // ----------------------------------------------------------------------------
27
28 #include <config.h>
29
30 #include <stdlib.h>
31
32 #include <map>
33
34 #include "confdialog.h"
35 #include "status.h"
36
37 #include "dominoex.h"
38 #include "trx.h"
39 #include "fl_digi.h"
40 #include "filters.h"
41 #include "misc.h"
42 #include "sound.h"
43 #include "mfskvaricode.h"
44 #include "debug.h"
45
46 LOG_FILE_SOURCE(debug::LOG_MODEM);
47
48 using namespace std;
49
50 char dommsg[80];
51 static map<int, unsigned char> mupsksec2pri;
52
53 bool usingFEC = false;
54
tx_init()55 void dominoex::tx_init()
56 {
57 txstate = TX_STATE_PREAMBLE;
58 txprevtone = 0;
59 Mu_bitstate = 0;
60 counter = 0;
61 txphase = 0;
62
63 strSecXmtText = progdefaults.secText;
64 if (strSecXmtText.length() == 0)
65 strSecXmtText = "fldigi " PACKAGE_VERSION " ";
66
67 videoText();
68 }
69
rx_init()70 void dominoex::rx_init()
71 {
72 synccounter = 0;
73 symcounter = 0;
74 Mu_symcounter = 0;
75 met1 = 0.0;
76 met2 = 0.0;
77 counter = 0;
78 phase[0] = 0.0;
79 for (int i = 0; i < MAXFFTS; i++)
80 phase[i+1] = 0.0;
81 put_MODEstatus(mode);
82 put_sec_char(0);
83 syncfilter->reset();
84
85 Mu_datashreg = 1;
86
87 staticburst = false;
88
89 sig = noise = 6;
90 }
91
reset_filters()92 void dominoex::reset_filters()
93 {
94 // fft filter at first IF frequency
95 fft->create_filter( (FIRSTIF - 0.5 * progdefaults.DOMINOEX_BW * bandwidth) / samplerate,
96 (FIRSTIF + 0.5 * progdefaults.DOMINOEX_BW * bandwidth)/ samplerate );
97
98 for (int i = 0; i < MAXFFTS; i++) {
99 if (binsfft[i]) delete binsfft[i];
100 binsfft[i] = 0;
101 }
102
103 if (slowcpu) {
104 extones = 4;
105 paths = 3;
106 } else {
107 extones = NUMTONES / 2;
108 paths = 5;
109 }
110
111 lotone = basetone - extones * doublespaced;
112 hitone = basetone + NUMTONES * doublespaced + extones * doublespaced;
113
114 numbins = hitone - lotone;
115
116 for (int i = 0; i < paths; i++)//MAXFFTS; i++)
117 binsfft[i] = new sfft (symlen, lotone, hitone);
118
119 filter_reset = false;
120 }
121
restart()122 void dominoex::restart()
123 {
124 filter_reset = true;
125 }
126
init()127 void dominoex::init()
128 {
129 if (mupsksec2pri.empty())
130 MuPsk_sec2pri_init();
131
132 modem::init();
133 // reset_filters();
134 rx_init();
135
136 if (progdefaults.StartAtSweetSpot)
137 set_freq(progdefaults.PSKsweetspot);
138 else if (progStatus.carrier != 0) {
139 set_freq(progStatus.carrier);
140 #if !BENCHMARK_MODE
141 progStatus.carrier = 0;
142 #endif
143 } else
144 set_freq(wf->Carrier());
145
146 set_scope_mode(Digiscope::DOMDATA);
147 }
148
MuPsk_sec2pri_init(void)149 void dominoex::MuPsk_sec2pri_init(void)
150 {
151 int chars[] = { 'A', 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, // À, Á, Â, Ã, Ä, Å
152 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, -1, // à, á, â, ã, ä, å
153 'B', 0xdf, -1, // ß
154 'C', 0xc7, 0xe7, 0xa9, -1, // Ç, ç, ©,
155 'D', 0xd0, 0xb0, -1, // Ð, °
156 'E', 0xc6, 0xe6, 0xc8, 0xc9, 0xca, 0xcb, // Æ, æ, È, É, Ê, Ë
157 0xe8, 0xe9, 0xea, 0xeb, -1, // è, é, ê, ë
158 'F', 0x192, -1, // ƒ
159 'I', 0xcc, 0xcd, 0xce, 0xcf, 0xec, 0xed, // Ì, Í, Î, Ï, ì, í
160 0xee, 0xef, 0xa1, -1, // î, ï, ¡
161 'L', 0xa3, -1, // £
162 'N', 0xd1, 0xf1, -1, // Ñ, ñ
163 'O', 0xf4, 0xf6, 0xf2, 0xd6, 0xf3, 0xd3, // ô, ö, ò, Ö, ó, Ó
164 0xd4, 0xd2, 0xf5, 0xd5, -1, // Ô, Ò, õ, Õ
165 'R', 0xae, -1, // ®
166 'U', 0xd9, 0xda, 0xdb, 0xdc, 0xf9, 0xfa, // Ù, Ú, Û, Ü, ù, ú
167 0xfb, 0xfc, -1, // û, ü
168 'X', 0xd7, -1, // ×
169 'Y', 0xff, 0xfd, 0xdd, -1, // ÿ, ý, Ý
170 '0', 0xd8, -1, // Ø
171 '1', 0xb9, -1, // ¹
172 '2', 0xb2, -1, // ²
173 '3', 0xb3, -1, // ³
174 '?', 0xbf, -1, // ¿
175 '!', 0xa1, -1, // ¡
176 '<', 0xab, -1, // «
177 '>', 0xbb, -1, // »
178 '{', '(', -1,
179 '}', ')', -1,
180 '|', '\\'
181 };
182
183 int c = chars[0];
184 for (size_t i = 1; i < sizeof(chars)/sizeof(*chars); i++) {
185 if (chars[i] != -1)
186 mupsksec2pri[chars[i]] = c;
187 else
188 c = chars[++i];
189 }
190 }
191
~dominoex()192 dominoex::~dominoex()
193 {
194 if (hilbert) delete hilbert;
195
196 for (int i = 0; i < MAXFFTS; i++) {
197 if (binsfft[i]) delete binsfft[i];
198 binsfft[i] = 0;
199 }
200
201 for (int i = 0; i < SCOPESIZE; i++) {
202 if (vidfilter[i]) delete vidfilter[i];
203 }
204 if (syncfilter) delete syncfilter;
205
206 if (pipe) delete [] pipe;
207 if (fft) delete fft;
208
209 if (MuPskRxinlv) delete MuPskRxinlv;
210 if (MuPskTxinlv) delete MuPskTxinlv;
211 if (MuPskDec) delete MuPskDec;
212 if (MuPskEnc) delete MuPskEnc;
213
214 }
215
dominoex(trx_mode md)216 dominoex::dominoex(trx_mode md)
217 {
218 cap |= CAP_REV;
219
220 mode = md;
221
222 switch (mode) {
223 // 11.025 kHz modes
224 case MODE_DOMINOEX5:
225 symlen = 2048;
226 doublespaced = 2;
227 samplerate = 11025;
228 break;
229 case MODE_DOMINOEX11:
230 symlen = 1024;
231 doublespaced = 1;
232 samplerate = 11025;
233 break;
234 case MODE_DOMINOEX22:
235 symlen = 512;
236 doublespaced = 1;
237 samplerate = 11025;
238 break;
239 // 8kHz modes
240 case MODE_DOMINOEXMICRO:
241 symlen = 4000;
242 doublespaced = 1;
243 samplerate = 8000;
244 break;
245 case MODE_DOMINOEX4:
246 symlen = 2048;
247 doublespaced = 2;
248 samplerate = 8000;
249 break;
250 case MODE_DOMINOEX8:
251 symlen = 1024;
252 doublespaced = 2;
253 samplerate = 8000;
254 break;
255 case MODE_DOMINOEX16:
256 symlen = 512;
257 doublespaced = 1;
258 samplerate = 8000;
259 break;
260 // experimental
261 case MODE_DOMINOEX44:
262 symlen = 256;
263 doublespaced = 2;
264 samplerate = 11025;
265 break;
266 case MODE_DOMINOEX88:
267 symlen = 128;
268 doublespaced = 1;
269 samplerate = 11025;
270 break;
271
272
273 default: // EX8
274 symlen = 1024;
275 doublespaced = 2;
276 samplerate = 8000;
277 }
278
279 tonespacing = 1.0 * samplerate * doublespaced / symlen;
280
281 bandwidth = NUMTONES * tonespacing;
282
283 hilbert = new C_FIR_filter();
284 hilbert->init_hilbert(37, 1);
285
286 // fft filter at first if frequency
287 fft = new fftfilt( (FIRSTIF - 0.5 * progdefaults.DOMINOEX_BW * bandwidth) / samplerate,
288 (FIRSTIF + 0.5 * progdefaults.DOMINOEX_BW * bandwidth)/ samplerate,
289 1024 );
290
291 basetone = (int)floor(BASEFREQ * symlen / samplerate + 0.5);
292
293 slowcpu = progdefaults.slowcpu;
294
295 for (int i = 0; i < MAXFFTS; i++)
296 binsfft[i] = 0;
297
298 reset_filters();
299
300 for (int i = 0; i < SCOPESIZE; i++)
301 vidfilter[i] = new Cmovavg(16);
302
303 syncfilter = new Cmovavg(16);
304
305 twosym = 2 * symlen;
306 pipe = new domrxpipe[twosym];
307
308 scopedata.alloc(SCOPESIZE);
309 videodata.alloc(MAXFFTS * numbins);
310
311 pipeptr = 0;
312
313 symcounter = 0;
314 Mu_symcounter = 0;
315 metric = 0.0;
316
317 fragmentsize = symlen;
318
319 s2n = 0.0;
320
321 prev1symbol = prev2symbol = 0;
322
323 MuPskEnc = new encoder (NASA_K, POLY1, POLY2);
324 MuPskDec = new viterbi (NASA_K, POLY1, POLY2);
325 MuPskDec->settraceback (45);
326 MuPskDec->setchunksize (1);
327 MuPskTxinlv = new interleave (4, 4, INTERLEAVE_FWD);
328 MuPskRxinlv = new interleave (4, 4, INTERLEAVE_REV);
329 Mu_bitstate = 0;
330 Mu_symbolpair[0] = Mu_symbolpair[1] = 0;
331 Mu_datashreg = 1;
332 // init();
333 }
334
335 //=====================================================================
336 // rx modules
mixer(int n,cmplx in)337 cmplx dominoex::mixer(int n, cmplx in)
338 {
339 cmplx z;
340 double f;
341
342 // first IF mixer (n == 0) plus
343 // MAXFFTS mixers are supported each separated by tonespacing/paths
344 // n == 1, 2, 3, 4 ... MAXFFTS
345 if (n == 0)
346 f = frequency - FIRSTIF;
347 else
348 f = FIRSTIF - BASEFREQ - bandwidth / 2.0 + tonespacing * (1.0 * (n - 1) / paths );
349 z = cmplx( cos(phase[n]), sin(phase[n]));
350 z = z * in;
351 phase[n] -= TWOPI * f / samplerate;
352 if (phase[n] < 0) phase[n] += TWOPI;
353
354 return z;
355 }
356
recvchar(int c)357 void dominoex::recvchar(int c)
358 {
359 if (!progStatus.sqlonoff || metric > progStatus.sldrSquelchValue) {
360
361 if (c == -1)
362 return;
363 if (c & 0x100)
364 put_sec_char(c & 0xFF);
365 else
366 put_rx_char(c & 0xFF);
367 }
368 }
369
decodeDomino(int c)370 void dominoex::decodeDomino(int c)
371 {
372 int sym, ch;
373 // If the new symbol is the start of a new character (MSB is low), complete the previous character
374 if (!(c & 0x8)) {
375 if (symcounter <= MAX_VARICODE_LEN) {
376 sym = 0;
377 for (int i = 0; i < symcounter; i++)
378 sym |= symbolbuf[i] << (4 * i);
379 ch = dominoex_varidec(sym);
380
381 if (!progdefaults.DOMINOEX_FEC)
382 if (!staticburst && !outofrange)
383 recvchar(ch);
384 }
385 symcounter = 0;
386 }
387
388 // Add to the symbol buffer. Position 0 is the newest symbol.
389 for (int i = MAX_VARICODE_LEN-1; i > 0; i--)
390 symbolbuf[i] = symbolbuf[i-1];
391 symbolbuf[0] = c;
392
393 // Increment the counter, but clamp at max+1 to avoid overflow
394 symcounter++;
395 if (symcounter > MAX_VARICODE_LEN + 1)
396 symcounter = MAX_VARICODE_LEN + 1;
397 }
398
decodesymbol()399 void dominoex::decodesymbol()
400 {
401 int c;
402 double fdiff;
403
404 // Decode the IFK+ sequence, which results in a single nibble
405
406 fdiff = currsymbol - prev1symbol;
407 if (reverse) fdiff = -fdiff;
408 fdiff /= doublespaced;
409 fdiff /= paths;
410
411 // if (fabs(fdiff) > 17)
412 // outofrange = true;
413 // else
414 outofrange = false;
415
416 c = (int)floor(fdiff + .5) - 2;
417 if (c < 0) c += NUMTONES;
418
419 decodeDomino(c);
420 decodeMuPskEX(c);
421 }
422
harddecode()423 int dominoex::harddecode()
424 {
425 double x, max = 0.0;
426 int symbol = 0;
427 double avg = 0.0;
428 bool cwi[paths * numbins];
429 double cwmag;
430
431 for (int i = 0; i < paths * numbins; i++)
432 avg += abs(pipe[pipeptr].vector[i]);
433 avg /= (paths * numbins);
434
435 if (avg < 1e-10) avg = 1e-10;
436
437 int numtests = 10;
438 int count = 0;
439 for (int i = 0; i < paths * numbins; i++) {
440 cwmag = 0.0;
441 count = 0;
442 for (int j = 1; j <= numtests; j++) {
443 int p = pipeptr - j;
444 if (p < 0) p += twosym;
445 cwmag = abs(pipe[j].vector[i])/numtests;
446 if (cwmag >= 50.0 * (1.0 - progdefaults.ThorCWI) * avg) count++;
447 }
448 cwi[i] = (count == numtests);
449 }
450
451 for (int i = 0; i < (paths * numbins); i++) {
452 if (cwi[i] == false) {
453 x = abs(pipe[pipeptr].vector[i]);
454 avg += x;
455 if (x > max) {
456 max = x;
457 symbol = i;
458 }
459 }
460 }
461 avg /= (paths * numbins);
462 staticburst = (max / avg < 1.2);
463
464 return symbol;
465 }
466
update_syncscope()467 void dominoex::update_syncscope()
468 {
469
470 double max = 0, min = 1e6, range, mag;
471
472 // dom waterfall
473 memset(videodata, 0, (paths * numbins) * sizeof(double));
474
475 if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
476 for (int i = 0; i < (paths * numbins); i++ ) {
477 mag = abs(pipe[pipeptr].vector[i]);
478 if (max < mag) max = mag;
479 if (min > mag) min = mag;
480 }
481 range = max - min;
482 for (int i = 0; i < (paths * numbins); i++ ) {
483 if (range > 2) {
484 mag = (abs(pipe[pipeptr].vector[i]) - min) / range + 0.0001;
485 mag = 1 + 2 * log10(mag);
486 if (mag < 0) mag = 0;
487 } else
488 mag = 0;
489 videodata[(i + paths * numbins / 2)/2] = 255*mag;
490 }
491 }
492 set_video(videodata, (paths * numbins), false);
493 videodata.next();
494
495 // set_scope(scopedata, twosym);
496 // 64 data points is sufficient to show the signal progression through the
497 // convolution filter.
498 memset(scopedata, 0, SCOPESIZE * sizeof(double));
499 if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
500 for (unsigned int i = 0, j = 0; i < SCOPESIZE; i++) {
501 j = (pipeptr + i * twosym / SCOPESIZE + 1) % (twosym);
502 scopedata[i] = vidfilter[i]->run(abs(pipe[j].vector[prev1symbol]));
503 }
504 }
505 set_scope(scopedata, SCOPESIZE);
506 scopedata.next();
507 }
508
synchronize()509 void dominoex::synchronize()
510 {
511 // int syn = -1;
512 double syn = -1;
513 double val, max = 0.0;
514
515 if (staticburst == true) return;
516
517 if (currsymbol == prev1symbol)
518 return;
519 if (prev1symbol == prev2symbol)
520 return;
521
522 for (unsigned int i = 0, j = pipeptr; i < twosym; i++) {
523 val = abs(pipe[j].vector[prev1symbol]);
524 if (val > max) {
525 max = val;
526 syn = i;
527 }
528 j = (j + 1) % twosym;
529 }
530
531 syn = syncfilter->run(syn);
532
533 synccounter += (int) floor(1.0 * (syn - symlen) / NUMTONES + 0.5);
534
535 update_syncscope();
536 }
537
eval_s2n()538 void dominoex::eval_s2n()
539 {
540 double s = abs(pipe[pipeptr].vector[currsymbol]);
541 double n = (NUMTONES - 1 ) * abs(pipe[(pipeptr + symlen) % twosym].vector[currsymbol]);
542
543 sig = decayavg( sig, s, abs( s - sig) > 4 ? 4 : 32);
544 noise = decayavg( noise, n, 64);
545
546 if (noise)
547 s2n = 20*log10(sig / noise) - 6;
548 else
549 s2n = 0;
550
551 // metric = 4 * s2n;
552 // To partially offset the increase of noise by (THORNUMTONES -1)
553 // in the noise calculation above,
554 // add 15*log10(THORNUMTONES -1) = 18.4, and multiply by 6
555 metric = 6 * (s2n + 18.4);
556
557 metric = metric < 0 ? 0 : metric > 100 ? 100 : metric;
558
559 display_metric(metric);
560
561 snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", s2n );
562 put_Status1(dommsg);
563 }
564
rx_process(const double * buf,int len)565 int dominoex::rx_process(const double *buf, int len)
566 {
567 cmplx zref, z, *zp;
568 cmplx zarray[1];
569 int n;
570
571 if (filter_reset) reset_filters();
572
573 if (slowcpu != progdefaults.slowcpu) {
574 slowcpu = progdefaults.slowcpu;
575 reset_filters();
576 }
577
578 while (len) {
579 // create analytic signal at first IF
580 zref = cmplx( *buf, *buf );
581 buf++;
582 hilbert->run(zref, zref);
583 zref = mixer(0, zref);
584
585 if (progdefaults.DOMINOEX_FILTER) {
586 // filter using fft convolution
587 n = fft->run(zref, &zp);
588 } else {
589 zarray[0] = zref;
590 zp = zarray;
591 n = 1;
592 }
593
594 if (n) {
595 for (int i = 0; i < n; i++) {
596 // process MAXFFTS sets of sliding FFTs spaced at 1/MAXFFTS bin intervals each of which
597 // is a matched filter for the current symbol length
598 for (int j = 0; j < paths; j++) {
599 // shift in frequency to base band for the sliding DFTs
600 z = mixer(j + 1, zp[i]);
601 // copy current vector to the pipe interleaving the FFT vectors
602 binsfft[j]->run(z, pipe[pipeptr].vector + j, paths );
603 }
604 if (--synccounter <= 0) {
605 synccounter = symlen;
606 currsymbol = harddecode();
607 decodesymbol();
608 synchronize();
609 // update_syncscope();
610 eval_s2n();
611 prev2symbol = prev1symbol;
612 prev1symbol = currsymbol;
613 }
614 pipeptr++;
615 if (pipeptr >= twosym)
616 pipeptr = 0;
617 }
618 }
619 --len;
620 }
621
622 return 0;
623 }
624
625 //=====================================================================
626 // dominoex tx modules
627
get_secondary_char()628 int dominoex::get_secondary_char()
629 {
630 static unsigned int cptr = 0;
631 char chr;
632 if (cptr >= strSecXmtText.length()) cptr = 0;
633 chr = strSecXmtText[cptr++];
634 put_sec_char( chr );
635 return chr;
636 }
637
sendtone(int tone,int duration)638 void dominoex::sendtone(int tone, int duration)
639 {
640 double f, phaseincr;
641 f = (tone + 0.5) * tonespacing + get_txfreq_woffset() - bandwidth / 2.0;
642 phaseincr = TWOPI * f / samplerate;
643 for (int j = 0; j < duration; j++) {
644 for (int i = 0; i < symlen; i++) {
645 outbuf[i] = cos(txphase);
646 txphase -= phaseincr;
647 if (txphase < 0) txphase += TWOPI;
648 }
649 ModulateXmtr(outbuf, symlen);
650 }
651 }
652
sendsymbol(int sym)653 void dominoex::sendsymbol(int sym)
654 {
655 //static int first = 0;
656 cmplx z;
657 int tone;
658
659 tone = (txprevtone + 2 + sym) % NUMTONES;
660 txprevtone = tone;
661 if (reverse)
662 tone = (NUMTONES - 1) - tone;
663 sendtone(tone, 1);
664 }
665
sendchar(unsigned char c,int secondary)666 void dominoex::sendchar(unsigned char c, int secondary)
667 {
668 if (progdefaults.DOMINOEX_FEC)
669 sendMuPskEX(c, secondary);
670 else {
671 unsigned char *code = dominoex_varienc(c, secondary);
672 sendsymbol(code[0]);
673 // Continuation nibbles all have the MSB set
674 for (int sym = 1; sym < 3; sym++) {
675 if (code[sym] & 0x8)
676 sendsymbol(code[sym]);
677 else
678 break;
679 }
680 }
681 if (!secondary)
682 put_echo_char(c);
683 }
684
sendidle()685 void dominoex::sendidle()
686 {
687 sendchar(0, 1); // <NUL>
688 }
689
sendsecondary()690 void dominoex::sendsecondary()
691 {
692 int c = get_secondary_char();
693 sendchar(c & 0xFF, 1);
694 }
695
flushtx()696 void dominoex::flushtx()
697 {
698 // if (progdefaults.DOMINOEX_FEC)
699 // MuPskFlushTx();
700 // else {
701 // flush the varicode decoder at the receiver end
702 for (int i = 0; i < 4; i++)
703 sendidle();
704 // }
705 }
706
tx_process()707 int dominoex::tx_process()
708 {
709 modem::tx_process();
710
711 int i;
712 switch (txstate) {
713 case TX_STATE_PREAMBLE:
714 if (progdefaults.DOMINOEX_FEC)
715 MuPskClearbits();
716 sendidle();
717 txstate = TX_STATE_START;
718 break;
719 case TX_STATE_START:
720 sendchar('\r', 0);
721 if (mode != MODE_DOMINOEXMICRO) {
722 sendchar(2, 0); // STX
723 sendchar('\r', 0);
724 }
725 txstate = TX_STATE_DATA;
726 break;
727 case TX_STATE_DATA:
728 i = get_tx_char();
729 if (i == GET_TX_CHAR_NODATA)
730 sendsecondary();
731 else if (i == GET_TX_CHAR_ETX)
732 txstate = TX_STATE_END;
733 else
734 sendchar(i, 0);
735 if (stopflag)
736 txstate = TX_STATE_END;
737 break;
738 case TX_STATE_END:
739 sendchar('\r', 0);
740 if (mode != MODE_DOMINOEXMICRO) {
741 sendchar(4, 0); // EOT
742 sendchar('\r', 0);
743 }
744 txstate = TX_STATE_FLUSH;
745 break;
746 case TX_STATE_FLUSH:
747 flushtx();
748 return -1;
749 }
750 return 0;
751 }
752
753 //=============================================================================
754 // MultiPsk compatible FEC methods
755 //=============================================================================
756
757 //=============================================================================
758 // Varicode support methods
759 // MultiPsk varicode is based on a modified MFSK varicode table in which
760 // Character substition is used for secondary text. The resulting table does
761 // NOT contain the full ASCII character set as the primary. Many of the
762 // control codes and characters above 0x80 are lost.
763 //=============================================================================
764
765 // Convert from Secondary to Primary character
766
MuPskSec2Pri(int c)767 unsigned char dominoex::MuPskSec2Pri(int c)
768 {
769 if (c >= 'a' && c <= 'z') c -= 32;
770
771 c = mupsksec2pri.find(c) != mupsksec2pri.end() ? mupsksec2pri[c] : c;
772
773 if (c >= 'A' && c <= 'Z') c = c - 'A' + 127;
774 else if (c >= '0' && c <= '9') c = c - '0' + 14;
775 else if (c >= ' ' && c <= '"') c = c - ' ' + 1;
776 else if (c == '_') c = 4;
777 else if (c >= '$' && c <= '&') c = c - '$' + 5;
778 else if (c >= '\'' && c <= '*') c = c - '\'' + 9;
779 else if (c >= '+' && c <= '/') c = c - '+' + 24;
780 else if (c >= ':' && c <= '<') c = c - ':' + 29;
781 else if (c >= '=' && c <= '@') c = c - '=' + 153;
782 else if (c >= '[' && c <= ']') c = c - '[' + 157;
783 else c = '_';
784
785 return c;
786 }
787
788 // Convert Primary to Split Primary / Secondary character
789
MuPskPriSecChar(unsigned int c)790 unsigned int dominoex::MuPskPriSecChar(unsigned int c)
791 {
792 if (c >= 127 && c < 153) c += ('A' - 127) + 0x100;
793 else if (c >=14 && c < 24) c += ('0' - 14) + 0x100;
794 else if (c >= 1 && c < 4) c += (' ' - 1) + 0x100;
795 else if (c == 4) c = '_' + 0x100;
796 else if (c >= 5 && c < 8) c += ('$' - 5) + 0x100;
797 else if (c >= 9 && c < 13) c += ('\'' - 9) + 0x100;
798 else if (c >= 24 && c < 29) c += ('+' - 24) + 0x100;
799 else if (c >= 29 && c < 32) c += (':' - 29) + 0x100;
800 else if (c >= 153 && c < 157) c += ('=' - 153) + 0x100;
801 else if (c >= 157 && c < 160) c += ('[' - 157) + 0x100;
802 return c;
803 }
804
805 //=============================================================================
806 // Receive
807 //=============================================================================
808
decodeMuPskSymbol(unsigned char symbol)809 void dominoex::decodeMuPskSymbol(unsigned char symbol)
810 {
811 int c, ch, met;
812
813 Mu_symbolpair[0] = Mu_symbolpair[1];
814 Mu_symbolpair[1] = symbol;
815
816 Mu_symcounter = Mu_symcounter ? 0 : 1;
817
818 if (Mu_symcounter) return;
819
820 c = MuPskDec->decode (Mu_symbolpair, &met);
821
822 if (c == -1)
823 return;
824
825 if (progStatus.sqlonoff && metric < progStatus.sldrSquelchValue)
826 return;
827
828 Mu_datashreg = (Mu_datashreg << 1) | !!c;
829 if ((Mu_datashreg & 7) == 1) {
830 ch = varidec(Mu_datashreg >> 1);
831 if (progdefaults.DOMINOEX_FEC)
832 recvchar(MuPskPriSecChar(ch));
833 Mu_datashreg = 1;
834 }
835 }
836
decodeMuPskEX(int ch)837 void dominoex::decodeMuPskEX(int ch)
838 {
839 unsigned char symbols[4];
840 int c = ch;
841
842 for (int i = 0; i < 4; i++) {
843 if ((c & 1) == 1) symbols[3-i] = 255;
844 else symbols[3-i] = 1;//-255;
845 c = c / 2;
846 }
847 if (staticburst == true || outofrange == true)
848 symbols[3] = symbols[2] = symbols[1] = symbols[0] = 0;
849
850 MuPskRxinlv->symbols(symbols);
851
852 for (int i = 0; i < 4; i++) decodeMuPskSymbol(symbols[i]);
853
854 }
855
856 //=============================================================================
857 // Transmit
858 //=============================================================================
859
MuPskFlushTx()860 void dominoex::MuPskFlushTx()
861 {
862 // flush the varicode decoder at the other end
863 // flush the convolutional encoder and interleaver
864 sendsymbol(1);
865 for (int i = 0; i < 107; i++)
866 sendsymbol(0);
867 Mu_bitstate = 0;
868 }
869
MuPskClearbits()870 void dominoex::MuPskClearbits()
871 {
872 int data = MuPskEnc->encode(0);
873 for (int k = 0; k < 100; k++) {
874 for (int i = 0; i < 2; i++) {
875 bitshreg = (bitshreg << 1) | ((data >> i) & 1);
876 Mu_bitstate++;
877
878 if (Mu_bitstate == 4) {
879 MuPskTxinlv->bits(&bitshreg);
880 Mu_bitstate = 0;
881 bitshreg = 0;
882 }
883 }
884 }
885 }
886
887 // Send MultiPsk FEC varicode with minimalist interleaver
888
sendMuPskEX(unsigned char c,int secondary)889 void dominoex::sendMuPskEX(unsigned char c, int secondary)
890 {
891 const char *code;
892 if (secondary == 1)
893 c = MuPskSec2Pri(c);
894 else {
895 if (c == 10)
896 return;
897 if ( (c >= 1 && c <= 7) || (c >= 9 && c <= 12) || (c >= 14 && c <= 31) ||
898 (c >= 127 && c <= 159))
899 c = '_';
900 }
901 code = varienc(c);
902 // if (secondary == 0)
903 // LOG_DEBUG("char=%hhu, code=\"%s\"", c, code);
904 while (*code) {
905 int data = MuPskEnc->encode(*code++ - '0');
906 // LOG_DEBUG("data=%d", data;
907 for (int i = 0; i < 2; i++) {
908 bitshreg = (bitshreg << 1) | ((data >> i) & 1);
909 Mu_bitstate++;
910 if (Mu_bitstate == 4) {
911
912 MuPskTxinlv->bits(&bitshreg);
913
914 // LOG_DEBUG("bitshreg=%d", bitshreg);
915
916 sendsymbol(bitshreg);
917
918 // decodeMuPskEX(bitshreg);
919
920 Mu_bitstate = 0;
921 bitshreg = 0;
922 }
923 }
924 }
925 }
926