74
for (int i = 0; i < MAXFFTS; i++)
66
76
put_MODEstatus(mode);
87
void dominoex::reset_filters()
89
// fft filter at first IF frequency
90
fft->create_filter( (FIRSTIF - 0.5 * progdefaults.DOMINOEX_BW * bandwidth) / samplerate,
91
(FIRSTIF + 0.5 * progdefaults.DOMINOEX_BW * bandwidth)/ samplerate );
93
for (int i = 0; i < MAXFFTS; i++)
94
if (binsfft[i]) delete binsfft[i];
100
extones = NUMTONES / 2;
104
lotone = basetone - extones * doublespaced;
105
hitone = basetone + NUMTONES * doublespaced + extones * doublespaced;
107
numbins = hitone - lotone;
109
for (int i = 0; i < paths; i++)//MAXFFTS; i++)
110
binsfft[i] = new sfft (symlen, lotone, hitone);
112
filter_reset = false;
70
115
void dominoex::restart()
72
double flo, fhi, bw, cf;
76
bw = bandwidth * progdefaults.DOMINOEX_BW;
77
cf = 1000.0 + bandwidth / 2.0; // basetone is always 1000 Hz
80
// mid frequency is always 1000 Hz + bandwidth / 2
81
flo = (cf - bw/2) / samplerate;
82
fhi = (cf + bw/2) / samplerate;
84
filt->init_bandpass (127, 1, flo, fhi);
86
strSecXmtText = txtSecondary->value();
87
if (strSecXmtText.length() == 0)
88
strSecXmtText = "fldigi "PACKAGE_VERSION" ";
91
120
void dominoex::init()
122
if (mupsksec2pri.empty())
123
MuPsk_sec2pri_init();
96
// digiscope->mode(Digiscope::SCOPE);
97
digiscope->mode(Digiscope::DOMDATA);
129
set_scope_mode(Digiscope::DOMDATA);
132
void dominoex::MuPsk_sec2pri_init(void)
134
int chars[] = { 'A', 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, // À, Á, Â, Ã, Ä, Å
135
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, -1, // à, á, â, ã, ä, å
137
'C', 0xc7, 0xe7, 0xa9, -1, // Ç, ç, ©,
138
'D', 0xd0, 0xb0, -1, // Ð, °
139
'E', 0xc6, 0xe6, 0xc8, 0xc9, 0xca, 0xcb, // Æ, æ, È, É, Ê, Ë
140
0xe8, 0xe9, 0xea, 0xeb, -1, // è, é, ê, ë
142
'I', 0xcc, 0xcd, 0xce, 0xcf, 0xec, 0xed, // Ì, Í, Î, Ï, ì, í
143
0xee, 0xef, 0xa1, -1, // î, ï, ¡
145
'N', 0xd1, 0xf1, -1, // Ñ, ñ
146
'O', 0xf4, 0xf6, 0xf2, 0xd6, 0xf3, 0xd3, // ô, ö, ò, Ö, ó, Ó
147
0xd4, 0xd2, 0xf5, 0xd5, -1, // Ô, Ò, õ, Õ
149
'U', 0xd9, 0xda, 0xdb, 0xdc, 0xf9, 0xfa, // Ù, Ú, Û, Ü, ù, ú
150
0xfb, 0xfc, -1, // û, ü
152
'Y', 0xff, 0xfd, 0xdd, -1, // ÿ, ý, Ý
167
for (size_t i = 1; i < sizeof(chars)/sizeof(*chars); i++) {
169
mupsksec2pri[chars[i]] = c;
100
175
dominoex::~dominoex()
102
if (binsfft) delete binsfft;
103
177
if (hilbert) delete hilbert;
179
for (int i = 0; i < paths; i++) {//MAXFFTS; i++) {
180
if (binsfft[i]) delete binsfft[i];
183
for (int i = 0; i < SCOPESIZE; i++) {
184
if (vidfilter[i]) delete vidfilter[i];
186
if (syncfilter) delete syncfilter;
104
188
if (pipe) delete [] pipe;
105
if (filt) delete filt;
106
// if (wfid) delete wfid;
191
if (MuPskRxinlv) delete MuPskRxinlv;
192
if (MuPskTxinlv) delete MuPskTxinlv;
193
if (MuPskDec) delete MuPskDec;
194
if (MuPskEnc) delete MuPskEnc;
109
198
dominoex::dominoex(trx_mode md)
111
double cf, bw, flo, fhi;
113
numtones = DOMNUMTONES;
212
case MODE_DOMINOEX11:
218
case MODE_DOMINOEX22:
118
224
case MODE_DOMINOEX4:
120
basetone = 256; // 1000 Hz
122
227
samplerate = 8000;
125
229
case MODE_DOMINOEX8:
127
basetone = 128; // 1000 Hz
129
232
samplerate = 8000;
132
234
case MODE_DOMINOEX16:
134
basetone = 64; // 1000 Hz
136
237
samplerate = 8000;
142
basetone = 186; // 1001.3 Hz
147
case MODE_DOMINOEX11:
149
basetone = 93; // 1001.3 Hz
154
case MODE_DOMINOEX22:
156
basetone = 46; // 990 Hz
161
// case MODE_DOMINOEX8:
163
basetone = 128; // 1000 Hz
165
242
samplerate = 8000;
168
tonespacing = (double) (samplerate * ((doublespaced) ? 2 : 1)) / symlen;
170
binsfft = new sfft( symlen,
171
basetone - numtones*(doublespaced?2:1),
172
basetone + 2*numtones*(doublespaced ? 2 : 1) );
245
tonespacing = 1.0 * samplerate * doublespaced / symlen;
247
bandwidth = NUMTONES * tonespacing;
175
249
hilbert = new C_FIR_filter();
176
250
hilbert->init_hilbert(37, 1);
177
afcfilt = new Cmovavg(AFC_COUNT);
179
pipe = new domrxpipe[2 * symlen];
180
scopedata.alloc(2 * symlen);
181
videodata.alloc(numtones * 6);
252
// fft filter at first if frequency
253
fft = new fftfilt( (FIRSTIF - 0.5 * progdefaults.DOMINOEX_BW * bandwidth) / samplerate,
254
(FIRSTIF + 0.5 * progdefaults.DOMINOEX_BW * bandwidth)/ samplerate,
257
basetone = (int)floor(BASEFREQ * symlen / samplerate + 0.5);
259
slowcpu = progdefaults.slowcpu;
261
for (int i = 0; i < MAXFFTS; i++)
266
for (int i = 0; i < SCOPESIZE; i++)
267
vidfilter[i] = new Cmovavg(16);
269
syncfilter = new Cmovavg(16);
272
pipe = new domrxpipe[twosym];
274
scopedata.alloc(SCOPESIZE);
275
videodata.alloc(MAXFFTS * numbins);
186
bandwidth = (numtones - 1) * tonespacing;
187
bw = bandwidth * progdefaults.DOMINOEX_BW;
189
cf = 1000.0 + bandwidth / 2.0; // basetone is always 1000 Hz
191
flo = (cf - bw/2) / samplerate;
192
fhi = (cf + bw/2) / samplerate;
194
filt = new C_FIR_filter();
195
filt->init_bandpass (127, 1, flo, fhi);
197
283
fragmentsize = symlen;
200
// wfid = new id(this);
202
287
prev1symbol = prev2symbol = 0;
203
prev1vector = prev2vector = complex(0.0, 0.0);
289
MuPskEnc = new encoder (K, POLY1, POLY2);
290
MuPskDec = new viterbi (K, POLY1, POLY2);
291
MuPskDec->settraceback (45);
292
MuPskDec->setchunksize (1);
293
MuPskTxinlv = new interleave (-1, INTERLEAVE_FWD);
294
MuPskRxinlv = new interleave (-1, INTERLEAVE_REV);
296
Mu_symbolpair[0] = Mu_symbolpair[1] = 0;
208
301
//=====================================================================
211
complex dominoex::mixer(complex in, double f)
303
complex dominoex::mixer(int n, complex in)
214
// Basetone is always 1000 Hz
215
f -= (1000.0 + bandwidth/2);
216
z.re = cos(phaseacc);
217
z.im = sin(phaseacc);
308
// first IF mixer (n == 0) plus
309
// MAXFFTS mixers are supported each separated by tonespacing/paths
310
// n == 1, 2, 3, 4 ... MAXFFTS
312
f = frequency - FIRSTIF;
314
f = FIRSTIF - BASEFREQ - bandwidth / 2.0 + tonespacing * (1.0 * (n - 1) / paths );
315
z.re = cos(phase[n]);
316
z.im = sin(phase[n]);
219
phaseacc -= twopi * f / samplerate;
222
else if (phaseacc < M_PI)
318
phase[n] -= TWOPI * f / samplerate;
321
else if (phase[n] < M_PI)
227
326
void dominoex::recvchar(int c)
232
put_sec_char(c & 0xFF);
234
put_rx_char(c & 0xFF);
328
if (!progStatus.sqlonoff || metric > progStatus.sldrSquelchValue) {
333
put_sec_char(c & 0xFF);
335
put_rx_char(c & 0xFF);
237
void dominoex::decodesymbol(unsigned char curtone, unsigned char prevtone)
339
void dominoex::decodeDomino(int c)
241
// Decode the IFK+ sequence, which results in a single nibble
242
c = curtone - prevtone;
244
if (doublespaced) c /= 2;
246
if (c < 0) c += numtones;
248
342
// If the new symbol is the start of a new character (MSB is low), complete the previous character
249
343
if (!(c & 0x8)) {
250
344
if (symcounter <= MAX_VARICODE_LEN) {
268
364
if (symcounter > MAX_VARICODE_LEN + 1)
269
365
symcounter = MAX_VARICODE_LEN + 1;
273
int dominoex::harddecode(complex *in)
368
void dominoex::decodesymbol()
373
// Decode the IFK+ sequence, which results in a single nibble
375
fdiff = currsymbol - prev1symbol;
376
if (reverse) fdiff = -fdiff;
377
fdiff /= doublespaced;
380
// if (fabs(fdiff) > 17)
381
// outofrange = true;
385
c = (int)floor(fdiff + .5) - 2;
386
if (c < 0) c += NUMTONES;
392
int dominoex::harddecode()
275
394
double x, max = 0.0;
278
for (int i = 0; i < numtones * 3*(doublespaced?2:1); i++) {
397
bool cwi[paths * numbins];
400
for (int i = 0; i < paths * numbins; i++)
401
avg += pipe[pipeptr].vector[i].mag();
402
avg /= (paths * numbins);
404
if (avg < 1e-10) avg = 1e-10;
408
for (int i = 0; i < paths * numbins; i++) {
411
for (int j = 1; j <= numtests; j++) {
413
if (p < 0) p += twosym;
414
cwmag = (pipe[j].vector[i].mag())/numtests;
415
if (cwmag >= 50.0 * (1.0 - progdefaults.ThorCWI) * avg) count++;
417
cwi[i] = (count == numtests);
420
for (int i = 0; i < (paths * numbins); i++) {
421
if (cwi[i] == false) {
422
x = pipe[pipeptr].vector[i].mag();
430
avg /= (paths * numbins);
431
staticburst = (max / avg < 1.2);
288
void dominoex::update_syncscope(complex *bins)
436
void dominoex::update_syncscope()
290
439
double max = 0, min = 1e6, range, mag;
291
int numbins = numtones * 3 * (doublespaced ? 2 : 1);
293
for (int i = 0; i < numbins; i++ ) {
295
if (max < mag) max = mag;
296
if (min > mag) min = mag;
299
for (int i = 0; i < numbins; i++ ) {
301
mag = (bins[i].mag() - min) / range;
302
mag = 1 + log10(mag);
303
if (mag < 0) mag = 0;
306
videodata[i] = 255*mag;
308
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
309
set_video(videodata, numbins);
310
videodata.next(); // change buffers
313
// dom symbol synch data
314
memset(scopedata, 0, 2 * symlen * sizeof(double));
315
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue)
316
for (int i = 0, j = 0; i < 2 * symlen; i++) {
317
j = (i + pipeptr) % (2 * symlen);
318
scopedata[i] = (pipe[j].vector[prev1symbol]).mag();
320
set_scope(scopedata, 2 * symlen);
321
scopedata.next(); // change buffers
442
memset(videodata, 0, (paths * numbins) * sizeof(double));
444
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
445
for (int i = 0; i < (paths * numbins); i++ ) {
446
mag = pipe[pipeptr].vector[i].mag();
447
if (max < mag) max = mag;
448
if (min > mag) min = mag;
451
for (int i = 0; i < (paths * numbins); i++ ) {
453
mag = (pipe[pipeptr].vector[i].mag() - min) / range + 0.0001;
454
mag = 1 + 2 * log10(mag);
455
if (mag < 0) mag = 0;
458
videodata[(i + paths * numbins / 2)/2] = 255*mag;
461
set_video(videodata, (paths * numbins), false);
464
// set_scope(scopedata, twosym);
465
// 64 data points is sufficient to show the signal progression through the
466
// convolution filter.
467
memset(scopedata, 0, SCOPESIZE * sizeof(double));
468
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
469
for (unsigned int i = 0, j = 0; i < SCOPESIZE; i++) {
470
j = (pipeptr + i * twosym / SCOPESIZE + 1) % (twosym);
471
scopedata[i] = vidfilter[i]->run(pipe[j].vector[prev1symbol].mag());
474
set_scope(scopedata, SCOPESIZE);
324
478
void dominoex::synchronize()
327
482
double val, max = 0.0;
484
if (staticburst == true) return;
329
486
if (currsymbol == prev1symbol)
331
488
if (prev1symbol == prev2symbol)
334
for (int i = 0, j = pipeptr; i < 2 * symlen; i++) {
491
for (unsigned int i = 0, j = pipeptr; i < twosym; i++) {
335
492
val = (pipe[j].vector[prev1symbol]).mag();
340
j = (j + 1) % (2 * symlen);
342
synccounter += (int) floor((syn - symlen) / numtones + 0.5);
345
void dominoex::reset_afc() {
347
for (int i = 0; i < AFC_COUNT; i++) afcfilt->run(0.0);
356
double ds = doublespaced ? 2 : 1;
364
prevvector = pipe[2*symlen - 1].vector[currsymbol];
366
prevvector = pipe[pipeptr - 1].vector[currsymbol];
368
z = prevvector % currvector;
370
f = z.arg() * samplerate / twopi;
371
fsym = (currsymbol / ds - numtones) * samplerate * ds / symlen;
375
// freqerr = afcfilt->run(freqerr/numtones);
376
freqerr = decayavg(freqerr, err, 64);
378
// std::cout << currsymbol << ", " << freqerr << std::endl;
381
if (progStatus.afconoff && (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == false)) {
382
set_freq(frequency + freqerr);
386
void dominoex::eval_s2n(complex curr, complex n)
388
sig = curr.mag(); // signal + noise energy
389
noise = n.mag();// + 1e-10; // noise energy
390
if (noise < 1e-20) noise = 1e-20;
392
s2n = decayavg( s2n, sig / noise, 8);
394
metric = 20*log10(s2n);
396
display_metric(metric);
398
snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", metric);
497
j = (j + 1) % twosym;
500
syn = syncfilter->run(syn);
502
synccounter += (int) floor(1.0 * (syn - symlen) / NUMTONES + 0.5);
504
set_AFCind(1.0 * (synccounter - symlen) / symlen);
510
void dominoex::eval_s2n()
512
if (currsymbol != prev1symbol && prev1symbol != prev2symbol) {
513
sig = pipe[pipeptr].vector[currsymbol].mag();
515
for (int i = 0; i < paths * numbins; i++) {
517
noise += pipe[pipeptr].vector[i].mag();
519
noise /= (paths * numbins - 1);
521
s2n = decayavg( s2n, sig / noise, 32);
523
metric = 3*(20*log10(s2n) - 9.0);
525
display_metric(metric);
527
snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", metric / 3.0 - 2.0);
403
532
int dominoex::rx_process(const double *buf, int len)
405
complex z, *bins, noise;
408
// create analytic signal...shift in frequency to base band & bandpass filter
409
z.re = z.im = *buf++;
411
z = mixer(z, frequency);
414
// feed it to the sliding FFT
415
bins = binsfft->run(z);
417
// copy current vector to the pipe
418
for (int i = 0; i < numtones*3*(doublespaced?2:1); i++)
419
pipe[pipeptr].vector[i] = bins[i];
421
if (--synccounter <= 0) {
422
synccounter = symlen;
423
currsymbol = harddecode(bins);
424
currvector = bins[currsymbol];
426
decodesymbol(currsymbol, prev1symbol);
428
update_syncscope(bins);
431
// frequency tracking
433
eval_s2n(currvector, bins[(numtones + 2) * (doublespaced ? 2 : 1)]);
435
prev2symbol = prev1symbol;
436
prev2vector = prev1vector;
437
prev1symbol = currsymbol;
438
prev1vector = currvector;
440
pipeptr = (pipeptr + 1) % (2 * symlen);
534
complex zref, z, *zp, *bins = 0;
538
if (filter_reset) reset_filters();
540
if (slowcpu != progdefaults.slowcpu) {
541
slowcpu = progdefaults.slowcpu;
546
// create analytic signal at first IF
547
zref.re = zref.im = *buf++;
548
hilbert->run(zref, zref);
549
zref = mixer(0, zref);
551
if (progdefaults.DOMINOEX_FILTER) {
552
// filter using fft convolution
553
n = fft->run(zref, &zp);
561
for (int i = 0; i < n; i++) {
562
// process MAXFFTS sets of sliding FFTs spaced at 1/MAXFFTS bin intervals each of which
563
// is a matched filter for the current symbol length
564
for (int j = 0; j < paths; j++) {
565
// shift in frequency to base band for the sliding DFTs
566
z = mixer(j + 1, zp[i]);
567
bins = binsfft[j]->run(z);
568
// copy current vector to the pipe interleaving the FFT vectors
569
for (int k = 0; k < numbins; k++) {
570
pipe[pipeptr].vector[j + paths * k] = bins[k];
573
if (--synccounter <= 0) {
574
synccounter = symlen;
575
currsymbol = harddecode();
578
// update_syncscope();
580
prev2symbol = prev1symbol;
581
prev1symbol = currsymbol;
584
if (pipeptr >= twosym)
721
//=============================================================================
722
// MultiPsk compatible FEC methods
723
//=============================================================================
725
//=============================================================================
726
// Varicode support methods
727
// MultiPsk varicode is based on a modified MFSK varicode table in which
728
// Character substition is used for secondary text. The resulting table does
729
// NOT contain the full ASCII character set as the primary. Many of the
730
// control codes and characters above 0x80 are lost.
731
//=============================================================================
733
// Convert from Secondary to Primary character
735
unsigned char dominoex::MuPskSec2Pri(int c)
737
if (c >= 'a' && c <= 'z') c -= 32;
739
c = mupsksec2pri.find(c) != mupsksec2pri.end() ? mupsksec2pri[c] : c;
741
if (c >= 'A' && c <= 'Z') c = c - 'A' + 127;
742
else if (c >= '0' && c <= '9') c = c - '0' + 14;
743
else if (c >= ' ' && c <= '"') c = c - ' ' + 1;
744
else if (c == '_') c = 4;
745
else if (c >= '$' && c <= '&') c = c - '$' + 5;
746
else if (c >= '\'' && c <= '*') c = c - '\'' + 9;
747
else if (c >= '+' && c <= '/') c = c - '+' + 24;
748
else if (c >= ':' && c <= '<') c = c - ':' + 29;
749
else if (c >= '=' && c <= '@') c = c - '=' + 153;
750
else if (c >= '[' && c <= ']') c = c - '[' + 157;
756
// Convert Primary to Split Primary / Secondary character
758
unsigned int dominoex::MuPskPriSecChar(unsigned int c)
760
if (c >= 127 && c < 153) c += ('A' - 127) + 0x100;
761
else if (c >=14 && c < 24) c += ('0' - 14) + 0x100;
762
else if (c >= 1 && c < 4) c += (' ' - 1) + 0x100;
763
else if (c == 4) c = '_' + 0x100;
764
else if (c >= 5 && c < 8) c += ('$' - 5) + 0x100;
765
else if (c >= 9 && c < 13) c += ('\'' - 9) + 0x100;
766
else if (c >= 24 && c < 29) c += ('+' - 24) + 0x100;
767
else if (c >= 29 && c < 32) c += (':' - 29) + 0x100;
768
else if (c >= 153 && c < 157) c += ('=' - 153) + 0x100;
769
else if (c >= 157 && c < 160) c += ('[' - 157) + 0x100;
773
//=============================================================================
775
//=============================================================================
777
void dominoex::decodeMuPskSymbol(unsigned char symbol)
781
Mu_symbolpair[0] = Mu_symbolpair[1];
782
Mu_symbolpair[1] = symbol;
784
Mu_symcounter = Mu_symcounter ? 0 : 1;
786
if (Mu_symcounter) return;
788
c = MuPskDec->decode (Mu_symbolpair, &met);
793
if (progStatus.sqlonoff && metric < progStatus.sldrSquelchValue)
796
Mu_datashreg = (Mu_datashreg << 1) | !!c;
797
if ((Mu_datashreg & 7) == 1) {
798
ch = varidec(Mu_datashreg >> 1);
799
if (progdefaults.DOMINOEX_FEC)
800
recvchar(MuPskPriSecChar(ch));
805
void dominoex::decodeMuPskEX(int ch)
807
unsigned char symbols[4];
810
for (int i = 0; i < 4; i++) {
811
if ((c & 1) == 1) symbols[3-i] = 255;
812
else symbols[3-i] = 1;//-255;
815
if (staticburst == true || outofrange == true)
816
symbols[3] = symbols[2] = symbols[1] = symbols[0] = 0;
818
MuPskRxinlv->symbols(symbols);
820
for (int i = 0; i < 4; i++) decodeMuPskSymbol(symbols[i]);
824
//=============================================================================
826
//=============================================================================
828
void dominoex::MuPskFlushTx()
830
// flush the varicode decoder at the other end
831
// flush the convolutional encoder and interleaver
833
for (int i = 0; i < 107; i++)
838
void dominoex::MuPskClearbits()
840
int data = MuPskEnc->encode(0);
841
for (int k = 0; k < 100; k++) {
842
for (int i = 0; i < 2; i++) {
843
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
846
if (Mu_bitstate == 4) {
847
MuPskTxinlv->bits(&bitshreg);
855
// Send MultiPsk FEC varicode with minimalist interleaver
857
void dominoex::sendMuPskEX(unsigned char c, int secondary)
865
if ( (c >= 1 && c <= 7) || (c >= 9 && c <= 12) || (c >= 14 && c <= 31) ||
866
(c >= 127 && c <= 159))
870
// if (secondary == 0)
871
// LOG_DEBUG("char=%hhu, code=\"%s\"", c, code);
873
int data = MuPskEnc->encode(*code++ - '0');
874
// LOG_DEBUG("data=%d", data;
875
for (int i = 0; i < 2; i++) {
876
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
878
if (Mu_bitstate == 4) {
880
MuPskTxinlv->bits(&bitshreg);
882
// LOG_DEBUG("bitshreg=%d", bitshreg);
884
sendsymbol(bitshreg);
886
// decodeMuPskEX(bitshreg);