mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2026-06-03 14:34:38 -04:00
update
This commit is contained in:
@@ -27,8 +27,6 @@
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void close_a();
|
||||
|
||||
const int h_len = 57;
|
||||
float h[h_len];
|
||||
firfilt_crcf qfilt = NULL;
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct _BAUDOTTAB_ {
|
||||
unsigned char baudot;
|
||||
char letter;
|
||||
char number;
|
||||
} BAUDOTTAB;
|
||||
+92
-4
@@ -47,6 +47,24 @@ int rxlevel_deteced = 0;
|
||||
int rx_in_sync = 0;
|
||||
msresamp_crcf fftdecim = NULL;
|
||||
|
||||
float doublePeak(float *f_fftout, int e)
|
||||
{
|
||||
// measure level at rtty freq
|
||||
// -100..-70 and +70..+100
|
||||
float v = 0;
|
||||
v += f_fftout[e - 7];
|
||||
v += f_fftout[e - 8];
|
||||
v += f_fftout[e - 9];
|
||||
v += f_fftout[e - 10];
|
||||
|
||||
v += f_fftout[e + 7];
|
||||
v += f_fftout[e + 8];
|
||||
v += f_fftout[e + 9];
|
||||
v += f_fftout[e + 10];
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
uint16_t *make_waterfall(float fre, int *retlen)
|
||||
{
|
||||
int fftrdy = 0;
|
||||
@@ -101,9 +119,27 @@ uint16_t *make_waterfall(float fre, int *retlen)
|
||||
|
||||
// measure level at mid band
|
||||
float midlevel = 0;
|
||||
for (int e = 100; e < 200; e++)
|
||||
midlevel += f_fftout[e];
|
||||
midlevel /= 100;
|
||||
if (speedmode == 10)
|
||||
{
|
||||
// RTTY
|
||||
int mid = (rtty_frequency - 170 / 2) / 10;
|
||||
int lowlow = mid - 5;
|
||||
int lowhigh = mid + 5;
|
||||
mid = (rtty_frequency + 170 / 2) / 10;
|
||||
int highlow = mid - 5;
|
||||
int highhigh = mid + 5;
|
||||
for (int e = lowlow; e < lowhigh; e++)
|
||||
midlevel += f_fftout[e];
|
||||
for (int e = highlow; e < highhigh; e++)
|
||||
midlevel += f_fftout[e];
|
||||
midlevel /= ((lowhigh-lowlow) + (highhigh-highlow));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int e = 100; e < 200; e++)
|
||||
midlevel += f_fftout[e];
|
||||
midlevel /= 100;
|
||||
}
|
||||
|
||||
//calc difference in %
|
||||
int idiff = (int)((edgelevel * 100) / midlevel);
|
||||
@@ -118,10 +154,62 @@ uint16_t *make_waterfall(float fre, int *retlen)
|
||||
|
||||
// check if signal detected or not
|
||||
if (idiff > 100) sig = 0;
|
||||
if (idiff < 30) sig = 1;
|
||||
if (idiff < 50) sig = 1;
|
||||
|
||||
rxlevel_deteced = sig;
|
||||
|
||||
if (speedmode == 10 && rtty_autosync == 1)
|
||||
{
|
||||
// find an RTTY signal
|
||||
// from 200 to 2800 Hz look for the beste double peak
|
||||
float dp = 0;
|
||||
int dpidx = 0;
|
||||
|
||||
for (int e = 20; e < 280; e++)
|
||||
{
|
||||
float d = doublePeak(f_fftout, e);
|
||||
if (d > dp)
|
||||
{
|
||||
dp = d;
|
||||
dpidx = e;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("Signal at: %d Hz\n", (int)(dpidx * 10));
|
||||
|
||||
// accept if we get 3 equal values after each other
|
||||
const static int simi = 3;
|
||||
static int simiarr[simi];
|
||||
static int simiidx = 0;
|
||||
simiarr[simiidx] = (int)(dpidx * 10);
|
||||
if (++simiidx >= simi) simiidx = 0;
|
||||
|
||||
int cp0 = simiarr[0];
|
||||
for (int i = 1; i < simi; i++)
|
||||
if (simiarr[i] < (cp0-10) || simiarr[i] > (cp0 + 10)) cp0 = 0;
|
||||
|
||||
if (cp0 > 0)
|
||||
{
|
||||
// mid value of last "arl" frequencies
|
||||
const static int arl = 10;
|
||||
static int fra[arl];
|
||||
static int fraidx = 0;
|
||||
fra[fraidx] = cp0;
|
||||
if (++fraidx >= arl) fraidx = 0;
|
||||
int fm = 0;
|
||||
for (int i = 0; i < arl; i++)
|
||||
fm += fra[i];
|
||||
fm /= arl;
|
||||
|
||||
static int lastfm = 0;
|
||||
if (fm == lastfm)
|
||||
{
|
||||
rtty_modifyRXfreq(fm);
|
||||
}
|
||||
lastfm = fm;
|
||||
}
|
||||
}
|
||||
|
||||
// check if changed since last check
|
||||
if (sig != lastsig)
|
||||
{
|
||||
|
||||
+166
-5
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void rtty_init_pipes();
|
||||
|
||||
#ifdef _WIN32_
|
||||
CRITICAL_SECTION io_cap_crit_sec;
|
||||
CRITICAL_SECTION io_pb_crit_sec;
|
||||
@@ -77,6 +79,7 @@ void io_init_pipes()
|
||||
#endif
|
||||
|
||||
io_voice_init_pipes();
|
||||
rtty_init_pipes();
|
||||
}
|
||||
|
||||
// write one sample into the fifo
|
||||
@@ -190,19 +193,25 @@ int io_pb_fifo_freespace(int nolock)
|
||||
|
||||
int io_pb_fifo_usedspace()
|
||||
{
|
||||
IO_PB_LOCK;
|
||||
int elemInFifo = (io_pb_wridx + AUDIO_PLAYBACK_BUFLEN - io_pb_rdidx) % AUDIO_PLAYBACK_BUFLEN;
|
||||
IO_PB_UNLOCK();
|
||||
|
||||
return elemInFifo;
|
||||
/*
|
||||
int anz = io_pb_fifo_freespace(0);
|
||||
return AUDIO_PLAYBACK_BUFLEN - anz;
|
||||
return AUDIO_PLAYBACK_BUFLEN - anz;*/
|
||||
}
|
||||
|
||||
// read num elements
|
||||
// if num elems not avail, return 0
|
||||
// if num elems not avail, return all what fifo has stored
|
||||
int io_pb_read_fifo_num(float* data, int num)
|
||||
{
|
||||
IO_PB_LOCK;
|
||||
|
||||
int elemInFifo = (io_pb_wridx + AUDIO_PLAYBACK_BUFLEN - io_pb_rdidx) % AUDIO_PLAYBACK_BUFLEN;
|
||||
|
||||
if (elemInFifo < num)
|
||||
if (elemInFifo == 0)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
//printf("only %d elements available\n", elemInFifo);
|
||||
@@ -210,14 +219,17 @@ int io_pb_read_fifo_num(float* data, int num)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num > elemInFifo)
|
||||
num = elemInFifo;
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
*data++ = io_pb_buffer[io_pb_rdidx];
|
||||
if (++io_pb_rdidx >= AUDIO_PLAYBACK_BUFLEN) io_pb_rdidx = 0;
|
||||
}
|
||||
IO_PB_UNLOCK();
|
||||
|
||||
return 1;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
void io_clear_audio_fifos()
|
||||
@@ -225,3 +237,152 @@ void io_clear_audio_fifos()
|
||||
io_pb_write_fifo_clear();
|
||||
io_cap_write_fifo_clear();
|
||||
}
|
||||
|
||||
// ================== RTTY FIFO ===================
|
||||
|
||||
void clear_rtty_fifos();
|
||||
|
||||
#ifdef _WIN32_
|
||||
CRITICAL_SECTION rtty_tx_crit_sec;
|
||||
CRITICAL_SECTION rtty_rx_crit_sec;
|
||||
#define RTTY_TX_LOCK EnterCriticalSection(&rtty_tx_crit_sec)
|
||||
#define RTTY_RX_LOCK EnterCriticalSection(&rtty_rx_crit_sec)
|
||||
void RTTY_TX_UNLOCK()
|
||||
{
|
||||
if (&rtty_tx_crit_sec != NULL)
|
||||
LeaveCriticalSection(&rtty_tx_crit_sec);
|
||||
}
|
||||
void RTTY_RX_UNLOCK()
|
||||
{
|
||||
if (&rtty_rx_crit_sec != NULL)
|
||||
LeaveCriticalSection(&rtty_rx_crit_sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _LINUX_
|
||||
pthread_mutex_t rtty_tx_crit_sec;
|
||||
pthread_mutex_t rtty_rx_crit_sec;
|
||||
#define RTTY_TX_LOCK pthread_mutex_lock(&rtty_tx_crit_sec)
|
||||
void RTTY_TX_UNLOCK() { pthread_mutex_unlock(&rtty_tx_crit_sec); }
|
||||
#define RTTY_RX_LOCK pthread_mutex_lock(&rtty_rx_crit_sec)
|
||||
void RTTY_RX_UNLOCK() { pthread_mutex_unlock(&rtty_rx_crit_sec); }
|
||||
#endif
|
||||
|
||||
void rtty_init_pipes()
|
||||
{
|
||||
#ifdef _WIN32_
|
||||
if (&rtty_tx_crit_sec != NULL) DeleteCriticalSection(&rtty_tx_crit_sec);
|
||||
InitializeCriticalSection(&rtty_tx_crit_sec);
|
||||
|
||||
if (&rtty_rx_crit_sec != NULL) DeleteCriticalSection(&rtty_rx_crit_sec);
|
||||
InitializeCriticalSection(&rtty_rx_crit_sec);
|
||||
|
||||
#endif
|
||||
|
||||
clear_rtty_fifos();
|
||||
}
|
||||
|
||||
#define RTTY_FIFOLEN 200
|
||||
|
||||
int rtty_tx_wridx = 0;
|
||||
int rtty_tx_rdidx = 0;
|
||||
char rtty_tx_buffer[RTTY_FIFOLEN];
|
||||
|
||||
int rtty_rx_wridx = 0;
|
||||
int rtty_rx_rdidx = 0;
|
||||
char rtty_rx_buffer[RTTY_FIFOLEN];
|
||||
|
||||
// TX char from GUI to RTTY TX thread
|
||||
|
||||
void clear_rtty_fifos()
|
||||
{
|
||||
rtty_tx_wridx = rtty_tx_rdidx = 0;
|
||||
rtty_rx_wridx = rtty_rx_rdidx = 0;
|
||||
}
|
||||
|
||||
int rtty_tx_fifo_freespace()
|
||||
{
|
||||
int elemInFifo = (rtty_tx_wridx + RTTY_FIFOLEN - rtty_tx_rdidx) % RTTY_FIFOLEN;
|
||||
return RTTY_FIFOLEN - elemInFifo;
|
||||
}
|
||||
|
||||
void clear_rtty_txfifo()
|
||||
{
|
||||
RTTY_TX_LOCK;
|
||||
rtty_tx_wridx = rtty_tx_rdidx = 0;
|
||||
RTTY_TX_UNLOCK();
|
||||
}
|
||||
|
||||
void rtty_tx_write_fifo(char c)
|
||||
{
|
||||
RTTY_TX_LOCK;
|
||||
|
||||
// check if there is free space in fifo
|
||||
if (rtty_tx_fifo_freespace() == 0)
|
||||
{
|
||||
RTTY_TX_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
rtty_tx_buffer[rtty_tx_wridx] = c;
|
||||
if (++rtty_tx_wridx >= RTTY_FIFOLEN) rtty_tx_wridx = 0;
|
||||
RTTY_TX_UNLOCK();
|
||||
}
|
||||
|
||||
int rtty_tx_read_fifo(char *pc)
|
||||
{
|
||||
RTTY_TX_LOCK;
|
||||
|
||||
if (rtty_tx_rdidx == rtty_tx_wridx)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
RTTY_TX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pc = rtty_tx_buffer[rtty_tx_rdidx];
|
||||
if (++rtty_tx_rdidx >= RTTY_FIFOLEN) rtty_tx_rdidx = 0;
|
||||
RTTY_TX_UNLOCK();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rtty_rx_fifo_freespace()
|
||||
{
|
||||
int elemInFifo = (rtty_rx_wridx + RTTY_FIFOLEN - rtty_rx_rdidx) % RTTY_FIFOLEN;
|
||||
return RTTY_FIFOLEN - elemInFifo;
|
||||
}
|
||||
|
||||
void rtty_rx_write_fifo(char c)
|
||||
{
|
||||
RTTY_RX_LOCK;
|
||||
|
||||
// check if there is free space in fifo
|
||||
if (rtty_rx_fifo_freespace() == 0)
|
||||
{
|
||||
RTTY_RX_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
rtty_rx_buffer[rtty_rx_wridx] = c;
|
||||
if (++rtty_rx_wridx >= RTTY_FIFOLEN) rtty_rx_wridx = 0;
|
||||
RTTY_RX_UNLOCK();
|
||||
}
|
||||
|
||||
int rtty_rx_read_fifo(char* pc)
|
||||
{
|
||||
RTTY_RX_LOCK;
|
||||
|
||||
if (rtty_rx_rdidx == rtty_rx_wridx)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
RTTY_RX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pc = rtty_rx_buffer[rtty_rx_rdidx];
|
||||
if (++rtty_rx_rdidx >= RTTY_FIFOLEN) rtty_rx_rdidx = 0;
|
||||
RTTY_RX_UNLOCK();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void initfm();
|
||||
void runfm();
|
||||
|
||||
freqmod fmmod;
|
||||
freqdem fdem = NULL;
|
||||
nco_crcf fm_dnnco = NULL;
|
||||
nco_crcf fm_upnco = NULL;
|
||||
|
||||
firfilt_crcf fm_q = NULL;
|
||||
|
||||
void fmtest()
|
||||
{
|
||||
static int f = 1;
|
||||
if (f)
|
||||
{
|
||||
f = 0;
|
||||
initfm();
|
||||
}
|
||||
|
||||
runfm();
|
||||
}
|
||||
|
||||
#define CENTERF 1700.0f
|
||||
|
||||
/*
|
||||
* fmin = 1200 Hz
|
||||
* fmax = 2300 Hz
|
||||
* fcarrier = 1700 Hz
|
||||
*
|
||||
* kf = max.deviation / samplerate
|
||||
* max deviation = 2300 - 1700 = 600 Hz
|
||||
* kf = 600/48000 = 0.0125
|
||||
*
|
||||
* this results in:
|
||||
* -0.99 ... 1100 Hz
|
||||
* +0.99 ... 2300 Hz
|
||||
*/
|
||||
const int sstv_fsync = 1200;
|
||||
const int sstv_fblack = 1500;
|
||||
const int sstv_fwhite = 2300;
|
||||
|
||||
const int sstv_maxbw = (sstv_fwhite - sstv_fsync);
|
||||
const float sstv_kf = ((float)sstv_maxbw / 2.0f) / (float)caprate;
|
||||
const float sstv_carrier = (sstv_maxbw / 2) + sstv_fsync;
|
||||
|
||||
// frequency values per millisecond
|
||||
#define COLLECT 5
|
||||
const float per_ms = (float)COLLECT * 1000.0f / 48000.0f;
|
||||
int realsync = 0;
|
||||
const int maxpixel = 5000;
|
||||
const int maxlines = 300;
|
||||
int pidx = 0;
|
||||
int lidx = 0;
|
||||
uint8_t fmap[maxlines][maxpixel*2];
|
||||
|
||||
void initfm()
|
||||
{
|
||||
fmmod = freqmod_create(sstv_kf); // modulator
|
||||
fdem = freqdem_create(sstv_kf);
|
||||
|
||||
// create NCO for up-mixing to 1500 Hz
|
||||
float fm_RX_RADIANS_PER_SAMPLE = 2.0f * (float)M_PI * sstv_carrier / (float)caprate;
|
||||
fm_upnco = nco_crcf_create(LIQUID_NCO);
|
||||
nco_crcf_set_phase(fm_upnco, 0.0f);
|
||||
nco_crcf_set_frequency(fm_upnco, fm_RX_RADIANS_PER_SAMPLE);
|
||||
|
||||
// create NCO for down-mixing from 1500 Hz
|
||||
fm_dnnco = nco_crcf_create(LIQUID_NCO);
|
||||
nco_crcf_set_phase(fm_dnnco, 0.0f);
|
||||
nco_crcf_set_frequency(fm_dnnco, fm_RX_RADIANS_PER_SAMPLE);
|
||||
|
||||
// RX Filter
|
||||
unsigned int flt_h_len = 31; // filter length
|
||||
// we filter at 48k samp rate
|
||||
float flt_fc = (float)sstv_fwhite / 2.0f / 48000.0f; // cutoff frequency
|
||||
float flt_As = 40.0f; // stop-band attenuation
|
||||
|
||||
fm_q = firfilt_crcf_create_kaiser(flt_h_len, flt_fc, flt_As, 0.0f);
|
||||
firfilt_crcf_set_scale(fm_q, 2.0f * flt_fc);
|
||||
}
|
||||
|
||||
void runfm()
|
||||
{
|
||||
int synpulse = 0;
|
||||
int syncanz = 0;
|
||||
float f;
|
||||
liquid_float_complex s; // modulated signal
|
||||
liquid_float_complex sbase;
|
||||
liquid_float_complex sbasef;
|
||||
float syntim = 0;
|
||||
float sigtim = 0;
|
||||
|
||||
while (keeprunning)
|
||||
{
|
||||
int ret = io_cap_read_fifo(&f);
|
||||
if (ret == 0)
|
||||
{
|
||||
sleep_ms(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
nco_crcf_step(fm_dnnco);
|
||||
s.real = f;
|
||||
s.imag = f;
|
||||
nco_crcf_mix_down(fm_dnnco, s, &sbase);
|
||||
|
||||
// sharp filter
|
||||
firfilt_crcf_push(fm_q, sbase); // push input sample
|
||||
firfilt_crcf_execute(fm_q, &sbasef); // compute output
|
||||
|
||||
float y; // output/demodulated message
|
||||
freqdem_demodulate(fdem, sbasef, &y);
|
||||
|
||||
// y: -1..+1 (fsync..fwhite)
|
||||
y += 1; // y: 0..2 (fsync..fwhite)
|
||||
y *= (sstv_maxbw/2); // y: 0..maxbw (1100)
|
||||
y += sstv_fsync; // y: fsync..fwhite
|
||||
int freq = (int)y;
|
||||
|
||||
printf("%d ", freq);
|
||||
if (freq < 2000) printf("\n");
|
||||
|
||||
static int farr[COLLECT];
|
||||
static int fidx = 0;
|
||||
|
||||
farr[fidx] = freq;
|
||||
if (++fidx < COLLECT) continue;
|
||||
fidx = 0;
|
||||
|
||||
for (int i = 0; i < COLLECT; i++)
|
||||
freq += farr[i];
|
||||
freq /= COLLECT;
|
||||
|
||||
if (freq < sstv_fsync) freq = sstv_fsync;
|
||||
if (freq > sstv_fwhite) freq = sstv_fwhite;
|
||||
|
||||
//printf("%d ", freq);
|
||||
//if (freq < 2000) printf("\n");
|
||||
|
||||
syntim += per_ms;
|
||||
sigtim += per_ms;
|
||||
|
||||
// detect start of sync pulse
|
||||
if (synpulse == 0 && freq < 1480 && sigtim > 430.0f)
|
||||
{
|
||||
// syn pulse starts
|
||||
if (sigtim < 431.0f)
|
||||
{
|
||||
printf("syn pulse missed, forced after %10.6f\n", sigtim);
|
||||
realsync = 0;
|
||||
syncanz = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("syn pulse starts after %10.6f\n", sigtim);
|
||||
realsync = 1;
|
||||
if(syncanz < 4) syncanz++;
|
||||
if (syncanz == 3)
|
||||
{
|
||||
printf("picture start\n");
|
||||
lidx = 0;
|
||||
}
|
||||
}
|
||||
synpulse = 1;
|
||||
syntim = 0;
|
||||
if (lidx < maxlines) lidx++;
|
||||
}
|
||||
|
||||
// detect end of syn pulse
|
||||
if (synpulse == 1 && freq > 1480)
|
||||
{
|
||||
// syn pulse ends
|
||||
// check if valid length
|
||||
if (syntim > 4.0f)
|
||||
{
|
||||
printf("syn pulse ends after %10.6f. In Sync:%d, written:%d to line:%d\n",syntim,realsync,pidx,lidx);
|
||||
synpulse = 0;
|
||||
sigtim = 0;
|
||||
pidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (synpulse == 0)
|
||||
{
|
||||
fmap[lidx][pidx * 2] = freq >> 8;
|
||||
fmap[lidx][pidx * 2 + 1] = freq & 0xff;
|
||||
if (pidx < maxpixel) pidx++;
|
||||
|
||||
if (lidx == 260)
|
||||
{
|
||||
FILE* fp = fopen("sstv.img", "wb");
|
||||
if(fp)
|
||||
{
|
||||
for (int i = 0; i < 260; i++)
|
||||
{
|
||||
fwrite(fmap[i], 1, maxpixel*2, fp);
|
||||
}
|
||||
fclose(fp);
|
||||
printf("file saved\n");
|
||||
sleep_ms(10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//printf("%10.6f %10.6f %d\n", f, y, freq);
|
||||
|
||||
/*
|
||||
// monitor
|
||||
nco_crcf_step(fm_upnco);
|
||||
nco_crcf_mix_up(fm_upnco, sbasef, &s);
|
||||
float usbf = s.real + s.imag;
|
||||
|
||||
io_pb_write_fifo(usbf * 0.2f); // reduce volume and send to soundcard
|
||||
*/
|
||||
}
|
||||
}
|
||||
+119
-36
@@ -56,6 +56,7 @@ int UdpDataPort_ModemToApp = 40133;
|
||||
// op mode depending values
|
||||
// default mode if not set by the app
|
||||
int speedmode = 4;
|
||||
int set_speedmode = 4;
|
||||
int bitsPerSymbol = 2; // QPSK=2, 8PSK=3
|
||||
int constellationSize = 4; // QPSK=4, 8PSK=8
|
||||
|
||||
@@ -208,10 +209,11 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
if (restart_modems == 1)
|
||||
{
|
||||
printf("restart modem requested\n");
|
||||
startModem();
|
||||
restart_modems = 0;
|
||||
}
|
||||
|
||||
|
||||
//doArraySend();
|
||||
if (VoiceAudioMode == VOICEMODE_INTERNALLOOP)
|
||||
{
|
||||
@@ -255,24 +257,34 @@ int main(int argc, char* argv[])
|
||||
do_tuning(tuning);
|
||||
}
|
||||
|
||||
// demodulate incoming audio data stream
|
||||
static uint64_t old_tm = 0;
|
||||
uint64_t tm = getms();
|
||||
if (tm >= (old_tm + 1000))
|
||||
if (speedmode == 10)
|
||||
{
|
||||
// read Audio device list every 1s
|
||||
io_readAudioDevices();
|
||||
old_tm = tm;
|
||||
//testall();
|
||||
//fmtest();
|
||||
sleep_ms(10); // nothing to do here
|
||||
}
|
||||
int dret = demodulator();
|
||||
if (dret == 0)
|
||||
else
|
||||
{
|
||||
// no new data in fifo
|
||||
// demodulate incoming audio data stream
|
||||
static uint64_t old_tm = 0;
|
||||
uint64_t tm = getms();
|
||||
if (tm >= (old_tm + 1000))
|
||||
{
|
||||
// read Audio device list every 1s
|
||||
io_readAudioDevices();
|
||||
old_tm = tm;
|
||||
}
|
||||
int dret = demodulator();
|
||||
if (dret == 0)
|
||||
{
|
||||
// no new data in fifo
|
||||
#ifdef _LINUX_
|
||||
// not important how long to sleep, 10ms is fine
|
||||
sleep_ms(10);
|
||||
sleep_ms(10);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
printf("stopped: %d\n", keeprunning);
|
||||
|
||||
@@ -283,7 +295,6 @@ int main(int argc, char* argv[])
|
||||
closesocket(BC_sock_AppToModem);
|
||||
#endif
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -298,7 +309,7 @@ typedef struct {
|
||||
} SPEEDRATE;
|
||||
|
||||
// AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, Codec-Rate
|
||||
SPEEDRATE sr[10] = {
|
||||
SPEEDRATE sr[11] = {
|
||||
// BPSK modes
|
||||
{48000, 40,10, 1, 1200, 800},
|
||||
{48000, 20, 5, 1, 2400, 2000},
|
||||
@@ -314,10 +325,19 @@ SPEEDRATE sr[10] = {
|
||||
{48000, 24, 6, 3, 6000, 4800},
|
||||
{44100, 20, 5, 3, 6600, 5200},
|
||||
{48000, 20, 5, 3, 7200, 6000},
|
||||
|
||||
// RTTY
|
||||
{48000, 0, 0, 0, 0, 0},
|
||||
};
|
||||
|
||||
void startModem()
|
||||
{
|
||||
printf("startModem\n");
|
||||
close_dsp();
|
||||
close_rtty();
|
||||
io_close_audio();
|
||||
speedmode = set_speedmode;
|
||||
|
||||
bitsPerSymbol = sr[speedmode].bpsym;
|
||||
constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8
|
||||
|
||||
@@ -328,10 +348,27 @@ void startModem()
|
||||
opusbitrate = sr[speedmode].codecrate;
|
||||
|
||||
// int TX audio and modulator
|
||||
close_dsp();
|
||||
init_audio_result = io_init_sound(playbackDeviceName, captureDeviceName);
|
||||
_init_fft();
|
||||
init_dsp();
|
||||
if (speedmode < 10)
|
||||
{
|
||||
init_dsp();
|
||||
}
|
||||
if (speedmode == 10)
|
||||
{
|
||||
rtty_txoff = 1;
|
||||
init_rtty();
|
||||
}
|
||||
}
|
||||
|
||||
// called from UDP callback ! DO NOT call any system functions
|
||||
void setSpeedmode(int spm)
|
||||
{
|
||||
printf("set speedmode:%d\n", spm);
|
||||
|
||||
set_speedmode = spm;
|
||||
restart_modems = 1;
|
||||
transmissions = 1000; // announcement at next TX
|
||||
}
|
||||
|
||||
void io_setAudioDevices(uint8_t pbvol, uint8_t capvol, uint8_t announce, uint8_t pbls, uint8_t pbmic, char *pbname, char*capname)
|
||||
@@ -374,7 +411,8 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
* 5 ... DV mic volume
|
||||
* 6 ... safe mode number
|
||||
* 7 ... send Intro
|
||||
* 8..9 ... unused
|
||||
* 8 ... rtty autosync
|
||||
* 9 ... unused
|
||||
* 10 .. 109 ... PB device name
|
||||
* 110 .. 209 ... CAP device name
|
||||
*/
|
||||
@@ -429,6 +467,7 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
io_setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], (char*)(pdata + 10), (char*)(pdata + 110));
|
||||
safemode = pdata[6];
|
||||
sendIntro = pdata[7];
|
||||
rtty_autosync = pdata[8];
|
||||
|
||||
lastms = actms;
|
||||
}
|
||||
@@ -440,19 +479,13 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
uint8_t type = pdata[0];
|
||||
uint8_t minfo = pdata[1];
|
||||
|
||||
//printf("from GUI: %d %d\n", pdata[0], pdata[1]);
|
||||
|
||||
// type values: see oscardata config.cs: frame types
|
||||
if (type == 16)
|
||||
{
|
||||
// Byte 1 contains the resampler ratio for TX and RX modem
|
||||
if (pdata[1] >= 12)
|
||||
{
|
||||
printf("wrong speedmode %d, ignoring\n", pdata[1]);
|
||||
return;
|
||||
}
|
||||
speedmode = pdata[1];
|
||||
printf("set speedmode to %d\n", speedmode);
|
||||
restart_modems = 1;
|
||||
transmissions = 1000; // announcement at next TX
|
||||
// Byte 1 contains the speed mode index
|
||||
setSpeedmode(pdata[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -544,7 +577,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
VoiceAudioMode = pdata[1];
|
||||
codec = pdata[2];
|
||||
|
||||
printf("LS:<%s> MIC:<%s> Mode:%d codec:%d\n", lsDeviceName, micDeviceName, VoiceAudioMode, codec);
|
||||
//printf("LS:<%s> MIC:<%s> Mode:%d codec:%d\n", lsDeviceName, micDeviceName, VoiceAudioMode, codec);
|
||||
|
||||
// init voice audio
|
||||
if (VoiceAudioMode == VOICEMODE_OFF)
|
||||
@@ -585,14 +618,61 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 29)
|
||||
if (speedmode == 10)
|
||||
{
|
||||
int v = minfo;
|
||||
if (v > 128)
|
||||
v = v - 255;
|
||||
modifyRXfreq(float(v));
|
||||
return;
|
||||
// rtty commands
|
||||
if (type == 29)
|
||||
{
|
||||
int16_t freq = pdata[1];
|
||||
freq <<= 8;
|
||||
freq += pdata[2];
|
||||
printf("set freq:%d\n", freq);
|
||||
|
||||
rtty_modifyRXfreq(freq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 30)
|
||||
{
|
||||
// rtty key pressed
|
||||
rtty_tx_write_fifo(minfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 31)
|
||||
{
|
||||
// rtty string
|
||||
int len = pdata[1];
|
||||
len <<= 8;
|
||||
len += pdata[2];
|
||||
len++; // the first toTX command
|
||||
//printf("hsmodem.cpp rtty_tx_write_fifo: ");
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
//printf("%c", pdata[3 + i]);
|
||||
rtty_tx_write_fifo(pdata[3 + i]);
|
||||
}
|
||||
//printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 32)
|
||||
{
|
||||
// TX on/off, but send buffer
|
||||
rtty_txoff = minfo?0:1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 33)
|
||||
{
|
||||
// stop TX immediately
|
||||
rtty_txoff = 1;
|
||||
clear_rtty_txfifo();
|
||||
}
|
||||
}
|
||||
if (type >= 29 && type <= 32) return;
|
||||
|
||||
if (speedmode == 10) return;
|
||||
|
||||
// here we are with payload data to be sent via the modulator
|
||||
|
||||
@@ -741,8 +821,11 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
// reset modem if more than 2 frames have not been received
|
||||
trigger_resetmodem = 0;
|
||||
rx_in_sync = 0;
|
||||
printf("no signal detected, reset RX modem\n");
|
||||
resetModem();
|
||||
if (speedmode < 10)
|
||||
{
|
||||
printf("no signal detected, reset RX modem\n");
|
||||
resetModem();
|
||||
}
|
||||
lasttime = acttm;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-11
@@ -64,6 +64,7 @@
|
||||
#include "symboltracker.h"
|
||||
#include "codec2.h"
|
||||
#include "soundio.h"
|
||||
#include "baudot.h"
|
||||
|
||||
#define jpg_tempfilename "rxdata.jpg"
|
||||
|
||||
@@ -191,14 +192,15 @@ void write_sample_s16ne(char* ptr, double sample);
|
||||
int io_ls_fifo_usedspace();
|
||||
void write_sample_float32ne(char* ptr, double sample);
|
||||
|
||||
void km_symtrack_cccf_create(int _ftype,
|
||||
unsigned int _k,
|
||||
unsigned int _m,
|
||||
float _beta,
|
||||
int _ms);
|
||||
void km_symtrack_cccf_reset(int mode);
|
||||
void km_symtrack_cccf_set_bandwidth(float _bw);
|
||||
void km_symtrack_execute(liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int* psym_out);
|
||||
SYMTRACK* km_symtrack_cccf_create( int _ftype,
|
||||
unsigned int _k,
|
||||
unsigned int _m,
|
||||
float _beta,
|
||||
int _ms);
|
||||
void km_symtrack_cccf_reset(SYMTRACK*, int mode);
|
||||
void km_symtrack_cccf_set_bandwidth(SYMTRACK* , float _bw);
|
||||
void km_symtrack_execute(SYMTRACK* ,liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int* psym_out);
|
||||
void km_symtrack_cccf_destroy(SYMTRACK*);
|
||||
|
||||
void io_saveStream(float f);
|
||||
void playIntro();
|
||||
@@ -206,10 +208,24 @@ void io_clear_voice_fifos();
|
||||
float do_tuning(int send);
|
||||
void init_tune();
|
||||
float singleFrequency();
|
||||
void rtty_tx();
|
||||
int rtty_rx();
|
||||
void modifyRXfreq(float fr);
|
||||
void modifyRXfreq(float diff_Hz, int absolute);
|
||||
void showbytestring16(char* title, uint16_t* data, int anz);
|
||||
void rtty_sendChar(int c);
|
||||
void init_rtty();
|
||||
int do_rtty();
|
||||
void make_FFTdata(float f);
|
||||
void getMax(float fv);
|
||||
void close_rtty();
|
||||
void close_a();
|
||||
void rtty_modifyRXfreq(int);
|
||||
void showbitstring(char* title, uint8_t* data, int totallen, int anz);
|
||||
void rtty_tx_write_fifo(char c);
|
||||
int rtty_tx_read_fifo(char* pc);
|
||||
void rtty_rx_write_fifo(char c);
|
||||
int rtty_rx_read_fifo(char* pc);
|
||||
void clear_rtty_txfifo();
|
||||
void fmtest();
|
||||
|
||||
|
||||
extern int speedmode;
|
||||
@@ -252,9 +268,13 @@ extern int sendIntro;
|
||||
extern int tuning;
|
||||
extern uint32_t tuning_runtime;
|
||||
extern int marker;
|
||||
extern int rtty_txoff;
|
||||
extern int rtty_txidx;
|
||||
extern int rtty_frequency;
|
||||
extern int rtty_autosync;
|
||||
|
||||
|
||||
#ifdef _LINUX_
|
||||
int isRunning(char* prgname);
|
||||
void install_signal_handler();
|
||||
int isRunning(char* prgname);
|
||||
#endif
|
||||
|
||||
@@ -223,6 +223,7 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="baudot.h" />
|
||||
<ClInclude Include="codec2.h" />
|
||||
<ClInclude Include="endian.h" />
|
||||
<ClInclude Include="fec.h" />
|
||||
|
||||
@@ -122,5 +122,8 @@
|
||||
<ClInclude Include="endian.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="baudot.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+23
-46
@@ -200,7 +200,7 @@ void modulator(uint8_t sym_in)
|
||||
|
||||
// adapt speed to soundcard samplerate
|
||||
int fs;
|
||||
while(1)
|
||||
while(keeprunning)
|
||||
{
|
||||
fs = io_pb_fifo_freespace(0);
|
||||
// wait until there is space in fifo
|
||||
@@ -218,7 +218,6 @@ void modulator(uint8_t sym_in)
|
||||
// =========== DEMODULATOR =============================
|
||||
|
||||
nco_crcf dnnco = NULL;
|
||||
symtrack_cccf symtrack = NULL;
|
||||
firdecim_crcf decim = NULL;
|
||||
msresamp_crcf adecim = NULL;
|
||||
msresamp_crcf lsresamp = NULL;
|
||||
@@ -243,14 +242,8 @@ uint8_t maxTXLevel = 0; // maximum TXlevel over the last x samples in %
|
||||
|
||||
float radians_per_sample = ((2.0f * (float)M_PI * (float)FREQUENCY) / (float)caprate);
|
||||
float last_radians_per_sample = 0;
|
||||
float actfrequency = (float)FREQUENCY;
|
||||
|
||||
void modifyRXfreq(float diff_Hz)
|
||||
{
|
||||
actfrequency += diff_Hz;
|
||||
printf("set:%f Hz\n", actfrequency);
|
||||
radians_per_sample = ((2.0f * (float)M_PI * actfrequency) / (float)caprate);
|
||||
}
|
||||
SYMTRACK *km_symtrack = NULL;
|
||||
|
||||
void init_demodulator()
|
||||
{
|
||||
@@ -275,8 +268,8 @@ void init_demodulator()
|
||||
lsresamp = msresamp_crcf_create((float)(48000.0/44100.0), As_adecim);
|
||||
|
||||
// create symbol tracking synchronizer
|
||||
km_symtrack_cccf_create(ftype_st, k_st, m_st, beta_st, getMod());
|
||||
km_symtrack_cccf_set_bandwidth(bandwidth_st);
|
||||
km_symtrack = km_symtrack_cccf_create(ftype_st, k_st, m_st, beta_st, getMod());
|
||||
km_symtrack_cccf_set_bandwidth(km_symtrack, bandwidth_st);
|
||||
}
|
||||
|
||||
void close_demodulator()
|
||||
@@ -287,16 +280,17 @@ void close_demodulator()
|
||||
adecim = NULL;
|
||||
if (lsresamp) msresamp_crcf_destroy(lsresamp);
|
||||
lsresamp = NULL;
|
||||
if (symtrack != NULL) symtrack_cccf_destroy(symtrack);
|
||||
symtrack = NULL;
|
||||
if (dnnco != NULL) nco_crcf_destroy(dnnco);
|
||||
dnnco = NULL;
|
||||
if (km_symtrack != NULL) km_symtrack_cccf_destroy(km_symtrack);
|
||||
km_symtrack = NULL;
|
||||
}
|
||||
|
||||
void resetModem()
|
||||
{
|
||||
//printf("Reset Symtrack\n");
|
||||
km_symtrack_cccf_reset(0xff);
|
||||
if (km_symtrack == NULL) return;
|
||||
km_symtrack_cccf_reset(km_symtrack,0xff);
|
||||
}
|
||||
|
||||
// called for Audio-Samples (FFT)
|
||||
@@ -307,33 +301,6 @@ void make_FFTdata(float f)
|
||||
uint16_t* fft = make_waterfall(f, &fftlen);
|
||||
if (fft != NULL)
|
||||
{
|
||||
// fft data are in fft[] size: 0..fftlen
|
||||
// 10 Hz per value
|
||||
float fdiff = (float)FREQUENCY - actfrequency;
|
||||
// shift spectrum if we are off 1500 Hz
|
||||
int diff10Hz = (int)(fdiff / 10.0);
|
||||
if (diff10Hz != 0)
|
||||
{
|
||||
//printf("%d %f %f %d\n", FREQUENCY, actfrequency, fdiff, diff10Hz);
|
||||
if (diff10Hz < 0)
|
||||
{
|
||||
diff10Hz = -diff10Hz;
|
||||
for (int i = 0; i < (fftlen-diff10Hz); i++)
|
||||
fft[i] = fft[i + diff10Hz];
|
||||
|
||||
for (int i = (fftlen - diff10Hz); i < fftlen; i++)
|
||||
fft[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = fftlen-1; i >= diff10Hz; i--)
|
||||
fft[i] = fft[i - diff10Hz];
|
||||
|
||||
for (int i = 0; i < diff10Hz; i++)
|
||||
fft[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t txpl[10000];
|
||||
if (fftlen > (10000 * 2 + 1))
|
||||
{
|
||||
@@ -344,10 +311,17 @@ void make_FFTdata(float f)
|
||||
int bidx = 0;
|
||||
txpl[bidx++] = 4; // type 4: FFT data follows
|
||||
|
||||
int us = io_pb_fifo_usedBlocks();
|
||||
int us = 0;
|
||||
if(speedmode < 10)
|
||||
us = io_pb_fifo_usedBlocks();
|
||||
if (speedmode == 10)
|
||||
{
|
||||
// RTTY
|
||||
us = io_pb_fifo_usedspace();
|
||||
}
|
||||
|
||||
if (us > 255 || ann_running == 1) us = 255;
|
||||
txpl[bidx++] = us; // usage of TX fifo
|
||||
|
||||
us = io_cap_fifo_usedPercent();
|
||||
if (us > 255) us = 255;
|
||||
txpl[bidx++] = us; // usage of TX fifo
|
||||
@@ -355,8 +329,11 @@ void make_FFTdata(float f)
|
||||
txpl[bidx++] = rxlevel_deteced; // RX level present
|
||||
txpl[bidx++] = rx_in_sync;
|
||||
|
||||
txpl[bidx++] = maxLevel; // actual max level on sound capture in %
|
||||
txpl[bidx++] = maxTXLevel; // actual max level on sound playback in %
|
||||
txpl[bidx++] = maxLevel; // actual max level on sound capture in %
|
||||
txpl[bidx++] = maxTXLevel; // actual max level on sound playback in %
|
||||
|
||||
txpl[bidx++] = rtty_frequency >> 8; // rtty qrg by autosync
|
||||
txpl[bidx++] = rtty_frequency & 0xff;
|
||||
|
||||
for (int i = 0; i < fftlen; i++)
|
||||
{
|
||||
@@ -491,7 +468,7 @@ static int const_idx = 0;
|
||||
unsigned int num_symbols_sync;
|
||||
liquid_float_complex syms;
|
||||
unsigned int nsym_out; // demodulated output symbol
|
||||
km_symtrack_execute(y, &syms, &num_symbols_sync, &nsym_out);
|
||||
km_symtrack_execute(km_symtrack,y, &syms, &num_symbols_sync, &nsym_out);
|
||||
|
||||
if (num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n", num_symbols_sync);
|
||||
if (num_symbols_sync != 0)
|
||||
|
||||
+11
-3
@@ -100,9 +100,17 @@ void closeAllandTerminate()
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void showbitstring(char* title, uint8_t* data, int totallen, int anz)
|
||||
{
|
||||
printf("%s. len %d: ", title, totallen);
|
||||
for (int i = 0; i < anz; i++)
|
||||
printf("%01X ", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void showbytestring(char *title, uint8_t *data, int totallen, int anz)
|
||||
{
|
||||
printf("%s. Len %d: ",title, totallen);
|
||||
printf("%s. len %d: ",title, totallen);
|
||||
for(int i=0; i<anz; i++)
|
||||
printf("%02X ",data[i]);
|
||||
printf("\n");
|
||||
@@ -110,7 +118,7 @@ void showbytestring(char *title, uint8_t *data, int totallen, int anz)
|
||||
|
||||
void showbytestring16(char *title, uint16_t *data, int anz)
|
||||
{
|
||||
printf("%s. Len %d: ",title,anz);
|
||||
printf("%s. len %d: ",title,anz);
|
||||
for(int i=0; i<anz; i++)
|
||||
printf("%04X ",data[i]);
|
||||
printf("\n");
|
||||
@@ -118,7 +126,7 @@ void showbytestring16(char *title, uint16_t *data, int anz)
|
||||
|
||||
void showbytestringf(char* title, float* data, int totallen, int anz)
|
||||
{
|
||||
printf("%s. Len %d: ", title, totallen);
|
||||
printf("%s. len %d: ", title, totallen);
|
||||
for (int i = 0; i < anz; i++)
|
||||
printf("%7.4f ", data[i]);
|
||||
printf("\n");
|
||||
|
||||
+687
-175
@@ -32,231 +32,743 @@
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
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);
|
||||
|
||||
#define rtty_CENTERFREQUENCY 1500
|
||||
|
||||
fskmod rtty_mod = NULL;
|
||||
fskdem rtty_dem = NULL;
|
||||
|
||||
unsigned int rtty_m = 1; // bits/symbol
|
||||
const unsigned int rtty_k = 33; // samples/symbol (periods of the 1500 Hz Carrier in one symbol)
|
||||
float rtty_bandwith = (170.0f/2)/ (float)rtty_CENTERFREQUENCY; // 170 Hz spacing normalized to 1500 Hz
|
||||
|
||||
firinterp_crcf rtty_TX_interpolator = NULL;
|
||||
unsigned int rtty_k_SampPerSymb = 20; // 44100 / (4410/2)
|
||||
unsigned int rtty_m_filterDelay_Symbols = 15; // not too short for good filter
|
||||
float rtty_beta_excessBW = 0.2f; // filter excess bandwidth factor
|
||||
float rtty_tau_FracSymbOffset = -0.2f; // fractional symbol offset
|
||||
float rtty_RADIANS_PER_SAMPLE = 0;
|
||||
float rtty_RX_RADIANS_PER_SAMPLE = 0;
|
||||
|
||||
nco_crcf rtty_upnco = NULL;
|
||||
nco_crcf rtty_dnnco = NULL;
|
||||
|
||||
firdecim_crcf rtty_decim = NULL;
|
||||
unsigned int rtty_m_predec = 8; // filter delay
|
||||
float rtty_As_predec = 40.0f; // stop-band att
|
||||
// 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
|
||||
|
||||
void close_rtty();
|
||||
int rtty_txoff = 1; // 1=off, 0=on , >1...downcount for off
|
||||
int synced = 0;
|
||||
int run_rtty_threads = 0;
|
||||
|
||||
void init_rtty()
|
||||
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)
|
||||
{
|
||||
close_rtty();
|
||||
printf("set:%d Hz\n", f_Hz);
|
||||
rtty_frequency = f_Hz;
|
||||
rtty_RX_RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (float)f_Hz) / (float)caprate);
|
||||
}
|
||||
|
||||
rtty_mod = fskmod_create(rtty_m, rtty_k, rtty_bandwith);
|
||||
rtty_dem = fskdem_create(rtty_m, rtty_k, rtty_bandwith);
|
||||
|
||||
// TX: Interpolator Filter
|
||||
rtty_k_SampPerSymb = caprate / rtty_CENTERFREQUENCY;
|
||||
void sendRttyToGUI(uint8_t b)
|
||||
{
|
||||
uint8_t txpl[7];
|
||||
txpl[0] = 6; // RTTY RX Byte follows
|
||||
txpl[1] = b; // RXed byte
|
||||
txpl[2] = rtty_txoff?0:1; // TX on/off
|
||||
txpl[3] = synced;
|
||||
txpl[4] = 0; // unused
|
||||
txpl[5] = 0;
|
||||
txpl[6] = 0;
|
||||
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, sizeof(txpl));
|
||||
}
|
||||
|
||||
// compute delay
|
||||
while (rtty_tau_FracSymbOffset < 0) rtty_tau_FracSymbOffset += 1.0f; // ensure positive tau
|
||||
float g = rtty_k_SampPerSymb * rtty_tau_FracSymbOffset; // number of samples offset
|
||||
int ds = (int)floorf(g); // additional symbol delay
|
||||
float dt = (g - (float)ds); // fractional sample offset
|
||||
// force dt to be in [0.5,0.5]
|
||||
if (dt > 0.5f)
|
||||
/*
|
||||
* 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,'§','§',}, // 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;
|
||||
|
||||
//printf("ascii:%c letter:%d (%d)\n", c, letters,isletters);
|
||||
|
||||
if (c == '#')
|
||||
{
|
||||
dt -= 1.0f;
|
||||
ds++;
|
||||
}
|
||||
|
||||
// calculate filter coeffs
|
||||
unsigned int h_len_NumFilterCoeefs = 2 * rtty_k_SampPerSymb * rtty_m_filterDelay_Symbols + 1;
|
||||
float h[4000];
|
||||
if (h_len_NumFilterCoeefs >= 4000)
|
||||
{
|
||||
printf("rtty h in h_len_NumFilterCoeefs too small, need %d\n", h_len_NumFilterCoeefs);
|
||||
rtty_txoff = 0;
|
||||
*pnum = 0;
|
||||
return;
|
||||
}
|
||||
liquid_firdes_prototype(LIQUID_FIRFILT_RRC,
|
||||
rtty_k_SampPerSymb,
|
||||
rtty_m_filterDelay_Symbols,
|
||||
rtty_beta_excessBW,
|
||||
dt,
|
||||
h);
|
||||
// create the filter
|
||||
rtty_TX_interpolator = firinterp_crcf_create(rtty_k_SampPerSymb, h, h_len_NumFilterCoeefs);
|
||||
|
||||
// create NCO for upmixing to 1500 Hz
|
||||
float rtty_RADIANS_PER_SAMPLE = ((2.0f * (float)M_PI * (float)rtty_CENTERFREQUENCY) / (float)caprate);
|
||||
if (c == '~')
|
||||
{
|
||||
rtty_txoff = 10;
|
||||
*pnum = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 0x08)
|
||||
{
|
||||
// backspace does not exist in RTTY
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == ' ')
|
||||
{
|
||||
bd[anz++] = 4;
|
||||
*pnum = anz;
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
bd[anz++] = getBaudot('\n', letters);
|
||||
bd[anz++] = getBaudot('\r', letters);
|
||||
*pnum = anz;
|
||||
return;
|
||||
}
|
||||
|
||||
if (c >= 'A' && c <= 'Z') letters = 1;
|
||||
|
||||
if (letters == 1 && isletters == 0)
|
||||
{
|
||||
bd[anz++] = getBaudot('§', 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_RADIANS_PER_SAMPLE);
|
||||
nco_crcf_set_frequency(rtty_dnnco, rtty_RX_RADIANS_PER_SAMPLE);
|
||||
|
||||
rtty_decim = firdecim_crcf_create_kaiser(rtty_k_SampPerSymb, rtty_m_predec, rtty_As_predec);
|
||||
firdecim_crcf_set_scale(rtty_decim, 1.0f / (float)rtty_k_SampPerSymb);
|
||||
// modulate, demodulate, count errors
|
||||
unsigned int num_symbol_errors = 0;
|
||||
}
|
||||
|
||||
void close_rtty()
|
||||
void ttest()
|
||||
{
|
||||
if (rtty_mod != NULL) fskmod_destroy(rtty_mod);
|
||||
rtty_mod = NULL;
|
||||
if (rtty_TX_interpolator != NULL) firinterp_crcf_destroy(rtty_TX_interpolator);
|
||||
rtty_TX_interpolator = NULL;
|
||||
if (rtty_upnco != NULL) nco_crcf_destroy(rtty_upnco);
|
||||
rtty_upnco = NULL;
|
||||
if (rtty_dnnco != NULL) nco_crcf_destroy(rtty_dnnco);
|
||||
rtty_dnnco = NULL;
|
||||
if (rtty_decim != NULL) firdecim_crcf_destroy(rtty_decim);
|
||||
rtty_decim = NULL;
|
||||
if (rtty_dem != NULL) fskdem_destroy(rtty_dem);
|
||||
rtty_dem = NULL;
|
||||
}
|
||||
int i, j;
|
||||
static unsigned int sym_in=0;
|
||||
|
||||
char text[6] = {"ABCD\n"};
|
||||
int tidx = 0;
|
||||
int bitidx = 1;
|
||||
int fs = io_pb_fifo_freespace(0);
|
||||
if (fs < 20000) return;
|
||||
|
||||
void rtty_tx()
|
||||
{
|
||||
if (rtty_mod == NULL)
|
||||
init_rtty();
|
||||
|
||||
unsigned int sym = (text[tidx] & bitidx) ? 1 : 0;
|
||||
bitidx <<= 1;
|
||||
if (bitidx == 0x0100)
|
||||
static int scnt = 0;
|
||||
if (++scnt == 8)
|
||||
{
|
||||
bitidx = 1;
|
||||
tidx++;
|
||||
if (tidx == 6) tidx = 0;
|
||||
scnt = 0;
|
||||
sym_in = 1 - sym_in;
|
||||
}
|
||||
//unsigned int sym_in = rand() % M;
|
||||
|
||||
liquid_float_complex rtty_txbuf[33+1];
|
||||
//measure_speed_bps(1);
|
||||
|
||||
// gets one symbol at a speed of 45.45
|
||||
fskmod_modulate(rtty_mod, sym, rtty_txbuf);
|
||||
// here we have the complex RTTY signal in baseband
|
||||
// one symbol was expanded to rtty_k periods:
|
||||
// 45.454545 * 33 = 1500 periods
|
||||
|
||||
for (unsigned int i = 0; i < rtty_k; i++)
|
||||
//printf("modulate\n");
|
||||
fskmod_modulate(modi, sym_in, &(buf_tx[0]));
|
||||
|
||||
// move sample to 1,5kHz carrier
|
||||
for (j = 0; j < k; j++)
|
||||
{
|
||||
// resample it to the soundcard rate caprate
|
||||
// interpolate by k_SampPerSymb
|
||||
liquid_float_complex y[40];
|
||||
if (rtty_k_SampPerSymb >= 40)
|
||||
{
|
||||
printf("y in k_SampPerSymb too small, need %d\n", rtty_k_SampPerSymb);
|
||||
return;
|
||||
}
|
||||
nco_crcf_step(rtty_upnco);
|
||||
nco_crcf_mix_up(rtty_upnco, buf_tx[j], &(buf_tx1500[j]));
|
||||
|
||||
firinterp_crcf_execute(rtty_TX_interpolator, rtty_txbuf[i], y);
|
||||
// here we have rtty_k_SampPerSymb samples in y[] in the baseband at caprate
|
||||
// speed
|
||||
|
||||
for (unsigned int i = 0; i < rtty_k_SampPerSymb; i++)
|
||||
{
|
||||
// move sample to 1,5kHz carrier
|
||||
nco_crcf_step(rtty_upnco);
|
||||
usbf = (buf_tx1500[j]).real + (buf_tx1500[j]).imag;
|
||||
|
||||
liquid_float_complex c;
|
||||
nco_crcf_mix_up(rtty_upnco, y[i], &c);
|
||||
float usb = c.real + c.imag;
|
||||
//measure_speed_bps(1);
|
||||
|
||||
// speed: 48000
|
||||
// adapt speed to soundcard samplerate
|
||||
int fs;
|
||||
while (1)
|
||||
{
|
||||
fs = io_pb_fifo_freespace(0);
|
||||
// wait until there is space in fifo
|
||||
if (fs > 20000) break;
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
io_pb_write_fifo(usb * 0.2f); // reduce volume and send to soundcard
|
||||
}
|
||||
io_pb_write_fifo(usbf * 0.2f); // reduce volume and send to soundcard
|
||||
}
|
||||
}
|
||||
|
||||
// RTTY: sample rate HAS TO BE 48000
|
||||
int rtty_rx()
|
||||
int rtest()
|
||||
{
|
||||
static liquid_float_complex ccol[500];
|
||||
static unsigned int ccol_idx = 0;
|
||||
static int bridx = 0;
|
||||
|
||||
if (rtty_dnnco == NULL) return 0;
|
||||
|
||||
// get one received sample
|
||||
int j;
|
||||
float f;
|
||||
int ret = io_cap_read_fifo(&f);
|
||||
if (ret == 0) return 0;
|
||||
|
||||
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
|
||||
io_ls_write_fifo(f);
|
||||
// noise
|
||||
//printf("%f\n", f);
|
||||
//f = f + nstd * randnf() * M_SQRT1_2;
|
||||
|
||||
// input volume
|
||||
f *= softwareCAPvolume;
|
||||
|
||||
//getMax(f);
|
||||
//make_FFTdata(f * 100);
|
||||
|
||||
// downconvert 1,5kHz into baseband, still at soundcard sample rate
|
||||
(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++;
|
||||
|
||||
liquid_float_complex in;
|
||||
in.real = f;
|
||||
in.imag = f;
|
||||
liquid_float_complex c;
|
||||
nco_crcf_mix_down(rtty_dnnco, in, &c);
|
||||
|
||||
// c is the actual sample, converted to complex and shifted to baseband
|
||||
|
||||
// this is the first decimator. We need to collect rtty_k_SampPerSymb number of samples
|
||||
// then call execute which will give us one decimated sample
|
||||
ccol[ccol_idx++] = c;
|
||||
if (ccol_idx < rtty_k_SampPerSymb) return 1;
|
||||
ccol_idx = 0;
|
||||
|
||||
// we have rtty_k_SampPerSymb samples in ccol
|
||||
liquid_float_complex y;
|
||||
firdecim_crcf_execute(rtty_decim, ccol, &y);
|
||||
// the output of the pre decimator is exactly one sample in y
|
||||
// ready for demodulation
|
||||
// here we have 1500 samples/s
|
||||
// collect rtty_k (33) samples then demodulate, 1500/33 = 45.45
|
||||
static unsigned int cs = 0;
|
||||
static liquid_float_complex camps[rtty_k];
|
||||
camps[cs] = y;
|
||||
if (++cs < rtty_k) return 1;
|
||||
cs = 0;
|
||||
|
||||
//measure_speed_bps(1);
|
||||
|
||||
unsigned int sym_out = fskdem_demodulate(rtty_dem, camps);
|
||||
|
||||
static int sym = 0;
|
||||
static int symidx = 0;
|
||||
sym |= sym_out << symidx;
|
||||
symidx++;
|
||||
if (symidx == 8)
|
||||
if (bridx == k)
|
||||
{
|
||||
symidx = 0;
|
||||
printf("%d: %c\n", sym, sym);
|
||||
sym = 0;
|
||||
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;
|
||||
|
||||
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);
|
||||
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
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// create the rtty TX threads
|
||||
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
|
||||
}
|
||||
|
||||
run_rtty_threads = 1;
|
||||
}
|
||||
|
||||
void close_rtty()
|
||||
{
|
||||
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;
|
||||
|
||||
if (rtty_upnco != NULL) nco_crcf_destroy(rtty_upnco);
|
||||
rtty_upnco = NULL;
|
||||
|
||||
if (rtty_dnnco != NULL) nco_crcf_destroy(rtty_dnnco);
|
||||
rtty_dnnco = NULL;
|
||||
|
||||
if (rtty_q != NULL) firfilt_crcf_destroy(rtty_q);
|
||||
rtty_q = NULL;
|
||||
}
|
||||
|
||||
// RTTY TX thread
|
||||
#ifdef _LINUX_
|
||||
void* rtty_tx_function(void* param)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
void rtty_tx_function(void* param)
|
||||
{
|
||||
#endif
|
||||
|
||||
uint8_t bd[2];
|
||||
int anz = 0;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/*static int last_txoff = -2;
|
||||
if (last_txoff != 0 && rtty_txoff == 0)
|
||||
{
|
||||
// just switched TX on
|
||||
//printf("force Bu/Zi switch: %d %d\n", rtty_txoff, last_txoff);
|
||||
isletters = isletters ? 0 : 1; // force Bu/Zi command
|
||||
}
|
||||
last_txoff = rtty_txoff;*/
|
||||
|
||||
char csend;
|
||||
if (rtty_tx_read_fifo(&csend))
|
||||
{
|
||||
baudot_encoder(csend, bd, &anz);
|
||||
//printf("read fifo: %d -> %02X\n", csend, bd[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bd[0] = 0x1f; // idle
|
||||
anz = 1;
|
||||
|
||||
if (rtty_txoff == 1)
|
||||
{
|
||||
sleep_ms(10);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rtty_txoff > 1) rtty_txoff--;
|
||||
}
|
||||
|
||||
//if(bd[0] != 0x1f) printf("send chars: %02X\n",bd[0]);
|
||||
|
||||
for (int i = 0; i < anz; i++)
|
||||
{
|
||||
char c = bd[i];
|
||||
// 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++)
|
||||
{
|
||||
if (bitidx == 0 && twice == 2) break; //last bit only once
|
||||
|
||||
fskmod_modulate(modi, sym_in, &(buf_tx[0]));
|
||||
|
||||
// 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;
|
||||
|
||||
// adapt to audio sample rate
|
||||
int fs;
|
||||
while (keeprunning && run_rtty_threads)
|
||||
{
|
||||
fs = io_pb_fifo_usedspace();
|
||||
//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(1);
|
||||
}
|
||||
|
||||
io_pb_write_fifo(usbf * 0.05f); // reduce volume and send to soundcard
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef _LINUX_
|
||||
pthread_exit(NULL); // self terminate this thread
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// RTTY RX thread
|
||||
#ifdef _LINUX_
|
||||
void* rtty_rx_function(void* param)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
void rtty_rx_function(void* param)
|
||||
{
|
||||
#endif
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
static int bridx = 0;
|
||||
float f;
|
||||
int ret = io_cap_read_fifo(&f);
|
||||
if (ret)
|
||||
{
|
||||
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
|
||||
io_ls_write_fifo(f);
|
||||
|
||||
// input volume
|
||||
f *= softwareCAPvolume;
|
||||
|
||||
// TODO check Multithreading !!!!!!!!!
|
||||
getMax(f);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// sharp filter
|
||||
firfilt_crcf_push(rtty_q, dc_out); // push input sample
|
||||
firfilt_crcf_execute(rtty_q, &(buf_rx[bridx])); // compute output
|
||||
|
||||
bridx++;
|
||||
|
||||
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);
|
||||
//printf("rxbyte:%02X deoced:%02X\n", db, lt);
|
||||
if (lt > 0)
|
||||
sendRttyToGUI(lt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
sleep_ms(1);
|
||||
}
|
||||
|
||||
#ifdef _LINUX_
|
||||
pthread_exit(NULL); // self terminate this thread
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
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++)
|
||||
{
|
||||
if (symbuf[pos + i]) h++;
|
||||
else l++;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
+7
-4
@@ -36,7 +36,7 @@ struct SoundIoDevice* io_cap_device = NULL;
|
||||
struct SoundIoInStream* instream = NULL;
|
||||
struct SoundIoOutStream* outstream = NULL;
|
||||
|
||||
float latenz = 0.1f;
|
||||
float latenz = 0.1f; // long (some seconds) delay can be caused by SDR console (not this program and not the VAC)
|
||||
|
||||
|
||||
typedef struct _AUDIODEV_ {
|
||||
@@ -83,6 +83,7 @@ static void get_channel_layout(const struct SoundIoChannelLayout* layout)
|
||||
|
||||
int print_device(struct SoundIoDevice* device)
|
||||
{
|
||||
if (soundio == NULL) return 0;
|
||||
if (!device->probe_error)
|
||||
{
|
||||
// ignore if exists
|
||||
@@ -119,6 +120,7 @@ int print_device(struct SoundIoDevice* device)
|
||||
|
||||
static int scan_devices(struct SoundIo* soundio)
|
||||
{
|
||||
if (soundio == NULL) return 0;
|
||||
audiodevidx = 0;
|
||||
for (int i = 0; i < soundio_input_device_count(soundio); i++)
|
||||
{
|
||||
@@ -224,14 +226,14 @@ int min_int(int a, int b)
|
||||
void read_callback(struct SoundIoInStream* instream, int frame_count_min, int frame_count_max)
|
||||
{
|
||||
int err;
|
||||
if (instream == NULL) return;
|
||||
if (instream == NULL || soundio == NULL) return;
|
||||
//printf("cap: %d %d\n", frame_count_min, frame_count_max);
|
||||
//int chans = instream->layout.channel_count;
|
||||
|
||||
struct SoundIoChannelArea* areas;
|
||||
// samples are in areas.ptr
|
||||
int frames_left = frame_count_max; // take all
|
||||
while (1)
|
||||
while (keeprunning)
|
||||
{
|
||||
int frame_count = frames_left;
|
||||
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
|
||||
@@ -340,6 +342,7 @@ static double seconds_offset = 0.0;
|
||||
|
||||
static void write_callback(struct SoundIoOutStream* outstream, int frame_count_min, int frame_count_max)
|
||||
{
|
||||
if(outstream == NULL || soundio == NULL) return;
|
||||
//printf("pb: %d %d\n", frame_count_min, frame_count_max);
|
||||
#ifdef SINEWAVETEST
|
||||
double float_sample_rate = outstream->sample_rate;
|
||||
@@ -431,7 +434,7 @@ int io_init_sound(char *pbname, char *capname)
|
||||
init_audio_result = 0;
|
||||
|
||||
printf("\n ==== IO INIT AUDIO devices ====\n");
|
||||
printf("requested\nTX:<%s>\nRX:<%s>\ncapture rate:%d\n\n",pbname,capname,caprate);
|
||||
//printf("requested\nTX:<%s>\nRX:<%s>\ncapture rate:%d\n\n",pbname,capname,caprate);
|
||||
|
||||
io_close_audio();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "hsmodem.h"
|
||||
|
||||
SYMTRACK km_symtrack;
|
||||
SYMTRACK* q = &km_symtrack;
|
||||
|
||||
|
||||
// create km_symtrack object with basic parameters
|
||||
// _ftype : filter type (e.g. LIQUID_FIRFILT_RRC)
|
||||
@@ -9,7 +8,7 @@ SYMTRACK* q = &km_symtrack;
|
||||
// _m : filter delay (symbols)
|
||||
// _beta : filter excess bandwidth
|
||||
// _ms : modulation scheme (e.g. LIQUID_MODEM_QPSK)
|
||||
void km_symtrack_cccf_create(int _ftype,
|
||||
SYMTRACK* km_symtrack_cccf_create(int _ftype,
|
||||
unsigned int _k,
|
||||
unsigned int _m,
|
||||
float _beta,
|
||||
@@ -17,7 +16,7 @@ void km_symtrack_cccf_create(int _ftype,
|
||||
{
|
||||
// validate input
|
||||
if (_k < 2)
|
||||
printf((char *)"symtrack_cccf_create(), filter samples/symbol must be at least 2\n");
|
||||
printf((char*)"symtrack_cccf_create(), filter samples/symbol must be at least 2\n");
|
||||
if (_m == 0)
|
||||
printf((char*)"symtrack_cccf_create(), filter delay must be greater than zero\n");
|
||||
if (_beta <= 0.0f || _beta > 1.0f)
|
||||
@@ -25,6 +24,9 @@ void km_symtrack_cccf_create(int _ftype,
|
||||
if (_ms == LIQUID_MODEM_UNKNOWN || _ms >= LIQUID_MODEM_NUM_SCHEMES)
|
||||
printf((char*)"symtrack_cccf_create(), invalid modulation scheme\n");
|
||||
|
||||
// allocate memory for main object
|
||||
SYMTRACK *q = (SYMTRACK *) malloc(sizeof(SYMTRACK));
|
||||
|
||||
// set input parameters
|
||||
q->filter_type = _ftype;
|
||||
q->k = _k;
|
||||
@@ -54,14 +56,33 @@ void km_symtrack_cccf_create(int _ftype,
|
||||
q->demod = modem_create((modulation_scheme)q->mod_scheme);
|
||||
|
||||
// set default bandwidth
|
||||
km_symtrack_cccf_set_bandwidth(0.9f);
|
||||
km_symtrack_cccf_set_bandwidth(q, 0.9f);
|
||||
|
||||
// reset and return main object
|
||||
km_symtrack_cccf_reset(0xff);
|
||||
km_symtrack_cccf_reset(q, 0xff);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
void km_symtrack_cccf_reset(int mode)
|
||||
void km_symtrack_cccf_destroy(SYMTRACK *_q)
|
||||
{
|
||||
if (_q == NULL) return;
|
||||
|
||||
// destroy objects
|
||||
agc_crcf_destroy(_q->agc);
|
||||
symsync_crcf_destroy(_q->symsync);
|
||||
eqlms_cccf_destroy(_q->eq);
|
||||
nco_crcf_destroy(_q->nco);
|
||||
modem_destroy(_q->demod);
|
||||
|
||||
// free main object
|
||||
free(_q);
|
||||
}
|
||||
|
||||
void km_symtrack_cccf_reset(SYMTRACK* q, int mode)
|
||||
{
|
||||
if (q == NULL) return;
|
||||
|
||||
// reset objects
|
||||
if (mode & 1) agc_crcf_reset(q->agc);
|
||||
if (mode & 2) symsync_crcf_reset(q->symsync);
|
||||
@@ -74,7 +95,7 @@ void km_symtrack_cccf_reset(int mode)
|
||||
q->num_syms_rx = 0;
|
||||
}
|
||||
|
||||
void km_symtrack_cccf_set_bandwidth(float _bw)
|
||||
void km_symtrack_cccf_set_bandwidth(SYMTRACK *q, float _bw)
|
||||
{
|
||||
// validate input
|
||||
if (_bw < 0)
|
||||
@@ -106,8 +127,10 @@ void km_symtrack_cccf_set_bandwidth(float _bw)
|
||||
// _x : input data sample
|
||||
// _y : output data array
|
||||
// _ny : number of samples written to output buffer
|
||||
void km_symtrack_execute(liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int *psym_out)
|
||||
void km_symtrack_execute(SYMTRACK* q, liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int *psym_out)
|
||||
{
|
||||
if (q == NULL) return;
|
||||
|
||||
liquid_float_complex v; // output sample
|
||||
unsigned int i;
|
||||
unsigned int num_outputs = 0;
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ float do_tuning(int send)
|
||||
|
||||
// adapt speed to soundcard samplerate
|
||||
int fs;
|
||||
while (1)
|
||||
while (keeprunning)
|
||||
{
|
||||
fs = io_pb_fifo_freespace(0);
|
||||
// wait until there is space in fifo
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ void read_voicecallback(struct SoundIoInStream* instream, int frame_count_min, i
|
||||
struct SoundIoChannelArea* areas;
|
||||
// samples are in areas.ptr
|
||||
int frames_left = frame_count_max; // take all
|
||||
while (1)
|
||||
while (keeprunning)
|
||||
{
|
||||
int frame_count = frames_left;
|
||||
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
|
||||
|
||||
@@ -168,7 +168,7 @@ void sendCodecToModulator(uint8_t *pdata, int len)
|
||||
toGR_sendData(payload, 6, 1 ,0); // 6 ... voice data, 1 ... valid voice data
|
||||
}
|
||||
|
||||
while (1)
|
||||
while (keeprunning)
|
||||
{
|
||||
// we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted
|
||||
int us = io_pb_fifo_usedspace();
|
||||
|
||||
Reference in New Issue
Block a user