codec2/src/freedv_api.c

1615 lines
55 KiB
C

/*---------------------------------------------------------------------------*\
FILE........: freedv_api.c
AUTHOR......: David Rowe
DATE CREATED: August 2014
Library of API functions that implement the FreeDV API, useful for
embedding FreeDV in other programs. Please see:
1. README_freedv.md
2. Notes on function use in this file
3. Simple demo programs in the "demo" directory
4. The full featured command line freedv_tx.c and freedv_rx.c programs
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2014 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "freedv_api.h"
#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 "fdmdv_internal.h"
#include "fmfsk.h"
#include "freedv_api_internal.h"
#include "freedv_vhf_framing.h"
#include "fsk.h"
#include "gp_interleaver.h"
#include "interldpc.h"
#include "mpdecode_core.h"
#include "ofdm_internal.h"
#include "varicode.h"
/* The API version number. The first version is 10. Increment if the API
changes in a way that would require changes by the API user. */
#define VERSION 15
/*
Version 10 Initial version August 2, 2015.
Version 11 September 2015
Added: freedv_zero_total_bit_errors(), freedv_get_sync()
Changed all input and output sample rates to 8000 sps. Rates
for FREEDV_MODE_700 and 700B were 7500.
Version 12 August 2018 Added OFDM configuration switch structure
Version 13 November 2019 Removed 700 and 700B modes
Version 14 May 2020 Number of returned speech samples can vary, use
freedv_get_n_max_speech_samples() to allocate buffers.
Version 15 December 2022 Removing rarely used DPSK support which is not
needed given fast fading modes
*/
char *ofdm_statemode[] = {"search", "trial", "synced"};
char *rx_sync_flags_to_text[] = {"----", "---T", "--S-", "--ST", "-B--", "-B-T",
"-BS-", "-BST", "E---", "E--T", "E-S-", "E-ST",
"EB--", "EB-T", "EBS-", "EBST"};
/*---------------------------------------------------------------------------* \
FUNCTION....: freedv_open
AUTHOR......: David Rowe
DATE CREATED: 3 August 2014
Call this first to initialise. Returns NULL if initialisation
fails. If a malloc() or calloc() fails in general asserts() will
fire.
\*---------------------------------------------------------------------------*/
struct freedv *freedv_open(int mode) {
// defaults for those modes that support the use of adv
struct freedv_advanced adv = {0, 2, 100, 8000, 1000, 200, "H_256_512_4"};
return freedv_open_advanced(mode, &adv);
}
struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) {
struct freedv *f;
assert(FREEDV_PEAK == OFDM_PEAK);
assert(FREEDV_VARICODE_MAX_BITS == VARICODE_MAX_BITS);
if ((FDV_MODE_ACTIVE(FREEDV_MODE_1600, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700C, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700D, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400A, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) == false)
return NULL;
/* set everything to zero just in case */
f = (struct freedv *)CALLOC(1, sizeof(struct freedv));
if (f == NULL) return NULL;
f->mode = mode;
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, mode)) freedv_1600_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, mode)) freedv_700c_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, mode))
freedv_ofdm_voice_open(f, "700D");
if (FDV_MODE_ACTIVE(FREEDV_MODE_700E, mode))
freedv_ofdm_voice_open(f, "700E");
#ifdef __LPCNET__
if (FDV_MODE_ACTIVE(FREEDV_MODE_2020, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, mode))
freedv_2020x_open(f);
#endif
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, mode)) freedv_2400a_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400B, mode)) freedv_2400b_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, mode)) freedv_800xa_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, mode)) freedv_fsk_ldpc_open(f, adv);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) freedv_ofdm_data_open(f);
varicode_decode_init(&f->varicode_dec_states, 1);
return f;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_close
AUTHOR......: David Rowe
DATE CREATED: 3 August 2014
Call to shut down a freedv instance and free memory.
\*---------------------------------------------------------------------------*/
void freedv_close(struct freedv *freedv) {
assert(freedv != NULL);
FREE(freedv->tx_payload_bits);
FREE(freedv->rx_payload_bits);
if (freedv->codec2) codec2_destroy(freedv->codec2);
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, freedv->mode)) {
FREE(freedv->fdmdv_bits);
FREE(freedv->fdmdv_tx_bits);
FREE(freedv->fdmdv_rx_bits);
fdmdv_destroy(freedv->fdmdv);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, freedv->mode)) {
cohpsk_destroy(freedv->cohpsk);
quisk_filt_destroy(freedv->ptFilter8000to7500);
FREE(freedv->ptFilter8000to7500);
quisk_filt_destroy(freedv->ptFilter7500to8000);
FREE(freedv->ptFilter7500to8000);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, freedv->mode)) {
FREE(freedv->rx_syms);
FREE(freedv->rx_amps);
FREE(freedv->ldpc);
ofdm_destroy(freedv->ofdm);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_2020, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, freedv->mode)) {
FREE(freedv->codeword_symbols);
FREE(freedv->codeword_amps);
FREE(freedv->ldpc);
FREE(freedv->passthrough_2020);
ofdm_destroy(freedv->ofdm);
#ifdef __LPCNET__
lpcnet_freedv_destroy(freedv->lpcnet);
#endif
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, freedv->mode)) {
fsk_destroy(freedv->fsk);
fvhff_destroy_deframer(freedv->deframer);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400B, freedv->mode)) {
fmfsk_destroy(freedv->fmfsk);
fvhff_destroy_deframer(freedv->deframer);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, freedv->mode)) {
fsk_destroy(freedv->fsk);
FREE(freedv->ldpc);
FREE(freedv->frame_llr);
FREE(freedv->twoframes_llr);
FREE(freedv->twoframes_hard);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, freedv->mode)) {
FREE(freedv->rx_syms);
FREE(freedv->rx_amps);
FREE(freedv->ldpc);
ofdm_destroy(freedv->ofdm);
}
FREE(freedv);
}
/* helper function, unpacked bits are much easier to work with inside the modem
*/
static void codec2_encode_upacked(struct freedv *f, uint8_t unpacked_bits[],
short speech_in[]) {
int n_packed = (f->bits_per_codec_frame + 7) / 8;
uint8_t packed_codec_bits[n_packed];
codec2_encode(f->codec2, packed_codec_bits, speech_in);
freedv_unpack(unpacked_bits, packed_codec_bits, f->bits_per_codec_frame);
}
static int is_ofdm_mode(struct freedv *f) {
return FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode);
}
static int is_ofdm_data_mode(struct freedv *f) {
return FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode);
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_tx
AUTHOR......: David Rowe
DATE CREATED: 3 August 2014
Takes a frame of input speech samples, encodes and modulates them to
produce a frame of modem samples that can be sent to the
transmitter. See demo/freedv_700d_tx.c for an example.
speech_in[] is sampled at freedv_get_speech_sample_rate() Hz, and
the user must supply exactly freedv_get_n_speech_samples(). The peak
level should be between +/- 16384 and +/- 32767.
The modem signal mod_out[] is sampled at
freedv_get_modem_sample_rate() and is always exactly
freedv_get_n_nom_modem_samples() long. mod_out[] will be scaled
such that the peak level is around +/-16384.
mod_out[] has a higher RMS power than SSB with the same peak level.
In other words, the crest factor or peak to average power ratio is
lower than typical SSB voice. Ensure your transmitter is capable of
continuous high RMS power operation, or consider reducing Tx power.
\*---------------------------------------------------------------------------*/
/* real-valued short output */
void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) {
assert(f != NULL);
COMP tx_fdm[f->n_nom_modem_samples];
int i;
/* FSK and MEFSK/FMFSK modems work only on real samples. It's simpler to just
* stick them in the real sample tx/rx functions than to add a comp->real
* converter to comptx */
if ((FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode))) {
/* 800XA has two codec frames per modem frame */
if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
codec2_encode(f->codec2, &f->tx_payload_bits[0], &speech_in[0]);
codec2_encode(f->codec2, &f->tx_payload_bits[4], &speech_in[320]);
} else {
codec2_encode(f->codec2, f->tx_payload_bits, speech_in);
}
freedv_tx_fsk_voice(f, mod_out);
} else {
freedv_comptx(f, tx_fdm, speech_in);
for (i = 0; i < f->n_nom_modem_samples; i++) mod_out[i] = tx_fdm[i].real;
}
}
/* complex float output version of freedv_tx() */
void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) {
assert(f != NULL);
assert(FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode));
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode)) {
codec2_encode_upacked(f, f->tx_payload_bits, speech_in);
freedv_comptx_fdmdv_1600(f, mod_out);
}
/* all these modes need to pack a bunch of codec frames into one modem frame
* ... */
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode)) {
for (int j = 0; j < f->n_codec_frames; j++) {
codec2_encode_upacked(f, f->tx_payload_bits + j * f->bits_per_codec_frame,
speech_in);
speech_in += codec2_samples_per_frame(f->codec2);
}
freedv_comptx_700c(f, mod_out);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode)) {
/* buffer up bits until we get enough encoded bits for interleaver */
for (int j = 0; j < f->n_codec_frames; j++) {
int offset = j * f->bits_per_codec_frame;
codec2_encode_upacked(f, f->tx_payload_bits + offset, speech_in);
speech_in += codec2_samples_per_frame(f->codec2);
}
freedv_comptx_ofdm(f, mod_out);
}
#ifdef __LPCNET__
if (FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode)) {
/* buffer up bits until we get enough encoded bits for interleaver */
for (int j = 0; j < f->n_codec_frames; j++) {
int offset = j * f->bits_per_codec_frame;
lpcnet_enc(f->lpcnet, speech_in, (char *)f->tx_payload_bits + offset);
speech_in += lpcnet_samples_per_frame(f->lpcnet);
}
freedv_comptx_2020(f, mod_out);
}
#endif
/* 2400 A and B are handled by the real-mode TX */
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) {
codec2_encode(f->codec2, f->tx_payload_bits, speech_in);
freedv_comptx_fsk_voice(f, mod_out);
}
}
/* pack bits */
void freedv_pack(uint8_t *bytes, uint8_t *bits, int nbits) {
memset(bytes, 0, (nbits + 7) / 8);
int bit = 7, byte = 0;
for (int i = 0; i < nbits; i++) {
bytes[byte] |= bits[i] << bit;
bit--;
if (bit < 0) {
bit = 7;
byte++;
}
}
}
/* unpack bits, MSB first */
void freedv_unpack(uint8_t *bits, uint8_t *bytes, int nbits) {
int bit = 7, byte = 0;
for (int i = 0; i < nbits; i++) {
bits[i] = (bytes[byte] >> bit) & 0x1;
bit--;
if (bit < 0) {
bit = 7;
byte++;
}
}
}
/* compute the CRC16 of a frame of unpacked bits */
unsigned short freedv_crc16_unpacked(unsigned char unpacked_bits[], int nbits) {
assert((nbits % 8) == 0);
int nbytes = nbits / 8;
uint8_t packed_bytes[nbytes];
freedv_pack(packed_bytes, unpacked_bits, nbits);
return freedv_gen_crc16(packed_bytes, nbytes);
}
/* Return non-zero if CRC16 of a frame of unpacked bits is correct */
int freedv_check_crc16_unpacked(unsigned char unpacked_bits[], int nbits) {
assert((nbits % 8) == 0);
int nbytes = nbits / 8;
uint8_t packed_bytes[nbytes];
freedv_pack(packed_bytes, unpacked_bits, nbits);
uint16_t tx_crc16 =
(packed_bytes[nbytes - 2] << 8) | packed_bytes[nbytes - 1];
uint16_t rx_crc16 = freedv_crc16_unpacked(unpacked_bits, nbits - 16);
return tx_crc16 == rx_crc16;
}
/* send raw frames of bytes, or speech data that was compressed externally,
* complex float output */
void freedv_rawdatacomptx(struct freedv *f, COMP mod_out[],
unsigned char *packed_payload_bits) {
assert(f != NULL);
freedv_unpack(f->tx_payload_bits, packed_payload_bits,
f->bits_per_modem_frame);
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode))
freedv_comptx_fdmdv_1600(f, mod_out);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode))
freedv_comptx_700c(f, mod_out);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode))
freedv_comptx_ofdm(f, mod_out);
if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) {
freedv_tx_fsk_ldpc_data(f, mod_out);
}
}
/* send raw frames of bytes, or speech data that was compressed externally, real
* short output */
void freedv_rawdatatx(struct freedv *f, short mod_out[],
unsigned char *packed_payload_bits) {
assert(f != NULL);
COMP mod_out_comp[f->n_nat_modem_samples];
/* Some FSK modes used packed bits, and coincidentally support real samples
* natively */
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
freedv_codec_frames_from_rawdata(f, f->tx_payload_bits,
packed_payload_bits);
freedv_tx_fsk_voice(f, mod_out);
return; /* output is already real */
}
freedv_rawdatacomptx(f, mod_out_comp, packed_payload_bits);
/* convert complex to real */
for (int i = 0; i < f->n_nat_modem_samples; i++)
mod_out[i] = mod_out_comp[i].real;
}
int freedv_rawdatapreamblecomptx(struct freedv *f, COMP mod_out[]) {
assert(f != NULL);
int npreamble_samples = 0;
if (f->mode == FREEDV_MODE_FSK_LDPC) {
struct FSK *fsk = f->fsk;
int npreamble_symbols = 50 * (fsk->mode >> 1);
int npreamble_bits = npreamble_symbols * (fsk->mode >> 1);
npreamble_samples = fsk->Ts * npreamble_symbols;
// fprintf(stderr, "npreamble_symbols: %d npreamble_bits: %d
// npreamble_samples: %d Nbits: %d N: %d\n", npreamble_symbols,
// npreamble_bits, npreamble_samples, fsk->Nbits, fsk->N);
assert(npreamble_samples <
f->n_nom_modem_samples); /* caller probably using an array of this
size */
freedv_tx_fsk_ldpc_data_preamble(f, mod_out, npreamble_bits,
npreamble_samples);
} else if (is_ofdm_data_mode(f)) {
struct OFDM *ofdm = f->ofdm;
complex float *tx_preamble = (complex float *)mod_out;
memcpy(tx_preamble, ofdm->tx_preamble,
sizeof(COMP) * ofdm->samplesperframe);
ofdm_hilbert_clipper(ofdm, tx_preamble, ofdm->samplesperframe);
npreamble_samples = ofdm->samplesperframe;
}
return npreamble_samples;
}
int freedv_rawdatapreambletx(struct freedv *f, short mod_out[]) {
assert(f != NULL);
COMP mod_out_comp[f->n_nat_modem_samples];
int npreamble_samples = freedv_rawdatapreamblecomptx(f, mod_out_comp);
assert(npreamble_samples <= f->n_nat_modem_samples);
/* convert complex to real */
for (int i = 0; i < npreamble_samples; i++) mod_out[i] = mod_out_comp[i].real;
return npreamble_samples;
}
int freedv_rawdatapostamblecomptx(struct freedv *f, COMP mod_out[]) {
assert(f != NULL);
int npostamble_samples = 0;
if (is_ofdm_data_mode(f)) {
struct OFDM *ofdm = f->ofdm;
complex float *tx_postamble = (complex float *)mod_out;
memcpy(tx_postamble, ofdm->tx_postamble,
sizeof(COMP) * ofdm->samplesperframe);
ofdm_hilbert_clipper(ofdm, tx_postamble, ofdm->samplesperframe);
npostamble_samples = ofdm->samplesperframe;
}
return npostamble_samples;
}
int freedv_rawdatapostambletx(struct freedv *f, short mod_out[]) {
assert(f != NULL);
COMP mod_out_comp[f->n_nat_modem_samples];
int npostamble_samples = freedv_rawdatapostamblecomptx(f, mod_out_comp);
assert(npostamble_samples <= f->n_nat_modem_samples);
/* convert complex to real */
for (int i = 0; i < npostamble_samples; i++)
mod_out[i] = mod_out_comp[i].real;
return npostamble_samples;
}
/* VHF packet data tx function */
void freedv_datatx(struct freedv *f, short mod_out[]) {
assert(f != NULL);
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
freedv_tx_fsk_data(f, mod_out);
}
}
/* VHF packet data: returns how many tx frames are queued up but not sent yet */
int freedv_data_ntxframes(struct freedv *f) {
assert(f != NULL);
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) {
if (f->deframer->fdc)
return freedv_data_get_n_tx_frames(f->deframer->fdc, 8);
} else if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
if (f->deframer->fdc)
return freedv_data_get_n_tx_frames(f->deframer->fdc, 6);
}
return 0;
}
int freedv_nin(struct freedv *f) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode))
// For mode 700C, the input rate is 8000 sps, but the modem rate is 7500 sps
// For mode 700C, we request a larger number of Rx samples that will be
// decimated to f->nin samples
return (16 * f->nin + f->ptFilter8000to7500->decim_index) / 15;
else
return f->nin;
}
int freedv_codec_frames_from_rawdata(struct freedv *f,
unsigned char *codec_frames,
unsigned char *rawdata) {
int cbit = 7;
int cbyte = 0;
int rbit = 7;
int rbyte = 0;
int modem_bits = freedv_get_bits_per_modem_frame(f);
int codec_bits = freedv_get_bits_per_codec_frame(f);
int nr_cbits = 0;
int i;
codec_frames[0] = 0;
for (i = 0; i < modem_bits; i++) {
codec_frames[cbyte] |= ((rawdata[rbyte] >> rbit) & 1) << cbit;
rbit--;
if (rbit < 0) {
rbit = 7;
rbyte++;
}
cbit--;
if (cbit < 0) {
cbit = 7;
cbyte++;
codec_frames[cbyte] = 0;
}
nr_cbits++;
if (nr_cbits == codec_bits) {
if (cbit) {
cbyte++;
codec_frames[cbyte] = 0;
}
cbit = 7;
nr_cbits = 0;
}
}
return f->n_codec_frames;
}
int freedv_rawdata_from_codec_frames(struct freedv *f, unsigned char *rawdata,
unsigned char *codec_frames) {
int cbit = 7;
int cbyte = 0;
int rbit = 7;
int rbyte = 0;
int modem_bits = freedv_get_bits_per_modem_frame(f);
int codec_bits = freedv_get_bits_per_codec_frame(f);
int nr_cbits = 0;
int i;
rawdata[rbyte] = 0;
for (i = 0; i < modem_bits; i++) {
rawdata[rbyte] |= ((codec_frames[cbyte] >> cbit) & 1) << rbit;
rbit--;
if (rbit < 0) {
rbit = 7;
rbyte++;
rawdata[rbyte] = 0;
}
cbit--;
if (cbit < 0) {
cbit = 7;
cbyte++;
}
nr_cbits++;
if (nr_cbits == codec_bits) {
if (cbit) cbyte++;
cbit = 7;
nr_cbits = 0;
}
}
return f->n_codec_frames;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_rx
AUTHOR......: David Rowe
DATE CREATED: 3 August 2014
Takes samples from the radio receiver and decodes
them, producing a frame of decoded speech samples. See
demo/freedv_700d_rx.c for an example.
demod_in[] is an array of received samples sampled at
freedv_get_modem_sample_rate(). To account for difference in the
transmit and receive sample clock frequencies, the number of
demod_in[] samples is time varying. You MUST call freedv_nin()
BEFORE EACH call to freedv_rx() and pass exactly that many samples
to this function:
short demod_in[freedv_get_n_max_modem_samples(f)];
short speech_out[freedv_get_n_max_speech_samples(f)];
nin = freedv_nin(f); // num input samples for first read
while(fread(demod_in, sizeof(short), nin, fin) == nin) {
nout = freedv_rx(f, speech_out, demod_in);
fwrite(speech_out, sizeof(short), nout, fout);
nin = freedv_nin(f); // num input samples for next read
}
To help set your buffer sizes, The maximum value of freedv_nin() is
freedv_get_n_max_modem_samples().
freedv_rx() returns the number of output speech samples available in
speech_out[], which is sampled at freedv_get_speech_sample_rate().
You should ALWAYS check the return value of freedv_rx(), and read
EXACTLY that number of speech samples from speech_out[].
Not every call to freedv_rx will return speech samples; in some
modes several modem frames are processed before speech samples are
returned. When squelch is active, zero samples may be returned.
The peak level of demod_in[] is not critical, as the demod works
well over a wide range of amplitude scaling. However avoid clipping
(overload, or samples pinned to +/- 32767). speech_out[] will peak
at just less than +/-32767.
When squelch is disabled, this function echoes the demod_in[]
samples to speech_out[]. This allows the user to listen to the
channel, which is useful for tuning FreeDV signals or reception of
non-FreeDV signals.
\*---------------------------------------------------------------------------*/
int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) {
assert(f != NULL);
int i;
int nin = freedv_nin(f);
f->nin_prev = nin;
assert(nin <= f->n_max_modem_samples);
/* FSK Rx happens in real floats, so convert to those and call their demod
* here */
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
float rx_float[f->n_max_modem_samples];
for (i = 0; i < nin; i++) {
rx_float[i] = ((float)demod_in[i]);
}
return freedv_floatrx(f, speech_out, rx_float);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode)) {
float gain = 1.0f;
assert(nin <= f->n_max_modem_samples);
COMP rx_fdm[f->n_max_modem_samples];
for (i = 0; i < nin; i++) {
rx_fdm[i].real = gain * (float)demod_in[i];
rx_fdm[i].imag = 0.0f;
}
return freedv_comprx(f, speech_out, rx_fdm);
}
/* special low memory version for 700D, to help with stm32 port */
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode)) {
float gain = 2.0f; /* keep levels the same as Octave simulations and C unit
tests for real signals */
return freedv_shortrx(f, speech_out, demod_in, gain);
}
assert(1); /* should never get here */
return 0;
}
/* complex sample input version of freedv_rx() */
int freedv_comprx(struct freedv *f, short speech_out[], COMP demod_in[]) {
assert(f != NULL);
assert(f->nin <= f->n_max_modem_samples);
int rx_status = 0;
f->nin_prev = freedv_nin(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode)) {
rx_status = freedv_comprx_fdmdv_1600(f, demod_in);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode)) {
rx_status = freedv_comprx_700c(f, demod_in);
}
if ((FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode))) {
rx_status = freedv_comprx_fsk(f, demod_in);
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode)) {
rx_status =
freedv_comp_short_rx_ofdm(f, (void *)demod_in, 0, 2.0f); // was 1.0 ??
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode)) {
#ifdef __LPCNET__
rx_status = freedv_comprx_2020(f, demod_in);
#endif
}
short demod_in_short[f->nin_prev];
for (int i = 0; i < f->nin_prev; i++) demod_in_short[i] = demod_in[i].real;
return freedv_bits_to_speech(f, speech_out, demod_in_short, rx_status);
}
/* memory efficient real short version - just for 700D on the SM1000 */
int freedv_shortrx(struct freedv *f, short speech_out[], short demod_in[],
float gain) {
assert(f != NULL);
int rx_status = 0;
f->nin_prev = f->nin;
// At this stage short interface only supported for 700D, to help
// memory requirements on stm32
assert((f->mode == FREEDV_MODE_700D) || (f->mode == FREEDV_MODE_700E));
assert(f->nin <= f->n_max_modem_samples);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode)) {
rx_status = freedv_comp_short_rx_ofdm(f, (void *)demod_in, 1, gain);
}
return freedv_bits_to_speech(f, speech_out, demod_in, rx_status);
}
/* helper function, unpacked bits are much easier to work with inside the modem
*/
static void codec2_decode_upacked(struct freedv *f, short speech_out[],
uint8_t unpacked_bits[]) {
int n_packed = (f->bits_per_codec_frame + 7) / 8;
uint8_t packed_codec_bits[n_packed];
freedv_pack(packed_codec_bits, unpacked_bits, f->bits_per_codec_frame);
codec2_decode(f->codec2, speech_out, packed_codec_bits);
}
/*---------------------------------------------------------------------------* \
FUNCTION....: freedv_rx_bits_to_speech
AUTHOR......: David Rowe
DATE CREATED: May 2020
The *_rx functions takes off air samples, demodulates and (for some
modes) FEC decodes, giving us a frame of bits.
This function captures a lot of tricky logic that has been distilled
through experience:
There may not be a frame of bits returned on every call freedv_*rx* call.
When there are valid bits we need to run the speech decoder.
We may not have demod sync, so various pass through options may happen
with the input samples.
We may squelch based on SNR.
Need to handle various codecs, and varying number of codec frames per modem
frame.
Squelch audio if test frames are being sent.
Determine how many speech samples to return, which will vary if in
sync/out of sync Work with real and complex inputs (complex wrapper)
Attenuate audio on pass through.
Deal with 700D first frame burble, and different sync states from OFDM
modes like 700D.
Output no samples if squelched, we assume it's OK for the audio sink to run
dry.
A FIFO is required on output to smooth sample flow to audio sink.
Don't decode when we are sending test frames
\*---------------------------------------------------------------------------*/
int freedv_bits_to_speech(struct freedv *f, short speech_out[],
short demod_in[], int rx_status) {
int nout = 0;
int decode_speech = 0;
if ((rx_status & FREEDV_RX_SYNC) == 0) {
if (!f->squelch_en) {
/* pass through received samples so we can hear what's going on, e.g.
* during tuning */
if ((f->mode == FREEDV_MODE_2020) || (f->mode == FREEDV_MODE_2020B)) {
/* 8kHz modem sample rate but 16 kHz speech sample
rate, so we need to resample */
nout = 2 * f->nin_prev;
assert(nout <= freedv_get_n_max_speech_samples(f));
float tmp[nout];
for (int i = 0; i < nout / 2; i++)
f->passthrough_2020[FDMDV_OS_TAPS_16K + i] = demod_in[i];
fdmdv_8_to_16(tmp, &f->passthrough_2020[FDMDV_OS_TAPS_16K], nout / 2);
for (int i = 0; i < nout; i++)
speech_out[i] = f->passthrough_gain * tmp[i];
} else {
/* Speech and modem rates might be different */
int rate_factor = f->modem_sample_rate / f->speech_sample_rate;
nout = f->nin_prev / rate_factor;
for (int i = 0; i < nout; i++)
speech_out[i] = f->passthrough_gain * demod_in[i * rate_factor];
}
}
}
if ((rx_status & FREEDV_RX_SYNC) && (rx_status & FREEDV_RX_BITS) &&
!f->test_frames) {
/* following logic is tricky so spell it out clearly, see table
in: https://github.com/drowe67/codec2/pull/111 */
if (!f->squelch_en) {
decode_speech = 1;
} else {
/* squelch is enabled */
/* anti-burble case - don't decode on trial sync unless the
frame has no bit errors. This prevents short lived trial
sync cases generating random bursts of audio */
if (rx_status & FREEDV_RX_TRIAL_SYNC) {
if ((rx_status & FREEDV_RX_BIT_ERRORS) == 0) decode_speech = 1;
} else {
/* sync is solid - decode even through fades as there is still some
* speech info there */
if (f->snr_est > f->snr_squelch_thresh) decode_speech = 1;
}
}
}
if (decode_speech) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode)) {
#ifdef __LPCNET__
/* LPCNet decoder */
int bits_per_codec_frame = lpcnet_bits_per_frame(f->lpcnet);
int data_bits_per_frame = f->ldpc->data_bits_per_frame;
int frames = data_bits_per_frame / bits_per_codec_frame;
nout = f->n_speech_samples;
for (int i = 0; i < frames; i++) {
lpcnet_dec(f->lpcnet,
(char *)f->rx_payload_bits + i * bits_per_codec_frame,
speech_out);
/* ear protection: on frames with errors and clipping, reduce level by
* 12dB */
if (rx_status & FREEDV_RX_BIT_ERRORS) {
int max = 0.0;
for (int j = 0; j < lpcnet_samples_per_frame(f->lpcnet); j++)
if (abs(speech_out[j]) > max) max = abs(speech_out[j]);
if (max == 32767)
for (int j = 0; j < lpcnet_samples_per_frame(f->lpcnet); j++)
speech_out[j] *= 0.25;
}
speech_out += lpcnet_samples_per_frame(f->lpcnet);
}
#endif
} else {
/* codec 2 decoder */
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode)) {
nout = f->n_speech_samples;
for (int i = 0; i < f->n_codec_frames; i++) {
codec2_decode_upacked(
f, speech_out, f->rx_payload_bits + i * f->bits_per_codec_frame);
speech_out += codec2_samples_per_frame(f->codec2);
}
} else {
/* non-interleaved Codec 2 modes */
nout = f->n_speech_samples;
if ((FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)))
codec2_decode(f->codec2, speech_out, f->rx_payload_bits);
else if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
codec2_decode(f->codec2, &speech_out[0], &f->rx_payload_bits[0]);
codec2_decode(f->codec2, &speech_out[320], &f->rx_payload_bits[4]);
} else {
for (int i = 0; i < f->n_codec_frames; i++) {
codec2_decode_upacked(
f, speech_out,
f->rx_payload_bits + i * f->bits_per_codec_frame);
speech_out += codec2_samples_per_frame(f->codec2);
}
}
}
}
}
if (f->verbose == 3) {
fprintf(stderr, " sqen: %d nout: %d decsp: %d\n", f->squelch_en, nout,
decode_speech);
}
f->rx_status = rx_status;
assert(nout <= freedv_get_n_max_speech_samples(f));
return nout;
}
/* a way to receive raw frames of bytes, or speech data that will be
* decompressed externally */
int freedv_rawdatarx(struct freedv *f, unsigned char *packed_payload_bits,
short demod_in[]) {
assert(f != NULL);
int nin = freedv_nin(f);
assert(nin <= f->n_max_modem_samples);
COMP demod_in_comp[f->n_max_modem_samples];
for (int i = 0; i < nin; i++) {
demod_in_comp[i].real = (float)demod_in[i];
demod_in_comp[i].imag = 0.0;
}
return freedv_rawdatacomprx(f, packed_payload_bits, demod_in_comp);
}
/* a way to receive raw frames of bytes, or speech data that will be
* decompressed externally */
int freedv_rawdatacomprx(struct freedv *f, unsigned char *packed_payload_bits,
COMP demod_in[]) {
assert(f != NULL);
int ret = 0;
int rx_status = 0;
/* FSK modes used packed bits internally */
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
rx_status = freedv_comprx_fsk(f, demod_in);
f->rx_status = rx_status;
if (rx_status & FREEDV_RX_BITS) {
ret = (freedv_get_bits_per_modem_frame(f) + 7) / 8;
freedv_rawdata_from_codec_frames(f, packed_payload_bits,
f->rx_payload_bits);
}
return ret;
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode))
rx_status = freedv_comprx_fdmdv_1600(f, demod_in);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode))
rx_status = freedv_comprx_700c(f, demod_in);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode))
rx_status = freedv_comp_short_rx_ofdm(f, (void *)demod_in, 0, 1.0f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) {
rx_status = freedv_rx_fsk_ldpc_data(f, demod_in);
}
if (rx_status & FREEDV_RX_BITS) {
ret = (f->bits_per_modem_frame + 7) / 8;
freedv_pack(packed_payload_bits, f->rx_payload_bits,
f->bits_per_modem_frame);
}
/* might want to check this for errors, e.g. if reliable data is important */
f->rx_status = rx_status;
return ret;
}
/*---------------------------------------------------------------------------* \
FUNCTION....: freedv_get_version
AUTHOR......: Jim Ahlstrom
DATE CREATED: 28 July 2015
Return the version of the FreeDV API. This is meant to help API
users determine when incompatible changes have occurred.
\*---------------------------------------------------------------------------*/
int freedv_get_version(void) { return VERSION; }
/*---------------------------------------------------------------------------* \
FUNCTION....: freedv_get_hash
AUTHOR......: David Rowe
DATE CREATED: July 2020
Return the a string with the Git hash of the repo used to build this code.
\*---------------------------------------------------------------------------*/
static char git_hash[] = GIT_HASH;
char *freedv_get_hash(void) { return git_hash; }
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_set_callback_txt
AUTHOR......: Jim Ahlstrom
DATE CREATED: 28 July 2015
Set the callback functions and the callback state pointer that will
be used for the aux txt channel. The freedv_callback_rx is a
function pointer that will be called to return received characters.
The freedv_callback_tx is a function pointer that will be called to
send transmitted characters. The callback state is a user-defined
void pointer that will be passed to the callback functions. Any or
all can be NULL, and the default is all NULL.
The function signatures are:
void receive_char(void *callback_state, char c);
char transmit_char(void *callback_state);
\*---------------------------------------------------------------------------*/
void freedv_set_callback_txt(struct freedv *f, freedv_callback_rx rx,
freedv_callback_tx tx, void *state) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode) == false) {
f->freedv_put_next_rx_char = rx;
f->freedv_get_next_tx_char = tx;
f->callback_state = state;
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_set_callback_txt_sym
AUTHOR......: Mooneer Salem
DATE CREATED: 19 August 2021
Set the callback functions and the callback state pointer that will
be used to provide the raw symbols for the aux txt channel. The
freedv_callback_rx_sym is a function pointer that will be called to
return received symbols. The callback state is a user-defined
void pointer that will be passed to the callback function. Any or
all can be NULL, and the default is all NULL.
The function signature is:
void receive_sym(void *callback_state, COMP sym, COMP amp);
Note: Active for OFDM modes only (700D/E, 2020).
\*---------------------------------------------------------------------------*/
void freedv_set_callback_txt_sym(struct freedv *f, freedv_callback_rx_sym rx,
void *state) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_700E, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode)) {
f->freedv_put_next_rx_symbol = rx;
f->callback_state_sym = state;
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_set_callback_protocol
AUTHOR......: Brady OBrien
DATE CREATED: 21 February 2016
VHF packet data function.
Set the callback functions and callback pointer that will be used
for the protocol data channel. freedv_callback_protorx will be
called when a frame containing protocol data
arrives. freedv_callback_prototx will be called when a frame
containing protocol information is being generated. Protocol
information is intended to be used to develop protocols and fancy
features atop VHF freedv, much like those present in DMR. Protocol
bits are to be passed in an msb-first char array The number of
protocol bits are findable with freedv_get_protocol_bits
\*---------------------------------------------------------------------------*/
void freedv_set_callback_protocol(struct freedv *f, freedv_callback_protorx rx,
freedv_callback_prototx tx,
void *callback_state) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode) == false) {
f->freedv_put_next_proto = rx;
f->freedv_get_next_proto = tx;
f->proto_callback_state = callback_state;
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_set_callback_datarx / freedv_set_callback_datatx
AUTHOR......: Jeroen Vreeken
DATE CREATED: 04 March 2016
VHF packet data function.
Set the callback functions and callback pointer that will be used
for the data channel. freedv_callback_datarx will be called when a
packet has been successfully received. freedv_callback_data_tx will
be called when transmission of a new packet can begin. If the
returned size of the datatx callback is zero the data frame is still
generated, but will contain only a header update.
\*---------------------------------------------------------------------------*/
void freedv_set_callback_data(struct freedv *f, freedv_callback_datarx datarx,
freedv_callback_datatx datatx,
void *callback_state) {
if ((FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode))) {
if (!f->deframer->fdc) f->deframer->fdc = freedv_data_channel_create();
if (!f->deframer->fdc) return;
freedv_data_set_cb_rx(f->deframer->fdc, datarx, callback_state);
freedv_data_set_cb_tx(f->deframer->fdc, datatx, callback_state);
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_set_data_header
AUTHOR......: Jeroen Vreeken
DATE CREATED: 04 March 2016
VHF packet data function.
Set the data header for the data channel. Header compression will
be used whenever packets from this header are sent. The header will
also be used for fill packets when a data frame is requested without
a packet available.
\*---------------------------------------------------------------------------*/
void freedv_set_data_header(struct freedv *f, unsigned char *header) {
if ((FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) ||
(FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode))) {
if (!f->deframer->fdc) f->deframer->fdc = freedv_data_channel_create();
if (!f->deframer->fdc) return;
freedv_data_set_header(f->deframer->fdc, header);
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: freedv_get_modem_stats
AUTHOR......: Jim Ahlstrom
DATE CREATED: 28 July 2015
Return data from the modem. The arguments are pointers to the data
items. The pointers can be NULL if the data item is not wanted.
\*---------------------------------------------------------------------------*/
void freedv_get_modem_stats(struct freedv *f, int *sync, float *snr_est) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode))
fdmdv_get_demod_stats(f->fdmdv, &f->stats);
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode))
cohpsk_get_demod_stats(f->cohpsk, &f->stats);
if (sync) *sync = f->sync;
if (snr_est) *snr_est = f->snr_est;
}
/*---------------------------------------------------------------------------*\
FUNCTIONS...: freedv_set_*
AUTHOR......: Jim Ahlstrom
DATE CREATED: 28 July 2015
Set some parameters used by FreeDV. It is possible to write a macro
using ## for this, but I wasn't sure it would be 100% portable.
\*---------------------------------------------------------------------------*/
void freedv_set_test_frames(struct freedv *f, int val) { f->test_frames = val; }
void freedv_set_test_frames_diversity(struct freedv *f, int val) {
f->test_frames_diversity = val;
}
void freedv_set_squelch_en(struct freedv *f, bool val) { f->squelch_en = val; }
void freedv_set_total_bit_errors(struct freedv *f, int val) {
f->total_bit_errors = val;
}
void freedv_set_total_bits(struct freedv *f, int val) { f->total_bits = val; }
void freedv_set_total_bit_errors_coded(struct freedv *f, int val) {
f->total_bit_errors_coded = val;
}
void freedv_set_total_bits_coded(struct freedv *f, int val) {
f->total_bits_coded = val;
}
void freedv_set_total_packet_errors(struct freedv *f, int val) {
f->total_packet_errors = val;
}
void freedv_set_total_packets(struct freedv *f, int val) {
f->total_packets = val;
}
void freedv_set_varicode_code_num(struct freedv *f, int val) {
varicode_set_code_num(&f->varicode_dec_states, val);
}
void freedv_set_ext_vco(struct freedv *f, int val) { f->ext_vco = val; }
void freedv_set_snr_squelch_thresh(struct freedv *f, float val) {
f->snr_squelch_thresh = val;
}
void freedv_set_tx_amp(struct freedv *f, float amp) { f->tx_amp = amp; }
void freedv_passthrough_gain(struct freedv *f, float g) {
f->passthrough_gain = g;
}
/* supported by 700C, 700D, 700E */
void freedv_set_clip(struct freedv *f, bool val) {
f->clip_en = val;
if (is_ofdm_mode(f)) {
f->ofdm->clip_en = val;
/* really should have BPF if we clip */
if (val) ofdm_set_tx_bpf(f->ofdm, true);
}
}
/* Band Pass Filter to cleanup OFDM tx waveform, only supported by some modes */
void freedv_set_tx_bpf(struct freedv *f, int val) {
if (is_ofdm_mode(f)) {
ofdm_set_tx_bpf(f->ofdm, val);
}
}
void freedv_set_phase_est_bandwidth_mode(struct freedv *f, int val) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_700D, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode)) {
ofdm_set_phase_est_bandwidth_mode(f->ofdm, val);
}
}
// For those FreeDV modes using the codec 2 700C vocoder 700C/D/E/800XA
void freedv_set_eq(struct freedv *f, bool val) {
if (f->codec2 != NULL) {
codec2_700c_eq(f->codec2, val);
}
}
void freedv_set_verbose(struct freedv *f, int verbosity) {
f->verbose = verbosity;
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode)) {
cohpsk_set_verbose(f->cohpsk, f->verbose);
}
if (is_ofdm_mode(f)) {
ofdm_set_verbose(f->ofdm, f->verbose - 1);
}
}
void freedv_set_callback_error_pattern(struct freedv *f,
freedv_calback_error_pattern cb,
void *state) {
f->freedv_put_error_pattern = cb;
f->error_pattern_callback_state = state;
}
void freedv_set_carrier_ampl(struct freedv *f, int c, float ampl) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode)) {
cohpsk_set_carrier_ampl(f->cohpsk, c, ampl);
}
}
/*---------------------------------------------------------------------------* \
FUNCTIONS...: freedv_set_sync
AUTHOR......: David Rowe
DATE CREATED: May 2018
Extended control of sync state machines for OFDM modes.
Ensure this is called in the same thread as freedv_rx().
\*---------------------------------------------------------------------------*/
void freedv_set_sync(struct freedv *freedv, int sync_cmd) {
assert(freedv != NULL);
if (freedv->ofdm != NULL) {
ofdm_set_sync(freedv->ofdm, sync_cmd);
}
}
// this also selects burst mode
void freedv_set_frames_per_burst(struct freedv *freedv, int framesperburst) {
assert(freedv != NULL);
if (freedv->ofdm != NULL) {
// change of nomenclature as we cross into the OFDM modem layer. In the
// OFDM modem we have packets that contain multiple "modem frames"
ofdm_set_packets_per_burst(freedv->ofdm, framesperburst);
}
}
struct FSK *freedv_get_fsk(struct freedv *f) {
return f->fsk;
}
/*---------------------------------------------------------------------------*\
FUNCTIONS...: freedv_get_*
AUTHOR......: Jim Ahlstrom
DATE CREATED: 28 July 2015
Get some parameters from FreeDV.
\*---------------------------------------------------------------------------*/
int freedv_get_protocol_bits(struct freedv *f) { return f->n_protocol_bits; }
int freedv_get_mode(struct freedv *f) { return f->mode; }
int freedv_get_test_frames(struct freedv *f) { return f->test_frames; }
int freedv_get_speech_sample_rate(struct freedv *f) {
return f->speech_sample_rate;
}
int freedv_get_n_speech_samples(struct freedv *f) {
return f->n_speech_samples;
}
int freedv_get_modem_sample_rate(struct freedv *f) {
return f->modem_sample_rate;
}
int freedv_get_modem_symbol_rate(struct freedv *f) {
return f->modem_symbol_rate;
}
int freedv_get_n_max_modem_samples(struct freedv *f) {
return f->n_max_modem_samples;
}
int freedv_get_n_nom_modem_samples(struct freedv *f) {
return f->n_nom_modem_samples;
}
int freedv_get_n_tx_modem_samples(struct freedv *f) {
return f->n_nat_modem_samples;
}
int freedv_get_total_bits(struct freedv *f) { return f->total_bits; }
int freedv_get_total_bit_errors(struct freedv *f) {
return f->total_bit_errors;
}
int freedv_get_total_bits_coded(struct freedv *f) {
return f->total_bits_coded;
}
int freedv_get_total_bit_errors_coded(struct freedv *f) {
return f->total_bit_errors_coded;
}
int freedv_get_total_packets(struct freedv *f) { return f->total_packets; }
int freedv_get_total_packet_errors(struct freedv *f) {
return f->total_packet_errors;
}
int freedv_get_sync(struct freedv *f) { return f->sync; }
struct CODEC2 *freedv_get_codec2(struct freedv *f) {
return f->codec2;
}
int freedv_get_bits_per_codec_frame(struct freedv *f) {
return f->bits_per_codec_frame;
}
int freedv_get_bits_per_modem_frame(struct freedv *f) {
return f->bits_per_modem_frame;
}
int freedv_get_rx_status(struct freedv *f) { return f->rx_status; }
void freedv_get_fsk_S_and_N(struct freedv *f, float *S, float *N) {
*S = f->fsk_S[0];
*N = f->fsk_N[0];
}
/*---------------------------------------------------------------------------*\
FUNCTIONS...: freedv_set_tuning_range
AUTHOR......: Simon Lang - DJ2LS
DATE CREATED: 18 feb 2022
DEFAULT.....: fmin: -50.0Hz fmax: 50.0Hz
DESCRIPTION.:
|<---fmin - | rx centre frequency | + fmax--->|
Useful for handling frequency offsets,
e.g. caused by an imprecise VFO, the trade off is more CPU power is required.
\*---------------------------------------------------------------------------*/
int freedv_set_tuning_range(struct freedv *freedv, float val_fmin,
float val_fmax) {
if (is_ofdm_data_mode(freedv) &&
(strcmp(freedv->ofdm->data_mode, "burst") == 0)) {
freedv->ofdm->fmin = val_fmin;
freedv->ofdm->fmax = val_fmax;
return 1;
} else {
return 0;
}
}
int freedv_get_n_max_speech_samples(struct freedv *f) {
/* When "passing through" demod samples to the speech output
(e.g. no sync and squelch off) f->nin bounces around with
timing variations. So we may return
freedv_get_n_max_modem_samples() via the speech_output[]
array */
int max_output_passthrough_samples;
if (FDV_MODE_ACTIVE(FREEDV_MODE_2020, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_2020B, f->mode))
// In 2020 we oversample the input modem samples from 8->16 kHz
max_output_passthrough_samples = 2 * freedv_get_n_max_modem_samples(f);
else
max_output_passthrough_samples = freedv_get_n_max_modem_samples(f);
if (max_output_passthrough_samples > f->n_speech_samples)
return max_output_passthrough_samples;
else
return f->n_speech_samples;
}
// Now dummy obsolete call
int freedv_get_sync_interleaver(struct freedv *f) { return 1; }
int freedv_get_sz_error_pattern(struct freedv *f) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode)) {
/* if diversity disabled callback sends error pattern for upper and lower
* carriers */
return f->sz_error_pattern * (2 - f->test_frames_diversity);
} else {
return f->sz_error_pattern;
}
}
// Get modem status, scatter/eye diagram for plotting, other goodies
void freedv_get_modem_extended_stats(struct freedv *f,
struct MODEM_STATS *stats) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_1600, f->mode))
fdmdv_get_demod_stats(f->fdmdv, stats);
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400A, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_800XA, f->mode)) {
fsk_get_demod_stats(f->fsk,
stats); /* eye diagram samples, clock offset etc */
stats->snr_est =
f->snr_est; /* estimated when fsk_demod() called in freedv_fsk.c */
stats->sync = f->sync; /* sync indicator comes from framing layer */
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_2400B, f->mode)) {
fmfsk_get_demod_stats(f->fmfsk, stats);
stats->snr_est = f->snr_est;
stats->sync = f->sync;
}
if (FDV_MODE_ACTIVE(FREEDV_MODE_700C, f->mode)) {
cohpsk_get_demod_stats(f->cohpsk, stats);
}
if (is_ofdm_mode(f)) {
// OFDM modem stats updated when demod runs, so copy last update
// We need to avoid over writing the FFT states which are updated by a
// different function
// TODO we need a better design here: Issue #182
#ifndef __EMBEDDED__
size_t ncopy = (void *)stats->rx_eye - (void *)stats;
memcpy(stats, &f->stats, ncopy);
#endif
stats->snr_est = f->snr_est;
stats->sync = f->sync;
}
}
int freedv_get_n_tx_preamble_modem_samples(struct freedv *f) {
if (f->mode == FREEDV_MODE_FSK_LDPC) {
struct FSK *fsk = f->fsk;
int npreamble_symbols = 50 * (fsk->mode >> 1);
return fsk->Ts * npreamble_symbols;
} else if (is_ofdm_data_mode(f)) {
return f->ofdm->samplesperframe;
}
return 0;
}
int freedv_get_n_tx_postamble_modem_samples(struct freedv *f) {
if (is_ofdm_data_mode(f)) {
return f->ofdm->samplesperframe;
}
return 0;
}
// from
// http://stackoverflow.com/questions/10564491/function-to-calculate-a-crc16-checksum
unsigned short freedv_gen_crc16(unsigned char *data_p, int length) {
unsigned char x;
unsigned short crc = 0xFFFF;
while (length--) {
x = crc >> 8 ^ *data_p++;
x ^= x >> 4;
crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^
((unsigned short)(x << 5)) ^ ((unsigned short)x);
}
return crc;
}