mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2024-11-25 13:48:47 -05:00
174 lines
5.7 KiB
C++
Executable File
174 lines
5.7 KiB
C++
Executable File
#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)
|
|
// _k : samples per symbol
|
|
// _m : filter delay (symbols)
|
|
// _beta : filter excess bandwidth
|
|
// _ms : modulation scheme (e.g. LIQUID_MODEM_QPSK)
|
|
void km_symtrack_cccf_create(int _ftype,
|
|
unsigned int _k,
|
|
unsigned int _m,
|
|
float _beta,
|
|
int _ms)
|
|
{
|
|
// validate input
|
|
if (_k < 2)
|
|
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)
|
|
printf((char*)"symtrack_cccf_create(), filter excess bandwidth must be in [0,1]\n");
|
|
if (_ms == LIQUID_MODEM_UNKNOWN || _ms >= LIQUID_MODEM_NUM_SCHEMES)
|
|
printf((char*)"symtrack_cccf_create(), invalid modulation scheme\n");
|
|
|
|
// set input parameters
|
|
q->filter_type = _ftype;
|
|
q->k = _k;
|
|
q->m = _m;
|
|
q->beta = _beta;
|
|
q->mod_scheme = _ms == LIQUID_MODEM_UNKNOWN ? LIQUID_MODEM_BPSK : _ms;
|
|
|
|
// create automatic gain control
|
|
q->agc = agc_crcf_create();
|
|
|
|
// create symbol synchronizer (output rate: 2 samples per symbol)
|
|
if (q->filter_type == LIQUID_FIRFILT_UNKNOWN)
|
|
q->symsync = symsync_crcf_create_kaiser(q->k, q->m, 0.9f, 16);
|
|
else
|
|
q->symsync = symsync_crcf_create_rnyquist(q->filter_type, q->k, q->m, q->beta, 16);
|
|
symsync_crcf_set_output_rate(q->symsync, 2);
|
|
|
|
// create equalizer as default low-pass filter with integer symbol delay (2 samples/symbol)
|
|
q->eq_len = 2 * 4 + 1;
|
|
q->eq = eqlms_cccf_create_lowpass(q->eq_len, 0.45f);
|
|
q->eq_strategy = q->SYMTRACK_EQ_DD;
|
|
|
|
// nco and phase-locked loop
|
|
q->nco = nco_crcf_create(LIQUID_VCO);
|
|
|
|
// demodulator
|
|
q->demod = modem_create((modulation_scheme)q->mod_scheme);
|
|
|
|
// set default bandwidth
|
|
km_symtrack_cccf_set_bandwidth(0.9f);
|
|
|
|
// reset and return main object
|
|
km_symtrack_cccf_reset(0xff);
|
|
}
|
|
|
|
void km_symtrack_cccf_reset(int mode)
|
|
{
|
|
// reset objects
|
|
if (mode & 1) agc_crcf_reset(q->agc);
|
|
if (mode & 2) symsync_crcf_reset(q->symsync);
|
|
if (mode & 4) eqlms_cccf_reset(q->eq);
|
|
if (mode & 8) nco_crcf_reset(q->nco);
|
|
if (mode & 0x10) modem_reset(q->demod);
|
|
|
|
// reset internal counters
|
|
q->symsync_index = 0;
|
|
q->num_syms_rx = 0;
|
|
}
|
|
|
|
void km_symtrack_cccf_set_bandwidth(float _bw)
|
|
{
|
|
// validate input
|
|
if (_bw < 0)
|
|
printf("symtrack_set_bandwidth(), bandwidth must be in [0,1]\n");
|
|
|
|
// set bandwidths accordingly
|
|
float agc_bandwidth = 0.02f * _bw;
|
|
float symsync_bandwidth = 0.001f * _bw;
|
|
float eq_bandwidth = 0.02f * _bw;
|
|
float pll_bandwidth = 0.001f * _bw;
|
|
|
|
// automatic gain control
|
|
agc_crcf_set_bandwidth(q->agc, agc_bandwidth);
|
|
|
|
// symbol timing recovery
|
|
symsync_crcf_set_lf_bw(q->symsync, symsync_bandwidth);
|
|
|
|
// equalizer
|
|
eqlms_cccf_set_bw(q->eq, eq_bandwidth);
|
|
|
|
// phase-locked loop
|
|
nco_crcf_pll_set_bandwidth(q->nco, pll_bandwidth);
|
|
}
|
|
|
|
#define MX 10
|
|
|
|
// execute synchronizer on single input sample
|
|
// _q : synchronizer object
|
|
// _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)
|
|
{
|
|
liquid_float_complex v; // output sample
|
|
unsigned int i;
|
|
unsigned int num_outputs = 0;
|
|
|
|
// run sample through automatic gain control
|
|
agc_crcf_execute(q->agc, _x, &v);
|
|
|
|
// symbol synchronizer
|
|
unsigned int nw = 0;
|
|
symsync_crcf_execute(q->symsync, &v, 1, q->symsync_buf, &nw);
|
|
|
|
// process each output sample
|
|
for (i = 0; i < nw; i++) {
|
|
// update phase-locked loop
|
|
nco_crcf_step(q->nco);
|
|
nco_crcf_mix_down(q->nco, q->symsync_buf[i], &v);
|
|
|
|
// equalizer/decimator
|
|
eqlms_cccf_push(q->eq, v);
|
|
|
|
// decimate result, noting that symsync outputs at exactly 2 samples/symbol
|
|
q->symsync_index++;
|
|
if (!(q->symsync_index % 2))
|
|
continue;
|
|
|
|
// increment number of symbols received
|
|
q->num_syms_rx++;
|
|
|
|
// compute equalizer filter output; updating coefficients is dependent upon strategy
|
|
liquid_float_complex d_hat;
|
|
eqlms_cccf_execute(q->eq, &d_hat);
|
|
|
|
// demodulate result, apply phase correction
|
|
unsigned int sym_out;
|
|
modem_demodulate(q->demod, d_hat, &sym_out);
|
|
*psym_out = sym_out;
|
|
float phase_error = modem_get_demodulator_phase_error(q->demod);
|
|
|
|
// update pll
|
|
nco_crcf_pll_step(q->nco, phase_error);
|
|
|
|
// update equalizer independent of the signal: estimate error
|
|
// assuming constant modulus signal
|
|
// TODO: check lock conditions of previous object to determine when to run equalizer
|
|
liquid_float_complex d_prime;
|
|
d_prime.real = d_prime.imag = 0;
|
|
if (q->num_syms_rx > 200)
|
|
{
|
|
modem_get_demodulator_sample(q->demod, &d_prime);
|
|
eqlms_cccf_step(q->eq, d_prime, d_hat);
|
|
}
|
|
|
|
// save result to output
|
|
_y[num_outputs++] = d_hat;
|
|
}
|
|
|
|
/*float fr = nco_crcf_get_frequency(q->nco);
|
|
float ph = nco_crcf_get_phase(q->nco);
|
|
|
|
printf("%10.6f %10.6f %10.6f %10.6f\n", fr, ph, _x.real, _x.imag);*/
|
|
|
|
* _ny = num_outputs;
|
|
}
|