dl-fldigi/src/mfsk/mfsk.cxx

1197 wiersze
24 KiB
C++

// ----------------------------------------------------------------------------
// mfsk.cxx -- mfsk modem
//
// Copyright (C) 2006-2009
// Dave Freese, W1HKJ
//
// This file is part of fldigi. Adapted from code contained in gmfsk source code
// distribution.
// gmfsk Copyright (C) 2001, 2002, 2003
// Tomi Manninen (oh2bns@sral.fi)
//
// 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 <cstdlib>
#include <cstring>
#include <libgen.h>
#include <FL/Fl.H>
#include "mfsk.h"
#include "modem.h"
#include "misc.h"
#include "main.h"
#include "fl_digi.h"
#include "configuration.h"
#include "status.h"
#include "trx.h"
#include "ascii.h"
#include "fileselect.h"
#include "qrunner.h"
#include "debug.h"
#define SOFTPROFILE false
using namespace std;
// MFSKpic receive start delay value based on a viterbi length of 45
// 44 nulls at 8 samples per pixel
// 88 nulls at 4 samples per pixel
// 176 nulls at 2 samples per pixel
struct TRACEPAIR {
int trace;
int delay;
TRACEPAIR( int a, int b) { trace = a; delay = b;}
};
TRACEPAIR tracepair(45, 352);
// enable to limit within band signal sidebands
// not really needed
static bool xmt_filter = false;
//=============================================================================
char mfskmsg[80];
//=============================================================================
#include "mfsk-pic.cxx"
void mfsk::tx_init(SoundBase *sc)
{
scard = sc;
txstate = TX_STATE_PREAMBLE;
bitstate = 0;
double factor = 1.5;
double bw2 = factor*(numtones + 1) * samplerate / symlen / 2.0;
double flo = (get_txfreq_woffset() - bw2);// / samplerate;
if (flo <= 100) flo = 100;
double fhi = (get_txfreq_woffset() + bw2);// / samplerate;
if (fhi >= samplerate/2 - 100) fhi = samplerate/2 - 100;
xmtfilt->init_bandpass (255, 1, flo/samplerate, fhi/samplerate);
videoText();
}
void mfsk::rx_init()
{
rxstate = RX_STATE_DATA;
synccounter = 0;
symcounter = 0;
met1 = 0.0;
met2 = 0.0;
counter = 0;
RXspp = 8;
for (int i = 0; i < 2 * symlen; i++) {
for (int j = 0; j < 32; j++)
pipe[i].vector[j] = cmplx(0,0);
}
reset_afc();
s2n = 0.0;
memset(picheader, ' ', PICHEADER - 1);
picheader[PICHEADER -1] = 0;
put_MODEstatus(mode);
syncfilter->reset();
staticburst = false;
s2n_valid = false;
}
void mfsk::init()
{
modem::init();
rx_init();
set_scope_mode(Digiscope::SCOPE);
// picture mode init
setpicture_link(this);
TXspp = txSPP;
RXspp = 8;
}
void mfsk::shutdown()
{
}
mfsk::~mfsk()
{
stopflag = true;
int msecs = 200;
while(--msecs && txstate != TX_STATE_PREAMBLE) MilliSleep(1);
// do not destroy picTxWin or picRxWin as there may be pending updates
// in the UI request queue
if (picTxWin) picTxWin->hide();
activate_mfsk_image_item(false);
if (bpfilt) delete bpfilt;
if (xmtfilt) delete xmtfilt;
if (rxinlv) delete rxinlv;
if (txinlv) delete txinlv;
if (dec2) delete dec2;
if (dec1) delete dec1;
if (enc) delete enc;
if (pipe) delete [] pipe;
if (hbfilt) delete hbfilt;
if (binsfft) delete binsfft;
for (int i = 0; i < SCOPESIZE; i++) {
if (vidfilter[i]) delete vidfilter[i];
}
if (syncfilter) delete syncfilter;
}
mfsk::mfsk(trx_mode mfsk_mode) : modem()
{
cap |= CAP_AFC | CAP_REV;
double bw, cf, flo, fhi;
mode = mfsk_mode;
depth = 10;
// CAP_IMG is set in cap iff image transfer supported
switch (mode) {
case MODE_MFSK4:
samplerate = 8000;
symlen = 2048;
symbits = 5;
depth = 5;
basetone = 256;
numtones = 32;
preamble = 107; // original mfsk modes
break;
case MODE_MFSK8:
samplerate = 8000;
symlen = 1024;
symbits = 5;
depth = 5;
basetone = 128;
numtones = 32;
preamble = 107; // original mfsk modes
break;
case MODE_MFSK31:
samplerate = 8000;
symlen = 256;
symbits = 3;
depth = 10;
basetone = 32;
numtones = 8;
preamble = 107; // original mfsk modes
break;
case MODE_MFSK32:
samplerate = 8000;
symlen = 256;
symbits = 4;
depth = 10;
basetone = 32;
numtones = 16;
preamble = 107; // original mfsk modes
cap |= CAP_IMG;
break;
case MODE_MFSK64:
samplerate = 8000;
symlen = 128;
symbits = 4;
depth = 10;
basetone = 16;
numtones = 16;
preamble = 180;
cap |= CAP_IMG;
break;
case MODE_MFSK128:
samplerate = 8000;
symlen = 64;
symbits = 4;
depth = 20;
basetone = 8;
numtones = 16;
cap |= CAP_IMG;
preamble = 214;
break;
case MODE_MFSK64L:
samplerate = 8000;
symlen = 128;
symbits = 4;
depth = 400;
preamble = 2500;
basetone = 16;
numtones = 16;
break;
case MODE_MFSK128L:
samplerate = 8000;
symlen = 64;
symbits = 4;
depth = 800;
preamble = 5000;
basetone = 8;
numtones = 16;
break;
case MODE_MFSK11:
samplerate = 11025;
symlen = 1024;
symbits = 4;
depth = 10;
basetone = 93;
numtones = 16;
preamble = 107;
break;
case MODE_MFSK22:
samplerate = 11025;
symlen = 512;
symbits = 4;
depth = 10;
basetone = 46;
numtones = 16;
preamble = 107;
break;
case MODE_MFSK16:
default:
samplerate = 8000;
symlen = 512;
symbits = 4;
depth = 10;
basetone = 64;
numtones = 16;
preamble = 107;
cap |= CAP_IMG;
break;
}
tonespacing = (double) samplerate / symlen;
basefreq = 1.0 * samplerate * basetone / symlen;
binsfft = new sfft (symlen, basetone, basetone + numtones );
hbfilt = new C_FIR_filter();
hbfilt->init_hilbert(37, 1);
syncfilter = new Cmovavg(8);
for (int i = 0; i < SCOPESIZE; i++)
vidfilter[i] = new Cmovavg(16);
pipe = new rxpipe[ 2 * symlen ];
enc = new encoder (K, POLY1, POLY2);
dec1 = new viterbi (K, POLY1, POLY2);
dec2 = new viterbi (K, POLY1, POLY2);
dec1->settraceback (tracepair.trace);
dec2->settraceback (tracepair.trace);
dec1->setchunksize (1);
dec2->setchunksize (1);
txinlv = new interleave (symbits, depth, INTERLEAVE_FWD);
rxinlv = new interleave (symbits, depth, INTERLEAVE_REV);
bw = (numtones - 1) * tonespacing;
cf = basefreq + bw / 2.0;
flo = (cf - bw/2 - 2 * tonespacing) / samplerate;
fhi = (cf + bw/2 + 2 * tonespacing) / samplerate;
bpfilt = new C_FIR_filter();
bpfilt->init_bandpass (127, 1, flo, fhi);
xmtfilt = new C_FIR_filter();
scopedata.alloc(symlen * 2);
fragmentsize = symlen;
bandwidth = (numtones - 1) * tonespacing;
startpic = false;
abortxmt = false;
stopflag = false;
bitshreg = 0;
bitstate = 0;
phaseacc = 0;
pipeptr = 0;
metric = 0;
prev1symbol = prev2symbol = 0;
symbolpair[0] = symbolpair[1] = 0;
// picTxWin and picRxWin are created once to support all instances of mfsk
if (!picTxWin) createTxViewer();
if (!picRxWin)
createRxViewer();
activate_mfsk_image_item(true);
afcmetric = 0.0;
datashreg = 1;
for (int i = 0; i < 128; i++) prepost[i] = 0;
}
//=====================================================================
// receive processing
//=====================================================================
void mfsk::s2nreport(void)
{
modem::s2nreport();
s2n_valid = false;
}
bool mfsk::check_picture_header(char c)
{
char *p;
if (c >= ' ' && c <= 'z') {
memmove(picheader, picheader + 1, PICHEADER - 1);
picheader[PICHEADER - 2] = c;
}
picW = 0;
picH = 0;
color = false;
p = strstr(picheader, "Pic:");
if (p == NULL)
return false;
p += 4;
if (*p == 0) return false;
while ( *p && isdigit(*p))
picW = (picW * 10) + (*p++ - '0');
if (*p++ != 'x')
return false;
while ( *p && isdigit(*p))
picH = (picH * 10) + (*p++ - '0');
if (*p == 'C') {
color = true;
p++;
}
if (*p == ';') {
if (picW == 0 || picH == 0 || picW > 4095 || picH > 4095)
return false;
RXspp = 8;
return true;
}
if (*p == 'p')
p++;
else
return false;
if (!*p)
return false;
RXspp = 8;
if (*p == '4') RXspp = 4;
if (*p == '2') RXspp = 2;
p++;
if (!*p)
return false;
if (*p != ';')
return false;
if (picW == 0 || picH == 0 || picW > 4095 || picH > 4095)
return false;
return true;
}
void mfsk::recvpic(cmplx z)
{
int byte;
picf += arg( conj(prevz) * z) * samplerate / TWOPI;
prevz = z;
if ((counter % RXspp) == 0) {
picf = 256 * (picf / RXspp - basefreq) / bandwidth;
byte = (int)CLAMP(picf, 0.0, 255.0);
if (reverse)
byte = 255 - byte;
if (color) {
pixelnbr = rgb + row + 3*col;
REQ(updateRxPic, byte, pixelnbr);
if (++col == picW) {
col = 0;
if (++rgb == 3) {
rgb = 0;
row += 3 * picW;
}
}
} else {
for (int i = 0; i < 3; i++)
REQ(updateRxPic, byte, pixelnbr++);
}
picf = 0.0;
int n = picW * picH * 3;
if (pixelnbr % (picW * 3) == 0) {
snprintf(mfskmsg, sizeof(mfskmsg),
"Rx pic: %3.1f%%",
(100.0f * pixelnbr) / n);
put_status(mfskmsg);
}
}
}
void mfsk::recvchar(int c)
{
if (c == -1 || c == 0)
return;
put_rx_char(c);
if (check_picture_header(c) == true) {
counter = tracepair.delay;
switch (mode) {
case MODE_MFSK16:
if (symbolbit == symbits) counter += symlen;
break;
case MODE_MFSK32:
if (symbolbit == symbits) counter += symlen;
break;
case MODE_MFSK64:
counter = 4956;
if (symbolbit % 2 == 0) counter += symlen;
break;
case MODE_MFSK128:
counter = 1824;
if (symbolbit % 2 == 0) counter += symlen;
break;
case MODE_MFSK31:
counter = 5216;
if (symbolbit == symbits) counter += symlen;
break;
case MODE_MFSK4:
case MODE_MFSK8:
case MODE_MFSK11:
case MODE_MFSK22:
default: break;
};
rxstate = RX_STATE_PICTURE_START;
picturesize = RXspp * picW * picH * (color ? 3 : 1);
pixelnbr = 0;
col = 0;
row = 0;
rgb = 0;
memset(picheader, ' ', PICHEADER - 1);
picheader[PICHEADER -1] = 0;
return;
} else counter = 0;
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
if ((c == SOH) && !s2n_valid) {
// starts collecting s2n from first SOH in stream (since start of RX)
s2n_valid = true;
s2n_sum = s2n_sum2 = s2n_ncount = 0.0;
}
if (s2n_valid) {
s2n_sum += s2n_metric;
s2n_sum2 += (s2n_metric * s2n_metric);
s2n_ncount++;
if (c == EOT)
s2nreport();
}
}
return;
}
void mfsk::recvbit(int bit)
{
int c;
datashreg = (datashreg << 1) | !!bit;
if ((datashreg & 7) == 1) {
c = varidec(datashreg >> 1);
recvchar(c);
datashreg = 1;
}
}
void mfsk::decodesymbol(unsigned char symbol)
{
int c, met;
symbolpair[0] = symbolpair[1];
symbolpair[1] = symbol;
symcounter = symcounter ? 0 : 1;
// only modes with odd number of symbits need a vote
if (symbits == 5 || symbits == 3) { // could use symbits % 2 == 0
if (symcounter) {
if ((c = dec1->decode(symbolpair, &met)) == -1)
return;
met1 = decayavg(met1, met, 50);//32);
if (met1 < met2)
return;
metric = met1;
} else {
if ((c = dec2->decode(symbolpair, &met)) == -1)
return;
met2 = decayavg(met2, met, 50);//32);
if (met2 < met1)
return;
metric = met2;
}
} else {
if (symcounter) return;
if ((c = dec2->decode(symbolpair, &met)) == -1)
return;
met2 = decayavg(met2, met, 50);//32);
metric = met2;
}
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
// s2n reporting: re-calibrate
s2n_metric = metric * 4.5 - 42;
s2n_metric = CLAMP(s2n_metric, 0.0, 100.0);
}
// Re-scale the metric and update main window
metric -= 60.0;
metric *= 0.5;
metric = CLAMP(metric, 0.0, 100.0);
display_metric(metric);
if (progStatus.sqlonoff && metric < progStatus.sldrSquelchValue)
return;
recvbit(c);
}
void mfsk::softdecode(cmplx *bins)
{
double binmag, sum=0, avg=0, b[symbits];
unsigned char symbols[symbits];
int i, j, k, CWIsymbol;
static int CWIcounter[MAX_SYMBOLS] = {0};
static const int CWI_MAXCOUNT=6; // this is the maximum number of repeated tones which is valid for the modem ( 0 excluded )
for (i = 0; i < symbits; i++)
b[i] = 0.0;
// Calculate the average signal, ignoring CWI tones
for (i = 0; i < numtones; i++) {
if ( CWIcounter[i] < CWI_MAXCOUNT )
sum += abs(bins[i]);
}
avg = sum / numtones;
// avoid divide by zero later
if ( sum < 1e-10 ) sum = 1e-10;
// dynamic CWI avoidance: use harddecode() result (currsymbol) for CWI detection
if (reverse)
CWIsymbol = (numtones - 1) - currsymbol;
else
CWIsymbol = currsymbol;
// Add or subtract the CWI counters based on harddecode result
// avoiding tone #0 by starting at 1
for (i = 1; i < numtones ; i++) {
if (reverse)
k = (numtones - 1) - i;
else
k = i;
if ( k == CWIsymbol)
CWIcounter[k]++;
else
CWIcounter[k]--;
// bounds-check the counts to keep the values sane
if (CWIcounter[k] < 0) CWIcounter[k] = 0;
if (CWIcounter[k] > CWI_MAXCOUNT) CWIcounter[k] = CWI_MAXCOUNT + 1;
}
// Grey decode and form soft decision samples
for (i = 0; i < numtones; i++) {
j = graydecode(i);
if (reverse)
k = (numtones - 1) - i;
else
k = i;
// Avoid CWI. This never affects tone #0
if ( CWIcounter[k] > CWI_MAXCOUNT ) {
binmag = avg; // soft-puncture to the average signal-level
} else if ( CWIsymbol == k )
binmag = 2.0f * abs(bins[k]); // give harddecode() a vote in softdecode's decision.
else
binmag = abs(bins[k]);
for (k = 0; k < symbits; k++)
b[k] += (j & (1 << (symbits - k - 1))) ? binmag : -binmag;
}
#if SOFTPROFILE
LOG_INFO("harddecode() symbol = %d", CWIsymbol );
#endif
// shift to range 0...255
for (i = 0; i < symbits; i++) {
unsigned char softbits;
if (staticburst)
softbits = 128; // puncturing
else
softbits = (unsigned char)clamp(128.0 + (b[i] / (sum) * 256.0), 0, 255);
symbols[i] = softbits;
#if SOFTPROFILE
LOG_INFO("softbits = %3u", softbits);
#endif
}
rxinlv->symbols(symbols);
for (i = 0; i < symbits; i++) {
symbolbit = i + 1;
decodesymbol(symbols[i]);
if (counter) return;
}
}
cmplx mfsk::mixer(cmplx in, double f)
{
cmplx z;
// Basetone is a nominal 1000 Hz
f -= tonespacing * basetone + bandwidth / 2;
z = in * cmplx( cos(phaseacc), sin(phaseacc) );
phaseacc -= TWOPI * f / samplerate;
if (phaseacc < 0) phaseacc += TWOPI;
return z;
}
// finds the tone bin with the largest signal level
// assumes that will be the present tone received
// with NO CW inteference
int mfsk::harddecode(cmplx *in)
{
double x, max = 0.0, avg = 0.0;
int i, symbol = 0;
int burstcount = 0;
for (int i = 0; i < numtones; i++)
avg += abs(in[i]);
avg /= numtones;
if (avg < 1e-20) avg = 1e-20;
for (i = 0; i < numtones; i++) {
x = abs(in[i]);
if ( x > max) {
max = x;
symbol = i;
}
if (x > 2.0 * avg) burstcount++;
}
staticburst = (burstcount == numtones);
if (!staticburst)
afcmetric = 0.95*afcmetric + 0.05 * (2 * max / avg);
else
afcmetric = 0.0;
return symbol;
}
void mfsk::update_syncscope()
{
int j;
int pipelen = 2 * symlen;
memset(scopedata, 0, 2 * symlen * sizeof(double));
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue)
for (unsigned int i = 0; i < SCOPESIZE; i++) {
j = (pipeptr + i * pipelen / SCOPESIZE + 1) % (pipelen);
scopedata[i] = vidfilter[i]->run(abs(pipe[j].vector[prev1symbol]));
}
set_scope(scopedata, SCOPESIZE);
scopedata.next(); // change buffers
snprintf(mfskmsg, sizeof(mfskmsg), "s/n %3.0f dB", 20.0 * log10(s2n));
put_Status1(mfskmsg);
}
void mfsk::synchronize()
{
int i, j;
double syn = -1;
double val, max = 0.0;
if (currsymbol == prev1symbol)
return;
if (prev1symbol == prev2symbol)
return;
j = pipeptr;
for (i = 0; i < 2 * symlen; i++) {
val = abs(pipe[j].vector[prev1symbol]);
if (val > max) {
max = val;
syn = i;
}
j = (j + 1) % (2 * symlen);
}
syn = syncfilter->run(syn);
synccounter += (int) floor((syn - symlen) / numtones + 0.5);
update_syncscope();
}
void mfsk::reset_afc() {
freqerr = 0.0;
syncfilter->reset();
return;
}
void mfsk::afc()
{
cmplx z;
cmplx prevvector;
double f, f1;
double ts = tonespacing / 4;
if (sigsearch) {
reset_afc();
sigsearch = 0;
}
if (staticburst || !progStatus.afconoff)
return;
if (metric < progStatus.sldrSquelchValue)
return;
if (afcmetric < 3.0)
return;
if (currsymbol != prev1symbol)
return;
if (pipeptr == 0)
prevvector = pipe[2*symlen - 1].vector[currsymbol];
else
prevvector = pipe[pipeptr - 1].vector[currsymbol];
z = conj(prevvector) * currvector;
f = arg(z) * samplerate / TWOPI;
f1 = tonespacing * (basetone + currsymbol);
if ( fabs(f1 - f) < ts) {
freqerr = decayavg(freqerr, (f1 - f), 32);
set_freq(frequency - freqerr);
}
}
void mfsk::eval_s2n()
{
sig = abs(pipe[pipeptr].vector[currsymbol]);
noise = (numtones -1) * abs(pipe[pipeptr].vector[prev2symbol]);
if (noise > 0)
s2n = decayavg ( s2n, sig / noise, 64 );
}
int mfsk::rx_process(const double *buf, int len)
{
cmplx z;
cmplx* bins = 0;
while (len-- > 0) {
// create analytic signal...
z = cmplx( *buf, *buf );
buf++;
hbfilt->run ( z, z );
// shift in frequency to the base freq
z = mixer(z, frequency);
// bandpass filter around the shifted center frequency
// with required bandwidth
bpfilt->run ( z, z );
// copy current vector to the pipe
binsfft->run (z, pipe[pipeptr].vector, 1);
bins = pipe[pipeptr].vector;
if (rxstate == RX_STATE_PICTURE_START) {
if (--counter == 0) {
counter = picturesize;
rxstate = RX_STATE_PICTURE;
REQ( showRxViewer, picW, picH );
}
}
if (rxstate == RX_STATE_PICTURE) {
if (--counter == 0) {
rxstate = RX_STATE_DATA;
put_status("");
string autosave_dir = PicsDir;
picRx->save_png(autosave_dir.c_str());
rx_init();
} else
recvpic(z);
continue;
}
// copy current vector to the pipe
// binsfft->run (z, pipe[pipeptr].vector, 1);
// bins = pipe[pipeptr].vector;
if (--synccounter <= 0) {
synccounter = symlen;
currsymbol = harddecode(bins);
currvector = bins[currsymbol];
softdecode(bins);
// symbol sync
synchronize();
// frequency tracking
afc();
eval_s2n();
prev2symbol = prev1symbol;
prev2vector = prev1vector;
prev1symbol = currsymbol;
prev1vector = currvector;
}
pipeptr = (pipeptr + 1) % (2 * symlen);
}
return 0;
}
//=====================================================================
// transmit processing
//=====================================================================
void mfsk::transmit(double *buf, int len)
{
if (xmt_filter)
for (int i = 0; i < len; i++) xmtfilt->Irun(buf[i], buf[i]);
ModulateXmtr(buf, len);
}
void mfsk::sendsymbol(int sym)
{
double f, phaseincr;
f = get_txfreq_woffset() - bandwidth / 2;
sym = grayencode(sym & (numtones - 1));
if (reverse)
sym = (numtones - 1) - sym;
phaseincr = TWOPI * (f + sym*tonespacing) / samplerate;
for (int i = 0; i < symlen; i++) {
outbuf[i] = cos(phaseacc);
phaseacc -= phaseincr;
if (phaseacc < 0) phaseacc += TWOPI;
}
transmit (outbuf, symlen);
}
void mfsk::sendbit(int bit)
{
int data = enc->encode(bit);
for (int i = 0; i < 2; i++) {
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
bitstate++;
if (bitstate == symbits) {
txinlv->bits(&bitshreg);
sendsymbol(bitshreg);
bitstate = 0;
bitshreg = 0;
}
}
}
void mfsk::sendchar(unsigned char c)
{
const char *code = varienc(c);
while (*code)
sendbit(*code++ - '0');
put_echo_char(c);
}
void mfsk::sendidle()
{
sendchar(0);
sendbit(1);
// extended zero bit stream
for (int i = 0; i < 32; i++)
sendbit(0);
}
void mfsk::flushtx(int nbits)
{
// flush the varicode decoder at the other end
sendbit(1);
// flush the convolutional encoder and interleaver
//VK2ETA high speed modes for (int i = 0; i < 107; i++)
//W1HKJ for (int i = 0; i < preamble; i++)
for (int i = 0; i < nbits; i++)
sendbit(0);
bitstate = 0;
}
void mfsk::sendpic(unsigned char *data, int len)
{
double *ptr;
double f;
int i, j;
ptr = outbuf;
for (i = 0; i < len; i++) {
if (txstate == TX_STATE_PICTURE)
REQ(updateTxPic, data[i]);
if (reverse)
f = get_txfreq_woffset() - bandwidth * (data[i] - 128) / 256.0;
else
f = get_txfreq_woffset() + bandwidth * (data[i] - 128) / 256.0;
for (j = 0; j < TXspp; j++) {
*ptr++ = cos(phaseacc);
phaseacc += TWOPI * f / samplerate;
if (phaseacc > TWOPI) phaseacc -= TWOPI;
}
}
transmit (outbuf, TXspp * len);
}
// -----------------------------------------------------------------------------
// send prologue consisting of tracepair.delay 0's
void mfsk::flush_xmt_filter(int n)
{
double f1 = get_txfreq_woffset() - bandwidth / 2.0;
double f2 = get_txfreq_woffset() + bandwidth / 2.0;
for (int i = 0; i < n; i++) {
outbuf[i] = cos(phaseacc);
phaseacc += TWOPI * (reverse ? f2 : f1) / samplerate;
if (phaseacc > TWOPI) phaseacc -= TWOPI;
}
transmit (outbuf, tracepair.delay);
}
void mfsk::send_prologue()
{
flush_xmt_filter(tracepair.delay);
}
void mfsk::send_epilogue()
{
flush_xmt_filter(64);
}
static bool close_after_transmit = false;
void mfsk::clearbits()
{
int data = enc->encode(0);
for (int k = 0; k < preamble; k++) {
for (int i = 0; i < 2; i++) {
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
bitstate++;
if (bitstate == symbits) {
txinlv->bits(&bitshreg);
bitstate = 0;
bitshreg = 0;
}
}
}
}
int mfsk::tx_process()
{
// filter test set to 1
#if 0
double *ptr;
double f;
char msg[100];
for (int i = 100; i < 3900; i++) {
ptr = outbuf;
f = 1.0 * i;
snprintf(msg, sizeof(msg), "freq: %.0f", f);
put_status(msg);
for (int j = 0; j < 32; j++) {
*ptr++ = cos(phaseacc);
phaseacc += TWOPI * f / samplerate;
if (phaseacc > TWOPI) phaseacc -= TWOPI;
}
transmit (outbuf, 32);
}
return -1;
#endif
int xmtbyte;
switch (txstate) {
case TX_STATE_PREAMBLE:
clearbits();
if (mode != MODE_MFSK64L && mode != MODE_MFSK128L )
for (int i = 0; i < preamble / 3; i++)
sendbit(0);
txstate = TX_STATE_START;
break;
case TX_STATE_START:
sendchar('\r');
sendchar(2); // STX
sendchar('\r');
txstate = TX_STATE_DATA;
break;
case TX_STATE_DATA:
xmtbyte = get_tx_char();
if(active_modem->XMLRPC_CPS_TEST) {
if(startpic) startpic = false;
if(xmtbyte == 0x05) {
sendchar(0x04); // 0x4 has the same symbol count as 0x5
break;
}
}
if (xmtbyte == 0x05 || startpic == true) {
put_status("Tx pic: start");
int len = (int)strlen(picheader);
for (int i = 0; i < len; i++)
sendchar(picheader[i]);
flushtx(preamble);
startpic = false;
txstate = TX_STATE_PICTURE_START;
}
else if ( xmtbyte == GET_TX_CHAR_ETX || stopflag)
txstate = TX_STATE_FLUSH;
else if (xmtbyte == GET_TX_CHAR_NODATA)
sendidle();
else
sendchar(xmtbyte);
break;
case TX_STATE_FLUSH:
sendchar('\r');
sendchar(4); // EOT
sendchar('\r');
flushtx(preamble);
rxstate = RX_STATE_DATA;
txstate = TX_STATE_PREAMBLE;
stopflag = false;
cwid();
return -1;
case TX_STATE_PICTURE_START:
send_prologue();
txstate = TX_STATE_PICTURE;
break;
case TX_STATE_PICTURE:
int i = 0;
int blocklen = 128;
while (i < xmtbytes) {
if (stopflag || abortxmt)
break;
if (i + blocklen < xmtbytes)
sendpic( &xmtpicbuff[i], blocklen);
else
sendpic( &xmtpicbuff[i], xmtbytes - i);
if ( (100 * i / xmtbytes) % 2 == 0) {
snprintf(mfskmsg, sizeof(mfskmsg),
"Tx pic: %3.1f%%",
(100.0f * i) / xmtbytes);
put_status(mfskmsg);
}
i += blocklen;
}
flushtx(preamble);
REQ_FLUSH(GET_THREAD_ID());
txstate = TX_STATE_DATA;
put_status("Tx pic: done");
FL_LOCK_E();
btnpicTxSendAbort->hide();
btnpicTxSPP->show();
btnpicTxSendColor->show();
btnpicTxSendGrey->show();
btnpicTxLoad->show();
btnpicTxClose->show();
if (close_after_transmit) picTxWin->hide();
close_after_transmit = false;
abortxmt = false;
rxstate = RX_STATE_DATA;
memset(picheader, ' ', PICHEADER - 1);
picheader[PICHEADER -1] = 0;
FL_UNLOCK_E();
break;
}
return 0;
}
void mfsk::send_color_image(std::string s)
{
if (load_image(s.c_str())) {
close_after_transmit = true;
pic_TxSendColor();
}
}
void mfsk::send_Grey_image(std::string s)
{
if (load_image(s.c_str())) {
close_after_transmit = true;
pic_TxSendGrey();
}
}