SSB_HighSpeed_Modem/hsmodem/rtty.cpp

263 lines
7.8 KiB
C++
Executable File

/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* RTTY:
* =====
* bit/symbol: 1
* BW: 170 Hz
* carrier: 1500 Hz
* speed: 45.45 bits/s
* samples/symbol: 33
*/
#include "hsmodem.h"
#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
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
void close_rtty();
void init_rtty()
{
close_rtty();
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;
// 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)
{
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);
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);
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);
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);
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);
}
void close_rtty()
{
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;
}
char text[6] = {"ABCD\n"};
int tidx = 0;
int bitidx = 1;
void rtty_tx()
{
if (rtty_mod == NULL)
init_rtty();
unsigned int sym = (text[tidx] & bitidx) ? 1 : 0;
bitidx <<= 1;
if (bitidx == 0x0100)
{
bitidx = 1;
tidx++;
if (tidx == 6) tidx = 0;
}
liquid_float_complex rtty_txbuf[33+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++)
{
// 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;
}
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);
liquid_float_complex c;
nco_crcf_mix_up(rtty_upnco, y[i], &c);
float usb = c.real + c.imag;
// 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
}
}
}
// RTTY: sample rate HAS TO BE 48000
int rtty_rx()
{
static liquid_float_complex ccol[500];
static unsigned int ccol_idx = 0;
if (rtty_dnnco == NULL) return 0;
// get one received sample
float f;
int ret = io_cap_read_fifo(&f);
if (ret == 0) return 0;
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
io_ls_write_fifo(f);
// input volume
f *= softwareCAPvolume;
//getMax(f);
//make_FFTdata(f * 100);
// downconvert 1,5kHz into baseband, still at soundcard sample rate
nco_crcf_step(rtty_dnnco);
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)
{
symidx = 0;
printf("%d: %c\n", sym, sym);
sym = 0;
}
return 1;
}