kopia lustrzana https://github.com/jamescoxon/dl-fldigi
Upstream version 2.11AA
rodzic
34adb843b8
commit
7c2464d222
22
ChangeLog
22
ChangeLog
|
@ -49,6 +49,28 @@ Change Log:
|
|||
(waterfall drop). A lower latency gives better resolution in time
|
||||
and less in frequency. The waterfall accuracy is still related to the
|
||||
FFT size and that remains fixed for an accuracy of 1 Hz.
|
||||
22) Added MultiPsk, DominoEX-FEC modes
|
||||
23) Added new modem type DEX (Domino Extended), an FEC only Domino mode
|
||||
that supports the full ASCII character set in the primary and the
|
||||
printable ASCII English character set (less control codes) in the
|
||||
secondary.
|
||||
24) Use Fl_Preferences for the status
|
||||
The new status filename is ~/.fldigi/status to avoid breaking older
|
||||
versions.
|
||||
25) Stacktrace updates - Some cosmetic changes to the output. Stack and
|
||||
version info no longer printed twice when a signal is caught.
|
||||
26) Use PortAudio extensions - Probes for PortAudio extensions with
|
||||
dlopen. right now the only extension used is the one that changes the
|
||||
JACK client name.
|
||||
27) Updated PulseAudio code so that non-ppm resampling is performed by
|
||||
PulseAudio when that sound i/o system is used.
|
||||
28) Changed the capture resampling code for PortAudio and PulseAudio to
|
||||
use libsamplerate's callback API.
|
||||
29) Use fileselect filter values. Made the audio format independent of
|
||||
the filename suffix in the generate/capture/playback code;
|
||||
uses the fileselector filter value instead.
|
||||
30) Update fileselector - Fileselector now remembers previous directory.
|
||||
|
||||
|
||||
2.10.3)
|
||||
1) Corrected memory leak bug.
|
||||
|
|
|
@ -9,7 +9,7 @@ dnl major and minor must be integers; patch may
|
|||
dnl contain other characters or be empty
|
||||
m4_define(FLDIGI_MAJOR, [2])
|
||||
m4_define(FLDIGI_MINOR, [11])
|
||||
m4_define(FLDIGI_PATCH, [Z])
|
||||
m4_define(FLDIGI_PATCH, [AA])
|
||||
|
||||
AC_INIT([fldigi], FLDIGI_MAJOR.FLDIGI_MINOR[FLDIGI_PATCH], [w1hkj AT w1hkj DOT com])
|
||||
|
||||
|
@ -81,6 +81,10 @@ AC_FUNC_STRFTIME
|
|||
AC_FUNC_STRTOD
|
||||
AC_CHECK_FUNCS([getaddrinfo gethostbyname localtime_r memmove memset mkdir select snprintf socket strcasecmp strchr strdup strerror strncasecmp strcasestr strrchr strstr strtol uname vsnprintf])
|
||||
|
||||
# Check for dlopen
|
||||
AC_SEARCH_LIBS([dlopen], [dl], [ac_cv_dlopen=1], [ac_cv_dlopen=0])
|
||||
AC_DEFINE_UNQUOTED(HAVE_DLOPEN, $ac_cv_dlopen, [Define to 1 if we have dlopen])
|
||||
|
||||
|
||||
AC_PRESERVE_HELP_ORDER
|
||||
|
||||
|
|
|
@ -115,6 +115,8 @@ fldigi_SOURCES += \
|
|||
dialogs/fl_digi.cxx \
|
||||
dialogs/font_browser.cxx \
|
||||
dialogs/Viewer.cxx \
|
||||
dex/dex.cxx \
|
||||
dex/dexvaricode.cxx \
|
||||
dominoex/dominoex.cxx \
|
||||
dominoex/dominovar.cxx \
|
||||
feld/feld.cxx \
|
||||
|
@ -146,6 +148,8 @@ fldigi_SOURCES += \
|
|||
include/configuration.h \
|
||||
include/cw.h \
|
||||
include/digiscope.h \
|
||||
include/dex.h \
|
||||
include/dexvaricode.h \
|
||||
include/dominoex.h \
|
||||
include/dominovar.h \
|
||||
include/feld.h \
|
||||
|
|
|
@ -0,0 +1,636 @@
|
|||
//
|
||||
// dex.cxx -- dex modem
|
||||
//
|
||||
// Copyright (C) 2008
|
||||
// David Freese (w1hkj@w1hkj.com)
|
||||
//
|
||||
// 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 2 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, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "confdialog.h"
|
||||
#include "status.h"
|
||||
|
||||
#include "dex.h"
|
||||
#include "trx.h"
|
||||
#include "fft.h"
|
||||
#include "filters.h"
|
||||
#include "misc.h"
|
||||
#include "sound.h"
|
||||
#include "dexvaricode.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
char dexmsg[80];
|
||||
|
||||
void dex::tx_init(SoundBase *sc)
|
||||
{
|
||||
scard = sc;
|
||||
txstate = TX_STATE_PREAMBLE;
|
||||
txprevtone = 0;
|
||||
bitstate = 0;
|
||||
counter = 0;
|
||||
txphase = 0;
|
||||
videoText();
|
||||
strSecXmtText = progdefaults.DEXsecText;
|
||||
if (strSecXmtText.length() == 0)
|
||||
strSecXmtText = "fldigi "PACKAGE_VERSION" ";
|
||||
cptr = 0;
|
||||
}
|
||||
|
||||
void dex::rx_init()
|
||||
{
|
||||
synccounter = 0;
|
||||
symcounter = 0;
|
||||
met1 = 0.0;
|
||||
met2 = 0.0;
|
||||
counter = 0;
|
||||
phase[0] = 0.0;
|
||||
for (int i = 0; i < DEXMAXFFTS; i++)
|
||||
phase[i+1] = 0.0;
|
||||
put_MODEstatus(mode);
|
||||
put_sec_char(0);
|
||||
syncfilter->reset();
|
||||
datashreg = 1;
|
||||
}
|
||||
|
||||
void dex::reset_filters()
|
||||
{
|
||||
// fft filter at first IF frequency
|
||||
if (progdefaults.DEX_FILTER == false) {
|
||||
fft->create_filter( (DEXFIRSTIF - 2.0 * bandwidth) / samplerate,
|
||||
(DEXFIRSTIF + 2.0 * bandwidth)/ samplerate );
|
||||
} else {
|
||||
fft->create_filter( (DEXFIRSTIF - 0.5 * progdefaults.DEX_BW * bandwidth) / samplerate,
|
||||
(DEXFIRSTIF + 0.5 * progdefaults.DEX_BW * bandwidth)/ samplerate );
|
||||
}
|
||||
filter_reset = false;
|
||||
}
|
||||
|
||||
void dex::restart()
|
||||
{
|
||||
filter_reset = true;
|
||||
}
|
||||
|
||||
void dex::init()
|
||||
{
|
||||
modem::init();
|
||||
reset_filters();
|
||||
rx_init();
|
||||
|
||||
set_scope_mode(Digiscope::DOMDATA);
|
||||
}
|
||||
|
||||
dex::~dex()
|
||||
{
|
||||
if (hilbert) delete hilbert;
|
||||
|
||||
for (int i = 0; i < DEXMAXFFTS; i++) {
|
||||
if (binsfft[i]) delete binsfft[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < DEXSCOPESIZE; i++) {
|
||||
if (vidfilter[i]) delete vidfilter[i];
|
||||
}
|
||||
if (syncfilter) delete syncfilter;
|
||||
|
||||
if (pipe) delete [] pipe;
|
||||
if (fft) delete fft;
|
||||
|
||||
if (Rxinlv) delete Rxinlv;
|
||||
if (Txinlv) delete Txinlv;
|
||||
if (Dec) delete Dec;
|
||||
if (Enc) delete Enc;
|
||||
|
||||
}
|
||||
|
||||
dex::dex(trx_mode md)
|
||||
{
|
||||
int basetone, lotone, hitone;
|
||||
|
||||
mode = md;
|
||||
|
||||
switch (mode) {
|
||||
// 11.025 kHz modes
|
||||
case MODE_DEX5:
|
||||
symlen = 2048;
|
||||
doublespaced = 1;
|
||||
samplerate = 11025;
|
||||
break;
|
||||
|
||||
case MODE_DEX11:
|
||||
symlen = 1024;
|
||||
doublespaced = 0;
|
||||
samplerate = 11025;
|
||||
break;
|
||||
|
||||
case MODE_DEX22:
|
||||
symlen = 512;
|
||||
doublespaced = 0;
|
||||
samplerate = 11025;
|
||||
break;
|
||||
// 8kHz modes
|
||||
case MODE_DEX4:
|
||||
symlen = 2048;
|
||||
doublespaced = 1;
|
||||
samplerate = 8000;
|
||||
break;
|
||||
|
||||
case MODE_DEX8:
|
||||
symlen = 1024;
|
||||
doublespaced = 1;
|
||||
samplerate = 8000;
|
||||
|
||||
case MODE_DEX16:
|
||||
default:
|
||||
symlen = 512;
|
||||
doublespaced = 0;
|
||||
samplerate = 8000;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
basetone = (int)floor(DEXBASEFREQ * symlen / samplerate + 0.5);
|
||||
lotone = basetone - (DEXNUMMTONES/2) * (doublespaced ? 2 : 1);
|
||||
hitone = basetone + 3 * (DEXNUMMTONES/2) * (doublespaced ? 2 : 1);
|
||||
|
||||
tonespacing = (double) (samplerate * ((doublespaced) ? 2 : 1)) / symlen;
|
||||
|
||||
bandwidth = DEXNUMMTONES * tonespacing;
|
||||
|
||||
hilbert = new C_FIR_filter();
|
||||
hilbert->init_hilbert(37, 1);
|
||||
|
||||
paths = progdefaults.DEX_PATHS;
|
||||
|
||||
for (int i = 0; i < DEXMAXFFTS; i++)
|
||||
binsfft[i] = new sfft (symlen, lotone, hitone);
|
||||
|
||||
// fft filter at first if frequency
|
||||
fft = new fftfilt( (DEXFIRSTIF - 0.5 * progdefaults.DEX_BW * bandwidth) / samplerate,
|
||||
(DEXFIRSTIF + 0.5 * progdefaults.DEX_BW * bandwidth)/ samplerate,
|
||||
1024 );
|
||||
|
||||
for (int i = 0; i < DEXSCOPESIZE; i++)
|
||||
vidfilter[i] = new Cmovavg(16);
|
||||
|
||||
syncfilter = new Cmovavg(8);
|
||||
|
||||
twosym = 2 * symlen;
|
||||
pipe = new DEXrxpipe[twosym];
|
||||
|
||||
scopedata.alloc(DEXSCOPESIZE);
|
||||
videodata.alloc((DEXMAXFFTS * DEXNUMMTONES * 2 * (doublespaced?2:1) ));
|
||||
|
||||
pipeptr = 0;
|
||||
|
||||
symcounter = 0;
|
||||
metric = 0.0;
|
||||
|
||||
fragmentsize = symlen;
|
||||
|
||||
s2n = 0.0;
|
||||
|
||||
prev1symbol = prev2symbol = 0;
|
||||
|
||||
Enc = new encoder (DEX_K, DEX_POLY1, DEX_POLY2);
|
||||
Dec = new viterbi (DEX_K, DEX_POLY1, DEX_POLY2);
|
||||
Dec->settraceback (45);
|
||||
Dec->setchunksize (1);
|
||||
Txinlv = new interleave (4, INTERLEAVE_FWD); // 4x4x10
|
||||
Rxinlv = new interleave (4, INTERLEAVE_REV); // 4x4x10
|
||||
bitstate = 0;
|
||||
symbolpair[0] = symbolpair[1] = 0;
|
||||
datashreg = 1;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
//=====================================================================
|
||||
// rx modules
|
||||
|
||||
complex dex::mixer(int n, complex in)
|
||||
{
|
||||
complex z;
|
||||
double f;
|
||||
// first IF mixer (n == 0) plus
|
||||
// DEXMAXFFTS mixers are supported each separated by 1/DEXMAXFFTS bin size
|
||||
// n == 1, 2, 3, 4 ... DEXMAXFFTS
|
||||
if (n == 0)
|
||||
f = frequency - DEXFIRSTIF;
|
||||
else
|
||||
f = DEXFIRSTIF - DEXBASEFREQ - bandwidth/2 + (samplerate / symlen) * (1.0 * n / paths );
|
||||
z.re = cos(phase[n]);
|
||||
z.im = sin(phase[n]);
|
||||
z = z * in;
|
||||
phase[n] -= twopi * f / samplerate;
|
||||
if (phase[n] > M_PI)
|
||||
phase[n] -= twopi;
|
||||
else if (phase[n] < M_PI)
|
||||
phase[n] += twopi;
|
||||
return z;
|
||||
}
|
||||
|
||||
void dex::recvchar(int c)
|
||||
{
|
||||
if (c == -1)
|
||||
return;
|
||||
if (c & 0x100)
|
||||
put_sec_char(c & 0xFF);
|
||||
else
|
||||
put_rx_char(c & 0xFF);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Receive
|
||||
//=============================================================================
|
||||
|
||||
void dex::decodePairs(unsigned char symbol)
|
||||
{
|
||||
int c, ch, met;
|
||||
|
||||
symbolpair[0] = symbolpair[1];
|
||||
symbolpair[1] = symbol;
|
||||
|
||||
symcounter = symcounter ? 0 : 1;
|
||||
|
||||
if (symcounter) return;
|
||||
|
||||
c = Dec->decode (symbolpair, &met);
|
||||
|
||||
if (c == -1)
|
||||
return;
|
||||
|
||||
if (progStatus.sqlonoff && metric < progStatus.sldrSquelchValue)
|
||||
return;
|
||||
|
||||
datashreg = (datashreg << 1) | !!c;
|
||||
if ((datashreg & 7) == 1) {
|
||||
ch = dexvaridec(datashreg >> 1);
|
||||
recvchar(ch);
|
||||
datashreg = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void dex::decodeEX(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;
|
||||
c = c / 2;
|
||||
}
|
||||
|
||||
Rxinlv->symbols(symbols);
|
||||
|
||||
for (int i = 0; i < 4; i++) decodePairs(symbols[i]);
|
||||
|
||||
}
|
||||
|
||||
void dex::decodesymbol()
|
||||
{
|
||||
int c;
|
||||
double fdiff;
|
||||
|
||||
// Decode the IFK+ sequence, which results in a single nibble
|
||||
|
||||
fdiff = currsymbol - prev1symbol;
|
||||
if (reverse) fdiff = -fdiff;
|
||||
if (doublespaced) fdiff /= 2 * paths;
|
||||
else fdiff /= paths;
|
||||
c = (int)floor(fdiff + .5) - 2;
|
||||
if (c < 0) c += DEXNUMMTONES;
|
||||
|
||||
decodeEX(c);
|
||||
}
|
||||
|
||||
int dex::harddecode()
|
||||
{
|
||||
double x, max = 0.0;
|
||||
int symbol = 0;
|
||||
for (int i = 0; i < (paths * DEXNUMMTONES * 2 * (doublespaced ? 2 : 1) ); i++) {
|
||||
x = pipe[pipeptr].vector[i].mag();
|
||||
if (x > max) {
|
||||
max = x;
|
||||
symbol = i;
|
||||
}
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void dex::update_syncscope()
|
||||
{
|
||||
|
||||
double max = 0, min = 1e6, range, mag;
|
||||
|
||||
// dom waterfall
|
||||
memset(videodata, 0, (paths * DEXNUMMTONES * 2 * (doublespaced?2:1) ) * sizeof(double));
|
||||
|
||||
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
|
||||
for (int i = 0; i < (paths * DEXNUMMTONES * 2 * (doublespaced?2:1) ); i++ ) {
|
||||
mag = pipe[pipeptr].vector[i].mag();
|
||||
if (max < mag) max = mag;
|
||||
if (min > mag) min = mag;
|
||||
}
|
||||
range = max - min;
|
||||
for (int i = 0; i < (paths * DEXNUMMTONES * 2 * (doublespaced?2:1) ); i++ ) {
|
||||
if (range > 2) {
|
||||
mag = (pipe[pipeptr].vector[i].mag() - min) / range + 0.0001;
|
||||
mag = 1 + 2 * log10(mag);
|
||||
if (mag < 0) mag = 0;
|
||||
} else
|
||||
mag = 0;
|
||||
videodata[i] = 255*mag;
|
||||
}
|
||||
}
|
||||
set_video(videodata, (paths * DEXNUMMTONES * 2 * (doublespaced?2:1) ), false);
|
||||
videodata.next();
|
||||
|
||||
// set_scope(scopedata, twosym);
|
||||
// 64 data points is sufficient to show the signal progression through the
|
||||
// convolution filter.
|
||||
memset(scopedata, 0, DEXSCOPESIZE * sizeof(double));
|
||||
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
|
||||
for (unsigned int i = 0, j = 0; i < DEXSCOPESIZE; i++) {
|
||||
j = (pipeptr + i * twosym / DEXSCOPESIZE + 1) % (twosym);
|
||||
scopedata[i] = vidfilter[i]->run(pipe[j].vector[prev1symbol].mag());
|
||||
}
|
||||
}
|
||||
set_scope(scopedata, DEXSCOPESIZE);
|
||||
scopedata.next();
|
||||
}
|
||||
|
||||
void dex::synchronize()
|
||||
{
|
||||
// int syn = -1;
|
||||
double syn = -1;
|
||||
double val, max = 0.0;
|
||||
|
||||
if (currsymbol == prev1symbol)
|
||||
return;
|
||||
if (prev1symbol == prev2symbol)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0, j = pipeptr; i < twosym; i++) {
|
||||
val = (pipe[j].vector[prev1symbol]).mag();
|
||||
if (val > max) {
|
||||
max = val;
|
||||
syn = i;
|
||||
}
|
||||
j = (j + 1) % twosym;
|
||||
}
|
||||
syn = syncfilter->run(syn);
|
||||
|
||||
synccounter += (int) floor(1.0 * (syn - symlen) / DEXNUMMTONES + 0.5);
|
||||
}
|
||||
|
||||
|
||||
void dex::eval_s2n()
|
||||
{
|
||||
if (currsymbol != prev1symbol && prev1symbol != prev2symbol) {
|
||||
sig = pipe[pipeptr].vector[currsymbol].mag();
|
||||
noise = 0.0;
|
||||
for (int i = 0; i < paths * DEXNUMMTONES * 2 * (doublespaced?2:1); i++) {
|
||||
if (i != currsymbol)
|
||||
noise += pipe[pipeptr].vector[i].mag();
|
||||
}
|
||||
noise /= (paths * DEXNUMMTONES * 2 * (doublespaced?2:1) - 1);
|
||||
|
||||
s2n = decayavg( s2n, sig / noise, 8);
|
||||
|
||||
metric = 3*(20*log10(s2n) - 9.0);
|
||||
|
||||
display_metric(metric);
|
||||
|
||||
snprintf(dexmsg, sizeof(dexmsg), "s/n %3.0f dB", metric / 3.0 - 2.0);
|
||||
put_Status1(dexmsg);
|
||||
}
|
||||
}
|
||||
|
||||
int dex::rx_process(const double *buf, int len)
|
||||
{
|
||||
complex zref, z, *zp, *bins = 0;
|
||||
int n;
|
||||
|
||||
if (filter_reset) reset_filters();
|
||||
|
||||
if (paths != progdefaults.DEX_PATHS) {
|
||||
paths = progdefaults.DEX_PATHS;
|
||||
reset_filters();
|
||||
}
|
||||
|
||||
while (len) {
|
||||
// create analytic signal at first IF
|
||||
zref.re = zref.im = *buf++;
|
||||
hilbert->run(zref, zref);
|
||||
zref = mixer(0, zref);
|
||||
// filter using fft convolution
|
||||
n = fft->run(zref, &zp);
|
||||
|
||||
if (n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
// process DEXMAXFFTS sets of sliding FFTs spaced at 1/DEXMAXFFTS bin intervals each of which
|
||||
// is a matched filter for the current symbol length
|
||||
for (int n = 0; n < paths; n++) {
|
||||
// shift in frequency to base band for the sliding DFTs
|
||||
z = mixer(n + 1, zp[i]);
|
||||
bins = binsfft[n]->run(z);
|
||||
// copy current vector to the pipe interleaving the FFT vectors
|
||||
for (int i = 0; i < DEXNUMMTONES * 2 * (doublespaced ? 2 : 1); i++) {
|
||||
pipe[pipeptr].vector[n + paths * i] = bins[i];
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Transmit methods
|
||||
//=============================================================================
|
||||
|
||||
int dex::get_secondary_char()
|
||||
{
|
||||
char chr;
|
||||
if (cptr > strSecXmtText.length()) cptr = 0;
|
||||
chr = strSecXmtText[cptr++];
|
||||
put_sec_char( chr );
|
||||
return chr;
|
||||
}
|
||||
|
||||
void dex::sendtone(int tone, int duration)
|
||||
{
|
||||
double f, phaseincr;
|
||||
f = tone * tonespacing + get_txfreq_woffset() - bandwidth / 2;
|
||||
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 > M_PI)
|
||||
txphase -= twopi;
|
||||
else if (txphase < M_PI)
|
||||
txphase += twopi;
|
||||
}
|
||||
ModulateXmtr(outbuf, symlen);
|
||||
}
|
||||
}
|
||||
|
||||
void dex::sendsymbol(int sym)
|
||||
{
|
||||
complex z;
|
||||
int tone;
|
||||
|
||||
tone = (txprevtone + 2 + sym) % DEXNUMMTONES;
|
||||
txprevtone = tone;
|
||||
if (reverse)
|
||||
tone = (DEXNUMMTONES - 1) - tone;
|
||||
sendtone(tone, 1);
|
||||
}
|
||||
|
||||
// Send DEX FEC varicode
|
||||
|
||||
void dex::sendchar(unsigned char c, int secondary)
|
||||
{
|
||||
const char *code;
|
||||
|
||||
code = dexvarienc(c, secondary);
|
||||
|
||||
while (*code) {
|
||||
int data = Enc->encode(*code++ - '0');
|
||||
for (int i = 0; i < 2; i++) {
|
||||
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
|
||||
bitstate++;
|
||||
if (bitstate == 4) {
|
||||
Txinlv->bits(&bitshreg);
|
||||
sendsymbol(bitshreg);
|
||||
bitstate = 0;
|
||||
bitshreg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!secondary)
|
||||
put_echo_char(c);
|
||||
}
|
||||
|
||||
void dex::sendidle()
|
||||
{
|
||||
sendchar(0, 0); // <NUL>
|
||||
}
|
||||
|
||||
void dex::sendsecondary()
|
||||
{
|
||||
int c = get_secondary_char();
|
||||
sendchar(c & 0xFF, 1);
|
||||
}
|
||||
|
||||
void dex::Clearbits()
|
||||
{
|
||||
int data = Enc->encode(0);
|
||||
for (int k = 0; k < 100; k++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
|
||||
bitstate++;
|
||||
if (bitstate == 4) {
|
||||
Txinlv->bits(&bitshreg);
|
||||
bitstate = 0;
|
||||
bitshreg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dex::flushtx()
|
||||
{
|
||||
// flush the varicode decoder at the other end
|
||||
// flush the convolutional encoder and interleaver
|
||||
for (int i = 0; i < 4; i++)
|
||||
sendidle();
|
||||
bitstate = 0;
|
||||
}
|
||||
|
||||
int dex::tx_process()
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (txstate) {
|
||||
case TX_STATE_PREAMBLE:
|
||||
Clearbits();
|
||||
for (int j = 0; j < 16; j++) sendsymbol(0);
|
||||
// sendtone(DEXNUMMTONES/2, 4);
|
||||
// for (int k = 0; k < 3; k++) {
|
||||
// sendtone(DEXNUMMTONES, 3);
|
||||
// sendtone(0, 3);
|
||||
// }
|
||||
// sendtone(DEXNUMMTONES/2, 4);
|
||||
|
||||
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 == -1)
|
||||
sendsecondary();
|
||||
else if (i == 3)
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// dexvaricode.cxx -- DEX Varicode
|
||||
//
|
||||
// Copyright (C) 2009
|
||||
// Dave Freese, W1HKJ
|
||||
//
|
||||
// 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 2 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, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "mfskvaricode.h"
|
||||
#include "dexvaricode.h"
|
||||
|
||||
// DEX varicode is an extended set of the IZ8BLY MFSK varicode that uses the
|
||||
// unallocated remaining 12 bit codes for a secondary character set.
|
||||
|
||||
// Primary character set (same as MFSK)
|
||||
|
||||
// extended 12 bit codes for secondary characters
|
||||
// 90 used, leaving 10 for possible special use
|
||||
|
||||
// encoding table
|
||||
|
||||
static const char *dex_varicode[] = {
|
||||
"101110000000", /* 032 - <SPC> */
|
||||
"101110100000", /* 033 - ! */
|
||||
"101110101000", /* 034 - '"' */
|
||||
"101110101100", /* 035 - # */
|
||||
"101110110000", /* 036 - $ */
|
||||
"101110110100", /* 037 - % */
|
||||
"101110111000", /* 038 - & */
|
||||
"101110111100", /* 039 - ' */
|
||||
"101111000000", /* 040 - ( */
|
||||
"101111010000", /* 041 - ) */
|
||||
"101111010100", /* 042 - * */
|
||||
"101111011000", /* 043 - + */
|
||||
"101111011100", /* 044 - , */
|
||||
"101111100000", /* 045 - - */
|
||||
"101111101000", /* 046 - . */
|
||||
"101111101100", /* 047 - / */
|
||||
"101111110000", /* 048 - 0 */
|
||||
"101111110100", /* 049 - 1 */
|
||||
"101111111000", /* 050 - 2 */
|
||||
"101111111100", /* 051 - 3 */
|
||||
"110000000000", /* 052 - 4 */
|
||||
"110100000000", /* 053 - 5 */
|
||||
"110101000000", /* 054 - 6 */
|
||||
"110101010100", /* 055 - 7 */
|
||||
"110101011000", /* 056 - 8 */
|
||||
"110101011100", /* 057 - 9 */
|
||||
"110101100000", /* 058 - : */
|
||||
"110101101000", /* 059 - ; */
|
||||
"110101101100", /* 060 - < */
|
||||
"110101110000", /* 061 - = */
|
||||
"110101110100", /* 062 - > */
|
||||
"110101111000", /* 063 - ? */
|
||||
"110101111100", /* 064 - @ */
|
||||
"110110000000", /* 065 - A */
|
||||
"110110100000", /* 066 - B */
|
||||
"110110101000", /* 067 - C */
|
||||
"110110101100", /* 068 - D */
|
||||
"110110110000", /* 069 - E */
|
||||
"110110110100", /* 070 - F */
|
||||
"110110111000", /* 071 - G */
|
||||
"110110111100", /* 072 - H */
|
||||
"110111000000", /* 073 - I */
|
||||
"110111010000", /* 074 - J */
|
||||
"110111010100", /* 075 - K */
|
||||
"110111011000", /* 076 - L */
|
||||
"110111011100", /* 077 - M */
|
||||
"110111100000", /* 078 - N */
|
||||
"110111101000", /* 079 - O */
|
||||
"110111101100", /* 080 - P */
|
||||
"110111110000", /* 081 - Q */
|
||||
"110111110100", /* 082 - R */
|
||||
"110111111000", /* 083 - S */
|
||||
"110111111100", /* 084 - T */
|
||||
"111000000000", /* 085 - U */
|
||||
"111010000000", /* 086 - V */
|
||||
"111010100000", /* 087 - W */
|
||||
"111010101100", /* 088 - X */
|
||||
"111010110000", /* 089 - Y */
|
||||
"111010110100", /* 090 - Z */
|
||||
"111010111000", /* 091 - [ */
|
||||
"111010111100", /* 092 - \ */
|
||||
"111011000000", /* 093 - ] */
|
||||
"111011010000", /* 094 - ^ */
|
||||
"111011010100", /* 095 - _ */
|
||||
"111011011000", /* 096 - ` */
|
||||
"111011011100", /* 097 - a */
|
||||
"111011100000", /* 098 - b */
|
||||
"111011101000", /* 099 - c */
|
||||
"111011101100", /* 100 - d */
|
||||
"111011110000", /* 101 - e */
|
||||
"111011110100", /* 102 - f */
|
||||
"111011111000", /* 103 - g */
|
||||
"111011111100", /* 104 - h */
|
||||
"111100000000", /* 105 - i */
|
||||
"111101000000", /* 106 - j */
|
||||
"111101010000", /* 107 - k */
|
||||
"111101010100", /* 108 - l */
|
||||
"111101011000", /* 109 - m */
|
||||
"111101011100", /* 110 - n */
|
||||
"111101100000", /* 111 - o */
|
||||
"111101101000", /* 112 - p */
|
||||
"111101101100", /* 113 - q */
|
||||
"111101110000", /* 114 - r */
|
||||
"111101110100", /* 115 - s */
|
||||
"111101111000", /* 116 - t */
|
||||
"111101111100", /* 117 - u */
|
||||
"111110000000", /* 118 - v */
|
||||
"111110100000", /* 119 - w */
|
||||
"111110101000", /* 120 - x */
|
||||
"111110101100", /* 121 - y */
|
||||
"111110110000" /* 122 - z */
|
||||
};
|
||||
|
||||
// unused 12 bit varicodes
|
||||
/*
|
||||
static char *unused[] = {
|
||||
"111110110100",
|
||||
"111110111000",
|
||||
"111110111100",
|
||||
"111111000000",
|
||||
"111111010100",
|
||||
"111111011000",
|
||||
"111111011100",
|
||||
"111111100000",
|
||||
"111111101000",
|
||||
"111111101100",
|
||||
"111111110000",
|
||||
"111111110100",
|
||||
"111111111100"
|
||||
};
|
||||
*/
|
||||
|
||||
// decoding table
|
||||
|
||||
static const unsigned int dex_varidecode[] = {
|
||||
0xB80, 0xBA0, 0xBA8, 0xBAC, 0xBB0, 0xBB4, 0xBB8, 0xBBC,
|
||||
0xBC0, 0xBD0, 0xBD4, 0xBD8, 0xBDC, 0xBE0, 0xBE8, 0xBEC,
|
||||
0xBF0, 0xBF4, 0xBF8, 0xBFC, 0xC00, 0xD00, 0xD40, 0xD54,
|
||||
0xD58, 0xD5C, 0xD60, 0xD68, 0xD6C, 0xD70, 0xD74, 0xD78,
|
||||
0xD7C, 0xD80, 0xDA0, 0xDA8, 0xDAC, 0xDB0, 0xDB4, 0xDB8,
|
||||
0xDBC, 0xDC0, 0xDD0, 0xDD4, 0xDD8, 0xDDC, 0xDE0, 0xDE8,
|
||||
0xDEC, 0xDF0, 0xDF4, 0xDF8, 0xDFC, 0xE00, 0xE80, 0xEA0,
|
||||
0xEAC, 0xEB0, 0xEB4, 0xEB8, 0xEBC, 0xEC0, 0xED0, 0xED4,
|
||||
0xED8, 0xEDC, 0xEE0, 0xEE8, 0xEEC, 0xEF0, 0xEF4, 0xEF8,
|
||||
0xEFC, 0xF00, 0xF40, 0xF50, 0xF54, 0xF58, 0xF5C, 0xF60,
|
||||
0xF68, 0xF6C, 0xF70, 0xF74, 0xF78, 0xF7C, 0xF80, 0xFA0,
|
||||
0xFA8, 0xFAC, 0xFB0
|
||||
};
|
||||
|
||||
const char *dexvarienc(int c, int sec)
|
||||
{
|
||||
if (sec == 0)
|
||||
return varienc(c); // mfsk varicode
|
||||
else
|
||||
if (c >= ' ' && c <= 'z')
|
||||
return dex_varicode[c - ' '];
|
||||
|
||||
return varienc(0); // return code for NULL if not in tables
|
||||
}
|
||||
|
||||
int dexvaridec(unsigned int symbol)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (symbol < 0xB80)
|
||||
return varidec(symbol); // find in the MFSK decode table
|
||||
|
||||
for (i = 0; i < 92; i++)
|
||||
if (symbol == dex_varidecode[i])
|
||||
return (' ' + i + 0x100); // found in the extended decode table
|
||||
|
||||
return -1; // not found
|
||||
}
|
||||
|
|
@ -884,6 +884,38 @@ static void cb_cntPostTiming(Fl_Counter* o, void*) {
|
|||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *tabDEX=(Fl_Group *)0;
|
||||
|
||||
Fl_Input *txtDEXSecondary=(Fl_Input *)0;
|
||||
|
||||
static void cb_txtDEXSecondary(Fl_Input* o, void*) {
|
||||
progdefaults.DEXsecText = o->value();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Counter *valDEX_BW=(Fl_Counter *)0;
|
||||
|
||||
static void cb_valDEX_BW(Fl_Counter* o, void*) {
|
||||
progdefaults.DEX_BW = o->value();
|
||||
resetDEX();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Check_Button *valDEX_FILTER=(Fl_Check_Button *)0;
|
||||
|
||||
static void cb_valDEX_FILTER(Fl_Check_Button* o, void*) {
|
||||
progdefaults.DEX_FILTER = o->value();
|
||||
resetDEX();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Counter *valDEX_PATHS=(Fl_Counter *)0;
|
||||
|
||||
static void cb_valDEX_PATHS(Fl_Counter* o, void*) {
|
||||
progdefaults.DEX_PATHS = (int)o->value();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *tabDomEX=(Fl_Group *)0;
|
||||
|
||||
Fl_Input *txtSecondary=(Fl_Input *)0;
|
||||
|
@ -893,13 +925,6 @@ static void cb_txtSecondary(Fl_Input* o, void*) {
|
|||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Button *btnRestartDomEX=(Fl_Button *)0;
|
||||
|
||||
static void cb_btnRestartDomEX(Fl_Button*, void*) {
|
||||
progdefaults.storeDefaults();
|
||||
resetDOMEX();
|
||||
}
|
||||
|
||||
Fl_Counter *valDominoEX_BW=(Fl_Counter *)0;
|
||||
|
||||
static void cb_valDominoEX_BW(Fl_Counter* o, void*) {
|
||||
|
@ -916,6 +941,20 @@ resetDOMEX();
|
|||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Check_Button *chkDominoEX_FEC=(Fl_Check_Button *)0;
|
||||
|
||||
static void cb_chkDominoEX_FEC(Fl_Check_Button* o, void*) {
|
||||
progdefaults.DOMINOEX_FEC = o->value();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Counter *valDominoEX_PATHS=(Fl_Counter *)0;
|
||||
|
||||
static void cb_valDominoEX_PATHS(Fl_Counter* o, void*) {
|
||||
progdefaults.DOMINOEX_PATHS = (int)o->value();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *tabFeld=(Fl_Group *)0;
|
||||
|
||||
Fl_Choice *selHellFont=(Fl_Choice *)0;
|
||||
|
@ -2053,33 +2092,80 @@ l with your sound hardware.");
|
|||
} // Fl_Counter* cntPostTiming
|
||||
tabCWQSK->end();
|
||||
} // Fl_Group* tabCWQSK
|
||||
{ tabDomEX = new Fl_Group(0, 50, 400, 170, "DomEX");
|
||||
{ tabDEX = new Fl_Group(0, 44, 400, 170, "Dex");
|
||||
tabDEX->color((Fl_Color)51);
|
||||
tabDEX->selection_color((Fl_Color)51);
|
||||
{ txtDEXSecondary = new Fl_Input(20, 75, 360, 44, "Secondary Text");
|
||||
txtDEXSecondary->type(4);
|
||||
txtDEXSecondary->callback((Fl_Callback*)cb_txtDEXSecondary);
|
||||
txtDEXSecondary->align(FL_ALIGN_TOP_LEFT);
|
||||
txtDEXSecondary->when(FL_WHEN_CHANGED);
|
||||
} // Fl_Input* txtDEXSecondary
|
||||
{ Fl_Counter* o = valDEX_BW = new Fl_Counter(20, 130, 63, 21, "BW factor:");
|
||||
valDEX_BW->type(1);
|
||||
valDEX_BW->minimum(1);
|
||||
valDEX_BW->maximum(2);
|
||||
valDEX_BW->step(0.1);
|
||||
valDEX_BW->value(1.5);
|
||||
valDEX_BW->callback((Fl_Callback*)cb_valDEX_BW);
|
||||
o->value(progdefaults.DEX_BW);
|
||||
} // Fl_Counter* valDEX_BW
|
||||
{ Fl_Check_Button* o = valDEX_FILTER = new Fl_Check_Button(110, 130, 83, 19, "Filter ON");
|
||||
valDEX_FILTER->down_box(FL_DOWN_BOX);
|
||||
valDEX_FILTER->value(1);
|
||||
valDEX_FILTER->callback((Fl_Callback*)cb_valDEX_FILTER);
|
||||
o->value(progdefaults.DEX_FILTER);
|
||||
} // Fl_Check_Button* valDEX_FILTER
|
||||
{ Fl_Counter* o = valDEX_PATHS = new Fl_Counter(20, 174, 63, 21, "Paths");
|
||||
valDEX_PATHS->type(1);
|
||||
valDEX_PATHS->minimum(4);
|
||||
valDEX_PATHS->maximum(8);
|
||||
valDEX_PATHS->step(1);
|
||||
valDEX_PATHS->value(5);
|
||||
valDEX_PATHS->callback((Fl_Callback*)cb_valDEX_PATHS);
|
||||
o->value(progdefaults.DEX_PATHS);
|
||||
} // Fl_Counter* valDEX_PATHS
|
||||
tabDEX->end();
|
||||
} // Fl_Group* tabDEX
|
||||
{ tabDomEX = new Fl_Group(0, 50, 400, 170, "Dom");
|
||||
tabDomEX->color((Fl_Color)51);
|
||||
tabDomEX->selection_color((Fl_Color)51);
|
||||
tabDomEX->hide();
|
||||
{ txtSecondary = new Fl_Input(20, 75, 360, 44, "Secondary Text");
|
||||
txtSecondary->type(4);
|
||||
txtSecondary->callback((Fl_Callback*)cb_txtSecondary);
|
||||
txtSecondary->align(FL_ALIGN_TOP_LEFT);
|
||||
txtSecondary->when(FL_WHEN_CHANGED);
|
||||
} // Fl_Input* txtSecondary
|
||||
{ btnRestartDomEX = new Fl_Button(300, 172, 79, 28, "Restart");
|
||||
btnRestartDomEX->callback((Fl_Callback*)cb_btnRestartDomEX);
|
||||
btnRestartDomEX->hide();
|
||||
} // Fl_Button* btnRestartDomEX
|
||||
{ Fl_Counter* o = valDominoEX_BW = new Fl_Counter(25, 134, 63, 21, "BW factor:");
|
||||
{ Fl_Counter* o = valDominoEX_BW = new Fl_Counter(20, 130, 63, 21, "BW factor:");
|
||||
valDominoEX_BW->type(1);
|
||||
valDominoEX_BW->minimum(1);
|
||||
valDominoEX_BW->maximum(2);
|
||||
valDominoEX_BW->step(0.1);
|
||||
valDominoEX_BW->value(2);
|
||||
valDominoEX_BW->value(1.5);
|
||||
valDominoEX_BW->callback((Fl_Callback*)cb_valDominoEX_BW);
|
||||
o->value(progdefaults.DOMINOEX_BW);
|
||||
} // Fl_Counter* valDominoEX_BW
|
||||
{ Fl_Check_Button* o = valDominoEX_FILTER = new Fl_Check_Button(107, 136, 83, 19, "Filter ON");
|
||||
{ Fl_Check_Button* o = valDominoEX_FILTER = new Fl_Check_Button(110, 130, 83, 19, "Filter ON");
|
||||
valDominoEX_FILTER->down_box(FL_DOWN_BOX);
|
||||
valDominoEX_FILTER->value(1);
|
||||
valDominoEX_FILTER->callback((Fl_Callback*)cb_valDominoEX_FILTER);
|
||||
o->value(progdefaults.DOMINOEX_FILTER);
|
||||
} // Fl_Check_Button* valDominoEX_FILTER
|
||||
{ Fl_Check_Button* o = chkDominoEX_FEC = new Fl_Check_Button(220, 130, 70, 15, "FEC");
|
||||
chkDominoEX_FEC->down_box(FL_DOWN_BOX);
|
||||
chkDominoEX_FEC->callback((Fl_Callback*)cb_chkDominoEX_FEC);
|
||||
o->value(progdefaults.DOMINOEX_FEC);
|
||||
} // Fl_Check_Button* chkDominoEX_FEC
|
||||
{ Fl_Counter* o = valDominoEX_PATHS = new Fl_Counter(20, 174, 63, 21, "Paths");
|
||||
valDominoEX_PATHS->type(1);
|
||||
valDominoEX_PATHS->minimum(4);
|
||||
valDominoEX_PATHS->maximum(8);
|
||||
valDominoEX_PATHS->step(1);
|
||||
valDominoEX_PATHS->value(5);
|
||||
valDominoEX_PATHS->callback((Fl_Callback*)cb_valDominoEX_PATHS);
|
||||
o->value(progdefaults.DOMINOEX_PATHS);
|
||||
} // Fl_Counter* valDominoEX_PATHS
|
||||
tabDomEX->end();
|
||||
} // Fl_Group* tabDomEX
|
||||
{ tabFeld = new Fl_Group(0, 50, 400, 170, "Feld");
|
||||
|
|
|
@ -1052,9 +1052,43 @@ progdefaults.changed = true;}
|
|||
code1 {o->maximum((int)(2400/progdefaults.CWspeed)/2.0);}
|
||||
}
|
||||
}
|
||||
Fl_Group tabDEX {
|
||||
label Dex open
|
||||
xywh {0 44 400 170} color 51 selection_color 51
|
||||
} {
|
||||
Fl_Input txtDEXSecondary {
|
||||
label {Secondary Text}
|
||||
callback {progdefaults.DEXsecText = o->value();
|
||||
progdefaults.changed = true;}
|
||||
xywh {20 75 360 44} type Multiline align 5 when 1
|
||||
}
|
||||
Fl_Counter valDEX_BW {
|
||||
label {BW factor:}
|
||||
callback {progdefaults.DEX_BW = o->value();
|
||||
resetDEX();
|
||||
progdefaults.changed = true;}
|
||||
xywh {20 130 63 21} type Simple minimum 1 maximum 2 step 0.1 value 1.5
|
||||
code0 {o->value(progdefaults.DEX_BW);}
|
||||
}
|
||||
Fl_Check_Button valDEX_FILTER {
|
||||
label {Filter ON}
|
||||
callback {progdefaults.DEX_FILTER = o->value();
|
||||
resetDEX();
|
||||
progdefaults.changed = true;}
|
||||
xywh {110 130 83 19} down_box DOWN_BOX value 1
|
||||
code0 {o->value(progdefaults.DEX_FILTER);}
|
||||
}
|
||||
Fl_Counter valDEX_PATHS {
|
||||
label Paths
|
||||
callback {progdefaults.DEX_PATHS = (int)o->value();
|
||||
progdefaults.changed = true;} selected
|
||||
xywh {20 174 63 21} type Simple minimum 4 maximum 8 step 1 value 5
|
||||
code0 {o->value(progdefaults.DEX_PATHS);}
|
||||
}
|
||||
}
|
||||
Fl_Group tabDomEX {
|
||||
label DomEX open
|
||||
xywh {0 50 400 170} color 51 selection_color 51
|
||||
label Dom open
|
||||
xywh {0 50 400 170} color 51 selection_color 51 hide
|
||||
} {
|
||||
Fl_Input txtSecondary {
|
||||
label {Secondary Text}
|
||||
|
@ -1062,28 +1096,36 @@ progdefaults.changed = true;}
|
|||
progdefaults.changed = true;}
|
||||
xywh {20 75 360 44} type Multiline align 5 when 1
|
||||
}
|
||||
Fl_Button btnRestartDomEX {
|
||||
label Restart
|
||||
callback {progdefaults.storeDefaults();
|
||||
resetDOMEX();}
|
||||
xywh {300 172 79 28} hide
|
||||
}
|
||||
Fl_Counter valDominoEX_BW {
|
||||
label {BW factor:}
|
||||
callback {progdefaults.DOMINOEX_BW = o->value();
|
||||
resetDOMEX();
|
||||
progdefaults.changed = true;}
|
||||
xywh {25 134 63 21} type Simple minimum 1 maximum 2 step 0.1 value 2
|
||||
xywh {20 130 63 21} type Simple minimum 1 maximum 2 step 0.1 value 1.5
|
||||
code0 {o->value(progdefaults.DOMINOEX_BW);}
|
||||
}
|
||||
Fl_Check_Button valDominoEX_FILTER {
|
||||
label {Filter ON}
|
||||
callback {progdefaults.DOMINOEX_FILTER = o->value();
|
||||
resetDOMEX();
|
||||
progdefaults.changed = true;} selected
|
||||
xywh {107 136 83 19} down_box DOWN_BOX
|
||||
progdefaults.changed = true;}
|
||||
xywh {110 130 83 19} down_box DOWN_BOX value 1
|
||||
code0 {o->value(progdefaults.DOMINOEX_FILTER);}
|
||||
}
|
||||
Fl_Check_Button chkDominoEX_FEC {
|
||||
label FEC
|
||||
callback {progdefaults.DOMINOEX_FEC = o->value();
|
||||
progdefaults.changed = true;}
|
||||
xywh {220 130 70 15} down_box DOWN_BOX
|
||||
code0 {o->value(progdefaults.DOMINOEX_FEC);}
|
||||
}
|
||||
Fl_Counter valDominoEX_PATHS {
|
||||
label Paths
|
||||
callback {progdefaults.DOMINOEX_PATHS = (int)o->value();
|
||||
progdefaults.changed = true;}
|
||||
xywh {20 174 63 21} type Simple minimum 4 maximum 8 step 1 value 5
|
||||
code0 {o->value(progdefaults.DOMINOEX_PATHS);}
|
||||
}
|
||||
}
|
||||
Fl_Group tabFeld {
|
||||
label Feld open
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "mt63.h"
|
||||
#include "rtty.h"
|
||||
#include "olivia.h"
|
||||
#include "dex.h"
|
||||
#include "dominoex.h"
|
||||
#include "feld.h"
|
||||
#include "throb.h"
|
||||
|
@ -201,6 +202,16 @@ Fl_Menu_Item quick_change_mt63[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
Fl_Menu_Item quick_change_dex[] = {
|
||||
{ mode_info[MODE_DEX4].name, 0, cb_init_mode, (void *)MODE_DEX4 },
|
||||
{ mode_info[MODE_DEX5].name, 0, cb_init_mode, (void *)MODE_DEX5 },
|
||||
{ mode_info[MODE_DEX8].name, 0, cb_init_mode, (void *)MODE_DEX8 },
|
||||
{ mode_info[MODE_DEX11].name, 0, cb_init_mode, (void *)MODE_DEX11 },
|
||||
{ mode_info[MODE_DEX16].name, 0, cb_init_mode, (void *)MODE_DEX16 },
|
||||
{ mode_info[MODE_DEX22].name, 0, cb_init_mode, (void *)MODE_DEX22 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
Fl_Menu_Item quick_change_domino[] = {
|
||||
{ mode_info[MODE_DOMINOEX4].name, 0, cb_init_mode, (void *)MODE_DOMINOEX4 },
|
||||
{ mode_info[MODE_DOMINOEX5].name, 0, cb_init_mode, (void *)MODE_DOMINOEX5 },
|
||||
|
@ -390,6 +401,14 @@ void init_modem(trx_mode mode)
|
|||
modem_config_tab = tabCW;
|
||||
break;
|
||||
|
||||
case MODE_DEX4: case MODE_DEX5: case MODE_DEX8:
|
||||
case MODE_DEX11: case MODE_DEX16: case MODE_DEX22:
|
||||
startup_modem(*mode_info[mode].modem ? *mode_info[mode].modem :
|
||||
*mode_info[mode].modem = new dex(mode));
|
||||
quick_change = quick_change_dex;
|
||||
modem_config_tab = tabDEX;
|
||||
break;
|
||||
|
||||
case MODE_DOMINOEX4: case MODE_DOMINOEX5: case MODE_DOMINOEX8:
|
||||
case MODE_DOMINOEX11: case MODE_DOMINOEX16: case MODE_DOMINOEX22:
|
||||
startup_modem(*mode_info[mode].modem ? *mode_info[mode].modem :
|
||||
|
@ -472,7 +491,7 @@ void init_modem(trx_mode mode)
|
|||
}
|
||||
|
||||
clear_StatusMessages();
|
||||
progStatus.saveModeState(mode);
|
||||
progStatus.lastmode = mode;
|
||||
}
|
||||
|
||||
void init_modem_sync(trx_mode m)
|
||||
|
@ -1020,6 +1039,15 @@ Fl_Menu_Item menu_[] = {
|
|||
|
||||
{ mode_info[MODE_CW].name, 0, cb_init_mode, (void *)MODE_CW, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
|
||||
{"DEX", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DEX4].name, 0, cb_init_mode, (void *)MODE_DEX4, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DEX5].name, 0, cb_init_mode, (void *)MODE_DEX5, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DEX8].name, 0, cb_init_mode, (void *)MODE_DEX8, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DEX11].name, 0, cb_init_mode, (void *)MODE_DEX11, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DEX16].name, 0, cb_init_mode, (void *)MODE_DEX16, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DEX22].name, 0, cb_init_mode, (void *)MODE_DEX22, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
|
||||
{"DominoEX", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DOMINOEX4].name, 0, cb_init_mode, (void *)MODE_DOMINOEX4, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
{ mode_info[MODE_DOMINOEX5].name, 0, cb_init_mode, (void *)MODE_DOMINOEX5, 0, FL_NORMAL_LABEL, 0, 14, 0},
|
||||
|
@ -1749,7 +1777,7 @@ void clear_status_cb(void *)
|
|||
|
||||
void put_status(const char *msg, double timeout)
|
||||
{
|
||||
static char m[60];
|
||||
static char m[50];
|
||||
strncpy(m, msg, sizeof(m));
|
||||
m[sizeof(m) - 1] = '\0';
|
||||
|
||||
|
@ -1927,6 +1955,18 @@ void resetOLIVIA() {
|
|||
active_modem->restart();
|
||||
}
|
||||
|
||||
void resetDEX() {
|
||||
trx_mode md = active_modem->get_mode();
|
||||
if (md == MODE_DEX4 ||
|
||||
md == MODE_DEX5 ||
|
||||
md == MODE_DEX8 ||
|
||||
md == MODE_DEX11 ||
|
||||
md == MODE_DEX16 ||
|
||||
md == MODE_DEX22 ) {
|
||||
active_modem->restart();
|
||||
}
|
||||
}
|
||||
|
||||
void resetDOMEX() {
|
||||
trx_mode md = active_modem->get_mode();
|
||||
if (md == MODE_DOMINOEX4 ||
|
||||
|
@ -1935,8 +1975,6 @@ void resetDOMEX() {
|
|||
md == MODE_DOMINOEX11 ||
|
||||
md == MODE_DOMINOEX16 ||
|
||||
md == MODE_DOMINOEX22 ) {
|
||||
|
||||
// trx_reset();
|
||||
active_modem->restart();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "filters.h"
|
||||
#include "misc.h"
|
||||
#include "sound.h"
|
||||
#include "mfskvaricode.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -46,6 +47,7 @@ void dominoex::tx_init(SoundBase *sc)
|
|||
scard = sc;
|
||||
txstate = TX_STATE_PREAMBLE;
|
||||
txprevtone = 0;
|
||||
bitstate = 0;
|
||||
counter = 0;
|
||||
txphase = 0;
|
||||
videoText();
|
||||
|
@ -59,10 +61,13 @@ void dominoex::rx_init()
|
|||
met2 = 0.0;
|
||||
counter = 0;
|
||||
phase[0] = 0.0;
|
||||
for (int i = 0; i < NUMFFTS; i++)
|
||||
for (int i = 0; i < MAXFFTS; i++)
|
||||
phase[i+1] = 0.0;
|
||||
put_MODEstatus(mode);
|
||||
put_sec_char(0);
|
||||
syncfilter->reset();
|
||||
datashreg = 1;
|
||||
for (int i = 0; i < VBINS; i++) vbins[i] = 0;
|
||||
}
|
||||
|
||||
void dominoex::reset_filters()
|
||||
|
@ -100,16 +105,23 @@ dominoex::~dominoex()
|
|||
{
|
||||
if (hilbert) delete hilbert;
|
||||
|
||||
for (int i = 0; i < NUMFFTS; i++) {
|
||||
for (int i = 0; i < MAXFFTS; i++) {
|
||||
if (binsfft[i]) delete binsfft[i];
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -168,23 +180,26 @@ dominoex::dominoex(trx_mode md)
|
|||
hilbert = new C_FIR_filter();
|
||||
hilbert->init_hilbert(37, 1);
|
||||
|
||||
for (int i = 0; i < NUMFFTS; i++) {
|
||||
paths = progdefaults.DOMINOEX_PATHS;
|
||||
|
||||
for (int i = 0; i < MAXFFTS; i++)
|
||||
binsfft[i] = new sfft (symlen, lotone, hitone);
|
||||
}
|
||||
|
||||
// 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 );
|
||||
|
||||
|
||||
for (int i = 0; i < SCOPESIZE; i++)
|
||||
vidfilter[i] = new Cmovavg(16);
|
||||
|
||||
syncfilter = new Cmovavg(8);
|
||||
|
||||
twosym = 2 * symlen;
|
||||
pipe = new domrxpipe[twosym];
|
||||
|
||||
scopedata.alloc(SCOPESIZE);
|
||||
videodata.alloc((NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) ));
|
||||
videodata.alloc((MAXFFTS * NUMTONES * 2 * (doublespaced?2:1) ));
|
||||
|
||||
pipeptr = 0;
|
||||
|
||||
|
@ -197,6 +212,16 @@ dominoex::dominoex(trx_mode md)
|
|||
|
||||
prev1symbol = prev2symbol = 0;
|
||||
|
||||
MuPskEnc = new encoder (K, POLY1, POLY2);
|
||||
MuPskDec = new viterbi (K, POLY1, POLY2);
|
||||
MuPskDec->settraceback (45);
|
||||
MuPskDec->setchunksize (1);
|
||||
MuPskTxinlv = new interleave (-1, INTERLEAVE_FWD);
|
||||
MuPskRxinlv = new interleave (-1, INTERLEAVE_REV);
|
||||
bitstate = 0;
|
||||
symbolpair[0] = symbolpair[1] = 0;
|
||||
datashreg = 1;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
@ -208,12 +233,12 @@ complex dominoex::mixer(int n, complex in)
|
|||
complex z;
|
||||
double f;
|
||||
// first IF mixer (n == 0) plus
|
||||
// NUMFFTS mixers are supported each separated by 1/NUMFFTS bin size
|
||||
// n == 1, 2, 3, 4 ... NUMFFTS
|
||||
// MAXFFTS mixers are supported each separated by 1/MAXFFTS bin size
|
||||
// n == 1, 2, 3, 4 ... MAXFFTS
|
||||
if (n == 0)
|
||||
f = frequency - FIRSTIF;
|
||||
else
|
||||
f = FIRSTIF - BASEFREQ - bandwidth/2 + (samplerate / symlen) * (1.0 * n / NUMFFTS);
|
||||
f = FIRSTIF - BASEFREQ - bandwidth/2 + (samplerate / symlen) * (1.0 * n / paths );
|
||||
z.re = cos(phase[n]);
|
||||
z.im = sin(phase[n]);
|
||||
z = z * in;
|
||||
|
@ -235,20 +260,9 @@ void dominoex::recvchar(int c)
|
|||
put_rx_char(c & 0xFF);
|
||||
}
|
||||
|
||||
void dominoex::decodesymbol()
|
||||
void dominoex::decodeDomino(int c)
|
||||
{
|
||||
int c, sym, ch;
|
||||
double fdiff;
|
||||
|
||||
// Decode the IFK+ sequence, which results in a single nibble
|
||||
|
||||
fdiff = currsymbol - prev1symbol;
|
||||
if (reverse) fdiff = -fdiff;
|
||||
if (doublespaced) fdiff /= 2 * NUMFFTS;
|
||||
else fdiff /= NUMFFTS;
|
||||
c = (int)floor(fdiff + .5) - 2;
|
||||
if (c < 0) c += NUMTONES;
|
||||
|
||||
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) {
|
||||
|
@ -271,14 +285,33 @@ void dominoex::decodesymbol()
|
|||
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;
|
||||
if (doublespaced) fdiff /= 2 * paths;
|
||||
else fdiff /= paths;
|
||||
c = (int)floor(fdiff + .5) - 2;
|
||||
if (c < 0) c += NUMTONES;
|
||||
|
||||
if (progdefaults.DOMINOEX_FEC)
|
||||
decodeMuPskEX(c);
|
||||
else
|
||||
decodeDomino(c);
|
||||
}
|
||||
|
||||
int dominoex::harddecode()
|
||||
{
|
||||
double x, max = 0.0;
|
||||
int symbol = 0;
|
||||
for (int i = 0; i < (NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) ); i++) {
|
||||
for (int i = 0; i < (paths * NUMTONES * 2 * (doublespaced ? 2 : 1) ); i++) {
|
||||
x = pipe[pipeptr].vector[i].mag();
|
||||
if (x > max) {
|
||||
max = x;
|
||||
|
@ -294,16 +327,16 @@ void dominoex::update_syncscope()
|
|||
double max = 0, min = 1e6, range, mag;
|
||||
|
||||
// dom waterfall
|
||||
memset(videodata, 0, (NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) ) * sizeof(double));
|
||||
memset(videodata, 0, (paths * NUMTONES * 2 * (doublespaced?2:1) ) * sizeof(double));
|
||||
|
||||
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) {
|
||||
for (int i = 0; i < (NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) ); i++ ) {
|
||||
for (int i = 0; i < (paths * NUMTONES * 2 * (doublespaced?2:1) ); i++ ) {
|
||||
mag = pipe[pipeptr].vector[i].mag();
|
||||
if (max < mag) max = mag;
|
||||
if (min > mag) min = mag;
|
||||
}
|
||||
range = max - min;
|
||||
for (int i = 0; i < (NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) ); i++ ) {
|
||||
for (int i = 0; i < (paths * NUMTONES * 2 * (doublespaced?2:1) ); i++ ) {
|
||||
if (range > 2) {
|
||||
mag = (pipe[pipeptr].vector[i].mag() - min) / range + 0.0001;
|
||||
mag = 1 + 2 * log10(mag);
|
||||
|
@ -313,7 +346,7 @@ void dominoex::update_syncscope()
|
|||
videodata[i] = 255*mag;
|
||||
}
|
||||
}
|
||||
set_video(videodata, (NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) ), false);
|
||||
set_video(videodata, (paths * NUMTONES * 2 * (doublespaced?2:1) ), false);
|
||||
videodata.next();
|
||||
|
||||
// set_scope(scopedata, twosym);
|
||||
|
@ -324,7 +357,6 @@ void dominoex::update_syncscope()
|
|||
for (unsigned int i = 0, j = 0; i < SCOPESIZE; i++) {
|
||||
j = (pipeptr + i * twosym / SCOPESIZE + 1) % (twosym);
|
||||
scopedata[i] = vidfilter[i]->run(pipe[j].vector[prev1symbol].mag());
|
||||
// scopedata[i] = pipe[j].vector[prev1symbol].mag();
|
||||
}
|
||||
}
|
||||
set_scope(scopedata, SCOPESIZE);
|
||||
|
@ -333,7 +365,8 @@ void dominoex::update_syncscope()
|
|||
|
||||
void dominoex::synchronize()
|
||||
{
|
||||
int syn = -1;
|
||||
// int syn = -1;
|
||||
double syn = -1;
|
||||
double val, max = 0.0;
|
||||
|
||||
if (currsymbol == prev1symbol)
|
||||
|
@ -349,6 +382,8 @@ void dominoex::synchronize()
|
|||
}
|
||||
j = (j + 1) % twosym;
|
||||
}
|
||||
syn = syncfilter->run(syn);
|
||||
|
||||
synccounter += (int) floor(1.0 * (syn - symlen) / NUMTONES + 0.5);
|
||||
}
|
||||
|
||||
|
@ -358,11 +393,11 @@ void dominoex::eval_s2n()
|
|||
if (currsymbol != prev1symbol && prev1symbol != prev2symbol) {
|
||||
sig = pipe[pipeptr].vector[currsymbol].mag();
|
||||
noise = 0.0;
|
||||
for (int i = 0; i < NUMFFTS * NUMTONES * 2 * (doublespaced?2:1); i++) {
|
||||
for (int i = 0; i < paths * NUMTONES * 2 * (doublespaced?2:1); i++) {
|
||||
if (i != currsymbol)
|
||||
noise += pipe[pipeptr].vector[i].mag();
|
||||
}
|
||||
noise /= (NUMFFTS * NUMTONES * 2 * (doublespaced?2:1) - 1);
|
||||
noise /= (paths * NUMTONES * 2 * (doublespaced?2:1) - 1);
|
||||
|
||||
s2n = decayavg( s2n, sig / noise, 8);
|
||||
|
||||
|
@ -370,17 +405,22 @@ void dominoex::eval_s2n()
|
|||
|
||||
display_metric(metric);
|
||||
|
||||
snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", metric / 3.0);
|
||||
snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", metric / 3.0 - 2.0);
|
||||
put_Status1(dommsg);
|
||||
}
|
||||
}
|
||||
|
||||
int dominoex::rx_process(const double *buf, int len)
|
||||
{
|
||||
complex zref, z, *zp, *bins;
|
||||
complex zref, z, *zp, *bins = 0;
|
||||
int n;
|
||||
|
||||
if (filter_reset) reset_filters();
|
||||
|
||||
if (paths != progdefaults.DOMINOEX_PATHS) {
|
||||
paths = progdefaults.DOMINOEX_PATHS;
|
||||
reset_filters();
|
||||
}
|
||||
|
||||
while (len) {
|
||||
// create analytic signal at first IF
|
||||
|
@ -392,21 +432,22 @@ int dominoex::rx_process(const double *buf, int len)
|
|||
|
||||
if (n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
// process NUMFFTS sets of sliding FFTs spaced at 1/NUMFFTS bin intervals each of which
|
||||
// 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 n = 0; n < NUMFFTS; n++) {
|
||||
// shift in frequency to base band for the sliding FFTs
|
||||
for (int n = 0; n < paths; n++) {
|
||||
// shift in frequency to base band for the sliding DFTs
|
||||
z = mixer(n + 1, zp[i]);
|
||||
bins = binsfft[n]->run(z);
|
||||
// copy current vector to the pipe interleaving the FFT vectors
|
||||
for (int i = 0; i < NUMTONES * 2 * (doublespaced ? 2 : 1); i++) {
|
||||
pipe[pipeptr].vector[n + NUMFFTS * i] = bins[i];
|
||||
pipe[pipeptr].vector[n + paths * i] = bins[i];
|
||||
}
|
||||
}
|
||||
if (--synccounter <= 0) {
|
||||
synccounter = symlen;
|
||||
currsymbol = harddecode();
|
||||
decodesymbol();
|
||||
// MuPskSoftdecode(bins);
|
||||
synchronize();
|
||||
update_syncscope();
|
||||
eval_s2n();
|
||||
|
@ -437,47 +478,52 @@ int dominoex::get_secondary_char()
|
|||
return chr;
|
||||
}
|
||||
|
||||
void dominoex::sendtone(int tone, int duration)
|
||||
{
|
||||
double f, phaseincr;
|
||||
f = tone * tonespacing + get_txfreq_woffset() - bandwidth / 2;
|
||||
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 > M_PI)
|
||||
txphase -= twopi;
|
||||
else if (txphase < M_PI)
|
||||
txphase += twopi;
|
||||
}
|
||||
ModulateXmtr(outbuf, symlen);
|
||||
}
|
||||
}
|
||||
|
||||
void dominoex::sendsymbol(int sym)
|
||||
{
|
||||
//static int first = 0;
|
||||
complex z;
|
||||
int tone;
|
||||
double f, phaseincr;
|
||||
|
||||
tone = (txprevtone + 2 + sym) % NUMTONES;
|
||||
txprevtone = tone;
|
||||
if (reverse)
|
||||
tone = (NUMTONES - 1) - tone;
|
||||
|
||||
f = tone * tonespacing + get_txfreq_woffset() - bandwidth / 2;
|
||||
|
||||
phaseincr = twopi * f / samplerate;
|
||||
|
||||
for (int i = 0; i < symlen; i++) {
|
||||
outbuf[i] = cos(txphase);
|
||||
txphase -= phaseincr;
|
||||
if (txphase > M_PI)
|
||||
txphase -= twopi;
|
||||
else if (txphase < M_PI)
|
||||
txphase += twopi;
|
||||
}
|
||||
ModulateXmtr(outbuf, symlen);
|
||||
|
||||
sendtone(tone, 1);
|
||||
}
|
||||
|
||||
|
||||
void dominoex::sendchar(unsigned char c, int secondary)
|
||||
{
|
||||
unsigned char *code = dominoex_varienc(c, secondary);
|
||||
|
||||
sendsymbol(code[0]);
|
||||
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;
|
||||
}
|
||||
for (int sym = 1; sym < 3; sym++) {
|
||||
if (code[sym] & 0x8)
|
||||
sendsymbol(code[sym]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!secondary)
|
||||
put_echo_char(c);
|
||||
}
|
||||
|
@ -495,9 +541,13 @@ void dominoex::sendsecondary()
|
|||
|
||||
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();
|
||||
for (int i = 0; i < 4; i++)
|
||||
sendidle();
|
||||
// }
|
||||
}
|
||||
|
||||
int dominoex::tx_process()
|
||||
|
@ -506,6 +556,8 @@ int dominoex::tx_process()
|
|||
|
||||
switch (txstate) {
|
||||
case TX_STATE_PREAMBLE:
|
||||
if (progdefaults.DOMINOEX_FEC)
|
||||
MuPskClearbits();
|
||||
sendidle();
|
||||
txstate = TX_STATE_START;
|
||||
break;
|
||||
|
@ -539,3 +591,215 @@ int dominoex::tx_process()
|
|||
}
|
||||
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;
|
||||
if (c == 'À' || c == 'Á' || c == 'Â' || c == 'Ã' || c == 'Ä' || c == 'Å' ||
|
||||
c == 'à' || c == 'á' || c == 'â' || c == 'ã' || c == 'ä' || c == 'å')
|
||||
c = 'A';
|
||||
if (c == 'ß') c = 'B';
|
||||
if (c == 'Ç' || c == 'ç' || c == '©') c = 'C';
|
||||
if (c == 'Ð' || c == '°') c = 'D';
|
||||
if (c == 'Æ' || c == 'æ' || c == 'È' || c == 'É' || c == 'Ê' || c == 'Ë' ||
|
||||
c == 'è' || c == 'é' || c == 'ê' || c == 'ë')
|
||||
c = 'E';
|
||||
if (c == 'ƒ') c = 'F';
|
||||
if (c == 'Ì' || c == 'Í' || c == 'Î' || c == 'Ï' || c == 'ì' || c == 'í' ||
|
||||
c == 'î' || c == 'ï' || c == '¡')
|
||||
c = 'I';
|
||||
if (c == '£') c = 'L';
|
||||
if (c == 'Ñ' || c == 'ñ') c = 'N';
|
||||
if (c == 'ô' || c == 'ö' || c == 'ò' || c == 'Ö' || c == 'ó' ||
|
||||
c == 'Ó' || c == 'Ô' || c == 'Ò' || c == 'õ' || c == 'Õ')
|
||||
c = 'O';
|
||||
if (c == '®') c = 'R';
|
||||
if (c == 'Ù' || c == 'Ú' || c == 'Û' || c == 'Ü' ||
|
||||
c == 'ù' || c == 'ú' || c == 'û' || c == 252)
|
||||
if (c == 'ü' || c == 'û' || c == 'ù' || c == 'Ü' ||
|
||||
c == 'ú' || c == 'Ú' || c == 'Û' || c == 'Ù')
|
||||
c = 'U';
|
||||
if (c == '×') c = 'X';
|
||||
if (c == 'ÿ' || c == 'ý' || c == 'Ý') c = 'Y';
|
||||
if (c == 'Ø') c = '0';
|
||||
if (c == '¹') c = '1';
|
||||
if (c == '²') c = '2';
|
||||
if (c == '³') c = '3';
|
||||
if (c == '¿') c = '?';
|
||||
if (c == '¡') c = '!';
|
||||
if (c == '«') c = '<';
|
||||
if (c == '»') c = '>';
|
||||
if (c == '{') c = '(';
|
||||
if (c == '}') c = ')';
|
||||
if (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;
|
||||
|
||||
symbolpair[0] = symbolpair[1];
|
||||
symbolpair[1] = symbol;
|
||||
|
||||
symcounter = symcounter ? 0 : 1;
|
||||
|
||||
if (symcounter) return;
|
||||
|
||||
c = MuPskDec->decode (symbolpair, &met);
|
||||
|
||||
if (c == -1)
|
||||
return;
|
||||
|
||||
if (progStatus.sqlonoff && metric < progStatus.sldrSquelchValue)
|
||||
return;
|
||||
|
||||
datashreg = (datashreg << 1) | !!c;
|
||||
if ((datashreg & 7) == 1) {
|
||||
ch = varidec(datashreg >> 1);
|
||||
recvchar(MuPskPriSecChar(ch));
|
||||
datashreg = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void dominoex::MuPskSoftdecode(complex *bins)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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] = -255;
|
||||
c = c / 2;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
bitstate++;
|
||||
|
||||
if (bitstate == 4) {
|
||||
MuPskTxinlv->bits(&bitshreg);
|
||||
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)
|
||||
//std::cout << (int)c << " ==> " << code << " ==> ";
|
||||
while (*code) {
|
||||
int data = MuPskEnc->encode(*code++ - '0');
|
||||
//std::cout << (int)data << " ";
|
||||
for (int i = 0; i < 2; i++) {
|
||||
bitshreg = (bitshreg << 1) | ((data >> i) & 1);
|
||||
bitstate++;
|
||||
if (bitstate == 4) {
|
||||
|
||||
MuPskTxinlv->bits(&bitshreg);
|
||||
|
||||
//std::cout << "(" << bitshreg << ") ";
|
||||
|
||||
sendsymbol(bitshreg);
|
||||
|
||||
//decodeMuPskEX(bitshreg);
|
||||
|
||||
bitstate = 0;
|
||||
bitshreg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//std::cout << std::endl;
|
||||
}
|
||||
|
|
|
@ -1,76 +1,82 @@
|
|||
#include <config.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "fileselect.h"
|
||||
|
||||
#include <FL/fl_ask.H>
|
||||
#include <FL/Fl_Native_File_Chooser.H>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static Fl_Native_File_Chooser* chooser = 0;
|
||||
static Fl_Native_File_Chooser chooser;
|
||||
static std::string filename;
|
||||
|
||||
static const char* get_file(void)
|
||||
{
|
||||
switch (chooser->show()) {
|
||||
const char* preset = chooser.preset_file();
|
||||
if (preset && *preset != '/' && chooser.directory()) {
|
||||
filename = chooser.directory();
|
||||
filename.append("/").append(preset);
|
||||
chooser.preset_file(filename.c_str());
|
||||
}
|
||||
|
||||
switch (chooser.show()) {
|
||||
case -1:
|
||||
fl_alert("%s", chooser->errmsg());
|
||||
fl_alert("%s", chooser.errmsg());
|
||||
// fall through
|
||||
case 1:
|
||||
return NULL;
|
||||
default:
|
||||
return chooser->filename();
|
||||
filename = chooser.filename();
|
||||
string::size_type i = filename.rfind('/');
|
||||
if (i != string::npos)
|
||||
chooser.directory(filename.substr(0, i).c_str());
|
||||
return filename.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
const char* file_select(const char* title, const char* filter, const char* def, int* fsel)
|
||||
{
|
||||
if (!chooser)
|
||||
chooser = new Fl_Native_File_Chooser;
|
||||
|
||||
chooser->title(title);
|
||||
chooser->filter(filter);
|
||||
chooser.title(title);
|
||||
chooser.filter(filter);
|
||||
if (def)
|
||||
chooser->preset_file(def);
|
||||
chooser->options(Fl_Native_File_Chooser::PREVIEW);
|
||||
chooser->type(Fl_Native_File_Chooser::BROWSE_FILE);
|
||||
chooser.preset_file(def);
|
||||
chooser.options(Fl_Native_File_Chooser::PREVIEW);
|
||||
chooser.type(Fl_Native_File_Chooser::BROWSE_FILE);
|
||||
|
||||
const char* fn = get_file();
|
||||
if (fsel)
|
||||
*fsel = chooser->filter_value();
|
||||
*fsel = chooser.filter_value();
|
||||
return fn;
|
||||
}
|
||||
|
||||
const char* file_saveas(const char* title, const char* filter, const char* def, int* fsel)
|
||||
{
|
||||
if (!chooser)
|
||||
chooser = new Fl_Native_File_Chooser;
|
||||
|
||||
chooser->title(title);
|
||||
chooser->filter(filter);
|
||||
chooser.title(title);
|
||||
chooser.filter(filter);
|
||||
if (def)
|
||||
chooser->preset_file(def);
|
||||
chooser->options(Fl_Native_File_Chooser::SAVEAS_CONFIRM |
|
||||
chooser.preset_file(def);
|
||||
chooser.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM |
|
||||
Fl_Native_File_Chooser::NEW_FOLDER |
|
||||
Fl_Native_File_Chooser::PREVIEW);
|
||||
chooser->type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
|
||||
chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
|
||||
|
||||
const char* fn = get_file();
|
||||
if (fsel)
|
||||
*fsel = chooser->filter_value();
|
||||
*fsel = chooser.filter_value();
|
||||
return fn;
|
||||
}
|
||||
|
||||
const char* dir_select(const char* title, const char* filter, const char* def)
|
||||
{
|
||||
if (!chooser)
|
||||
chooser = new Fl_Native_File_Chooser;
|
||||
|
||||
chooser->title(title);
|
||||
chooser->filter(filter);
|
||||
chooser.title(title);
|
||||
chooser.filter(filter);
|
||||
if (def)
|
||||
chooser->directory(def);
|
||||
chooser->options(Fl_Native_File_Chooser::NEW_FOLDER |
|
||||
chooser.directory(def);
|
||||
chooser.options(Fl_Native_File_Chooser::NEW_FOLDER |
|
||||
Fl_Native_File_Chooser::PREVIEW);
|
||||
chooser->type(Fl_Native_File_Chooser::BROWSE_DIRECTORY);
|
||||
chooser.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY);
|
||||
|
||||
return get_file();
|
||||
}
|
||||
|
|
|
@ -44,7 +44,15 @@ const char *state_names[] = {
|
|||
// first string (sname), so its length should be a multiple of 2.
|
||||
const struct mode_info_t mode_info[NUM_MODES] = {
|
||||
{ MODE_CW, &cw_modem, "CW", "CW", "CW", "CW" },
|
||||
{ MODE_DOMINOEX4, &dominoex4_modem, "DomEX4", "DominoEX 4", "DOMINOEX4", "DOMINO" }, // These aren't Domino FEC, right?
|
||||
|
||||
{ MODE_DEX4, &dex4_modem, "DEX4", "DEX 4", "DEX4", "DEX" },
|
||||
{ MODE_DEX5, &dex5_modem, "DEX5", "DEX 5", "DEX5", "DEX" },
|
||||
{ MODE_DEX8, &dex8_modem, "DEX8", "DEX 8", "DEX8", "DEX" },
|
||||
{ MODE_DEX11, &dex11_modem, "DEX11", "DEX 11", "DEX11", "DEX" },
|
||||
{ MODE_DEX16, &dex16_modem, "DEX16", "DEX 16", "DEX16", "DEX" },
|
||||
{ MODE_DEX22, &dex22_modem, "DEX22", "DEX 22", "DEX22", "DEX" },
|
||||
|
||||
{ MODE_DOMINOEX4, &dominoex4_modem, "DomEX4", "DominoEX 4", "DOMINOEX4", "DOMINO" },
|
||||
{ MODE_DOMINOEX5, &dominoex5_modem, "DomEX5", "DominoEX 5", "DOMINOEX5", "DOMINO" },
|
||||
{ MODE_DOMINOEX8, &dominoex8_modem, "DomEX8", "DominoEX 8", "DOMINOEX8", "DOMINO" },
|
||||
{ MODE_DOMINOEX11, &dominoex11_modem, "DomX11", "DominoEX 11", "DOMINOEX11", "DOMINO" },
|
||||
|
|
|
@ -137,11 +137,17 @@ extern Fl_Group *tabCWQSK;
|
|||
extern Fl_Check_Button *btnQSK;
|
||||
extern Fl_Counter *cntPreTiming;
|
||||
extern Fl_Counter *cntPostTiming;
|
||||
extern Fl_Group *tabDEX;
|
||||
extern Fl_Input *txtDEXSecondary;
|
||||
extern Fl_Counter *valDEX_BW;
|
||||
extern Fl_Check_Button *valDEX_FILTER;
|
||||
extern Fl_Counter *valDEX_PATHS;
|
||||
extern Fl_Group *tabDomEX;
|
||||
extern Fl_Input *txtSecondary;
|
||||
extern Fl_Button *btnRestartDomEX;
|
||||
extern Fl_Counter *valDominoEX_BW;
|
||||
extern Fl_Check_Button *valDominoEX_FILTER;
|
||||
extern Fl_Check_Button *chkDominoEX_FEC;
|
||||
extern Fl_Counter *valDominoEX_PATHS;
|
||||
extern Fl_Group *tabFeld;
|
||||
#include "fontdef.h"
|
||||
extern Fl_Choice *selHellFont;
|
||||
|
|
|
@ -72,10 +72,17 @@ struct configuration {
|
|||
int oliviabw;
|
||||
int oliviasmargin;
|
||||
int oliviasinteg;
|
||||
bool olivia8bit;
|
||||
bool olivia8bit;
|
||||
// DEX
|
||||
double DEX_BW;
|
||||
bool DEX_FILTER;
|
||||
string DEXsecText;
|
||||
int DEX_PATHS;
|
||||
// DOMINOEX
|
||||
double DOMINOEX_BW;
|
||||
bool DOMINOEX_FILTER;
|
||||
bool DOMINOEX_FEC;
|
||||
int DOMINOEX_PATHS;
|
||||
// MT63
|
||||
bool mt63_8bit;
|
||||
int mt63_interleave;
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// dex.h -- dex modem
|
||||
//
|
||||
// Copyright (C) 2008
|
||||
// David Freese (w1hkj@w1hkj.com)
|
||||
//
|
||||
// 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 2 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, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _dex_H
|
||||
#define _dex_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "complex.h"
|
||||
#include "trx.h"
|
||||
#include "fft.h"
|
||||
#include "filters.h"
|
||||
#include "fftfilt.h"
|
||||
#include "dominovar.h"
|
||||
#include "mbuffer.h"
|
||||
|
||||
// NASA coefficients for viterbi encode/decode algorithms
|
||||
#define DEX_K 7
|
||||
#define DEX_POLY1 0x6d
|
||||
#define DEX_POLY2 0x4f
|
||||
|
||||
//#include "mfskvaricode.h"
|
||||
#include "interleave.h"
|
||||
#include "viterbi.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DEXNUMMTONES 18
|
||||
#define DEXMAXFFTS 8
|
||||
#define DEXBASEFREQ 500.0
|
||||
#define DEXFIRSTIF 1000.0
|
||||
|
||||
#define DEXSCOPESIZE 64
|
||||
|
||||
struct DEXrxpipe {
|
||||
complex vector[DEXMAXFFTS * DEXNUMMTONES * 6];
|
||||
};
|
||||
|
||||
class dex : public modem {
|
||||
public:
|
||||
enum {
|
||||
TX_STATE_PREAMBLE,
|
||||
TX_STATE_START,
|
||||
TX_STATE_DATA,
|
||||
TX_STATE_END,
|
||||
TX_STATE_FLUSH
|
||||
};
|
||||
protected:
|
||||
// common variables
|
||||
double phase[DEXMAXFFTS + 1];
|
||||
double txphase;
|
||||
int symlen;
|
||||
int doublespaced;
|
||||
double tonespacing;
|
||||
int counter;
|
||||
unsigned int twosym;
|
||||
int paths;
|
||||
|
||||
// rx variables
|
||||
C_FIR_filter *hilbert;
|
||||
sfft *binsfft[DEXMAXFFTS];
|
||||
fftfilt *fft;
|
||||
Cmovavg *vidfilter[DEXSCOPESIZE];
|
||||
Cmovavg *syncfilter;
|
||||
|
||||
DEXrxpipe *pipe;
|
||||
unsigned int pipeptr;
|
||||
unsigned int datashreg;
|
||||
mbuffer<double, 0, 2> scopedata;
|
||||
mbuffer<double, 0, 2> videodata;
|
||||
|
||||
complex currvector;
|
||||
|
||||
int currsymbol;
|
||||
int prev1symbol;
|
||||
int prev2symbol;
|
||||
|
||||
double met1;
|
||||
double met2;
|
||||
double sig;
|
||||
double noise;
|
||||
double s2n;
|
||||
|
||||
int synccounter;
|
||||
|
||||
unsigned char symbolbuf[MAX_VARICODE_LEN];
|
||||
int symcounter;
|
||||
|
||||
int symbolbit;
|
||||
|
||||
bool filter_reset;
|
||||
|
||||
// tx variables
|
||||
int txstate;
|
||||
int txprevtone;
|
||||
unsigned int bitshreg;
|
||||
string strSecXmtText;
|
||||
unsigned int cptr;
|
||||
|
||||
viterbi *Dec;
|
||||
interleave *Rxinlv;
|
||||
encoder *Enc;
|
||||
interleave *Txinlv;
|
||||
int bitstate;
|
||||
unsigned char symbolpair[2];
|
||||
|
||||
private:
|
||||
complex mixer(int n, complex in);
|
||||
|
||||
// Rx
|
||||
void recvchar(int c);
|
||||
void decodesymbol();
|
||||
int harddecode();
|
||||
void update_syncscope();
|
||||
void synchronize();
|
||||
void reset_afc();
|
||||
void eval_s2n();
|
||||
int get_secondary_char();
|
||||
void reset_filters();
|
||||
void decodePairs(unsigned char symbol);
|
||||
void decodeEX(int c);
|
||||
|
||||
// Tx
|
||||
void sendtone(int tone, int duration);
|
||||
void sendsymbol(int sym);
|
||||
void sendchar(unsigned char c, int secondary);
|
||||
void sendidle();
|
||||
void sendsecondary();
|
||||
void flushtx();
|
||||
void Clearbits();
|
||||
|
||||
public:
|
||||
dex (trx_mode md);
|
||||
~dex ();
|
||||
void init();
|
||||
void rx_init();
|
||||
void tx_init(SoundBase *sc);
|
||||
void restart();
|
||||
int rx_process(const double *buf, int len);
|
||||
int tx_process();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// dexvaricode.h -- DEX Varicode
|
||||
//
|
||||
// Copyright (C) 2008
|
||||
// Dave Freese, W1HKJ
|
||||
//
|
||||
// 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 2 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, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DEXVARICODE_H
|
||||
#define _DEXVARICODE_H
|
||||
|
||||
extern const char *dexvarienc(int c, int secondary);
|
||||
extern int dexvaridec(unsigned int symbol);
|
||||
|
||||
#endif
|
|
@ -36,18 +36,31 @@
|
|||
#include "dominovar.h"
|
||||
#include "mbuffer.h"
|
||||
|
||||
// NASA coefficients for viterbi encode/decode algorithms
|
||||
#define K 7
|
||||
#define POLY1 0x6d
|
||||
#define POLY2 0x4f
|
||||
|
||||
//#include "mfskvaricode.h"
|
||||
#include "interleave.h"
|
||||
#include "viterbi.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define NUMTONES 18
|
||||
//#define NUMFFTS 4
|
||||
#define NUMFFTS 8
|
||||
//#define MAXFFTS 4
|
||||
#define MAXFFTS 8
|
||||
//#define BASEFREQ 1000.0
|
||||
#define BASEFREQ 500.0
|
||||
#define FIRSTIF 1000.0
|
||||
|
||||
#define SCOPESIZE 64
|
||||
// NUMBER OF BINS IN VIRTUAL MFSK array
|
||||
#define VBINS 16
|
||||
|
||||
struct domrxpipe {
|
||||
complex vector[NUMFFTS * NUMTONES * 6];
|
||||
complex vector[MAXFFTS * NUMTONES * 6];
|
||||
};
|
||||
|
||||
class dominoex : public modem {
|
||||
|
@ -61,19 +74,21 @@ public:
|
|||
};
|
||||
protected:
|
||||
// common variables
|
||||
double phase[NUMFFTS + 1];
|
||||
double phase[MAXFFTS + 1];
|
||||
double txphase;
|
||||
int symlen;
|
||||
int doublespaced;
|
||||
double tonespacing;
|
||||
int counter;
|
||||
unsigned int twosym;
|
||||
unsigned int twosym;
|
||||
int paths;
|
||||
|
||||
// rx variables
|
||||
C_FIR_filter *hilbert;
|
||||
sfft *binsfft[NUMFFTS];
|
||||
sfft *binsfft[MAXFFTS];
|
||||
fftfilt *fft;
|
||||
Cmovavg *vidfilter[SCOPESIZE];
|
||||
Cmovavg *syncfilter;
|
||||
|
||||
domrxpipe *pipe;
|
||||
unsigned int pipeptr;
|
||||
|
@ -108,16 +123,26 @@ protected:
|
|||
unsigned int bitshreg;
|
||||
string strSecXmtText;
|
||||
|
||||
viterbi *MuPskDec;
|
||||
interleave *MuPskRxinlv;
|
||||
encoder *MuPskEnc;
|
||||
interleave *MuPskTxinlv;
|
||||
int bitstate;
|
||||
int vbins[VBINS];
|
||||
unsigned char symbolpair[2];
|
||||
|
||||
private:
|
||||
complex mixer(int n, complex in);
|
||||
void recvchar(int c);
|
||||
void decodesymbol();
|
||||
void decodeDomino(int c);
|
||||
int harddecode();
|
||||
void update_syncscope();
|
||||
void synchronize();
|
||||
void afc();
|
||||
void reset_afc();
|
||||
void eval_s2n();
|
||||
void sendtone(int tone, int duration);
|
||||
void sendsymbol(int sym);
|
||||
void sendchar(unsigned char c, int secondary);
|
||||
void sendidle();
|
||||
|
@ -125,6 +150,18 @@ private:
|
|||
void flushtx();
|
||||
int get_secondary_char();
|
||||
void reset_filters();
|
||||
// MultiPsk FEC scheme
|
||||
// Rx
|
||||
unsigned int MuPskPriSecChar(unsigned int c);
|
||||
void decodeMuPskSymbol(unsigned char symbol);
|
||||
void MuPskSoftdecode(complex *bins);
|
||||
void decodeMuPskEX(int c);
|
||||
// Tx
|
||||
unsigned char MuPskSec2Pri(int c);
|
||||
void sendMuPskEX(unsigned char c, int secondary);
|
||||
void MuPskClearbits();
|
||||
void MuPskFlushTx();
|
||||
|
||||
public:
|
||||
dominoex (trx_mode md);
|
||||
~dominoex ();
|
||||
|
|
|
@ -133,6 +133,7 @@ extern int get_secondary_char();
|
|||
extern void put_echo_char(unsigned int data);
|
||||
extern void resetRTTY();
|
||||
extern void resetOLIVIA();
|
||||
extern void resetDEX();
|
||||
extern void resetDOMEX();
|
||||
extern void resetSoundCard();
|
||||
extern void restoreFocus();
|
||||
|
|
|
@ -51,6 +51,13 @@ enum {
|
|||
|
||||
MODE_CW,
|
||||
|
||||
MODE_DEX4,
|
||||
MODE_DEX5,
|
||||
MODE_DEX8,
|
||||
MODE_DEX11,
|
||||
MODE_DEX16,
|
||||
MODE_DEX22,
|
||||
|
||||
MODE_DOMINOEX4,
|
||||
MODE_DOMINOEX5,
|
||||
MODE_DOMINOEX8,
|
||||
|
|
|
@ -83,6 +83,8 @@ struct history {
|
|||
|
||||
class mfsk : public modem {
|
||||
|
||||
#define SCOPESIZE 64
|
||||
|
||||
friend void updateTxPic(unsigned char data, mfsk *me);
|
||||
friend void cb_picRxClose( Fl_Widget *w, void *who);
|
||||
friend void cb_picRxAbort( Fl_Widget *w, void *who);
|
||||
|
@ -127,6 +129,7 @@ protected:
|
|||
Cmovavg *afcfilt;
|
||||
Cmovavg *met1filt;
|
||||
Cmovavg *met2filt;
|
||||
Cmovavg *vidfilter[SCOPESIZE];
|
||||
|
||||
viterbi *dec1;
|
||||
viterbi *dec2;
|
||||
|
@ -202,7 +205,7 @@ protected:
|
|||
void synchronize();
|
||||
void afc();
|
||||
void reset_afc();
|
||||
void eval_s2n(complex, complex);
|
||||
void eval_s2n();
|
||||
void sendsymbol(int sym);
|
||||
void sendbit(int bit);
|
||||
void sendchar(unsigned char c);
|
||||
|
|
|
@ -194,6 +194,12 @@ extern modem *qpsk125_modem;
|
|||
extern modem *qpsk250_modem;
|
||||
extern modem *rtty_modem;
|
||||
extern modem *olivia_modem;
|
||||
extern modem *dex4_modem;
|
||||
extern modem *dex5_modem;
|
||||
extern modem *dex8_modem;
|
||||
extern modem *dex11_modem;
|
||||
extern modem *dex16_modem;
|
||||
extern modem *dex22_modem;
|
||||
extern modem *dominoex4_modem;
|
||||
extern modem *dominoex5_modem;
|
||||
extern modem *dominoex8_modem;
|
||||
|
|
|
@ -231,7 +231,7 @@ public:
|
|||
|
||||
private:
|
||||
void src_data_reset(int mode);
|
||||
void resample(int mode, float* inbuf, float* outbuf, size_t count, size_t max = 0);
|
||||
static long src_read_cb(void* arg, float** data);
|
||||
size_t resample_write(float* buf, size_t count);
|
||||
void init_stream(unsigned dir);
|
||||
void start_stream(unsigned dir);
|
||||
|
@ -239,6 +239,7 @@ private:
|
|||
bool full_duplex_device(const PaDeviceInfo* dev);
|
||||
double find_srate(unsigned dir);
|
||||
void pa_perror(int err, const char* str = 0);
|
||||
static void init_hostapi_ext(void);
|
||||
static PaStreamCallback stream_process;
|
||||
static PaStreamFinishedCallback stream_stopped;
|
||||
|
||||
|
@ -262,11 +263,14 @@ private:
|
|||
|
||||
unsigned frames_per_buffer;
|
||||
double dev_sample_rate;
|
||||
double src_ratio;
|
||||
|
||||
sem_t* rwsem;
|
||||
sem_t* csem;
|
||||
int state;
|
||||
ringbuffer<float>* rb;
|
||||
size_t blocksize;
|
||||
size_t advance;
|
||||
} sd[2];
|
||||
};
|
||||
|
||||
|
@ -291,13 +295,17 @@ public:
|
|||
|
||||
private:
|
||||
void src_data_reset(int mode);
|
||||
void resample(int mode, float *buf, size_t count, size_t max = 0);
|
||||
static long src_read_cb(void* arg, float** data);
|
||||
size_t resample_write(float* buf, size_t count);
|
||||
|
||||
private:
|
||||
double dev_sample_rate[2];
|
||||
pa_simple* stream[2];
|
||||
pa_sample_spec stream_params;
|
||||
|
||||
struct stream_data {
|
||||
pa_simple* stream;
|
||||
pa_sample_spec stream_params;
|
||||
pa_stream_direction_t dir;
|
||||
double src_ratio;
|
||||
size_t blocksize;
|
||||
} sd[2];
|
||||
float* fbuf;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
#ifndef _status_H
|
||||
#define _status_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include "main.h"
|
||||
#include "globals.h"
|
||||
|
||||
struct status {
|
||||
int lastmode;
|
||||
trx_mode lastmode;
|
||||
int mainX;
|
||||
int mainY;
|
||||
int mainW;
|
||||
|
@ -39,17 +33,12 @@ struct status {
|
|||
bool scopeVisible;
|
||||
int scopeW;
|
||||
int scopeH;
|
||||
|
||||
|
||||
bool bLastStateRead;
|
||||
|
||||
|
||||
public:
|
||||
void saveModeState(trx_mode m);
|
||||
|
||||
void initLastState();
|
||||
void saveLastState();
|
||||
void loadLastState();
|
||||
friend std::istream &operator>>(std::istream &stream, status &c);
|
||||
friend std::ostream &operator<<(std::ostream &ostream, status c);
|
||||
};
|
||||
|
||||
extern status progStatus;
|
||||
|
|
|
@ -35,7 +35,11 @@
|
|||
interleave::interleave (int _size, int dir)
|
||||
{
|
||||
size = _size;
|
||||
if (size == 4) depth = 10;
|
||||
if (size == -1) { // dominoEX interleaver
|
||||
size = 4;
|
||||
depth = 4;
|
||||
} else if (size == 4)
|
||||
depth = 10;
|
||||
else depth = 5;
|
||||
direction = dir;
|
||||
table = new unsigned char [depth * size * size];
|
||||
|
|
|
@ -99,6 +99,9 @@ mfsk::~mfsk()
|
|||
if (met1filt) delete met1filt;
|
||||
if (met2filt) delete met2filt;
|
||||
if (xmtimg) delete [] xmtimg;
|
||||
for (int i = 0; i < SCOPESIZE; i++) {
|
||||
if (vidfilter[i]) delete vidfilter[i];
|
||||
}
|
||||
deleteRxViewer();
|
||||
deleteTxViewer();
|
||||
}
|
||||
|
@ -132,6 +135,9 @@ mfsk::mfsk(trx_mode mfsk_mode) : modem()
|
|||
afcfilt = new Cmovavg(AFC_COUNT);
|
||||
met1filt = new Cmovavg(32);
|
||||
met2filt = new Cmovavg(32);
|
||||
|
||||
for (int i = 0; i < SCOPESIZE; i++)
|
||||
vidfilter[i] = new Cmovavg(16);
|
||||
|
||||
pipe = new rxpipe[ 2 * symlen ];
|
||||
|
||||
|
@ -150,8 +156,8 @@ mfsk::mfsk(trx_mode mfsk_mode) : modem()
|
|||
bw = (numtones - 1) * tonespacing;
|
||||
cf = 1000.0 + bw / 2.0;
|
||||
|
||||
flo = (cf - bw/2 - tonespacing) / MFSKSampleRate;
|
||||
fhi = (cf + bw/2 + tonespacing) / MFSKSampleRate;
|
||||
flo = (cf - bw/2 - 2 * tonespacing) / MFSKSampleRate;
|
||||
fhi = (cf + bw/2 + 2 * tonespacing) / MFSKSampleRate;
|
||||
|
||||
bpfilt = new C_FIR_filter();
|
||||
bpfilt->init_bandpass (127, 1, flo, fhi);
|
||||
|
@ -344,7 +350,7 @@ void mfsk::decodesymbol(unsigned char symbol)
|
|||
if (met1 < met2)
|
||||
return;
|
||||
|
||||
metric = met1 / 2.5;
|
||||
metric = met1 / 2.0;
|
||||
} else {
|
||||
if ((c = dec2->decode(symbolpair, &met)) == -1)
|
||||
return;
|
||||
|
@ -354,7 +360,7 @@ void mfsk::decodesymbol(unsigned char symbol)
|
|||
if (met2 < met1)
|
||||
return;
|
||||
|
||||
metric = met2 / 2.5;
|
||||
metric = met2 / 2.0;
|
||||
}
|
||||
display_metric(metric);
|
||||
|
||||
|
@ -448,14 +454,14 @@ void mfsk::update_syncscope()
|
|||
if (max == 0.0) max = 1e10;
|
||||
memset(scopedata, 0, 2 * symlen * sizeof(double));
|
||||
if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue)
|
||||
for (int i = 0; i < pipelen; i++) {
|
||||
j = (pipeptr - i);
|
||||
if (j < 0) j += pipelen;
|
||||
scopedata[i] = (pipe[j].vector[prev1symbol]).mag() / max;
|
||||
for (unsigned int i = 0; i < SCOPESIZE; i++) {
|
||||
j = (pipeptr + i * pipelen / SCOPESIZE + 1) % (pipelen);
|
||||
scopedata[i] = vidfilter[i]->run(pipe[j].vector[prev1symbol].mag());
|
||||
}
|
||||
set_scope(scopedata, pipelen, false);
|
||||
set_scope(scopedata, SCOPESIZE);
|
||||
|
||||
scopedata.next(); // change buffers
|
||||
snprintf(mfskmsg, sizeof(mfskmsg), "s/n %3.0f dB", 20.0 * log10(s2n) );
|
||||
snprintf(mfskmsg, sizeof(mfskmsg), "s/n %3.0f dB", 20.0 * log10(s2n) - 9.0 );
|
||||
put_Status1(mfskmsg);
|
||||
}
|
||||
|
||||
|
@ -503,6 +509,9 @@ void mfsk::afc()
|
|||
reset_afc();
|
||||
sigsearch = 0;
|
||||
}
|
||||
if (prev1symbol != currsymbol)
|
||||
return;
|
||||
|
||||
if (pipeptr == 0) {
|
||||
prevvector = pipe[2*symlen - 1].vector[currsymbol];
|
||||
} else {
|
||||
|
@ -522,15 +531,17 @@ void mfsk::afc()
|
|||
}
|
||||
}
|
||||
|
||||
void mfsk::eval_s2n(complex c, complex n)
|
||||
void mfsk::eval_s2n()
|
||||
{
|
||||
sig = c.mag(); // signal + noise energy
|
||||
noise = n.mag() + 1e-20; // noise energy
|
||||
|
||||
if (metric > progStatus.sldrSquelchValue)
|
||||
s2n = decayavg( s2n, fabs(sig / noise), 16 );
|
||||
else
|
||||
s2n = decayavg( s2n, 1.0, 16 );
|
||||
sig = pipe[pipeptr].vector[currsymbol].mag();
|
||||
noise = 0.0;
|
||||
for (int i = 0; i < numtones; i++) {
|
||||
if (i != currsymbol)
|
||||
noise += pipe[pipeptr].vector[i].mag();
|
||||
}
|
||||
noise /= (numtones - 1);
|
||||
if (noise > 0)
|
||||
s2n = decayavg ( s2n, sig / noise, 16 );
|
||||
}
|
||||
|
||||
int mfsk::rx_process(const double *buf, int len)
|
||||
|
@ -604,7 +615,7 @@ int mfsk::rx_process(const double *buf, int len)
|
|||
// frequency tracking
|
||||
afc();
|
||||
|
||||
eval_s2n(currvector, bins[(currsymbol + numtones/2) % numtones]);
|
||||
eval_s2n();
|
||||
// decode symbol
|
||||
softdecode(bins);
|
||||
// symbol sync
|
||||
|
|
|
@ -77,10 +77,17 @@ configuration progdefaults = {
|
|||
2, // int oliviabw;
|
||||
8, // int oliviasmargin
|
||||
4, // int oliviasinteg
|
||||
false, // bool olivia8bit
|
||||
false, // bool olivia8bit
|
||||
// DEX
|
||||
2.0, // double DEX_BW;
|
||||
true, // bool DEX_FILTER;
|
||||
"fldigi-dex ", // string DEXsecText;
|
||||
5, // int DEX_PATHS;
|
||||
// DOMINOEX
|
||||
2.0, // double DOMINOEX_BW;
|
||||
false, // bool DOMINOEX_FILTER
|
||||
true, // bool DOMINOEX_FILTER
|
||||
false, // bool DOMINOEX_FEC
|
||||
5, // int DOMINOEX_PATHS
|
||||
// MT63
|
||||
false, // bool mt63_8bit;
|
||||
32, // int mt63_interleave;
|
||||
|
@ -243,7 +250,8 @@ enum TAG { \
|
|||
CWTRACK, CWRISETIME, CWDASH2DOT,
|
||||
XQSK, CWPRE, CWPOST, CWID, CWIDWPM,
|
||||
OLIVIATONES, OLIVIABW, OLIVIASMARGIN, OLIVIASINTEG, OLIVIA8BIT,
|
||||
DOMINOEXBW, DOMINOEXFILTER,
|
||||
DEXBW, DEXFILTER, DEXSECTEXT, DEXPATHS,
|
||||
DOMINOEXBW, DOMINOEXFILTER, DOMINOEXFEC, DOMINOEXPATHS,
|
||||
FELDFONTNBR, FELDIDLE,
|
||||
WFPREFILTER, LATENCY,
|
||||
USECURSORLINES, USECURSORCENTERLINE, USEBWTRACKS,
|
||||
|
@ -382,8 +390,17 @@ void configuration::writeDefaultsXML()
|
|||
writeXMLint(f, "OLIVIASMARGIN", oliviasmargin);
|
||||
writeXMLint(f, "OLIVIASINTEG", oliviasinteg);
|
||||
writeXMLbool(f, "OLIVIA8BIT", olivia8bit);
|
||||
|
||||
writeXMLdbl(f, "DEXBW", DEX_BW);
|
||||
writeXMLbool(f, "DEXFILTER", DEX_FILTER);
|
||||
writeXMLstr(f, "DEXSECTEXT", DEXsecText);
|
||||
writeXMLint(f, "DEXPATHS", DEX_PATHS);
|
||||
|
||||
writeXMLdbl(f, "DOMINOEXBW", DOMINOEX_BW);
|
||||
writeXMLbool(f, "DOMINOEXFILTER", DOMINOEX_FILTER);
|
||||
writeXMLbool(f, "DOMINOEXFEC", DOMINOEX_FEC);
|
||||
writeXMLint(f, "DOMINOEXPATHS", DOMINOEX_PATHS);
|
||||
|
||||
writeXMLint(f, "FELDFONTNBR", feldfontnbr);
|
||||
writeXMLbool(f, "FELDIDLE", FELD_IDLE);
|
||||
|
||||
|
@ -671,12 +688,30 @@ bool configuration::readDefaultsXML()
|
|||
case OLIVIA8BIT :
|
||||
olivia8bit = atoi(xml->getNodeData());
|
||||
break;
|
||||
case DEXBW :
|
||||
DEX_BW = atof(xml->getNodeData());
|
||||
break;
|
||||
case DEXFILTER :
|
||||
DEX_FILTER = atoi(xml->getNodeData());
|
||||
break;
|
||||
case DEXSECTEXT :
|
||||
DEXsecText = xml->getNodeData();
|
||||
break;
|
||||
case DEXPATHS :
|
||||
DEX_PATHS = atoi(xml->getNodeData());
|
||||
break;
|
||||
case DOMINOEXBW :
|
||||
DOMINOEX_BW = atof(xml->getNodeData());
|
||||
break;
|
||||
case DOMINOEXFILTER :
|
||||
DOMINOEX_FILTER = atoi(xml->getNodeData());
|
||||
break;
|
||||
case DOMINOEXFEC :
|
||||
DOMINOEX_FEC = atoi(xml->getNodeData());
|
||||
break;
|
||||
case DOMINOEXPATHS :
|
||||
DOMINOEX_PATHS = atoi(xml->getNodeData());
|
||||
break;
|
||||
case FELDFONTNBR :
|
||||
feldfontnbr = atoi(xml->getNodeData());
|
||||
break;
|
||||
|
@ -1027,8 +1062,14 @@ bool configuration::readDefaultsXML()
|
|||
else if (!strcmp("OLIVIASMARGIN", nodeName)) tag = OLIVIASMARGIN;
|
||||
else if (!strcmp("OLIVIASINTEG", nodeName)) tag = OLIVIASINTEG;
|
||||
else if (!strcmp("OLIVIA8BIT", nodeName)) tag = OLIVIA8BIT;
|
||||
else if (!strcmp("DEXBW", nodeName)) tag = DEXBW;
|
||||
else if (!strcmp("DEXFILTER", nodeName)) tag = DEXFILTER;
|
||||
else if (!strcmp("DEXSECTEXT", nodeName)) tag = DEXSECTEXT;
|
||||
else if (!strcmp("DEXPATHS", nodeName)) tag = DEXPATHS;
|
||||
else if (!strcmp("DOMINOEXBW", nodeName)) tag = DOMINOEXBW;
|
||||
else if (!strcmp("DOMINOEXFILTER", nodeName)) tag = DOMINOEXFILTER;
|
||||
else if (!strcmp("DOMINOEXFEC", nodeName)) tag = DOMINOEXFEC;
|
||||
else if (!strcmp("DOMINOEXPATHS", nodeName)) tag = DOMINOEXPATHS;
|
||||
else if (!strcmp("FELDFONTNBR", nodeName)) tag = FELDFONTNBR;
|
||||
else if (!strcmp("FELDIDLE", nodeName)) tag = FELDIDLE;
|
||||
else if (!strcmp("WFPREFILTER", nodeName)) tag = WFPREFILTER;
|
||||
|
@ -1210,6 +1251,7 @@ void configuration::saveDefaults() {
|
|||
myQth = inpMyQth->value();
|
||||
myLocator = inpMyLocator->value();
|
||||
secText = txtSecondary->value();
|
||||
DEXsecText = txtDEXSecondary->value();
|
||||
PTTdev = inpTTYdev->value();
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
|
@ -1237,9 +1279,17 @@ int configuration::setDefaults() {
|
|||
ContestDigits = (int)nbrContestDigits->value();
|
||||
|
||||
txtSecondary->value(secText.c_str());
|
||||
|
||||
txtDEXSecondary->value(DEXsecText.c_str());
|
||||
valDEX_BW->value(DEX_BW);
|
||||
valDEX_FILTER->value(DEX_FILTER);
|
||||
valDEX_PATHS->value(DEX_PATHS);
|
||||
|
||||
valDominoEX_BW->value(DOMINOEX_BW);
|
||||
valDominoEX_FILTER->value(DOMINOEX_FILTER);
|
||||
|
||||
chkDominoEX_FEC->value(DOMINOEX_FEC);
|
||||
valDominoEX_PATHS->value(DOMINOEX_PATHS);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
btnPTT[i]->value(0);
|
||||
btnPTT[i]->activate();
|
||||
|
|
|
@ -47,7 +47,7 @@ void pstack(int fd, unsigned skip)
|
|||
|
||||
void pstack_maybe(void)
|
||||
{
|
||||
static bool trace = getenv("TRACE_LOCKS");
|
||||
static bool trace = getenv("FLDIGI_TRACE_LOCKS");
|
||||
|
||||
if (trace)
|
||||
pstack(STDERR_FILENO, 1);
|
||||
|
@ -55,14 +55,20 @@ void pstack_maybe(void)
|
|||
|
||||
void diediedie(void)
|
||||
{
|
||||
std::cerr << "\nAborting " PACKAGE
|
||||
" due to a fatal error.\nPlease report this to "
|
||||
PACKAGE_BUGREPORT << "\n\n";
|
||||
pstack(STDERR_FILENO);
|
||||
extern std::string version_text;
|
||||
std::cerr << "\nVersion information:\n" << version_text;
|
||||
if (signum != SIGABRT)
|
||||
abort();
|
||||
static bool print_trace = true;
|
||||
|
||||
if (print_trace) {
|
||||
if (signum)
|
||||
std::cerr << "\nCaught signal " << signum;
|
||||
std::cerr << "\nAborting " PACKAGE_TARNAME
|
||||
" due to a fatal error.\nPlease report this to "
|
||||
PACKAGE_BUGREPORT << "\n\n*** Stack trace:\n";
|
||||
pstack(STDERR_FILENO);
|
||||
extern std::string version_text;
|
||||
std::cerr << "\n*** Version information:\n" << version_text;
|
||||
print_trace = false;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
void handle_unexpected(void)
|
||||
|
@ -74,7 +80,6 @@ void handle_unexpected(void)
|
|||
// this may not give us anything useful, but we can try...
|
||||
void handle_signal(int s)
|
||||
{
|
||||
std::cerr << "Caught signal " << s;
|
||||
signum = s;
|
||||
diediedie();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include <FL/Fl_Preferences.H>
|
||||
|
||||
#include "main.h"
|
||||
#include "globals.h"
|
||||
|
||||
#include "status.h"
|
||||
#include "configuration.h"
|
||||
|
@ -24,12 +30,12 @@
|
|||
|
||||
#include "rigsupport.h"
|
||||
|
||||
extern void startup_modem(modem *m);
|
||||
extern Fl_Double_Window *dlgViewer;
|
||||
extern void openViewer();
|
||||
#include "Viewer.h"
|
||||
|
||||
#define STATUS_FILENAME "status"
|
||||
|
||||
status progStatus = {
|
||||
(int)MODE_BPSK31, // trx_mode lastmode;
|
||||
MODE_BPSK31, // trx_mode lastmode;
|
||||
50, // int mainX;
|
||||
50, // int mainY;
|
||||
WNOM, // int mainW;
|
||||
|
@ -58,16 +64,9 @@ status progStatus = {
|
|||
false, // bool scopeVisible;
|
||||
50, // int scopeW;
|
||||
50, // int scopeH;
|
||||
|
||||
false // bool bLastStateRead;
|
||||
|
||||
};
|
||||
|
||||
|
||||
void status::saveModeState(trx_mode m)
|
||||
{
|
||||
lastmode = (int)m;
|
||||
}
|
||||
false // bool bLastStateRead;
|
||||
};
|
||||
|
||||
void status::saveLastState()
|
||||
{
|
||||
|
@ -76,137 +75,135 @@ void status::saveLastState()
|
|||
mainW = fl_digi_main->w();
|
||||
mainH = fl_digi_main->h();
|
||||
RxTextHeight = ReceiveText->h();
|
||||
rigShown = false;
|
||||
rigX = 0;
|
||||
rigY = 0;
|
||||
carrier = wf->Carrier();
|
||||
mag = wf->Mag();
|
||||
speed = wf->Speed();
|
||||
reflevel = progdefaults.wfRefLevel;
|
||||
ampspan = progdefaults.wfAmpSpan;
|
||||
|
||||
LOGenabled = false;
|
||||
Fl_Menu_Item *mnulogging = getMenuItem("Log File");
|
||||
if (mnulogging)
|
||||
LOGenabled = mnulogging->value();
|
||||
else
|
||||
LOGenabled = false;
|
||||
|
||||
if (dlgViewer) {
|
||||
if (dlgViewer->visible()) {
|
||||
VIEWERxpos = dlgViewer->x();
|
||||
VIEWERypos = dlgViewer->y();
|
||||
VIEWERvisible = true;
|
||||
} else
|
||||
VIEWERvisible = false;
|
||||
|
||||
VIEWERvisible = false;
|
||||
if (dlgViewer && dlgViewer->visible()) {
|
||||
VIEWERxpos = dlgViewer->x();
|
||||
VIEWERypos = dlgViewer->y();
|
||||
VIEWERvisible = true;
|
||||
}
|
||||
|
||||
if (rigcontrol)
|
||||
if (rigcontrol->visible()) {
|
||||
rigShown = rigcontrol->visible();
|
||||
rigX = rigcontrol->x();
|
||||
rigY = rigcontrol->y();
|
||||
}
|
||||
if (scopeview) {
|
||||
if (scopeview->visible())
|
||||
scopeVisible = true;
|
||||
else
|
||||
scopeVisible = false;
|
||||
|
||||
rigShown = false;
|
||||
if (rigcontrol && rigcontrol->visible()) {
|
||||
rigX = rigcontrol->x();
|
||||
rigY = rigcontrol->y();
|
||||
rigShown = true;
|
||||
}
|
||||
|
||||
scopeVisible = false;
|
||||
if (scopeview && scopeview->visible()) {
|
||||
scopeVisible = true;
|
||||
scopeX = scopeview->x();
|
||||
scopeY = scopeview->y();
|
||||
scopeW = scopeview->w();
|
||||
scopeH = scopeview->h();
|
||||
}
|
||||
|
||||
string str = PACKAGE_NAME;
|
||||
str.append(" ");
|
||||
str.append(PACKAGE_VERSION);
|
||||
|
||||
string deffname = HomeDir;
|
||||
deffname.append("fldigi.status");
|
||||
ofstream deffile(deffname.c_str(), ios::out);
|
||||
Fl_Preferences spref(string(HomeDir).append(STATUS_FILENAME).c_str(), "w1hkj.com", 0);
|
||||
|
||||
deffile << str.c_str() << endl;
|
||||
deffile << lastmode << endl;
|
||||
deffile << mainX << endl;
|
||||
deffile << mainY << endl;
|
||||
deffile << mainW << endl;
|
||||
deffile << mainH << endl;
|
||||
deffile << rigShown << endl;
|
||||
deffile << rigX << endl;
|
||||
deffile << rigY << endl;
|
||||
deffile << RxTextHeight << endl;
|
||||
deffile << carrier << endl;
|
||||
deffile << mag << endl;
|
||||
deffile << speed << endl;
|
||||
deffile << reflevel << endl;
|
||||
deffile << ampspan << endl;
|
||||
deffile << VIEWERnchars << endl;
|
||||
deffile << VIEWERxpos << endl;
|
||||
deffile << VIEWERypos << endl;
|
||||
deffile << VIEWERvisible << endl;
|
||||
deffile << LOGenabled << endl;
|
||||
deffile << sldrSquelchValue << endl;
|
||||
deffile << afconoff << endl;
|
||||
deffile << sqlonoff << endl;
|
||||
deffile << RcvMixer << endl;
|
||||
deffile << XmtMixer << endl;
|
||||
deffile << scopeX << endl;
|
||||
deffile << scopeY << endl;
|
||||
deffile << scopeVisible << endl;
|
||||
deffile << scopeW << endl;
|
||||
deffile << scopeH << endl;
|
||||
|
||||
deffile.close();
|
||||
spref.set("version", PACKAGE_VERSION);
|
||||
|
||||
spref.set("mode", (int)lastmode);
|
||||
spref.set("squelch_enabled", sqlonoff);
|
||||
spref.set("squelch_level", sldrSquelchValue);
|
||||
spref.set("afc_enabled", afconoff);
|
||||
spref.set("rx_mixer_level", RcvMixer);
|
||||
spref.set("tx_mixer_level", XmtMixer);
|
||||
|
||||
spref.set("rx_text_height", RxTextHeight);
|
||||
spref.set("log_enabled", LOGenabled);
|
||||
|
||||
spref.set("wf_carrier", carrier);
|
||||
spref.set("wf_mag", mag);
|
||||
spref.set("wf_speed", speed);
|
||||
spref.set("wf_reflevel", reflevel);
|
||||
spref.set("wf_ampspan", ampspan);
|
||||
|
||||
spref.set("main_x", mainX);
|
||||
spref.set("main_y", mainY);
|
||||
spref.set("main_w", mainW);
|
||||
spref.set("main_h", mainH);
|
||||
|
||||
spref.set("rigctl_visible", rigShown);
|
||||
spref.set("rigctl_x", rigX);
|
||||
spref.set("rigctl_y", rigY);
|
||||
|
||||
spref.set("viewer_visible", VIEWERvisible);
|
||||
spref.set("viewer_x", static_cast<int>(VIEWERxpos));
|
||||
spref.set("viewer_y", static_cast<int>(VIEWERypos));
|
||||
spref.set("viewer_nchars", static_cast<int>(VIEWERnchars));
|
||||
|
||||
spref.set("scope_visible", scopeVisible);
|
||||
spref.set("scope_x", scopeX);
|
||||
spref.set("scope_y", scopeY);
|
||||
spref.set("scope_w", scopeW);
|
||||
spref.set("scope_h", scopeH);
|
||||
}
|
||||
|
||||
void status::loadLastState()
|
||||
{
|
||||
char line[255];
|
||||
string str = PACKAGE_NAME;
|
||||
str.append(" ");
|
||||
str.append(PACKAGE_VERSION);
|
||||
|
||||
string deffname = HomeDir;
|
||||
deffname.append("fldigi.status");
|
||||
ifstream deffile(deffname.c_str(), ios::in);
|
||||
if (deffile) {
|
||||
deffile.getline(line, 255);
|
||||
if (str == line) {
|
||||
deffile >> lastmode;
|
||||
deffile >> mainX;
|
||||
deffile >> mainY;
|
||||
deffile >> mainW;
|
||||
deffile >> mainH;
|
||||
deffile >> rigShown;
|
||||
deffile >> rigX;
|
||||
deffile >> rigY;
|
||||
deffile >> RxTextHeight;
|
||||
deffile >> carrier;
|
||||
deffile >> mag;
|
||||
deffile >> speed;
|
||||
deffile >> reflevel;
|
||||
deffile >> ampspan;
|
||||
deffile >> VIEWERnchars;
|
||||
deffile >> VIEWERxpos;
|
||||
deffile >> VIEWERypos;
|
||||
deffile >> VIEWERvisible;
|
||||
deffile >> LOGenabled;
|
||||
deffile >> sldrSquelchValue;
|
||||
deffile >> afconoff;
|
||||
deffile >> sqlonoff;
|
||||
deffile >> RcvMixer;
|
||||
deffile >> XmtMixer;
|
||||
deffile >> scopeX;
|
||||
deffile >> scopeY;
|
||||
deffile >> scopeVisible;
|
||||
deffile >> scopeW;
|
||||
deffile >> scopeH;
|
||||
deffile.close();
|
||||
progdefaults.wfRefLevel = reflevel;
|
||||
progdefaults.wfAmpSpan = ampspan;
|
||||
bLastStateRead = true;
|
||||
}
|
||||
Fl_Preferences spref(string(HomeDir).append(STATUS_FILENAME).c_str(), "w1hkj.com", 0);
|
||||
|
||||
char version[64]; version[sizeof(version)-1] = '\0';
|
||||
bLastStateRead = spref.get("version", version, "", sizeof(version)-1);
|
||||
// Skip loading the rest of the status variables if we didn't read a
|
||||
// version name/value pair; also clear everything to avoid creating
|
||||
// entries out of existing file contents.
|
||||
if (!bLastStateRead) {
|
||||
while (spref.entries())
|
||||
spref.deleteEntry(spref.entry(0));
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
spref.get("mode", i, i); lastmode = (trx_mode) i;
|
||||
spref.get("squelch_enabled", i, i); sqlonoff = i;
|
||||
spref.get("squelch_level", sldrSquelchValue, sldrSquelchValue);
|
||||
spref.get("afc_enabled", i, i); afconoff = i;
|
||||
spref.get("rx_mixer_level", RcvMixer, RcvMixer);
|
||||
spref.get("tx_mixer_level", XmtMixer, XmtMixer);
|
||||
|
||||
spref.get("rx_text_height", RxTextHeight, RxTextHeight);
|
||||
spref.get("log_enabled", i, i); LOGenabled = i;
|
||||
|
||||
spref.get("wf_carrier", carrier, carrier);
|
||||
spref.get("wf_mag", mag, mag);
|
||||
spref.get("wf_speed", speed, speed);
|
||||
spref.get("wf_reflevel", reflevel, reflevel);
|
||||
progdefaults.wfRefLevel = reflevel;
|
||||
spref.get("wf_ampspan", ampspan, ampspan);
|
||||
progdefaults.wfAmpSpan = ampspan;
|
||||
|
||||
spref.get("main_x", mainX, mainX);
|
||||
spref.get("main_y", mainY, mainY);
|
||||
spref.get("main_w", mainW, mainW);
|
||||
spref.get("main_h", mainH, mainH);
|
||||
|
||||
spref.get("rigctl_visible", i, i); rigShown = i;
|
||||
spref.get("rigctl_x", rigX, rigX);
|
||||
spref.get("rigctl_y", rigY, rigY);
|
||||
|
||||
spref.get("viewer_visible", i, i); VIEWERvisible = i;
|
||||
spref.get("viewer_x", i, i); VIEWERxpos = i;
|
||||
spref.get("viewer_y", i, i); VIEWERypos = i;
|
||||
spref.get("viewer_nchars", i, i); VIEWERnchars = i;
|
||||
|
||||
spref.get("scope_visible", i, i); scopeVisible = i;
|
||||
spref.get("scope_x", scopeX, scopeX);
|
||||
spref.get("scope_y", scopeY, scopeY);
|
||||
spref.get("scope_w", scopeW, scopeW);
|
||||
spref.get("scope_h", scopeH, scopeH);
|
||||
}
|
||||
|
||||
void status::initLastState()
|
||||
|
@ -214,7 +211,7 @@ void status::initLastState()
|
|||
if (!bLastStateRead)
|
||||
loadLastState();
|
||||
|
||||
init_modem((trx_mode)lastmode);
|
||||
init_modem(lastmode);
|
||||
|
||||
while (!active_modem) MilliSleep(100);
|
||||
|
||||
|
|
|
@ -47,10 +47,13 @@
|
|||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#if HAVE_DLOPEN
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include "sound.h"
|
||||
#include "configuration.h"
|
||||
#include "status.h"
|
||||
#include <FL/Fl.H>
|
||||
#include "fileselect.h"
|
||||
|
||||
#include "timeops.h"
|
||||
|
@ -98,20 +101,25 @@ void SoundBase::get_file_params(const char* def_fname, const char** fname, int*
|
|||
if (format_supported(SF_FORMAT_FLAC | SF_FORMAT_PCM_16))
|
||||
filters += "Free Lossless Audio Codec\t*.flac";
|
||||
|
||||
int fsel;
|
||||
if (strstr(def_fname, "playback"))
|
||||
*fname = file_select("Audio file", filters.c_str(), def_fname);
|
||||
*fname = file_select("Audio file", filters.c_str(), def_fname, &fsel);
|
||||
else
|
||||
*fname = file_saveas("Audio file", filters.c_str(), def_fname);
|
||||
*fname = file_saveas("Audio file", filters.c_str(), def_fname, &fsel);
|
||||
if (!*fname)
|
||||
return;
|
||||
|
||||
char* suffix = strrchr(*fname, '.');
|
||||
if (suffix && !strcasecmp(suffix, ".flac"))
|
||||
*format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
|
||||
else if (suffix && !strcasecmp(suffix, ".au"))
|
||||
*format = SF_FORMAT_AU | SF_FORMAT_FLOAT | SF_ENDIAN_CPU;
|
||||
else
|
||||
switch (fsel) {
|
||||
case 0:
|
||||
*format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
|
||||
break;
|
||||
case 1:
|
||||
*format = SF_FORMAT_AU | SF_FORMAT_FLOAT | SF_ENDIAN_CPU;
|
||||
break;
|
||||
case 2:
|
||||
*format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int SoundBase::Capture(bool val)
|
||||
|
@ -129,7 +137,7 @@ int SoundBase::Capture(bool val)
|
|||
|
||||
const char* fname;
|
||||
int format;
|
||||
get_file_params("./capture.wav", &fname, &format);
|
||||
get_file_params("capture.wav", &fname, &format);
|
||||
if (!fname)
|
||||
return 0;
|
||||
|
||||
|
@ -161,7 +169,7 @@ int SoundBase::Playback(bool val)
|
|||
}
|
||||
const char* fname;
|
||||
int format;
|
||||
get_file_params("./playback.wav", &fname, &format);
|
||||
get_file_params("playback.wav", &fname, &format);
|
||||
if (!fname)
|
||||
return 0;
|
||||
|
||||
|
@ -190,7 +198,7 @@ int SoundBase::Generate(bool val)
|
|||
|
||||
const char* fname;
|
||||
int format;
|
||||
get_file_params("./generate.wav", &fname, &format);
|
||||
get_file_params("generate.wav", &fname, &format);
|
||||
if (!fname)
|
||||
return 0;
|
||||
|
||||
|
@ -668,6 +676,8 @@ void SoundPort::initialize(void)
|
|||
if (pa_init)
|
||||
return;
|
||||
|
||||
init_hostapi_ext();
|
||||
|
||||
int err;
|
||||
|
||||
if ((err = Pa_Initialize()) != paNoError)
|
||||
|
@ -708,6 +718,7 @@ SoundPort::SoundPort(const char *in_dev, const char *out_dev)
|
|||
sd[0].dev_sample_rate = sd[1].dev_sample_rate = 0;
|
||||
sd[0].state = sd[1].state = spa_continue;
|
||||
sd[0].rb = sd[1].rb = 0;
|
||||
sd[0].advance = sd[1].advance = 0;
|
||||
|
||||
sem_t** sems[] = { &sd[0].rwsem, &sd[0].csem, &sd[1].rwsem, &sd[1].csem };
|
||||
for (size_t i = 0; i < sizeof(sems)/sizeof(*sems); i++) {
|
||||
|
@ -717,7 +728,6 @@ SoundPort::SoundPort(const char *in_dev, const char *out_dev)
|
|||
}
|
||||
|
||||
try {
|
||||
rx_src_data = new SRC_DATA;
|
||||
tx_src_data = new SRC_DATA;
|
||||
}
|
||||
catch (const std::bad_alloc& e) {
|
||||
|
@ -780,7 +790,7 @@ int SoundPort::Open(int mode, int freq)
|
|||
|
||||
start_stream(i);
|
||||
}
|
||||
else if (old_sample_rate != freq)
|
||||
else
|
||||
src_data_reset(m[i]);
|
||||
}
|
||||
|
||||
|
@ -879,7 +889,13 @@ size_t SoundPort::Read(double *buf, size_t count)
|
|||
}
|
||||
#endif
|
||||
|
||||
size_t maxframes = (size_t)floor((sd[0].rb->length() / CHANNELS) * rx_src_data->src_ratio);
|
||||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
sd[0].src_ratio = req_sample_rate / (sd[0].dev_sample_rate * (1.0 + rxppm / 1e6));
|
||||
src_set_ratio(rx_src_state, sd[0].src_ratio);
|
||||
}
|
||||
|
||||
size_t maxframes = (size_t)floor((sd[0].rb->length() / CHANNELS) * sd[0].src_ratio);
|
||||
if (unlikely(count > maxframes)) {
|
||||
size_t n = 0;
|
||||
while (count > maxframes) {
|
||||
|
@ -892,38 +908,36 @@ size_t SoundPort::Read(double *buf, size_t count)
|
|||
return n;
|
||||
}
|
||||
|
||||
// new sample count, taking into account the samplerate ratio
|
||||
size_t ncount = (size_t)floor(count / rx_src_data->src_ratio);
|
||||
|
||||
// wait for data
|
||||
bool timeout = false;
|
||||
WAIT_FOR_COND( (sd[0].rb->read_space() >= CHANNELS * ncount), sd[0].rwsem,
|
||||
(MAX(1.0, 2 * CHANNELS * ncount / sd[0].dev_sample_rate)) );
|
||||
if (timeout)
|
||||
throw SndException(ETIMEDOUT);
|
||||
|
||||
// copy to fbuf if the data is not contiguous inside the ringbuffer
|
||||
float* rbuf = 0;
|
||||
bool rbadv = true;
|
||||
ringbuffer<float>::vector_type vec[2];
|
||||
sd[0].rb->get_rv(vec);
|
||||
if (likely(vec[0].len >= CHANNELS * ncount))
|
||||
rbuf = vec[0].buf;
|
||||
else { // copy
|
||||
sd[0].rb->read(fbuf, CHANNELS * ncount);
|
||||
rbuf = fbuf;
|
||||
rbadv = false;
|
||||
}
|
||||
|
||||
// resample
|
||||
if (req_sample_rate != sd[0].dev_sample_rate || progdefaults.RX_corr != 0) {
|
||||
resample(1 << O_RDONLY, rbuf, snd_buffer, ncount, count);
|
||||
rbuf = rx_src_data->data_out;
|
||||
count = rx_src_data->output_frames_gen;
|
||||
}
|
||||
// if we did a no-copy read we must advance the read pointer
|
||||
if (rbadv)
|
||||
sd[0].rb->read_advance(CHANNELS * ncount);
|
||||
float* rbuf = fbuf;
|
||||
if (req_sample_rate != sd[0].dev_sample_rate || rxppm != 0) {
|
||||
long r;
|
||||
size_t n = 0;
|
||||
sd[0].blocksize = SCBLOCKSIZE;
|
||||
while (n < count) {
|
||||
if ((r = src_callback_read(rx_src_state, sd[0].src_ratio, count - n, rbuf + n*CHANNELS)) == 0)
|
||||
return n;
|
||||
n += r;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool timeout = false;
|
||||
WAIT_FOR_COND( (sd[0].rb->read_space() >= count * CHANNELS / sd[0].src_ratio), sd[0].rwsem,
|
||||
(MAX(1.0, 2 * CHANNELS * count / sd->dev_sample_rate)) );
|
||||
if (timeout)
|
||||
throw SndException(ETIMEDOUT);
|
||||
ringbuffer<float>::vector_type vec[2];
|
||||
sd[0].rb->get_rv(vec);
|
||||
if (vec[0].len >= count * CHANNELS) {
|
||||
rbuf = vec[0].buf;
|
||||
sd[0].advance = vec[0].len;
|
||||
}
|
||||
else
|
||||
sd[0].rb->read(fbuf, count * CHANNELS);
|
||||
}
|
||||
if (sd[0].advance) {
|
||||
sd[0].rb->read_advance(sd[0].advance);
|
||||
sd[0].advance = 0;
|
||||
}
|
||||
|
||||
// deinterleave first channel into buf
|
||||
for (size_t i = 0; i < count; i++)
|
||||
|
@ -995,7 +1009,21 @@ size_t SoundPort::resample_write(float* buf, size_t count)
|
|||
wbuf = vec[0].buf; // direct write in the rb
|
||||
else
|
||||
wbuf = src_buffer;
|
||||
resample(1 << O_WRONLY, buf, wbuf, count);
|
||||
|
||||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = sd[1].dev_sample_rate * (1.0 + txppm / 1e6) / req_sample_rate;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
}
|
||||
tx_src_data->data_in = buf;
|
||||
tx_src_data->input_frames = count;
|
||||
tx_src_data->data_out = wbuf;
|
||||
tx_src_data->output_frames = (wbuf == vec[0].buf ? vec[0].len : SND_BUF_LEN);
|
||||
tx_src_data->end_of_input = 0;
|
||||
int r;
|
||||
if ((r = src_process(tx_src_state, tx_src_data)) != 0)
|
||||
throw SndException(src_strerror(r));
|
||||
|
||||
count = tx_src_data->output_frames_gen;
|
||||
if (wbuf == vec[0].buf) { // advance write pointer and return
|
||||
sd[1].rb->write_advance(CHANNELS * count);
|
||||
|
@ -1044,10 +1072,11 @@ void SoundPort::src_data_reset(int mode)
|
|||
if (mode & 1 << O_RDONLY) {
|
||||
if (rx_src_state)
|
||||
src_delete(rx_src_state);
|
||||
rx_src_state = src_new(progdefaults.sample_converter, CHANNELS, &err);
|
||||
rx_src_state = src_callback_new(src_read_cb, progdefaults.sample_converter,
|
||||
CHANNELS, &err, &sd[0]);
|
||||
if (!rx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
rx_src_data->src_ratio = req_sample_rate / (sd[0].dev_sample_rate * (1.0 + rxppm / 1e6));
|
||||
sd[0].src_ratio = req_sample_rate / (sd[0].dev_sample_rate * (1.0 + rxppm / 1e6));
|
||||
|
||||
rbsize = ceil2((unsigned)(2 * CHANNELS * SCBLOCKSIZE *
|
||||
MAX(req_sample_rate, sd[0].dev_sample_rate) /
|
||||
|
@ -1085,42 +1114,30 @@ void SoundPort::src_data_reset(int mode)
|
|||
}
|
||||
}
|
||||
|
||||
void SoundPort::resample(int mode, float* inbuf, float* outbuf, size_t count, size_t max)
|
||||
long SoundPort::src_read_cb(void* arg, float** data)
|
||||
{
|
||||
int r;
|
||||
struct stream_data* sd = reinterpret_cast<stream_data*>(arg);
|
||||
|
||||
if (mode & 1 << O_RDONLY) {
|
||||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
rx_src_data->src_ratio = req_sample_rate / (sd[0].dev_sample_rate * (1.0 + rxppm / 1e6));
|
||||
src_set_ratio(rx_src_state, rx_src_data->src_ratio);
|
||||
}
|
||||
|
||||
rx_src_data->data_in = inbuf;
|
||||
rx_src_data->input_frames = count;
|
||||
rx_src_data->data_out = outbuf;
|
||||
rx_src_data->output_frames = max ? max : SND_BUF_LEN;
|
||||
rx_src_data->end_of_input = 0;
|
||||
|
||||
if ((r = src_process(rx_src_state, rx_src_data)) != 0)
|
||||
throw SndException(src_strerror(r));
|
||||
// advance read pointer for previous read
|
||||
if (sd->advance) {
|
||||
sd->rb->read_advance(sd->advance);
|
||||
sd->advance = 0;
|
||||
}
|
||||
else if (mode & 1 << O_WRONLY) {
|
||||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = sd[1].dev_sample_rate * (1.0 + txppm / 1e6) / req_sample_rate;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
}
|
||||
|
||||
tx_src_data->data_in = inbuf;
|
||||
tx_src_data->input_frames = count;
|
||||
tx_src_data->data_out = outbuf;
|
||||
tx_src_data->output_frames = max ? max : SND_BUF_LEN;
|
||||
tx_src_data->end_of_input = 0;
|
||||
// wait for data
|
||||
bool timeout = false;
|
||||
WAIT_FOR_COND( (sd->rb->read_space() >= CHANNELS * SCBLOCKSIZE), sd->rwsem,
|
||||
(MAX(1.0, 2 * CHANNELS * SCBLOCKSIZE / sd->dev_sample_rate)) );
|
||||
if (timeout)
|
||||
return 0;
|
||||
|
||||
if ((r = src_process(tx_src_state, tx_src_data)) != 0)
|
||||
throw SndException(src_strerror(r));
|
||||
}
|
||||
ringbuffer<float>::vector_type vec[2];
|
||||
sd->rb->get_rv(vec);
|
||||
|
||||
*data = vec[0].buf;
|
||||
sd->advance = vec[0].len;
|
||||
|
||||
return vec[0].len / CHANNELS;
|
||||
}
|
||||
|
||||
void SoundPort::init_stream(unsigned dir)
|
||||
|
@ -1384,6 +1401,21 @@ void SoundPort::pa_perror(int err, const char* str)
|
|||
}
|
||||
}
|
||||
|
||||
void SoundPort::init_hostapi_ext(void)
|
||||
{
|
||||
#if HAVE_DLOPEN
|
||||
void* handle = dlopen(NULL, RTLD_LAZY);
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
PaError (*set_jack_client_name)(const char*);
|
||||
char* err = dlerror();
|
||||
set_jack_client_name = (PaError (*)(const char*))dlsym(handle, "PaJack_SetClientName");
|
||||
if (!(err = dlerror()))
|
||||
set_jack_client_name(PACKAGE_TARNAME);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // USE_PORTAUDIO
|
||||
|
||||
|
||||
|
@ -1392,10 +1424,12 @@ void SoundPort::pa_perror(int err, const char* str)
|
|||
SoundPulse::SoundPulse(const char *dev)
|
||||
: fbuf(0)
|
||||
{
|
||||
stream[0] = stream[1] = 0;
|
||||
sd[0].stream = sd[1].stream = 0;
|
||||
sd[0].dir = PA_STREAM_RECORD; sd[1].dir = PA_STREAM_PLAYBACK;
|
||||
sd[0].stream_params.format = sd[1].stream_params.format = PA_SAMPLE_FLOAT32LE;
|
||||
sd[0].stream_params.channels = sd[1].stream_params.channels = CHANNELS;
|
||||
|
||||
try {
|
||||
rx_src_data = new SRC_DATA;
|
||||
tx_src_data = new SRC_DATA;
|
||||
}
|
||||
catch (const std::bad_alloc& e) {
|
||||
|
@ -1404,9 +1438,9 @@ SoundPulse::SoundPulse(const char *dev)
|
|||
}
|
||||
|
||||
try {
|
||||
snd_buffer = new float[2 * SND_BUF_LEN];
|
||||
src_buffer = new float[2 * SND_BUF_LEN];
|
||||
fbuf = new float[2 * SND_BUF_LEN];
|
||||
snd_buffer = new float[CHANNELS * SND_BUF_LEN];
|
||||
src_buffer = new float[CHANNELS * SND_BUF_LEN];
|
||||
fbuf = new float[CHANNELS * SND_BUF_LEN];
|
||||
}
|
||||
catch (const std::bad_alloc& e) {
|
||||
cerr << "Cannot allocate libsamplerate buffers\n";
|
||||
|
@ -1422,51 +1456,27 @@ SoundPulse::~SoundPulse()
|
|||
|
||||
int SoundPulse::Open(int mode, int freq)
|
||||
{
|
||||
int old_sample_rate = sample_frequency;
|
||||
|
||||
dev_sample_rate[0] = (progdefaults.in_sample_rate > 1 ?
|
||||
progdefaults.in_sample_rate : 48000);
|
||||
dev_sample_rate[1] = (progdefaults.out_sample_rate > 1 ?
|
||||
progdefaults.out_sample_rate : 48000);
|
||||
|
||||
sample_frequency = freq;
|
||||
if (stream[0] && stream[1]) {
|
||||
if (sample_frequency != old_sample_rate) {
|
||||
src_data_reset(1 << O_RDONLY | 1 << O_WRONLY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
Close();
|
||||
|
||||
const char* server = (progdefaults.PulseServer.length() ?
|
||||
progdefaults.PulseServer.c_str() : NULL);
|
||||
char sname[32];
|
||||
int err;
|
||||
|
||||
stream_params.format = PA_SAMPLE_FLOAT32LE;
|
||||
stream_params.channels = 2;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
src_data_reset(1 << O_RDONLY | 1 << O_WRONLY);
|
||||
|
||||
stream_params.rate = dev_sample_rate[0];
|
||||
snprintf(sname, sizeof(sname), "capture (%u)", getpid());
|
||||
if (!stream[0]) {
|
||||
stream[0] = pa_simple_new(server, PACKAGE_NAME, PA_STREAM_RECORD, NULL,
|
||||
sname, &stream_params, NULL, NULL, &err);
|
||||
if (!stream[0])
|
||||
if ((unsigned)freq != sd[i].stream_params.rate)
|
||||
Close(i);
|
||||
if (sd[i].stream)
|
||||
continue;
|
||||
|
||||
sd[i].stream_params.rate = freq;
|
||||
snprintf(sname, sizeof(sname), "%s (%u)", (i ? "playback" : "capture"), getpid());
|
||||
sd[i].stream = pa_simple_new(server, PACKAGE_TARNAME, sd[i].dir, NULL,
|
||||
sname, &sd[i].stream_params, NULL, NULL, &err);
|
||||
if (!sd[i].stream)
|
||||
throw SndPulseException(err);
|
||||
}
|
||||
|
||||
stream_params.rate = dev_sample_rate[1];
|
||||
snprintf(sname, sizeof(sname), "playback (%u)", getpid());
|
||||
if (!stream[1]) {
|
||||
stream[1] = pa_simple_new(server, PACKAGE_NAME, PA_STREAM_PLAYBACK, NULL,
|
||||
sname, &stream_params, NULL, NULL, &err);
|
||||
if (!stream[1])
|
||||
throw SndPulseException(err);
|
||||
}
|
||||
|
||||
src_data_reset(1 << O_RDONLY | 1 << O_WRONLY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1481,7 +1491,7 @@ void SoundPulse::Close(unsigned dir)
|
|||
start = end = dir;
|
||||
|
||||
for (unsigned i = start; i <= end; i++) {
|
||||
if (stream[i]) {
|
||||
if (sd[i].stream) {
|
||||
flush(i);
|
||||
Abort(i);
|
||||
}
|
||||
|
@ -1499,9 +1509,9 @@ void SoundPulse::Abort(unsigned dir)
|
|||
start = end = dir;
|
||||
|
||||
for (unsigned i = start; i <= end; i++) {
|
||||
if (stream[i]) {
|
||||
pa_simple_free(stream[i]);
|
||||
stream[i] = 0;
|
||||
if (sd[i].stream) {
|
||||
pa_simple_free(sd[i].stream);
|
||||
sd[i].stream = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1519,9 +1529,9 @@ void SoundPulse::flush(unsigned dir)
|
|||
|
||||
int err = PA_OK;
|
||||
for (unsigned i = start; i <= end; i++) {
|
||||
if (!stream[i])
|
||||
if (!sd[i].stream)
|
||||
continue;
|
||||
pa_simple_drain(stream[i], &err);
|
||||
pa_simple_drain(sd[i].stream, &err);
|
||||
if (err != PA_OK)
|
||||
cerr << pa_strerror(err) << '\n';
|
||||
}
|
||||
|
@ -1535,20 +1545,9 @@ size_t SoundPulse::Write(double* buf, size_t count)
|
|||
#endif
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
fbuf[2*i] = fbuf[2*i + 1] = buf[i];
|
||||
fbuf[CHANNELS * i] = fbuf[CHANNELS * i + 1] = buf[i];
|
||||
|
||||
float *wbuf = fbuf;
|
||||
if (sample_frequency != dev_sample_rate[1] || progdefaults.TX_corr != 0) {
|
||||
resample(1 << O_WRONLY, wbuf, count);
|
||||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
|
||||
int err;
|
||||
if (pa_simple_write(stream[1], wbuf, count * sizeof(double), &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
|
||||
return count;
|
||||
return resample_write(fbuf, count);
|
||||
}
|
||||
|
||||
size_t SoundPulse::Write_stereo(double* bufleft, double* bufright, size_t count)
|
||||
|
@ -1559,34 +1558,58 @@ size_t SoundPulse::Write_stereo(double* bufleft, double* bufright, size_t count)
|
|||
#endif
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
fbuf[2*i] = bufleft[i];
|
||||
fbuf[2*i + 1] = bufright[i];
|
||||
fbuf[CHANNELS * i] = bufleft[i];
|
||||
fbuf[CHANNELS * i + 1] = bufright[i];
|
||||
}
|
||||
|
||||
float *wbuf = fbuf;
|
||||
if (sample_frequency != dev_sample_rate[1] || progdefaults.TX_corr != 0) {
|
||||
resample(1 << O_WRONLY, wbuf, count);
|
||||
return resample_write(fbuf, count);
|
||||
}
|
||||
|
||||
size_t SoundPulse::resample_write(float* buf, size_t count)
|
||||
{
|
||||
int err;
|
||||
float *wbuf = buf;
|
||||
|
||||
if (progdefaults.TX_corr != 0) {
|
||||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = 1.0 + txppm / 1e6;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
}
|
||||
tx_src_data->data_in = wbuf;
|
||||
tx_src_data->input_frames = count;
|
||||
tx_src_data->data_out = src_buffer;
|
||||
tx_src_data->output_frames = SND_BUF_LEN;
|
||||
tx_src_data->end_of_input = 0;
|
||||
if ((err = src_process(tx_src_state, tx_src_data)) != 0)
|
||||
throw SndException(src_strerror(err));
|
||||
|
||||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
|
||||
int err;
|
||||
if (pa_simple_write(stream[1], wbuf, count * sizeof(double), &err) == -1)
|
||||
if (pa_simple_write(sd[1].stream, wbuf, count * CHANNELS * sizeof(float), &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t SoundPulse::Read(double *buf, size_t count)
|
||||
long SoundPulse::src_read_cb(void* arg, float** data)
|
||||
{
|
||||
size_t ncount = (size_t)MIN(SND_BUF_LEN, floor(count / rx_src_data->src_ratio));
|
||||
if (count == 1 && ncount == 0)
|
||||
ncount = 1;
|
||||
SoundPulse* p = reinterpret_cast<SoundPulse*>(arg);
|
||||
|
||||
int err;
|
||||
if (pa_simple_read(stream[0], fbuf, sizeof(double) * ncount, &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
if (pa_simple_read(p->sd[0].stream, p->snd_buffer, CHANNELS * sizeof(float) * p->sd[0].blocksize, &err) == -1) {
|
||||
cerr << "SoundPulse::pa_simple_read error: " << pa_strerror(err) << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
*data = p->snd_buffer;
|
||||
return p->sd[0].blocksize;
|
||||
}
|
||||
|
||||
size_t SoundPulse::Read(double *buf, size_t count)
|
||||
{
|
||||
#if USE_SNDFILE
|
||||
if (playback) {
|
||||
read_file(ifPlayback, buf, count);
|
||||
|
@ -1599,15 +1622,29 @@ size_t SoundPulse::Read(double *buf, size_t count)
|
|||
}
|
||||
#endif
|
||||
|
||||
float *rbuf = fbuf;
|
||||
if (sample_frequency != dev_sample_rate[0] || progdefaults.RX_corr != 0) {
|
||||
resample(1 << O_RDONLY, rbuf, ncount, count);
|
||||
rbuf = rx_src_data->data_out;
|
||||
count = rx_src_data->output_frames_gen;
|
||||
if (progdefaults.RX_corr != 0) {
|
||||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
sd[0].src_ratio = 1.0 / (1.0 + rxppm / 1e6);
|
||||
src_set_ratio(rx_src_state, sd[0].src_ratio);
|
||||
}
|
||||
long r;
|
||||
size_t n = 0;
|
||||
sd[0].blocksize = SCBLOCKSIZE;
|
||||
while (n < count) {
|
||||
if ((r = src_callback_read(rx_src_state, sd[0].src_ratio, count - n, fbuf + n*CHANNELS)) == 0)
|
||||
return n;
|
||||
n += r;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int err;
|
||||
if (pa_simple_read(sd[0].stream, fbuf, CHANNELS * sizeof(float) * count, &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
buf[i] = rbuf[2*i];
|
||||
buf[i] = fbuf[CHANNELS * i];
|
||||
|
||||
#if USE_SNDFILE
|
||||
if (capture)
|
||||
|
@ -1623,56 +1660,19 @@ void SoundPulse::src_data_reset(int mode)
|
|||
if (mode & 1 << O_RDONLY) {
|
||||
if (rx_src_state)
|
||||
src_delete(rx_src_state);
|
||||
rx_src_state = src_new(progdefaults.sample_converter, stream_params.channels, &err);
|
||||
rx_src_state = src_callback_new(src_read_cb, progdefaults.sample_converter,
|
||||
sd[0].stream_params.channels, &err, this);
|
||||
if (!rx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
rx_src_data->src_ratio = sample_frequency / (dev_sample_rate[0] * (1.0 + rxppm / 1e6));
|
||||
sd[0].src_ratio = 1.0 / (1.0 + rxppm / 1e6);
|
||||
}
|
||||
if (mode & 1 << O_WRONLY) {
|
||||
if (tx_src_state)
|
||||
src_delete(tx_src_state);
|
||||
tx_src_state = src_new(progdefaults.sample_converter, stream_params.channels, &err);
|
||||
tx_src_state = src_new(progdefaults.sample_converter, sd[1].stream_params.channels, &err);
|
||||
if (!tx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
tx_src_data->src_ratio = dev_sample_rate[1] * (1.0 + txppm / 1e6) / sample_frequency;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPulse::resample(int mode, float *buf, size_t count, size_t max)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (mode & 1 << O_RDONLY) {
|
||||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
rx_src_data->src_ratio = sample_frequency / (dev_sample_rate[0] * (1.0 + rxppm / 1e6));
|
||||
src_set_ratio(rx_src_state, rx_src_data->src_ratio);
|
||||
}
|
||||
|
||||
rx_src_data->data_in = buf;
|
||||
rx_src_data->input_frames = count;
|
||||
rx_src_data->data_out = snd_buffer;
|
||||
rx_src_data->output_frames = max ? max : SND_BUF_LEN;
|
||||
rx_src_data->end_of_input = 0;
|
||||
|
||||
if ((r = src_process(rx_src_state, rx_src_data)) != 0)
|
||||
throw SndException(src_strerror(r));
|
||||
}
|
||||
else if (mode & 1 << O_WRONLY) {
|
||||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = dev_sample_rate[1] * (1.0 + txppm / 1e6) / sample_frequency;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
}
|
||||
|
||||
tx_src_data->data_in = buf;
|
||||
tx_src_data->input_frames = count;
|
||||
tx_src_data->data_out = src_buffer;
|
||||
tx_src_data->output_frames = max ? max : SND_BUF_LEN;
|
||||
tx_src_data->end_of_input = 0;
|
||||
|
||||
if ((r = src_process(tx_src_state, tx_src_data)) != 0)
|
||||
throw SndException(src_strerror(r));
|
||||
tx_src_data->src_ratio = 1.0 + txppm / 1e6;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -317,8 +317,6 @@ void sound_update(unsigned idx)
|
|||
break;
|
||||
case SND_IDX_PULSE:
|
||||
inpPulseServer->activate();
|
||||
menuInSampleRate->activate();
|
||||
menuOutSampleRate->activate();
|
||||
scDevice[0] = scDevice[1] = inpPulseServer->value();
|
||||
break;
|
||||
case SND_IDX_NULL:
|
||||
|
|
|
@ -35,6 +35,12 @@ modem *qpsk125_modem = 0;
|
|||
modem *qpsk250_modem = 0;
|
||||
modem *olivia_modem = 0;
|
||||
modem *rtty_modem = 0;
|
||||
modem *dex4_modem = 0;
|
||||
modem *dex5_modem = 0;
|
||||
modem *dex8_modem = 0;
|
||||
modem *dex11_modem = 0;
|
||||
modem *dex16_modem = 0;
|
||||
modem *dex22_modem = 0;
|
||||
modem *dominoex4_modem = 0;
|
||||
modem *dominoex5_modem = 0;
|
||||
modem *dominoex8_modem = 0;
|
||||
|
|
Ładowanie…
Reference in New Issue