This commit is contained in:
Kurt Moraw
2021-01-03 00:34:35 +01:00
parent 573107a3d0
commit 429ec008a9
54 changed files with 3480 additions and 851 deletions
-2
View File
@@ -27,8 +27,6 @@
#include "hsmodem.h"
void close_a();
const int h_len = 57;
float h[h_len];
firfilt_crcf qfilt = NULL;
+7
View File
@@ -0,0 +1,7 @@
#pragma once
typedef struct _BAUDOTTAB_ {
unsigned char baudot;
char letter;
char number;
} BAUDOTTAB;
+92 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1
View File
@@ -223,6 +223,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="baudot.h" />
<ClInclude Include="codec2.h" />
<ClInclude Include="endian.h" />
<ClInclude Include="fec.h" />
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+32 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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)))
+1 -1
View File
@@ -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();