dl-fldigi/src/dominoex/dominoex.cxx

909 wiersze
20 KiB
C++
Czysty Wina Historia

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// ----------------------------------------------------------------------------
//
// dominoex.cxx -- DominoEX modem
//
// Copyright (C) 2008-20012
// David Freese <w1hkj@w1hkj.com>
// Hamish Moffatt <hamish@debian.org>
// John Phelps <kl4yfd@gmail.com>
//
// based on code in gmfsk
//
// This file is part of fldigi.
//
// Fldigi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Fldigi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <config.h>
#include <stdlib.h>
#include <map>
#include "confdialog.h"
#include "status.h"
#include "dominoex.h"
#include "trx.h"
#include "fl_digi.h"
#include "filters.h"
#include "misc.h"
#include "sound.h"
#include "mfskvaricode.h"
#include "debug.h"
LOG_FILE_SOURCE(debug::LOG_MODEM);
using namespace std;
char dommsg[80];
static map<int, unsigned char> mupsksec2pri;
bool usingFEC = false;
void dominoex::tx_init(SoundBase *sc)
{
scard = sc;
txstate = TX_STATE_PREAMBLE;
txprevtone = 0;
Mu_bitstate = 0;
counter = 0;
txphase = 0;
strSecXmtText = progdefaults.secText;
if (strSecXmtText.length() == 0)
strSecXmtText = "fldigi "PACKAGE_VERSION" ";
videoText();
}
void dominoex::rx_init()
{
synccounter = 0;
symcounter = 0;
Mu_symcounter = 0;
met1 = 0.0;
met2 = 0.0;
counter = 0;
phase[0] = 0.0;
for (int i = 0; i < MAXFFTS; i++)
phase[i+1] = 0.0;
put_MODEstatus(mode);
put_sec_char(0);
syncfilter->reset();
Mu_datashreg = 1;
staticburst = false;
sig = noise = 6;
}
void dominoex::reset_filters()
{
// fft filter at first IF frequency
fft->create_filter( (FIRSTIF - 0.5 * progdefaults.DOMINOEX_BW * bandwidth) / samplerate,
(FIRSTIF + 0.5 * progdefaults.DOMINOEX_BW * bandwidth)/ samplerate );
for (int i = 0; i < MAXFFTS; i++) {
if (binsfft[i]) delete binsfft[i];
binsfft[i] = 0;
}
if (slowcpu) {
extones = 4;
paths = 3;
} else {
extones = NUMTONES / 2;
paths = 5;
}
lotone = basetone - extones * doublespaced;
hitone = basetone + NUMTONES * doublespaced + extones * doublespaced;
numbins = hitone - lotone;
for (int i = 0; i < paths; i++)//MAXFFTS; i++)
binsfft[i] = new sfft (symlen, lotone, hitone);
filter_reset = false;
}
void dominoex::restart()
{
filter_reset = true;
}
void dominoex::init()
{
if (mupsksec2pri.empty())
MuPsk_sec2pri_init();
modem::init();
// reset_filters();
rx_init();
set_scope_mode(Digiscope::DOMDATA);
}
void dominoex::MuPsk_sec2pri_init(void)
{
int chars[] = { 'A', 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, // À, Á, Â, Ã, Ä, Å
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, -1, // à, á, â, ã, ä, å
'B', 0xdf, -1, // ß
'C', 0xc7, 0xe7, 0xa9, -1, // Ç, ç, ©,
'D', 0xd0, 0xb0, -1, // Ð, °
'E', 0xc6, 0xe6, 0xc8, 0xc9, 0xca, 0xcb, // Æ, æ, È, É, Ê, Ë
0xe8, 0xe9, 0xea, 0xeb, -1, // è, é, ê, ë
'F', 0x192, -1, // ƒ
'I', 0xcc, 0xcd, 0xce, 0xcf, 0xec, 0xed, // Ì, Í, Î, Ï, ì, í
0xee, 0xef, 0xa1, -1, // î, ï, ¡
'L', 0xa3, -1, // £
'N', 0xd1, 0xf1, -1, // Ñ, ñ
'O', 0xf4, 0xf6, 0xf2, 0xd6, 0xf3, 0xd3, // ô, ö, ò, Ö, ó, Ó
0xd4, 0xd2, 0xf5, 0xd5, -1, // Ô, Ò, õ, Õ
'R', 0xae, -1, // ®
'U', 0xd9, 0xda, 0xdb, 0xdc, 0xf9, 0xfa, // Ù, Ú, Û, Ü, ù, ú
0xfb, 0xfc, -1, // û, ü
'X', 0xd7, -1, // ×
'Y', 0xff, 0xfd, 0xdd, -1, // ÿ, ý, Ý
'0', 0xd8, -1, // Ø
'1', 0xb9, -1, // ¹
'2', 0xb2, -1, // ²
'3', 0xb3, -1, // ³
'?', 0xbf, -1, // ¿
'!', 0xa1, -1, // ¡
'<', 0xab, -1, // «
'>', 0xbb, -1, // »
'{', '(', -1,
'}', ')', -1,
'|', '\\'
};
int c = chars[0];
for (size_t i = 1; i < sizeof(chars)/sizeof(*chars); i++) {
if (chars[i] != -1)
mupsksec2pri[chars[i]] = c;
else
c = chars[++i];
}
}
dominoex::~dominoex()
{
if (hilbert) delete hilbert;
for (int i = 0; i < MAXFFTS; i++) {
if (binsfft[i]) delete binsfft[i];
binsfft[i] = 0;
}
for (int i = 0; i < SCOPESIZE; i++) {
if (vidfilter[i]) delete vidfilter[i];
}
if (syncfilter) delete syncfilter;
if (pipe) delete [] pipe;
if (fft) delete fft;
if (MuPskRxinlv) delete MuPskRxinlv;
if (MuPskTxinlv) delete MuPskTxinlv;
if (MuPskDec) delete MuPskDec;
if (MuPskEnc) delete MuPskEnc;
}
dominoex::dominoex(trx_mode md)
{
cap |= CAP_REV;
mode = md;
switch (mode) {
// 11.025 kHz modes
case MODE_DOMINOEX5:
symlen = 2048;
doublespaced = 2;
samplerate = 11025;
break;
case MODE_DOMINOEX11:
symlen = 1024;
doublespaced = 1;
samplerate = 11025;
break;
case MODE_DOMINOEX22:
symlen = 512;
doublespaced = 1;
samplerate = 11025;
break;
// 8kHz modes
case MODE_DOMINOEX4:
symlen = 2048;
doublespaced = 2;
samplerate = 8000;
break;
case MODE_DOMINOEX8:
symlen = 1024;
doublespaced = 2;
samplerate = 8000;
break;
case MODE_DOMINOEX16:
symlen = 512;
doublespaced = 1;
samplerate = 8000;
break;
// experimental
case MODE_DOMINOEX44:
symlen = 256;
doublespaced = 2;
samplerate = 11025;
break;
case MODE_DOMINOEX88:
symlen = 128;
doublespaced = 1;
samplerate = 11025;
break;
default: // EX8
symlen = 1024;
doublespaced = 2;
samplerate = 8000;
}
tonespacing = 1.0 * samplerate * doublespaced / symlen;
tonespacing += progdefaults.DOMINOEX_ADJ;
bandwidth = NUMTONES * tonespacing;
hilbert = new C_FIR_filter();
hilbert->init_hilbert(37, 1);
// fft filter at first if frequency
fft = new fftfilt( (FIRSTIF - 0.5 * progdefaults.DOMINOEX_BW * bandwidth) / samplerate,
(FIRSTIF + 0.5 * progdefaults.DOMINOEX_BW * bandwidth)/ samplerate,
1024 );
basetone = (int)floor(BASEFREQ * symlen / samplerate + 0.5);
slowcpu = progdefaults.slowcpu;
for (int i = 0; i < MAXFFTS; i++)
binsfft[i] = 0;
reset_filters();
for (int i = 0; i < SCOPESIZE; i++)
vidfilter[i] = new Cmovavg(16);
syncfilter = new Cmovavg(16);
twosym = 2 * symlen;
pipe = new domrxpipe[twosym];
scopedata.alloc(SCOPESIZE);
videodata.alloc(MAXFFTS * numbins);
pipeptr = 0;
symcounter = 0;
Mu_symcounter = 0;
metric = 0.0;
fragmentsize = symlen;
s2n = 0.0;
prev1symbol = prev2symbol = 0;
MuPskEnc = new encoder (K, POLY1, POLY2);
MuPskDec = new viterbi (K, POLY1, POLY2);
MuPskDec->settraceback (45);
MuPskDec->setchunksize (1);
MuPskTxinlv = new interleave (4, 4, INTERLEAVE_FWD);
MuPskRxinlv = new interleave (4, 4, INTERLEAVE_REV);
Mu_bitstate = 0;
Mu_symbolpair[0] = Mu_symbolpair[1] = 0;
Mu_datashreg = 1;
// init();
}
//=====================================================================
// rx modules
cmplx dominoex::mixer(int n, cmplx in)
{
cmplx z;
double f;
// first IF mixer (n == 0) plus
// MAXFFTS mixers are supported each separated by tonespacing/paths
// n == 1, 2, 3, 4 ... MAXFFTS
if (n == 0)
f = frequency - FIRSTIF;
else
f = FIRSTIF - BASEFREQ - bandwidth / 2.0 + tonespacing * (1.0 * (n - 1) / paths );
z = cmplx( cos(phase[n]), sin(phase[n]));
z = z * in;
phase[n] -= TWOPI * f / samplerate;
if (phase[n] < 0) phase[n] += TWOPI;
return z;
}
void dominoex::recvchar(int c)
{
if (!progStatus.sqlonoff || metric > progStatus.sldrSquelchValue) {
if (c == -1)
return;
if (c & 0x100)
put_sec_char(c & 0xFF);
else
put_rx_char(c & 0xFF);
}
}
void dominoex::decodeDomino(int c)
{
int sym, ch;
// If the new symbol is the start of a new character (MSB is low), complete the previous character
if (!(c & 0x8)) {
if (symcounter <= MAX_VARICODE_LEN) {
sym = 0;
for (int i = 0; i < symcounter; i++)
sym |= symbolbuf[i] << (4 * i);
ch = dominoex_varidec(sym);
if (!progdefaults.DOMINOEX_FEC)
if (!staticburst && !outofrange)
recvchar(ch);
}
symcounter = 0;
}
// Add to the symbol buffer. Position 0 is the newest symbol.
for (int i = MAX_VARICODE_LEN-1; i > 0; i--)
symbolbuf[i] = symbolbuf[i-1];
symbolbuf[0] = c;
// Increment the counter, but clamp at max+1 to avoid overflow
symcounter++;
if (symcounter > MAX_VARICODE_LEN + 1)
symcounter = MAX_VARICODE_LEN + 1;
}
void dominoex::decodesymbol()
{
int c;
double fdiff;
// Decode the IFK+ sequence, which results in a single nibble
fdiff = currsymbol - prev1symbol;
if (reverse) fdiff = -fdiff;
fdiff /= doublespaced;
fdiff /= paths;
// if (fabs(fdiff) > 17)
// outofrange = true;
// else
outofrange = false;
c = (int)floor(fdiff + .5) - 2;
if (c < 0) c += NUMTONES;
decodeDomino(c);
decodeMuPskEX(c);
}
int dominoex::harddecode()
{
double x, max = 0.0;
int symbol = 0;
double avg = 0.0;
bool cwi[paths * numbins];
double cwmag;
for (int i = 0; i < paths * numbins; i++)
avg += abs(pipe[pipeptr].vector[i]);
avg /= (paths * numbins);
if (avg < 1e-10) avg = 1e-10;
int numtests = 10;
int count = 0;
for (int i = 0; i < paths * numbins; i++) {
cwmag = 0.0;
count = 0;
for (int j = 1; j <= numtests; j++) {
int p = pipeptr - j;
if (p < 0) p += twosym;
cwmag = abs(pipe[j].vector[i])/numtests;
if (cwmag >= 50.0 * (1.0 - progdefaults.ThorCWI) * avg) count++;
}
cwi[i] = (count == numtests);
}
for (int i = 0; i < (paths * numbins); i++) {
if (cwi[i] == false) {
x = abs(pipe[pipeptr].vector[i]);
avg += x;
if (x > max) {
max = x;
symbol = i;
}
}
}
avg /= (paths * numbins);
staticburst = (max / avg < 1.2);
return symbol;
}
void dominoex::update_syncscope()
{
double max = 0, min = 1e6, range, mag;
// dom waterfall
memset(videodata, 0, (paths * numbins) * sizeof(double));
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
for (int i = 0; i < (paths * numbins); i++ ) {
mag = abs(pipe[pipeptr].vector[i]);
if (max < mag) max = mag;
if (min > mag) min = mag;
}
range = max - min;
for (int i = 0; i < (paths * numbins); i++ ) {
if (range > 2) {
mag = (abs(pipe[pipeptr].vector[i]) - min) / range + 0.0001;
mag = 1 + 2 * log10(mag);
if (mag < 0) mag = 0;
} else
mag = 0;
videodata[(i + paths * numbins / 2)/2] = 255*mag;
}
}
set_video(videodata, (paths * numbins), false);
videodata.next();
// set_scope(scopedata, twosym);
// 64 data points is sufficient to show the signal progression through the
// convolution filter.
memset(scopedata, 0, SCOPESIZE * sizeof(double));
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
for (unsigned int i = 0, j = 0; i < SCOPESIZE; i++) {
j = (pipeptr + i * twosym / SCOPESIZE + 1) % (twosym);
scopedata[i] = vidfilter[i]->run(abs(pipe[j].vector[prev1symbol]));
}
}
set_scope(scopedata, SCOPESIZE);
scopedata.next();
}
void dominoex::synchronize()
{
// int syn = -1;
double syn = -1;
double val, max = 0.0;
if (staticburst == true) return;
if (currsymbol == prev1symbol)
return;
if (prev1symbol == prev2symbol)
return;
for (unsigned int i = 0, j = pipeptr; i < twosym; i++) {
val = abs(pipe[j].vector[prev1symbol]);
if (val > max) {
max = val;
syn = i;
}
j = (j + 1) % twosym;
}
syn = syncfilter->run(syn);
synccounter += (int) floor(1.0 * (syn - symlen) / NUMTONES + 0.5);
update_syncscope();
}
void dominoex::eval_s2n()
{
double s = abs(pipe[pipeptr].vector[currsymbol]);
double n = (NUMTONES - 1 ) * abs(pipe[(pipeptr + symlen) % twosym].vector[currsymbol]);
sig = decayavg( sig, s, abs( s - sig) > 4 ? 4 : 32);
noise = decayavg( noise, n, 64);
if (noise)
s2n = 20*log10(sig / noise) - 6;
else
s2n = 0;
// metric = 4 * s2n;
// To partially offset the increase of noise by (THORNUMTONES -1)
// in the noise calculation above,
// add 15*log10(THORNUMTONES -1) = 18.4, and multiply by 6
metric = 6 * (s2n + 18.4);
metric = metric < 0 ? 0 : metric > 100 ? 100 : metric;
display_metric(metric);
snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", s2n );
put_Status1(dommsg);
}
int dominoex::rx_process(const double *buf, int len)
{
cmplx zref, z, *zp;
cmplx zarray[1];
int n;
if (filter_reset) reset_filters();
if (slowcpu != progdefaults.slowcpu) {
slowcpu = progdefaults.slowcpu;
reset_filters();
}
while (len) {
// create analytic signal at first IF
zref = cmplx( *buf, *buf );
buf++;
hilbert->run(zref, zref);
zref = mixer(0, zref);
if (progdefaults.DOMINOEX_FILTER) {
// filter using fft convolution
n = fft->run(zref, &zp);
} else {
zarray[0] = zref;
zp = zarray;
n = 1;
}
if (n) {
for (int i = 0; i < n; i++) {
// process MAXFFTS sets of sliding FFTs spaced at 1/MAXFFTS bin intervals each of which
// is a matched filter for the current symbol length
for (int j = 0; j < paths; j++) {
// shift in frequency to base band for the sliding DFTs
z = mixer(j + 1, zp[i]);
// copy current vector to the pipe interleaving the FFT vectors
binsfft[j]->run(z, pipe[pipeptr].vector + j, paths );
}
if (--synccounter <= 0) {
synccounter = symlen;
currsymbol = harddecode();
decodesymbol();
synchronize();
// update_syncscope();
eval_s2n();
prev2symbol = prev1symbol;
prev1symbol = currsymbol;
}
pipeptr++;
if (pipeptr >= twosym)
pipeptr = 0;
}
}
--len;
}
return 0;
}
//=====================================================================
// dominoex tx modules
int dominoex::get_secondary_char()
{
static unsigned int cptr = 0;
char chr;
if (cptr >= strSecXmtText.length()) cptr = 0;
chr = strSecXmtText[cptr++];
put_sec_char( chr );
return chr;
}
void dominoex::sendtone(int tone, int duration)
{
double f, phaseincr;
f = (tone + 0.5) * tonespacing + get_txfreq_woffset() - bandwidth / 2.0;
phaseincr = TWOPI * f / samplerate;
for (int j = 0; j < duration; j++) {
for (int i = 0; i < symlen; i++) {
outbuf[i] = cos(txphase);
txphase -= phaseincr;
if (txphase < 0) txphase += TWOPI;
}
ModulateXmtr(outbuf, symlen);
}
}
void dominoex::sendsymbol(int sym)
{
//static int first = 0;
cmplx z;
int tone;
tone = (txprevtone + 2 + sym) % NUMTONES;
txprevtone = tone;
if (reverse)
tone = (NUMTONES - 1) - tone;
sendtone(tone, 1);
}
void dominoex::sendchar(unsigned char c, int secondary)
{
if (progdefaults.DOMINOEX_FEC)
sendMuPskEX(c, secondary);
else {
unsigned char *code = dominoex_varienc(c, secondary);
sendsymbol(code[0]);
// Continuation nibbles all have the MSB set
for (int sym = 1; sym < 3; sym++) {
if (code[sym] & 0x8)
sendsymbol(code[sym]);
else
break;
}
}
if (!secondary)
put_echo_char(c);
}
void dominoex::sendidle()
{
sendchar(0, 1); // <NUL>
}
void dominoex::sendsecondary()
{
int c = get_secondary_char();
sendchar(c & 0xFF, 1);
}
void dominoex::flushtx()
{
// if (progdefaults.DOMINOEX_FEC)
// MuPskFlushTx();
// else {
// flush the varicode decoder at the receiver end
for (int i = 0; i < 4; i++)
sendidle();
// }
}
int dominoex::tx_process()
{
int i;
switch (txstate) {
case TX_STATE_PREAMBLE:
if (progdefaults.DOMINOEX_FEC)
MuPskClearbits();
sendidle();
txstate = TX_STATE_START;
break;
case TX_STATE_START:
sendchar('\r', 0);
sendchar(2, 0); // STX
sendchar('\r', 0);
txstate = TX_STATE_DATA;
break;
case TX_STATE_DATA:
i = get_tx_char();
if (i == GET_TX_CHAR_NODATA)
sendsecondary();
else if (i == GET_TX_CHAR_ETX)
txstate = TX_STATE_END;
else
sendchar(i, 0);
if (stopflag)
txstate = TX_STATE_END;
break;
case TX_STATE_END:
sendchar('\r', 0);
sendchar(4, 0); // EOT
sendchar('\r', 0);
txstate = TX_STATE_FLUSH;
break;
case TX_STATE_FLUSH:
flushtx();
cwid();
return -1;
}
return 0;
}
//=============================================================================
// MultiPsk compatible FEC methods
//=============================================================================
//=============================================================================
// Varicode support methods
// MultiPsk varicode is based on a modified MFSK varicode table in which
// Character substition is used for secondary text. The resulting table does
// NOT contain the full ASCII character set as the primary. Many of the
// control codes and characters above 0x80 are lost.
//=============================================================================
// Convert from Secondary to Primary character
unsigned char dominoex::MuPskSec2Pri(int c)
{
if (c >= 'a' && c <= 'z') c -= 32;
c = mupsksec2pri.find(c) != mupsksec2pri.end() ? mupsksec2pri[c] : c;
if (c >= 'A' && c <= 'Z') c = c - 'A' + 127;
else if (c >= '0' && c <= '9') c = c - '0' + 14;
else if (c >= ' ' && c <= '"') c = c - ' ' + 1;
else if (c == '_') c = 4;
else if (c >= '$' && c <= '&') c = c - '$' + 5;
else if (c >= '\'' && c <= '*') c = c - '\'' + 9;
else if (c >= '+' && c <= '/') c = c - '+' + 24;
else if (c >= ':' && c <= '<') c = c - ':' + 29;
else if (c >= '=' && c <= '@') c = c - '=' + 153;
else if (c >= '[' && c <= ']') c = c - '[' + 157;
else c = '_';
return c;
}
// Convert Primary to Split Primary / Secondary character
unsigned int dominoex::MuPskPriSecChar(unsigned int c)
{
if (c >= 127 && c < 153) c += ('A' - 127) + 0x100;
else if (c >=14 && c < 24) c += ('0' - 14) + 0x100;
else if (c >= 1 && c < 4) c += (' ' - 1) + 0x100;
else if (c == 4) c = '_' + 0x100;
else if (c >= 5 && c < 8) c += ('$' - 5) + 0x100;
else if (c >= 9 && c < 13) c += ('\'' - 9) + 0x100;
else if (c >= 24 && c < 29) c += ('+' - 24) + 0x100;
else if (c >= 29 && c < 32) c += (':' - 29) + 0x100;
else if (c >= 153 && c < 157) c += ('=' - 153) + 0x100;
else if (c >= 157 && c < 160) c += ('[' - 157) + 0x100;
return c;
}
//=============================================================================
// Receive
//=============================================================================
void dominoex::decodeMuPskSymbol(unsigned char symbol)
{
int c, ch, met;
Mu_symbolpair[0] = Mu_symbolpair[1];
Mu_symbolpair[1] = symbol;
Mu_symcounter = Mu_symcounter ? 0 : 1;
if (Mu_symcounter) return;
c = MuPskDec->decode (Mu_symbolpair, &met);
if (c == -1)
return;
if (progStatus.sqlonoff && metric < progStatus.sldrSquelchValue)
return;
Mu_datashreg = (Mu_datashreg << 1) | !!c;
if ((Mu_datashreg & 7) == 1) {
ch = varidec(Mu_datashreg >> 1);
if (progdefaults.DOMINOEX_FEC)
recvchar(MuPskPriSecChar(ch));
Mu_datashreg = 1;
}
}
void dominoex::decodeMuPskEX(int ch)
{
unsigned char symbols[4];
int c = ch;
for (int i = 0; i < 4; i++) {
if ((c & 1) == 1) symbols[3-i] = 255;
else symbols[3-i] = 1;//-255;
c = c / 2;
}
if (staticburst == true || outofrange == true)
symbols[3] = symbols[2] = symbols[1] = symbols[0] = 0;
MuPskRxinlv->symbols(symbols);
for (int i = 0; i < 4; i++) decodeMuPskSymbol(symbols[i]);
}
//=============================================================================
// Transmit
//=============================================================================
void dominoex::MuPskFlushTx()
{
// flush the varicode decoder at the other end
// flush the convolutional encoder and interleaver
sendsymbol(1);
for (int i = 0; i < 107; i++)
sendsymbol(0);
Mu_bitstate = 0;
}
void dominoex::MuPskClearbits()
{
int data = MuPskEnc->encode(0);
for (int k = 0; k < 100; k++) {
for (int i = 0; i < 2; i++) {
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
Mu_bitstate++;
if (Mu_bitstate == 4) {
MuPskTxinlv->bits(&bitshreg);
Mu_bitstate = 0;
bitshreg = 0;
}
}
}
}
// Send MultiPsk FEC varicode with minimalist interleaver
void dominoex::sendMuPskEX(unsigned char c, int secondary)
{
const char *code;
if (secondary == 1)
c = MuPskSec2Pri(c);
else {
if (c == 10)
return;
if ( (c >= 1 && c <= 7) || (c >= 9 && c <= 12) || (c >= 14 && c <= 31) ||
(c >= 127 && c <= 159))
c = '_';
}
code = varienc(c);
// if (secondary == 0)
// LOG_DEBUG("char=%hhu, code=\"%s\"", c, code);
while (*code) {
int data = MuPskEnc->encode(*code++ - '0');
// LOG_DEBUG("data=%d", data;
for (int i = 0; i < 2; i++) {
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
Mu_bitstate++;
if (Mu_bitstate == 4) {
MuPskTxinlv->bits(&bitshreg);
// LOG_DEBUG("bitshreg=%d", bitshreg);
sendsymbol(bitshreg);
// decodeMuPskEX(bitshreg);
Mu_bitstate = 0;
bitshreg = 0;
}
}
}
}