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