dl-fldigi/src/contestia/contestia.cxx

378 wiersze
8.5 KiB
C++

// ----------------------------------------------------------------------------
// contestia.cxx -- CONTESTIA modem
//
// Copyright (C) 2006-2010
// Dave Freese, W1HKJ
//
// This file is part of fldigi. Adapted from code contained in gmfsk source code
// distribution.
// Copyright (C) 2005
// Tomi Manninen (oh2bns@sral.fi)
//
// 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 <FL/Fl.H>
#include "contestia.h"
#include "modem.h"
#include "fl_digi.h"
#include "misc.h"
#include "confdialog.h"
#include "status.h"
#include "debug.h"
LOG_FILE_SOURCE(debug::LOG_MODEM);
using namespace std;
double contestia::nco(double freq)
{
preamblephase += 2.0 * M_PI * freq / samplerate;
if (preamblephase > TWOPI)
preamblephase -= TWOPI;
return cos(preamblephase);
}
void contestia::tx_init(SoundBase *sc)
{
scard = sc;
phaseacc = 0;
prevsymbol = cmplx (1.0, 0.0);
preamble = 32;
shreg = 0;
preamblesent = 0;
postamblesent = 0;
txbasefreq = get_txfreq_woffset();
rx_flush();
if (reverse) {
Tx->FirstCarrierMultiplier = (txbasefreq + (Tx->Bandwidth / 2)) / 500;
Tx->Reverse = 1;
} else {
Tx->FirstCarrierMultiplier = (txbasefreq - (Tx->Bandwidth / 2)) / 500;
Tx->Reverse = 0;
}
videoText();
Tx->Preset();
Tx->Start();
escape = 0;
}
void contestia::rx_flush()
{
unsigned char c;
Rx->Flush();
while (Rx->GetChar(c) > 0)
put_rx_char(c);
}
void contestia::send_tones()
{
if (!progdefaults.contestia_start_tones) return;
double freqa, freqb;
tone_bw = bandwidth;
tone_midfreq = txbasefreq;
if (reverse) {
freqa = tone_midfreq + (tone_bw / 2.0);
freqb = tone_midfreq - (tone_bw / 2.0);
} else {
freqa = tone_midfreq - (tone_bw / 2.0);
freqb = tone_midfreq + (tone_bw / 2.0);
}
preamblephase = 0;
for (int i = 0; i < SR4; i++)
tonebuff[2*SR4 + i] = tonebuff[i] = nco(freqa) * ampshape[i];
preamblephase = 0;
for (int i = 0; i < SR4; i++)
tonebuff[3*SR4 + i] = tonebuff[SR4 + i] = nco(freqb) * ampshape[i];
for (int j = 0; j < TONE_DURATION; j += SCBLOCKSIZE)
ModulateXmtr(&tonebuff[j], SCBLOCKSIZE);
}
void contestia::rx_init()
{
Rx->Reset();
escape = 0;
}
int contestia::unescape(int c)
{
if (progdefaults.contestia8bit == 0)
return c;
if (escape) {
escape = 0;
return c + 128;
}
if (c == 127) {
escape = 1;
return -1;
}
return c;
}
int contestia::tx_process()
{
int c = 0, len = 0;
unsigned char ch;
if (tones != progdefaults.contestiatones ||
bw != progdefaults.contestiabw ||
smargin != progdefaults.contestiasmargin ||
sinteg != progdefaults.contestiasinteg )
restart();
if (preamblesent != 1) {
send_tones();
preamblesent = 1;
// Olivia Transmitter class requires at least character
Tx->PutChar(0);
}
// The encoder works with BitsPerSymbol length blocks. If the
// modem already has that many characters buffered, don't try
// to read any more. If stopflag is set, we will always read
// whatever there is.
if (stopflag || (Tx->GetReadReady() < Tx->BitsPerSymbol)) {
if (!stopflag && (c = get_tx_char()) == GET_TX_CHAR_ETX)
stopflag = true;
if (stopflag)
Tx->Stop();
else {
if (c == GET_TX_CHAR_NODATA)
c = 0;
/* Replace un-representable characters with a dot */
if (c > (progdefaults.contestia8bit ? 255 : 127))
c = '.';
if (c > 127) {
c &= 127;
Tx->PutChar(127);
}
Tx->PutChar(c);
}
}
if (Tx->GetChar(ch) > 0)
if ((c = unescape(ch)) != -1)
put_echo_char(progdefaults.rx_lowercase ? tolower(c) : toupper(c));
if ((len = Tx->Output(txfbuffer)) > 0)
ModulateXmtr(txfbuffer, len);
if (stopflag && Tx->DoPostambleYet() == 1 && postamblesent != 1) {
postamblesent = 1;
send_tones();
}
if (!Tx->Running()) {
cwid();
stopflag = false;
return -1;
}
return 0;
}
int contestia::rx_process(const double *buf, int len)
{
int c;
unsigned char ch = 0;
static double snr = 1e-3;
static char msg1[20];
static char msg2[20];
if (tones != progdefaults.contestiatones ||
bw != progdefaults.contestiabw ||
smargin != progdefaults.contestiasmargin ||
sinteg != progdefaults.contestiasinteg )
restart();
if ((lastfreq != frequency || Rx->Reverse) && !reverse) {
Rx->FirstCarrierMultiplier = (frequency - (Rx->Bandwidth / 2)) / 500;
Rx->Reverse = 0;
lastfreq = frequency;
Rx->Preset();
}
else if ((lastfreq != frequency || !Rx->Reverse) && reverse) {
Rx->FirstCarrierMultiplier = (frequency + (Rx->Bandwidth / 2)) / 500;
Rx->Reverse = 1;
lastfreq = frequency;
Rx->Preset();
}
Rx->SyncThreshold = progStatus.sqlonoff ?
clamp(progStatus.sldrSquelchValue / 5.0 + 3.0, 3.0, 90.0) : 3.0;
Rx->Process(buf, len);
sp = 0;
for (int i = frequency - Rx->Bandwidth/2; i < frequency - 1 + Rx->Bandwidth/2; i++)
if (wf->Pwr(i) > sp)
sp = wf->Pwr(i);
np = wf->Pwr(frequency + Rx->Bandwidth/2 + 2*Rx->Bandwidth/Rx->Tones);
if (np == 0) np = sp + 1e-8;
sigpwr = decayavg( sigpwr, sp, 10);
noisepwr = decayavg( noisepwr, np, 50);
snr = CLAMP(sigpwr / noisepwr, 0.001, 100000);
metric = clamp( 5.0 * (Rx->SignalToNoiseRatio() - 3.0), 0, 100);
display_metric(metric);
bool gotchar = false;
while (Rx->GetChar(ch) > 0) {
if ((c = unescape(ch)) != -1 && c > 7) {
put_rx_char(progdefaults.rx_lowercase ? tolower(c) : c);
gotchar = true;
}
}
if (gotchar) {
snprintf(msg1, sizeof(msg1), "s/n: %4.1f dB", 10*log10(snr) - 20);
put_Status1(msg1, 5, STATUS_CLEAR);
snprintf(msg2, sizeof(msg2), "f/o %+4.1f Hz", Rx->FrequencyOffset());
put_Status2(msg2, 5, STATUS_CLEAR);
}
return 0;
}
void contestia::restart()
{
tones = progdefaults.contestiatones;
bw = progdefaults.contestiabw;
smargin = progdefaults.contestiasmargin;
sinteg = progdefaults.contestiasinteg;
samplerate = 8000;
bandwidth = 125 * (1 << bw);
Tx->Tones = 2 * (1 << tones);
Tx->Bandwidth = bandwidth;
Tx->SampleRate = samplerate;
Tx->OutputSampleRate = samplerate;
txbasefreq = get_txfreq_woffset();
Tx->bContestia = true;
if (reverse) {
Tx->FirstCarrierMultiplier = (txbasefreq + (Tx->Bandwidth / 2)) / 500;
Tx->Reverse = 1;
} else {
Tx->FirstCarrierMultiplier = (txbasefreq - (Tx->Bandwidth / 2)) / 500;
Tx->Reverse = 0;
}
if (Tx->Preset() < 0) {
LOG_ERROR("contestia: transmitter preset failed!");
return;
}
txbufferlen = Tx->MaxOutputLen;
if (txfbuffer) delete [] txfbuffer;
txfbuffer = new double[txbufferlen];
Rx->Tones = Tx->Tones;
Rx->Bandwidth = bandwidth;
Rx->SyncMargin = smargin;
Rx->SyncIntegLen = sinteg;
Rx->SyncThreshold = progStatus.sqlonoff ?
clamp(progStatus.sldrSquelchValue / 5.0 + 3.0, 0, 90.0) : 0.0;
Rx->SampleRate = samplerate;
Rx->InputSampleRate = samplerate;
Rx->bContestia = true;
if (reverse) {
Rx->FirstCarrierMultiplier = (frequency + (Rx->Bandwidth / 2)) / 500;
Rx->Reverse = 1;
} else {
Rx->FirstCarrierMultiplier = (frequency - (Rx->Bandwidth /2)) / 500;
Rx->Reverse = 0;
}
if (Rx->Preset() < 0) {
LOG_ERROR("contestia: receiver preset failed!");
return;
}
fragmentsize = 1024;
set_bandwidth(Tx->Bandwidth - Tx->Bandwidth / Tx->Tones);
put_MODEstatus("%s %" PRIuSZ "/%" PRIuSZ "", get_mode_name(), Tx->Tones, Tx->Bandwidth);
metric = 0;
sigpwr = 1e-10; noisepwr = 1e-8;
LOG_DEBUG("\nContestia Rx parameters:\n%s", Rx->PrintParameters());
}
void contestia::init()
{
restart();
modem::init();
set_scope_mode(Digiscope::BLANK);
}
contestia::contestia()
{
cap |= CAP_REV;
txfbuffer = 0;
samplerate = 8000;
Tx = new MFSK_Transmitter< double >;
Rx = new MFSK_Receiver< double >;
Tx->bContestia = true;
Rx->bContestia = true;
mode = MODE_CONTESTIA;
lastfreq = 0;
for (int i = 0; i < SR4; i++) ampshape[i] = 1.0;
for (int i = 0; i < SR4 / 8; i++)
ampshape[i] = ampshape[SR4 - 1 - i] = 0.5 * (1.0 - cos(M_PI * i / (SR4/8)));
for (int i = 0; i < TONE_DURATION; i++) tonebuff[i] = 0;
tone_bw = -1;
tone_midfreq = -1;
}
contestia::~contestia()
{
if (Tx) delete Tx;
if (Rx) delete Rx;
if (txfbuffer) delete [] txfbuffer;
}