2020-12-22 12:30:00 -05:00
|
|
|
|
/*
|
|
|
|
|
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
|
|
|
|
* =========================================================
|
|
|
|
|
* Author: DJ0ABR
|
|
|
|
|
*
|
|
|
|
|
* (c) DJ0ABR
|
|
|
|
|
* www.dj0abr.de
|
|
|
|
|
*
|
|
|
|
|
* This program 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.
|
|
|
|
|
*
|
|
|
|
|
* This program 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 this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* RTTY:
|
|
|
|
|
* =====
|
|
|
|
|
* bit/symbol: 1
|
|
|
|
|
* BW: 170 Hz
|
|
|
|
|
* carrier: 1500 Hz
|
|
|
|
|
* speed: 45.45 bits/s
|
|
|
|
|
* samples/symbol: 33
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "hsmodem.h"
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
int evalSymbols(uint8_t sym);
|
|
|
|
|
void baudot_encoder(char c, uint8_t bd[2], int* pnum);
|
|
|
|
|
uint8_t getBaudot(char c, int letters);
|
|
|
|
|
char baudot_decoder(char c);
|
|
|
|
|
void sendRttyToGUI(uint8_t b);
|
2021-02-22 09:58:19 -05:00
|
|
|
|
void send_baudot(char c);
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
#define rtty_CENTERFREQUENCY 1500
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
float rtty_RADIANS_PER_SAMPLE = 0;
|
|
|
|
|
float rtty_RX_RADIANS_PER_SAMPLE = 0;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
|
|
|
|
nco_crcf rtty_upnco = NULL;
|
|
|
|
|
nco_crcf rtty_dnnco = NULL;
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
// RX RTTY Filter
|
|
|
|
|
firfilt_crcf rtty_q;
|
|
|
|
|
unsigned int flt_h_len = 65; // filter length
|
|
|
|
|
// we filter at 48k samp rate, half BW=170/2, so lets filter at 80 Hz
|
|
|
|
|
float flt_fc = 80.0f/48000.0f; // cutoff frequency
|
|
|
|
|
float flt_As = 60.0f; // stop-band attenuation
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
int rtty_txoff = 1; // 1=off, 0=on , >1...downcount for off
|
|
|
|
|
int synced = 0;
|
|
|
|
|
int run_rtty_threads = 0;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
int rtty_frequency = rtty_CENTERFREQUENCY;
|
|
|
|
|
|
|
|
|
|
unsigned int m = 1; // number of bits/symbol
|
|
|
|
|
#define k 264 // filter samples/symbol
|
|
|
|
|
float SNRdB = 0.0f; // signal-to-noise ratio [dB]
|
|
|
|
|
float bandwidth = 0.001894f; // frequency spacing
|
|
|
|
|
unsigned int nfft = 1200; // FFT size for compute spectrum
|
|
|
|
|
|
|
|
|
|
liquid_float_complex buf_tx[k]; // transmit buffer
|
|
|
|
|
liquid_float_complex buf_rx[k]; // transmit buffer
|
|
|
|
|
unsigned int sym_out;
|
|
|
|
|
float nstd;
|
|
|
|
|
|
|
|
|
|
fskmod modi = NULL;
|
|
|
|
|
fskdem dem = NULL;
|
|
|
|
|
|
|
|
|
|
int rtty_autosync = 0;
|
|
|
|
|
|
|
|
|
|
void rtty_modifyRXfreq(int f_Hz)
|
|
|
|
|
{
|
2021-01-15 16:15:58 -05:00
|
|
|
|
//printf("set:%d Hz\n", f_Hz);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
rtty_frequency = f_Hz;
|
|
|
|
|
rtty_RX_RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (float)f_Hz) / (float)caprate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void sendRttyToGUI(uint8_t b)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
uint8_t txpl[7];
|
|
|
|
|
txpl[0] = 6; // RTTY RX Byte follows
|
|
|
|
|
txpl[1] = b; // RXed byte
|
2021-02-22 09:58:19 -05:00
|
|
|
|
txpl[2] = 0;
|
2021-01-02 18:34:35 -05:00
|
|
|
|
txpl[3] = synced;
|
|
|
|
|
txpl[4] = 0; // unused
|
|
|
|
|
txpl[5] = 0;
|
|
|
|
|
txpl[6] = 0;
|
|
|
|
|
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, sizeof(txpl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* space=lower tone (Bitvalue=1)
|
|
|
|
|
* mark=higher tone (Bitvalue=0)
|
|
|
|
|
*
|
|
|
|
|
* start (space)
|
|
|
|
|
* bit 4
|
|
|
|
|
* bit 3
|
|
|
|
|
* bit 2
|
|
|
|
|
* bit 1
|
|
|
|
|
* bit 0
|
|
|
|
|
* stop (mark)
|
|
|
|
|
* stop (mark)
|
|
|
|
|
*
|
|
|
|
|
* send space if nothing to transmit
|
|
|
|
|
*/
|
|
|
|
|
BAUDOTTAB baudot[] = {
|
|
|
|
|
{0b00010111,'Q','1',},
|
|
|
|
|
{0b00010011,'W','2',},
|
|
|
|
|
{0b00000001,'E','3',},
|
|
|
|
|
{0b00001010,'R','4',},
|
|
|
|
|
{0b00010000,'T','5',},
|
|
|
|
|
{0b00010101,'Y','6',},
|
|
|
|
|
{0b00000111,'U','7',},
|
|
|
|
|
{0b00000110,'I','8',},
|
|
|
|
|
{0b00011000,'O','9',},
|
|
|
|
|
{0b00010110,'P','0',},
|
|
|
|
|
{0b00000011,'A','-',},
|
|
|
|
|
{0b00000101,'S','\'',},
|
|
|
|
|
{0b00001001,'D','|',},
|
|
|
|
|
{0b00001101,'F','|',},
|
|
|
|
|
{0b00011010,'G','|',},
|
|
|
|
|
{0b00010100,'H','|',},
|
|
|
|
|
{0b00001011,'J','|',},
|
|
|
|
|
{0b00001111,'K','(',},
|
|
|
|
|
{0b00010010,'L',')',},
|
|
|
|
|
{0b00010001,'Z','+',},
|
|
|
|
|
{0b00011101,'X','/',},
|
|
|
|
|
{0b00001110,'C',':',},
|
|
|
|
|
{0b00011110,'V','=',},
|
|
|
|
|
{0b00011001,'B','?',},
|
|
|
|
|
{0b00001100,'N',',',},
|
|
|
|
|
{0b00011100,'M','.',},
|
|
|
|
|
{0b00001000,'\r','\r',},
|
|
|
|
|
{0b00000010,'\n','\n',},
|
|
|
|
|
{0b00000100,' ',' ',},
|
|
|
|
|
{0b00011011,'@','@',}, // switch to numbers
|
|
|
|
|
{0b00011111,'<EFBFBD>','<EFBFBD>',}, // switch to letters (if already letter, then backspace)
|
|
|
|
|
{0xff,' ',' '} // end of table
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int isletters = 1;
|
|
|
|
|
|
|
|
|
|
void baudot_encoder(char c, uint8_t bd[2], int* pnum)
|
|
|
|
|
{
|
|
|
|
|
int anz = 0;
|
|
|
|
|
int letters = 0;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
//printf("ascii:%c letter:%d (%d)\n", c, letters,isletters);
|
|
|
|
|
|
|
|
|
|
if (c == '#')
|
|
|
|
|
{
|
|
|
|
|
rtty_txoff = 0;
|
|
|
|
|
*pnum = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == '~')
|
|
|
|
|
{
|
|
|
|
|
rtty_txoff = 10;
|
|
|
|
|
*pnum = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (c == 0x08)
|
|
|
|
|
{
|
|
|
|
|
// backspace does not exist in RTTY
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (c == ' ')
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
bd[anz++] = 4;
|
|
|
|
|
*pnum = anz;
|
|
|
|
|
return;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (c == '\n')
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
bd[anz++] = getBaudot('\n', letters);
|
|
|
|
|
bd[anz++] = getBaudot('\r', letters);
|
|
|
|
|
*pnum = anz;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
return;
|
|
|
|
|
}
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
|
|
|
|
if (c >= 'A' && c <= 'Z') letters = 1;
|
|
|
|
|
|
|
|
|
|
if (letters == 1 && isletters == 0)
|
|
|
|
|
{
|
|
|
|
|
bd[anz++] = getBaudot('<EFBFBD>', letters);
|
|
|
|
|
isletters = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letters == 0 && isletters == 1)
|
|
|
|
|
{
|
|
|
|
|
bd[anz++] = getBaudot('@', letters);
|
|
|
|
|
isletters = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bd[anz++] = getBaudot(c, letters);
|
|
|
|
|
|
|
|
|
|
*pnum = anz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t getBaudot(char c, int letters)
|
|
|
|
|
{
|
|
|
|
|
uint8_t res = 0;
|
|
|
|
|
int idx = 0;
|
|
|
|
|
|
|
|
|
|
while (baudot[idx].baudot != 0xff)
|
|
|
|
|
{
|
|
|
|
|
if (letters == 1 && c == baudot[idx].letter)
|
|
|
|
|
{
|
|
|
|
|
res = baudot[idx].baudot;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letters == 0 && c == baudot[idx].number)
|
|
|
|
|
{
|
|
|
|
|
res = baudot[idx].baudot;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char baudot_decoder(char c)
|
|
|
|
|
{
|
|
|
|
|
static int letter = 1;
|
|
|
|
|
|
|
|
|
|
if (c == 0x1b)
|
|
|
|
|
{
|
|
|
|
|
letter = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == 0x1f)
|
|
|
|
|
{
|
|
|
|
|
letter = 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int idx = 0;
|
|
|
|
|
while (baudot[idx].baudot != 0xff)
|
|
|
|
|
{
|
|
|
|
|
if (baudot[idx].baudot == c)
|
|
|
|
|
{
|
|
|
|
|
if (letter == 1)
|
|
|
|
|
return baudot[idx].letter;
|
|
|
|
|
else
|
|
|
|
|
return baudot[idx].number;
|
|
|
|
|
}
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
// ======================================================================================
|
|
|
|
|
// Testfunctions: sends 010101.. in rtty speed and shows RX
|
|
|
|
|
// 1. disable rtty_init()
|
|
|
|
|
// 2. call testall() from hsmodem if speedmode==10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ttest();
|
|
|
|
|
int rtest();
|
|
|
|
|
void itest();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void testall()
|
|
|
|
|
{
|
|
|
|
|
static int f = 1;
|
|
|
|
|
if (f)
|
|
|
|
|
{
|
|
|
|
|
itest();
|
|
|
|
|
f = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (keeprunning)
|
|
|
|
|
{
|
|
|
|
|
ttest();
|
|
|
|
|
while(keeprunning && rtest());
|
|
|
|
|
sleep_ms(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void itest()
|
|
|
|
|
{
|
|
|
|
|
M = 1 << m;
|
|
|
|
|
nstd = powf(10.0f, -SNRdB / 20.0f);
|
|
|
|
|
|
|
|
|
|
// create modulator/demodulator pair
|
|
|
|
|
modi = fskmod_create(m, k, bandwidth);
|
|
|
|
|
dem = fskdem_create(m, k, bandwidth);
|
|
|
|
|
fskdem_print(dem);
|
|
|
|
|
|
|
|
|
|
// create NCO for up-mixing to 1500 Hz
|
|
|
|
|
rtty_RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (float)rtty_CENTERFREQUENCY) / (float)caprate);
|
|
|
|
|
rtty_upnco = nco_crcf_create(LIQUID_NCO);
|
|
|
|
|
nco_crcf_set_phase(rtty_upnco, 0.0f);
|
|
|
|
|
nco_crcf_set_frequency(rtty_upnco, rtty_RADIANS_PER_SAMPLE);
|
|
|
|
|
|
|
|
|
|
// create NCO for down-mixing from 1500 Hz
|
|
|
|
|
float rtty_RX_RADIANS_PER_SAMPLE = 2.0f * (float)M_PI * (float)rtty_CENTERFREQUENCY / (float)caprate;
|
|
|
|
|
rtty_dnnco = nco_crcf_create(LIQUID_NCO);
|
|
|
|
|
nco_crcf_set_phase(rtty_dnnco, 0.0f);
|
|
|
|
|
nco_crcf_set_frequency(rtty_dnnco, rtty_RX_RADIANS_PER_SAMPLE);
|
|
|
|
|
|
|
|
|
|
// modulate, demodulate, count errors
|
|
|
|
|
unsigned int num_symbol_errors = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ttest()
|
|
|
|
|
{
|
|
|
|
|
int i, j;
|
|
|
|
|
static unsigned int sym_in=0;
|
|
|
|
|
|
|
|
|
|
int fs = io_pb_fifo_freespace(0);
|
|
|
|
|
if (fs < 20000) return;
|
|
|
|
|
|
|
|
|
|
static int scnt = 0;
|
|
|
|
|
if (++scnt == 8)
|
|
|
|
|
{
|
|
|
|
|
scnt = 0;
|
|
|
|
|
sym_in = 1 - sym_in;
|
|
|
|
|
}
|
|
|
|
|
//unsigned int sym_in = rand() % M;
|
|
|
|
|
|
|
|
|
|
//measure_speed_bps(1);
|
|
|
|
|
|
|
|
|
|
//printf("modulate\n");
|
|
|
|
|
fskmod_modulate(modi, sym_in, &(buf_tx[0]));
|
|
|
|
|
|
|
|
|
|
// move sample to 1,5kHz carrier
|
|
|
|
|
for (j = 0; j < k; j++)
|
|
|
|
|
{
|
|
|
|
|
nco_crcf_step(rtty_upnco);
|
|
|
|
|
nco_crcf_mix_up(rtty_upnco, buf_tx[j], &(buf_tx1500[j]));
|
|
|
|
|
|
|
|
|
|
usbf = (buf_tx1500[j]).real + (buf_tx1500[j]).imag;
|
|
|
|
|
|
|
|
|
|
//measure_speed_bps(1);
|
|
|
|
|
|
|
|
|
|
io_pb_write_fifo(usbf * 0.2f); // reduce volume and send to soundcard
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rtest()
|
|
|
|
|
{
|
|
|
|
|
static int bridx = 0;
|
|
|
|
|
|
|
|
|
|
int j;
|
|
|
|
|
float f;
|
|
|
|
|
int ret = io_cap_read_fifo(&f);
|
|
|
|
|
if (ret == 0) return 0;
|
|
|
|
|
|
|
|
|
|
// noise
|
|
|
|
|
//printf("%f\n", f);
|
|
|
|
|
//f = f + nstd * randnf() * M_SQRT1_2;
|
|
|
|
|
|
|
|
|
|
(buf_rx1500[bridx]).real = f;
|
|
|
|
|
(buf_rx1500[bridx]).imag = f;
|
|
|
|
|
nco_crcf_step(rtty_dnnco);
|
|
|
|
|
nco_crcf_mix_down(rtty_dnnco, buf_rx1500[bridx], &(buf_rx[bridx]));
|
|
|
|
|
bridx++;
|
|
|
|
|
|
|
|
|
|
if (bridx == k)
|
|
|
|
|
{
|
|
|
|
|
bridx = 0;
|
|
|
|
|
|
|
|
|
|
sym_out = fskdem_demodulate(dem, buf_rx);
|
|
|
|
|
|
|
|
|
|
static int nlixd = 0;
|
|
|
|
|
|
|
|
|
|
//measure_speed_bps(1);
|
|
|
|
|
|
|
|
|
|
printf("%01X ", sym_out);
|
|
|
|
|
if (++nlixd == 16)
|
|
|
|
|
{
|
|
|
|
|
nlixd = 0;
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
void* rtty_tx_function(void* param);
|
|
|
|
|
void* rtty_rx_function(void* param);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32_
|
|
|
|
|
void rtty_tx_function(void* param);
|
|
|
|
|
void rtty_rx_function(void* param);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void init_rtty()
|
|
|
|
|
{
|
|
|
|
|
//printf("wegen FM test, kein Init RTTY\n");
|
|
|
|
|
//return;
|
|
|
|
|
|
2021-02-22 09:58:19 -05:00
|
|
|
|
fifo_clear(FIFO_RTTYTX);
|
2021-01-15 16:15:58 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
close_rtty();
|
|
|
|
|
|
|
|
|
|
printf("Init RTTY\n");
|
|
|
|
|
nstd = powf(10.0f, -SNRdB / 20.0f); // SNR Simulation referenced to -1..+1
|
|
|
|
|
|
|
|
|
|
// create modulator/demodulator pair
|
|
|
|
|
modi = fskmod_create(m, k, bandwidth);
|
|
|
|
|
dem = fskdem_create(m, k, bandwidth);
|
|
|
|
|
|
|
|
|
|
// create NCO for up-mixing to 1500 Hz
|
|
|
|
|
rtty_RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (float)rtty_CENTERFREQUENCY) / (float)caprate);
|
2020-12-22 12:30:00 -05:00
|
|
|
|
rtty_upnco = nco_crcf_create(LIQUID_NCO);
|
|
|
|
|
nco_crcf_set_phase(rtty_upnco, 0.0f);
|
|
|
|
|
nco_crcf_set_frequency(rtty_upnco, rtty_RADIANS_PER_SAMPLE);
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
// create NCO for down-mixing from 1500 Hz
|
|
|
|
|
rtty_RX_RADIANS_PER_SAMPLE = 2.0f * (float)M_PI * (float)rtty_CENTERFREQUENCY / (float)caprate;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
rtty_dnnco = nco_crcf_create(LIQUID_NCO);
|
|
|
|
|
nco_crcf_set_phase(rtty_dnnco, 0.0f);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
nco_crcf_set_frequency(rtty_dnnco, rtty_RX_RADIANS_PER_SAMPLE);
|
|
|
|
|
|
|
|
|
|
// RTTY RX Filter
|
|
|
|
|
rtty_q = firfilt_crcf_create_kaiser(flt_h_len, flt_fc, flt_As, 0.0f);
|
|
|
|
|
firfilt_crcf_set_scale(rtty_q, 2.0f * flt_fc);
|
2021-01-15 16:15:58 -05:00
|
|
|
|
|
|
|
|
|
// create the rtty threads
|
2021-01-02 18:34:35 -05:00
|
|
|
|
static int f = 1;
|
|
|
|
|
if (f)
|
|
|
|
|
{
|
|
|
|
|
f = 0;
|
|
|
|
|
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
pthread_t rtty_txthread;
|
|
|
|
|
pthread_create(&rtty_txthread, NULL, rtty_tx_function, NULL);
|
|
|
|
|
pthread_t rtty_rxthread;
|
|
|
|
|
pthread_create(&rtty_rxthread, NULL, rtty_rx_function, NULL);
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef _WIN32_
|
|
|
|
|
_beginthread(rtty_tx_function, 0, NULL);
|
|
|
|
|
_beginthread(rtty_rx_function, 0, NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
run_rtty_threads = 1;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void close_rtty()
|
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
printf("Close RTTY\n");
|
|
|
|
|
|
|
|
|
|
run_rtty_threads = 0;
|
|
|
|
|
sleep_ms(100);
|
|
|
|
|
|
|
|
|
|
if (modi != NULL) fskmod_destroy(modi);
|
|
|
|
|
modi = NULL;
|
|
|
|
|
|
|
|
|
|
if (dem != NULL) fskdem_destroy(dem);
|
|
|
|
|
dem = NULL;
|
|
|
|
|
|
2020-12-22 12:30:00 -05:00
|
|
|
|
if (rtty_upnco != NULL) nco_crcf_destroy(rtty_upnco);
|
|
|
|
|
rtty_upnco = NULL;
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
2020-12-22 12:30:00 -05:00
|
|
|
|
if (rtty_dnnco != NULL) nco_crcf_destroy(rtty_dnnco);
|
|
|
|
|
rtty_dnnco = NULL;
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (rtty_q != NULL) firfilt_crcf_destroy(rtty_q);
|
|
|
|
|
rtty_q = NULL;
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
// RTTY TX thread
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
void* rtty_tx_function(void* param)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
pthread_detach(pthread_self());
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef _WIN32_
|
|
|
|
|
void rtty_tx_function(void* param)
|
|
|
|
|
{
|
|
|
|
|
#endif
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
uint8_t bd[2];
|
|
|
|
|
int anz = 0;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-15 16:15:58 -05:00
|
|
|
|
printf("TX thread\n");
|
2021-01-02 18:34:35 -05:00
|
|
|
|
while (keeprunning)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
while (run_rtty_threads == 0)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
sleep_ms(100);
|
|
|
|
|
if (keeprunning == 0)
|
|
|
|
|
{
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
pthread_exit(NULL); // self terminate this thread
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef _WIN32_
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-22 09:58:19 -05:00
|
|
|
|
uint8_t pcsend[200];
|
|
|
|
|
int rlen = read_fifo(FIFO_RTTYTX, pcsend, 200);
|
|
|
|
|
|
|
|
|
|
if(rlen > 0)
|
2021-01-02 18:34:35 -05:00
|
|
|
|
{
|
2021-02-22 09:58:19 -05:00
|
|
|
|
//printf("from fifo:%d <%s>\n", rlen, pcsend);
|
|
|
|
|
for (int ilen = 0; ilen < rlen; ilen++)
|
|
|
|
|
{
|
|
|
|
|
baudot_encoder(pcsend[ilen], bd, &anz);
|
|
|
|
|
for (int il = 0; il < anz; il++)
|
|
|
|
|
{
|
|
|
|
|
send_baudot(bd[il]);
|
|
|
|
|
//printf("send: %d -> %02X\n", pcsend[ilen], bd[il]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-02 18:34:35 -05:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (rtty_txoff == 1)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
|
|
|
|
sleep_ms(10);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
continue;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (rtty_txoff > 1) rtty_txoff--;
|
2021-02-22 09:58:19 -05:00
|
|
|
|
send_baudot(0); // idle
|
2021-01-02 18:34:35 -05:00
|
|
|
|
}
|
2021-02-22 09:58:19 -05:00
|
|
|
|
}
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
pthread_exit(NULL); // self terminate this thread
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
2021-02-22 09:58:19 -05:00
|
|
|
|
// send one baudot byte
|
|
|
|
|
void send_baudot(char c)
|
|
|
|
|
{
|
|
|
|
|
// c is the baudot code, fill into final byte cs
|
|
|
|
|
uint8_t cs = 0;
|
|
|
|
|
cs |= ((c & 1) ? 0x40 : 0);
|
|
|
|
|
cs |= ((c & 2) ? 0x20 : 0);
|
|
|
|
|
cs |= ((c & 4) ? 0x10 : 0);
|
|
|
|
|
cs |= ((c & 8) ? 0x08 : 0);
|
|
|
|
|
cs |= ((c & 16) ? 0x04 : 0);
|
|
|
|
|
cs &= ~0x80; // Start bit to 1
|
|
|
|
|
cs |= 3; // 2 stop bits
|
|
|
|
|
|
|
|
|
|
// send cs bit per bit
|
|
|
|
|
for (int bitidx = 7; bitidx >= 0; bitidx--)
|
|
|
|
|
{
|
|
|
|
|
if (run_rtty_threads == 0) break;
|
|
|
|
|
|
|
|
|
|
//measure_speed_bps(1);
|
|
|
|
|
|
|
|
|
|
unsigned int sym_in = (cs & (1 << bitidx)) ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
for (int twice = 0; twice < 4; twice++)
|
2021-01-02 18:34:35 -05:00
|
|
|
|
{
|
2021-02-22 09:58:19 -05:00
|
|
|
|
if (bitidx == 0 && twice == 2) break; //last bit only once
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
2021-02-22 09:58:19 -05:00
|
|
|
|
fskmod_modulate(modi, sym_in, &(buf_tx[0]));
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
2021-02-22 09:58:19 -05:00
|
|
|
|
// move sample to 1,5kHz carrier
|
|
|
|
|
for (int j = 0; j < k; j++)
|
|
|
|
|
{
|
|
|
|
|
nco_crcf_step(rtty_upnco);
|
|
|
|
|
liquid_float_complex outb;
|
|
|
|
|
nco_crcf_mix_up(rtty_upnco, buf_tx[j], &outb);
|
|
|
|
|
|
|
|
|
|
float usbf = outb.real + outb.imag;
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
2021-02-22 09:58:19 -05:00
|
|
|
|
// adapt to audio sample rate
|
|
|
|
|
int fs;
|
|
|
|
|
while (keeprunning && run_rtty_threads)
|
2021-01-02 18:34:35 -05:00
|
|
|
|
{
|
2021-02-22 09:58:19 -05:00
|
|
|
|
fs = io_fifo_usedspace(io_pbidx);
|
|
|
|
|
//printf("%d\n", fs);
|
|
|
|
|
// attention: if this number is too low, the audio write callback will not process it
|
|
|
|
|
if (fs < 24000) break;
|
|
|
|
|
sleep_ms(10);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
}
|
2021-02-22 09:58:19 -05:00
|
|
|
|
|
|
|
|
|
usbf *= 0.015f; // make RTTY signal smaller then PSK
|
|
|
|
|
kmaudio_playsamples(io_pbidx, &usbf, 1, pbvol);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
// RTTY RX thread
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
void* rtty_rx_function(void* param)
|
|
|
|
|
{
|
|
|
|
|
pthread_detach(pthread_self());
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef _WIN32_
|
|
|
|
|
void rtty_rx_function(void* param)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
#endif
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
while (keeprunning)
|
|
|
|
|
{
|
|
|
|
|
while (run_rtty_threads == 0)
|
|
|
|
|
{
|
|
|
|
|
sleep_ms(100);
|
|
|
|
|
if (keeprunning == 0)
|
|
|
|
|
{
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
pthread_exit(NULL); // self terminate this thread
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef _WIN32_
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
static int bridx = 0;
|
2021-01-15 16:15:58 -05:00
|
|
|
|
// get available received samples
|
|
|
|
|
float farr[1100];
|
|
|
|
|
int ret = kmaudio_readsamples(io_capidx, farr, 1000, capvol,0);
|
|
|
|
|
if (ret == 0)
|
2021-01-02 18:34:35 -05:00
|
|
|
|
{
|
2021-01-15 16:15:58 -05:00
|
|
|
|
sleep_ms(10);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
2021-01-15 16:15:58 -05:00
|
|
|
|
for (int fanz = 0; fanz < ret; fanz++)
|
|
|
|
|
{
|
|
|
|
|
float f = farr[fanz];
|
|
|
|
|
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
|
|
|
|
|
kmaudio_playsamples(voice_pbidx, &f, 1,lsvol);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
|
|
|
|
|
make_FFTdata(f * 25);
|
|
|
|
|
|
|
|
|
|
static float last_rs = 0;
|
|
|
|
|
if (rtty_RX_RADIANS_PER_SAMPLE != last_rs)
|
|
|
|
|
{
|
|
|
|
|
// tuning freq was changed, set NCO
|
|
|
|
|
nco_crcf_set_frequency(rtty_dnnco, rtty_RX_RADIANS_PER_SAMPLE);
|
|
|
|
|
last_rs = rtty_RX_RADIANS_PER_SAMPLE;
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
liquid_float_complex rx1500;
|
|
|
|
|
rx1500.real = f;
|
|
|
|
|
rx1500.imag = f;
|
|
|
|
|
nco_crcf_step(rtty_dnnco);
|
|
|
|
|
liquid_float_complex dc_out;
|
|
|
|
|
nco_crcf_mix_down(rtty_dnnco, rx1500, &dc_out);
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
// sharp filter
|
2021-02-22 09:58:19 -05:00
|
|
|
|
firfilt_crcf_push(rtty_q, dc_out); // push input sample
|
2021-01-02 18:34:35 -05:00
|
|
|
|
firfilt_crcf_execute(rtty_q, &(buf_rx[bridx])); // compute output
|
|
|
|
|
|
|
|
|
|
bridx++;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (bridx == k)
|
|
|
|
|
{
|
|
|
|
|
bridx = 0;
|
|
|
|
|
|
|
|
|
|
sym_out = fskdem_demodulate(dem, buf_rx);
|
|
|
|
|
|
|
|
|
|
int db = evalSymbols(sym_out);
|
|
|
|
|
if (db != -1)
|
|
|
|
|
{
|
|
|
|
|
char lt = baudot_decoder((uint8_t)db);
|
2021-02-22 09:58:19 -05:00
|
|
|
|
//printf("rxbyte:%02X decoded:%02X\n", db, lt);
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (lt > 0)
|
|
|
|
|
sendRttyToGUI(lt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef _LINUX_
|
|
|
|
|
pthread_exit(NULL); // self terminate this thread
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
// get the bit level,
|
|
|
|
|
// offs ... offset from beginning of the symbol buffer in bits,
|
|
|
|
|
// so offset 0 ist the beginning
|
|
|
|
|
// and offset 1 is the "overs" bit, i.e.: symbol[4]
|
|
|
|
|
// this eliminates the need of thinking in oversampled bits
|
2020-12-22 12:30:00 -05:00
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
const int maxsym = 8; // stop-start-1-2-3-4-5-stop
|
|
|
|
|
const int overs = 4; // symbols per bit
|
|
|
|
|
uint8_t symbuf[maxsym * overs];
|
|
|
|
|
int bitcnt = 0;
|
|
|
|
|
|
|
|
|
|
uint8_t getBit(int offs)
|
|
|
|
|
{
|
|
|
|
|
// we have overs symbols per bit
|
|
|
|
|
// the first or maybe last symbol may be wrong, so we don't use it
|
|
|
|
|
// looks like that ignoring the first sample is the best solution
|
|
|
|
|
// lets evaluate the value of three symbols
|
|
|
|
|
int pos = offs * overs;
|
|
|
|
|
uint8_t h = 0, l = 0;
|
|
|
|
|
for (int i = 0+1; i < overs - 1+1; i++)
|
2020-12-22 12:30:00 -05:00
|
|
|
|
{
|
2021-01-02 18:34:35 -05:00
|
|
|
|
if (symbuf[pos + i]) h++;
|
|
|
|
|
else l++;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-02 18:34:35 -05:00
|
|
|
|
return (h > l) ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t getDatebyte()
|
|
|
|
|
{
|
|
|
|
|
uint8_t db = 0;
|
|
|
|
|
|
|
|
|
|
db |= getBit(2) ? 0x01 : 0;
|
|
|
|
|
db |= getBit(3) ? 0x02 : 0;
|
|
|
|
|
db |= getBit(4) ? 0x04 : 0;
|
|
|
|
|
db |= getBit(5) ? 0x08 : 0;
|
|
|
|
|
db |= getBit(6) ? 0x10 : 0;
|
|
|
|
|
|
|
|
|
|
return db;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if there is a complete frame in the symbol buffer
|
|
|
|
|
int findStart()
|
|
|
|
|
{
|
|
|
|
|
if ((synced == 0 || bitcnt > overs * 6) && symbuf[3] == 1 && symbuf[4] == 0)
|
|
|
|
|
{
|
|
|
|
|
if (getBit(0) == 1 && getBit(1) == 0 && getBit(7) == 1)
|
|
|
|
|
{
|
|
|
|
|
//printf("possible Frame Detection at index:%d\n", bitcnt);
|
|
|
|
|
bitcnt = 0;
|
|
|
|
|
synced = 1;
|
|
|
|
|
return getDatebyte();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
synced = 0;
|
|
|
|
|
}
|
|
|
|
|
bitcnt++;
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int evalSymbols(uint8_t sym)
|
|
|
|
|
{
|
|
|
|
|
// feed smbol in buffer
|
|
|
|
|
memmove(symbuf, symbuf + 1, maxsym * overs - 1);
|
|
|
|
|
symbuf[maxsym * overs - 1] = sym;
|
|
|
|
|
|
|
|
|
|
//showbitstring("rx: ", symbuf, sizeof(symbuf), sizeof(symbuf));
|
|
|
|
|
|
|
|
|
|
int db = findStart();
|
|
|
|
|
if (db != -1)
|
|
|
|
|
{
|
|
|
|
|
//printf("Data_ %02X\n", db);
|
|
|
|
|
}
|
|
|
|
|
return db;
|
2020-12-22 12:30:00 -05:00
|
|
|
|
}
|