mirror of https://github.com/drowe67/codec2.git
613 lines
22 KiB
C
613 lines
22 KiB
C
/*---------------------------------------------------------------------------*\
|
|
|
|
FILE........: freedv_700.c
|
|
AUTHOR......: David Rowe
|
|
DATE CREATED: May 2020
|
|
|
|
Functions that implement the various FreeDV 700 modes, and more generally
|
|
OFDM data modes.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "codec2.h"
|
|
#include "codec2_fdmdv.h"
|
|
#include "codec2_ofdm.h"
|
|
#include "comp_prim.h"
|
|
#include "debug_alloc.h"
|
|
#include "filter.h"
|
|
#include "fmfsk.h"
|
|
#include "freedv_api.h"
|
|
#include "freedv_api_internal.h"
|
|
#include "fsk.h"
|
|
#include "gp_interleaver.h"
|
|
#include "interldpc.h"
|
|
#include "ldpc_codes.h"
|
|
#include "mpdecode_core.h"
|
|
#include "ofdm_internal.h"
|
|
#include "varicode.h"
|
|
|
|
extern char *ofdm_statemode[];
|
|
|
|
void freedv_700c_open(struct freedv *f) {
|
|
f->snr_squelch_thresh = 0.0;
|
|
f->squelch_en = false;
|
|
|
|
f->cohpsk = cohpsk_create();
|
|
f->nin = f->nin_prev = COHPSK_NOM_SAMPLES_PER_FRAME;
|
|
f->n_nat_modem_samples =
|
|
COHPSK_NOM_SAMPLES_PER_FRAME; // native modem samples as used by the
|
|
// modem
|
|
f->n_nom_modem_samples = f->n_nat_modem_samples * FREEDV_FS_8000 /
|
|
COHPSK_FS; // number of samples after native samples
|
|
// are interpolated to 8000 sps
|
|
f->n_max_modem_samples =
|
|
COHPSK_MAX_SAMPLES_PER_FRAME * FREEDV_FS_8000 / COHPSK_FS + 1;
|
|
f->modem_sample_rate =
|
|
FREEDV_FS_8000; // note weird sample rate tamed by resampling
|
|
f->clip_en = true;
|
|
f->sz_error_pattern = cohpsk_error_pattern_size();
|
|
f->test_frames_diversity = 1;
|
|
|
|
f->ptFilter7500to8000 =
|
|
(struct quisk_cfFilter *)MALLOC(sizeof(struct quisk_cfFilter));
|
|
f->ptFilter8000to7500 =
|
|
(struct quisk_cfFilter *)MALLOC(sizeof(struct quisk_cfFilter));
|
|
quisk_filt_cfInit(f->ptFilter8000to7500, quiskFilt120t480,
|
|
sizeof(quiskFilt120t480) / sizeof(float));
|
|
quisk_filt_cfInit(f->ptFilter7500to8000, quiskFilt120t480,
|
|
sizeof(quiskFilt120t480) / sizeof(float));
|
|
|
|
f->speech_sample_rate = FREEDV_FS_8000;
|
|
f->codec2 = codec2_create(CODEC2_MODE_700C);
|
|
assert(f->codec2 != NULL);
|
|
|
|
f->n_codec_frames = 2;
|
|
f->n_speech_samples = f->n_codec_frames * codec2_samples_per_frame(f->codec2);
|
|
f->bits_per_codec_frame = codec2_bits_per_frame(f->codec2);
|
|
f->bits_per_modem_frame =
|
|
f->n_codec_frames * codec2_bits_per_frame(f->codec2);
|
|
assert(f->bits_per_modem_frame == COHPSK_BITS_PER_FRAME);
|
|
|
|
f->tx_payload_bits =
|
|
(uint8_t *)MALLOC(f->bits_per_modem_frame * sizeof(char));
|
|
assert(f->tx_payload_bits != NULL);
|
|
f->rx_payload_bits =
|
|
(uint8_t *)MALLOC(f->bits_per_modem_frame * sizeof(char));
|
|
assert(f->rx_payload_bits != NULL);
|
|
}
|
|
|
|
void freedv_comptx_700c(struct freedv *f, COMP mod_out[]) {
|
|
int i;
|
|
COMP tx_fdm[f->n_nat_modem_samples];
|
|
int tx_bits[COHPSK_BITS_PER_FRAME];
|
|
|
|
/* earlier modems used one bit per int for unpacked bits */
|
|
for (i = 0; i < COHPSK_BITS_PER_FRAME; i++)
|
|
tx_bits[i] = f->tx_payload_bits[i];
|
|
|
|
/* optionally overwrite the codec bits with test frames */
|
|
if (f->test_frames) {
|
|
cohpsk_get_test_bits(f->cohpsk, tx_bits);
|
|
}
|
|
|
|
/* cohpsk modulator */
|
|
cohpsk_mod(f->cohpsk, tx_fdm, tx_bits, COHPSK_BITS_PER_FRAME);
|
|
|
|
float gain = 1.0;
|
|
if (f->clip_en) {
|
|
cohpsk_clip(tx_fdm, COHPSK_CLIP, COHPSK_NOM_SAMPLES_PER_FRAME);
|
|
gain = 2.5;
|
|
}
|
|
for (i = 0; i < f->n_nat_modem_samples; i++)
|
|
mod_out[i] = fcmult(gain * COHPSK_SCALE, tx_fdm[i]);
|
|
i = quisk_cfInterpDecim((complex float *)mod_out, f->n_nat_modem_samples,
|
|
f->ptFilter7500to8000, 16, 15);
|
|
}
|
|
|
|
// open function for OFDM voice modes
|
|
void freedv_ofdm_voice_open(struct freedv *f, char *mode) {
|
|
f->snr_squelch_thresh = 0.0;
|
|
f->squelch_en = false;
|
|
struct OFDM_CONFIG *ofdm_config =
|
|
(struct OFDM_CONFIG *)calloc(1, sizeof(struct OFDM_CONFIG));
|
|
assert(ofdm_config != NULL);
|
|
ofdm_init_mode(mode, ofdm_config);
|
|
|
|
f->ofdm = ofdm_create(ofdm_config);
|
|
assert(f->ofdm != NULL);
|
|
free(ofdm_config);
|
|
|
|
ofdm_config = ofdm_get_config_param(f->ofdm);
|
|
f->ofdm_bitsperpacket = ofdm_get_bits_per_packet(f->ofdm);
|
|
f->ofdm_bitsperframe = ofdm_get_bits_per_frame(f->ofdm);
|
|
f->ofdm_nuwbits = ofdm_config->nuwbits;
|
|
f->ofdm_ntxtbits = ofdm_config->txtbits;
|
|
|
|
f->ldpc = (struct LDPC *)MALLOC(sizeof(struct LDPC));
|
|
assert(f->ldpc != NULL);
|
|
|
|
ldpc_codes_setup(f->ldpc, f->ofdm->codename);
|
|
ldpc_mode_specific_setup(f->ofdm, f->ldpc);
|
|
#ifdef __EMBEDDED__
|
|
f->ldpc->max_iter = 10; /* limit LDPC decoder iterations to limit CPU load */
|
|
#endif
|
|
int Nsymsperpacket = ofdm_get_bits_per_packet(f->ofdm) / f->ofdm->bps;
|
|
f->rx_syms = (COMP *)MALLOC(sizeof(COMP) * Nsymsperpacket);
|
|
assert(f->rx_syms != NULL);
|
|
f->rx_amps = (float *)MALLOC(sizeof(float) * Nsymsperpacket);
|
|
assert(f->rx_amps != NULL);
|
|
for (int i = 0; i < Nsymsperpacket; i++) {
|
|
f->rx_syms[i].real = f->rx_syms[i].imag = 0.0;
|
|
f->rx_amps[i] = 0.0;
|
|
}
|
|
|
|
f->nin = f->nin_prev = ofdm_get_samples_per_frame(f->ofdm);
|
|
f->n_nat_modem_samples = ofdm_get_samples_per_frame(f->ofdm);
|
|
f->n_nom_modem_samples = ofdm_get_samples_per_frame(f->ofdm);
|
|
f->n_max_modem_samples = ofdm_get_max_samples_per_frame(f->ofdm);
|
|
f->modem_sample_rate = f->ofdm->config.fs;
|
|
f->clip_en = false;
|
|
f->sz_error_pattern = f->ofdm_bitsperframe;
|
|
|
|
f->tx_bits = NULL; /* not used for 700D */
|
|
|
|
f->speech_sample_rate = FREEDV_FS_8000;
|
|
f->codec2 = codec2_create(CODEC2_MODE_700C);
|
|
assert(f->codec2 != NULL);
|
|
/* should be exactly an integer number of Codec 2 frames in a OFDM modem frame
|
|
*/
|
|
assert((f->ldpc->data_bits_per_frame % codec2_bits_per_frame(f->codec2)) ==
|
|
0);
|
|
|
|
f->n_codec_frames =
|
|
f->ldpc->data_bits_per_frame / codec2_bits_per_frame(f->codec2);
|
|
f->n_speech_samples = f->n_codec_frames * codec2_samples_per_frame(f->codec2);
|
|
f->bits_per_codec_frame = codec2_bits_per_frame(f->codec2);
|
|
f->bits_per_modem_frame = f->n_codec_frames * f->bits_per_codec_frame;
|
|
|
|
f->tx_payload_bits = (unsigned char *)MALLOC(f->bits_per_modem_frame);
|
|
assert(f->tx_payload_bits != NULL);
|
|
f->rx_payload_bits = (unsigned char *)MALLOC(f->bits_per_modem_frame);
|
|
assert(f->rx_payload_bits != NULL);
|
|
|
|
/* attenuate audio 12dB as channel noise isn't that pleasant */
|
|
f->passthrough_gain = 0.25;
|
|
|
|
/* should all add up to a complete frame */
|
|
assert((ofdm_config->ns - 1) * ofdm_config->nc * ofdm_config->bps ==
|
|
f->ldpc->coded_bits_per_frame + ofdm_config->txtbits +
|
|
f->ofdm_nuwbits);
|
|
}
|
|
|
|
// open function for OFDM data modes, TODO consider moving to a new
|
|
// (freedv_ofdm_data.c) file
|
|
void freedv_ofdm_data_open(struct freedv *f) {
|
|
struct OFDM_CONFIG ofdm_config;
|
|
char mode[32];
|
|
if (f->mode == FREEDV_MODE_DATAC0) strcpy(mode, "datac0");
|
|
if (f->mode == FREEDV_MODE_DATAC1) strcpy(mode, "datac1");
|
|
if (f->mode == FREEDV_MODE_DATAC3) strcpy(mode, "datac3");
|
|
if (f->mode == FREEDV_MODE_DATAC4) strcpy(mode, "datac4");
|
|
if (f->mode == FREEDV_MODE_DATAC13) strcpy(mode, "datac13");
|
|
if (f->mode == FREEDV_MODE_DATAC14) strcpy(mode, "datac14");
|
|
|
|
ofdm_init_mode(mode, &ofdm_config);
|
|
f->ofdm = ofdm_create(&ofdm_config);
|
|
assert(f->ofdm != NULL);
|
|
|
|
// LDPC set up
|
|
f->ldpc = (struct LDPC *)MALLOC(sizeof(struct LDPC));
|
|
assert(f->ldpc != NULL);
|
|
ldpc_codes_setup(f->ldpc, f->ofdm->codename);
|
|
ldpc_mode_specific_setup(f->ofdm, f->ldpc);
|
|
#ifdef __EMBEDDED__
|
|
f->ldpc->max_iter = 10; /* limit LDPC decoder iterations to limit CPU load */
|
|
#endif
|
|
|
|
// useful constants
|
|
f->ofdm_bitsperpacket = ofdm_get_bits_per_packet(f->ofdm);
|
|
f->ofdm_bitsperframe = ofdm_get_bits_per_frame(f->ofdm);
|
|
f->ofdm_nuwbits = ofdm_config.nuwbits;
|
|
f->ofdm_ntxtbits = ofdm_config.txtbits;
|
|
|
|
/* payload bits per FreeDV API "frame". In OFDM modem nomenclature this is
|
|
the number of payload data bits per packet, or the number of data bits in a
|
|
LDPC codeword */
|
|
f->bits_per_modem_frame = f->ldpc->data_bits_per_frame;
|
|
|
|
// buffers for received symbols for one packet/LDPC codeword - may span many
|
|
// OFDM modem frames
|
|
int Nsymsperpacket = ofdm_get_bits_per_packet(f->ofdm) / f->ofdm->bps;
|
|
f->rx_syms = (COMP *)MALLOC(sizeof(COMP) * Nsymsperpacket);
|
|
assert(f->rx_syms != NULL);
|
|
f->rx_amps = (float *)MALLOC(sizeof(float) * Nsymsperpacket);
|
|
assert(f->rx_amps != NULL);
|
|
for (int i = 0; i < Nsymsperpacket; i++) {
|
|
f->rx_syms[i].real = f->rx_syms[i].imag = 0.0;
|
|
f->rx_amps[i] = 0.0;
|
|
}
|
|
|
|
f->nin = f->nin_prev = ofdm_get_nin(f->ofdm);
|
|
f->n_nat_modem_samples = ofdm_get_samples_per_packet(f->ofdm);
|
|
f->n_nom_modem_samples = ofdm_get_samples_per_frame(f->ofdm);
|
|
/* in burst mode we might jump a preamble frame */
|
|
f->n_max_modem_samples = 2 * ofdm_get_max_samples_per_frame(f->ofdm);
|
|
f->modem_sample_rate = f->ofdm->config.fs;
|
|
f->sz_error_pattern = f->ofdm_bitsperpacket;
|
|
|
|
// Note inconsistency: freedv API modem "frame" is a OFDM modem packet
|
|
f->tx_payload_bits = (unsigned char *)MALLOC(f->bits_per_modem_frame);
|
|
assert(f->tx_payload_bits != NULL);
|
|
f->rx_payload_bits = (unsigned char *)MALLOC(f->bits_per_modem_frame);
|
|
assert(f->rx_payload_bits != NULL);
|
|
}
|
|
|
|
/* speech or raw data, complex OFDM modulation out */
|
|
void freedv_comptx_ofdm(struct freedv *f, COMP mod_out[]) {
|
|
int i, k;
|
|
int nspare;
|
|
|
|
/* Generate Varicode txt bits (if used), waren't protected by FEC */
|
|
nspare = f->ofdm_ntxtbits;
|
|
uint8_t txt_bits[nspare];
|
|
|
|
for (k = 0; k < nspare; k++) {
|
|
if (f->nvaricode_bits == 0) {
|
|
/* get new char and encode */
|
|
char s[2];
|
|
if (f->freedv_get_next_tx_char != NULL) {
|
|
s[0] = (*f->freedv_get_next_tx_char)(f->callback_state);
|
|
f->nvaricode_bits =
|
|
varicode_encode(f->tx_varicode_bits, s, VARICODE_MAX_BITS, 1,
|
|
f->varicode_dec_states.code_num);
|
|
f->varicode_bit_index = 0;
|
|
}
|
|
}
|
|
if (f->nvaricode_bits) {
|
|
txt_bits[k] = f->tx_varicode_bits[f->varicode_bit_index++];
|
|
f->nvaricode_bits--;
|
|
} else
|
|
txt_bits[k] = 0;
|
|
}
|
|
|
|
/* optionally replace payload bits with test frames known to rx */
|
|
if (f->test_frames) {
|
|
uint8_t payload_data_bits[f->bits_per_modem_frame];
|
|
ofdm_generate_payload_data_bits(payload_data_bits, f->bits_per_modem_frame);
|
|
|
|
for (i = 0; i < f->bits_per_modem_frame; i++) {
|
|
f->tx_payload_bits[i] = payload_data_bits[i];
|
|
}
|
|
}
|
|
|
|
/* OK now ready to LDPC encode, interleave, and OFDM modulate */
|
|
ofdm_ldpc_interleave_tx(f->ofdm, f->ldpc, (complex float *)mod_out,
|
|
f->tx_payload_bits, txt_bits);
|
|
}
|
|
|
|
int freedv_comprx_700c(struct freedv *f, COMP demod_in_8kHz[]) {
|
|
int i;
|
|
int sync;
|
|
|
|
int rx_status = 0;
|
|
|
|
// quisk_cfInterpDecim() modifies input data so lets make a copy just in case
|
|
// there is no sync and we need to echo input to output
|
|
|
|
// freedv_nin(f): input samples at Fs=8000 Hz
|
|
// f->nin: input samples at Fs=7500 Hz
|
|
|
|
COMP demod_in[freedv_nin(f)];
|
|
|
|
for (i = 0; i < freedv_nin(f); i++) demod_in[i] = demod_in_8kHz[i];
|
|
|
|
i = quisk_cfInterpDecim((complex float *)demod_in, freedv_nin(f),
|
|
f->ptFilter8000to7500, 15, 16);
|
|
|
|
for (i = 0; i < f->nin; i++)
|
|
demod_in[i] = fcmult(1.0 / COHPSK_SCALE, demod_in[i]);
|
|
|
|
float rx_soft_bits[COHPSK_BITS_PER_FRAME];
|
|
|
|
cohpsk_demod(f->cohpsk, rx_soft_bits, &sync, demod_in, &f->nin);
|
|
|
|
for (i = 0; i < f->bits_per_modem_frame; i++)
|
|
f->rx_payload_bits[i] = rx_soft_bits[i] < 0.0f;
|
|
|
|
f->sync = sync;
|
|
cohpsk_get_demod_stats(f->cohpsk, &f->stats);
|
|
f->snr_est = f->stats.snr_est;
|
|
|
|
if (sync) {
|
|
rx_status = FREEDV_RX_SYNC;
|
|
if (f->test_frames == 0) {
|
|
rx_status |= FREEDV_RX_BITS;
|
|
} else {
|
|
if (f->test_frames_diversity) {
|
|
/* normal operation - error pattern on frame after diveristy combination
|
|
*/
|
|
short error_pattern[COHPSK_BITS_PER_FRAME];
|
|
int bit_errors;
|
|
|
|
/* test data, lets see if we can sync to the test data sequence */
|
|
|
|
char rx_bits_char[COHPSK_BITS_PER_FRAME];
|
|
for (i = 0; i < COHPSK_BITS_PER_FRAME; i++)
|
|
rx_bits_char[i] = rx_soft_bits[i] < 0.0;
|
|
cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state,
|
|
error_pattern, &bit_errors, rx_bits_char, 0);
|
|
if (f->test_frame_sync_state) {
|
|
f->total_bit_errors += bit_errors;
|
|
f->total_bits += COHPSK_BITS_PER_FRAME;
|
|
if (f->freedv_put_error_pattern != NULL) {
|
|
(*f->freedv_put_error_pattern)(f->error_pattern_callback_state,
|
|
error_pattern,
|
|
COHPSK_BITS_PER_FRAME);
|
|
}
|
|
}
|
|
} else {
|
|
/* calculate error pattern on uncombined carriers - test mode to spot
|
|
any carrier specific issues like tx passband filtering */
|
|
|
|
short error_pattern[2 * COHPSK_BITS_PER_FRAME];
|
|
char rx_bits_char[COHPSK_BITS_PER_FRAME];
|
|
int bit_errors_lower, bit_errors_upper;
|
|
|
|
/* lower group of carriers */
|
|
|
|
float *rx_bits_lower = cohpsk_get_rx_bits_lower(f->cohpsk);
|
|
for (i = 0; i < COHPSK_BITS_PER_FRAME; i++) {
|
|
rx_bits_char[i] = rx_bits_lower[i] < 0.0;
|
|
}
|
|
cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state,
|
|
error_pattern, &bit_errors_lower, rx_bits_char, 0);
|
|
|
|
/* upper group of carriers */
|
|
|
|
float *rx_bits_upper = cohpsk_get_rx_bits_upper(f->cohpsk);
|
|
for (i = 0; i < COHPSK_BITS_PER_FRAME; i++) {
|
|
rx_bits_char[i] = rx_bits_upper[i] < 0.0;
|
|
}
|
|
cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state_upper,
|
|
&error_pattern[COHPSK_BITS_PER_FRAME],
|
|
&bit_errors_upper, rx_bits_char, 1);
|
|
|
|
/* combine total errors and call callback */
|
|
|
|
if (f->test_frame_sync_state && f->test_frame_sync_state_upper) {
|
|
f->total_bit_errors += bit_errors_lower + bit_errors_upper;
|
|
f->total_bits += 2 * COHPSK_BITS_PER_FRAME;
|
|
if (f->freedv_put_error_pattern != NULL) {
|
|
(*f->freedv_put_error_pattern)(f->error_pattern_callback_state,
|
|
error_pattern,
|
|
2 * COHPSK_BITS_PER_FRAME);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rx_status;
|
|
}
|
|
|
|
/*
|
|
OFDM demod function that can support complex (float) or real (short)
|
|
samples. The real short samples are useful for low memory platforms such as
|
|
the SM1000.
|
|
*/
|
|
|
|
int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz,
|
|
int demod_in_is_short, float gain) {
|
|
int i, k;
|
|
int n_ascii;
|
|
char ascii_out;
|
|
struct OFDM *ofdm = f->ofdm;
|
|
struct LDPC *ldpc = f->ldpc;
|
|
|
|
/* useful constants */
|
|
int Nbitsperframe = ofdm_get_bits_per_frame(ofdm);
|
|
int Nbitsperpacket = ofdm_get_bits_per_packet(ofdm);
|
|
int Nsymsperframe = Nbitsperframe / ofdm->bps;
|
|
int Nsymsperpacket = Nbitsperpacket / ofdm->bps;
|
|
int Npayloadbitsperpacket = Nbitsperpacket - ofdm->nuwbits - ofdm->ntxtbits;
|
|
int Npayloadsymsperpacket = Npayloadbitsperpacket / ofdm->bps;
|
|
int Ndatabitsperpacket = ldpc->data_bits_per_frame;
|
|
|
|
complex float *rx_syms = (complex float *)f->rx_syms;
|
|
float *rx_amps = f->rx_amps;
|
|
|
|
int rx_bits[Nbitsperframe];
|
|
short txt_bits[f->ofdm_ntxtbits];
|
|
COMP payload_syms[Npayloadsymsperpacket];
|
|
float payload_amps[Npayloadsymsperpacket];
|
|
|
|
int Nerrs_raw = 0;
|
|
int Nerrs_coded = 0;
|
|
int iter = 0;
|
|
int parityCheckCount = 0;
|
|
uint8_t rx_uw[f->ofdm_nuwbits];
|
|
|
|
float new_gain = gain / f->ofdm->amp_scale;
|
|
|
|
assert((demod_in_is_short == 0) || (demod_in_is_short == 1));
|
|
|
|
int rx_status = 0;
|
|
float EsNo = 3.0; /* further work: estimate this properly from signal */
|
|
f->sync = 0;
|
|
|
|
/* looking for OFDM modem sync */
|
|
if (ofdm->sync_state == search) {
|
|
if (demod_in_is_short)
|
|
ofdm_sync_search_shorts(f->ofdm, (short *)demod_in_8kHz, new_gain);
|
|
else
|
|
ofdm_sync_search(f->ofdm, (COMP *)demod_in_8kHz);
|
|
f->snr_est = -5.0;
|
|
}
|
|
|
|
if ((ofdm->sync_state == synced) || (ofdm->sync_state == trial)) {
|
|
/* OK we have OFDM modem sync */
|
|
rx_status |= FREEDV_RX_SYNC;
|
|
if (ofdm->sync_state == trial) rx_status |= FREEDV_RX_TRIAL_SYNC;
|
|
if (demod_in_is_short)
|
|
ofdm_demod_shorts(ofdm, rx_bits, (short *)demod_in_8kHz, new_gain);
|
|
else
|
|
ofdm_demod(ofdm, rx_bits, (COMP *)demod_in_8kHz);
|
|
|
|
/* accumulate a buffer of data symbols for this packet */
|
|
for (i = 0; i < Nsymsperpacket - Nsymsperframe; i++) {
|
|
rx_syms[i] = rx_syms[i + Nsymsperframe];
|
|
rx_amps[i] = rx_amps[i + Nsymsperframe];
|
|
}
|
|
memcpy(&rx_syms[Nsymsperpacket - Nsymsperframe], ofdm->rx_np,
|
|
sizeof(complex float) * Nsymsperframe);
|
|
memcpy(&rx_amps[Nsymsperpacket - Nsymsperframe], ofdm->rx_amp,
|
|
sizeof(float) * Nsymsperframe);
|
|
|
|
/* look for UW as frames enter packet buffer, note UW may span several modem
|
|
* frames */
|
|
int st_uw = Nsymsperpacket - ofdm->nuwframes * Nsymsperframe;
|
|
ofdm_extract_uw(ofdm, &rx_syms[st_uw], &rx_amps[st_uw], rx_uw);
|
|
|
|
// update some FreeDV API level stats
|
|
f->sync = 1;
|
|
|
|
if (ofdm->modem_frame == (ofdm->np - 1)) {
|
|
/* we have received enough modem frames to complete packet and run LDPC
|
|
* decoder */
|
|
int txt_sym_index = 0;
|
|
ofdm_disassemble_qpsk_modem_packet_with_text_amps(
|
|
ofdm, rx_syms, rx_amps, payload_syms, payload_amps, txt_bits,
|
|
&txt_sym_index);
|
|
|
|
COMP payload_syms_de[Npayloadsymsperpacket];
|
|
float payload_amps_de[Npayloadsymsperpacket];
|
|
gp_deinterleave_comp(payload_syms_de, payload_syms,
|
|
Npayloadsymsperpacket);
|
|
gp_deinterleave_float(payload_amps_de, payload_amps,
|
|
Npayloadsymsperpacket);
|
|
|
|
float llr[Npayloadbitsperpacket];
|
|
uint8_t decoded_codeword[Npayloadbitsperpacket];
|
|
symbols_to_llrs(llr, payload_syms_de, payload_amps_de, EsNo,
|
|
ofdm->mean_amp, Npayloadsymsperpacket);
|
|
ldpc_decode_frame(ldpc, &parityCheckCount, &iter, decoded_codeword, llr);
|
|
// iter = run_ldpc_decoder(ldpc, decoded_codeword, llr,
|
|
// &parityCheckCount);
|
|
memcpy(f->rx_payload_bits, decoded_codeword, Ndatabitsperpacket);
|
|
|
|
if (strlen(ofdm->data_mode)) {
|
|
// we need a valid CRC to declare a data packet valid
|
|
if (freedv_check_crc16_unpacked(f->rx_payload_bits, Ndatabitsperpacket))
|
|
rx_status |= FREEDV_RX_BITS;
|
|
else
|
|
rx_status |= FREEDV_RX_BIT_ERRORS;
|
|
} else {
|
|
// voice modes aren't as strict - pass everything through to the speech
|
|
// decoder, but flag frame with possible errors
|
|
rx_status |= FREEDV_RX_BITS;
|
|
if (parityCheckCount != ldpc->NumberParityBits)
|
|
rx_status |= FREEDV_RX_BIT_ERRORS;
|
|
}
|
|
|
|
if (f->test_frames) {
|
|
/* est uncoded BER from payload bits */
|
|
Nerrs_raw = count_uncoded_errors(
|
|
ldpc, &f->ofdm->config, payload_syms_de, strlen(ofdm->data_mode));
|
|
f->total_bit_errors += Nerrs_raw;
|
|
f->total_bits += Npayloadbitsperpacket;
|
|
|
|
/* coded errors from decoded bits */
|
|
uint8_t payload_data_bits[Ndatabitsperpacket];
|
|
ofdm_generate_payload_data_bits(payload_data_bits, Ndatabitsperpacket);
|
|
if (strlen(ofdm->data_mode)) {
|
|
uint16_t tx_crc16 =
|
|
freedv_crc16_unpacked(payload_data_bits, Ndatabitsperpacket - 16);
|
|
uint8_t tx_crc16_bytes[] = {tx_crc16 >> 8, tx_crc16 & 0xff};
|
|
freedv_unpack(payload_data_bits + Ndatabitsperpacket - 16,
|
|
tx_crc16_bytes, 16);
|
|
}
|
|
Nerrs_coded = count_errors(payload_data_bits, f->rx_payload_bits,
|
|
Ndatabitsperpacket);
|
|
f->total_bit_errors_coded += Nerrs_coded;
|
|
f->total_bits_coded += Ndatabitsperpacket;
|
|
if (Nerrs_coded) f->total_packet_errors++;
|
|
f->total_packets++;
|
|
}
|
|
|
|
/* decode txt bits (if used) */
|
|
for (k = 0; k < f->ofdm_ntxtbits; k++) {
|
|
if (k % 2 == 0 && (f->freedv_put_next_rx_symbol != NULL)) {
|
|
(*f->freedv_put_next_rx_symbol)(f->callback_state_sym,
|
|
rx_syms[txt_sym_index],
|
|
rx_amps[txt_sym_index]);
|
|
txt_sym_index++;
|
|
}
|
|
n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out,
|
|
&txt_bits[k], 1, 1);
|
|
if (n_ascii && (f->freedv_put_next_rx_char != NULL)) {
|
|
(*f->freedv_put_next_rx_char)(f->callback_state, ascii_out);
|
|
}
|
|
}
|
|
|
|
ofdm_get_demod_stats(ofdm, &f->stats, rx_syms, Nsymsperpacket);
|
|
f->snr_est = f->stats.snr_est;
|
|
} /* complete packet */
|
|
|
|
if ((ofdm->np == 1) && (ofdm->modem_frame == 0)) {
|
|
/* add in UW bit errors, useful in non-testframe,
|
|
single modem frame per packet modes */
|
|
for (i = 0; i < f->ofdm_nuwbits; i++) {
|
|
if (rx_uw[i] != ofdm->tx_uw[i]) {
|
|
f->total_bit_errors++;
|
|
}
|
|
}
|
|
f->total_bits += f->ofdm_nuwbits;
|
|
}
|
|
}
|
|
|
|
/* iterate state machine and update nin for next call */
|
|
|
|
f->nin = ofdm_get_nin(ofdm);
|
|
ofdm_sync_state_machine(ofdm, rx_uw);
|
|
|
|
int print_full = 0;
|
|
int print_truncated = 0;
|
|
if (f->verbose &&
|
|
((rx_status & FREEDV_RX_BITS) || (rx_status & FREEDV_RX_BIT_ERRORS)))
|
|
print_full = 1;
|
|
if ((f->verbose == 2) &&
|
|
!((rx_status & FREEDV_RX_BITS) || (rx_status & FREEDV_RX_BIT_ERRORS)))
|
|
print_truncated = 1;
|
|
if (print_full) {
|
|
fprintf(stderr,
|
|
"%3d nin: %4d st: %-6s euw: %2d %2d mf: %2d f: %5.1f pbw: %d snr: "
|
|
"%4.1f eraw: %4d ecdd: %4d iter: %3d "
|
|
"pcc: %4d rxst: %s\n",
|
|
f->frames++, ofdm->nin, ofdm_statemode[ofdm->last_sync_state],
|
|
ofdm->uw_errors, ofdm->sync_counter, ofdm->modem_frame,
|
|
(double)ofdm->foff_est_hz, ofdm->phase_est_bandwidth,
|
|
(double)f->snr_est, Nerrs_raw, Nerrs_coded, iter, parityCheckCount,
|
|
rx_sync_flags_to_text[rx_status]);
|
|
}
|
|
if (print_truncated) {
|
|
fprintf(stderr,
|
|
"%3d nin: %4d st: %-6s euw: %2d %2d mf: %2d f: %5.1f pbw: %d "
|
|
" "
|
|
" rxst: %s\n",
|
|
f->frames++, ofdm->nin, ofdm_statemode[ofdm->last_sync_state],
|
|
ofdm->uw_errors, ofdm->sync_counter, ofdm->modem_frame,
|
|
(double)ofdm->foff_est_hz, ofdm->phase_est_bandwidth,
|
|
rx_sync_flags_to_text[rx_status]);
|
|
}
|
|
|
|
return rx_status;
|
|
}
|