diff --git a/CMakeLists.txt b/CMakeLists.txt index 65dd6c33a..c590030cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -363,6 +363,11 @@ if (BUILD_DEBIAN) add_subdirectory(libsoapysdr) endif (BUILD_DEBIAN) +find_package(Codec2) +if (CODEC2_FOUND) + add_subdirectory(libfreedv) +endif(CODEC2_FOUND) + add_subdirectory(devices) if (BUILD_GUI) add_subdirectory(plugins) diff --git a/libfreedv/CMakeLists.txt b/libfreedv/CMakeLists.txt new file mode 100644 index 000000000..2d9e5bb94 --- /dev/null +++ b/libfreedv/CMakeLists.txt @@ -0,0 +1,43 @@ +project(freedv) + +set(freedv_SOURCES + freedv_api.cpp +) + +set(freedv_HEADERS + codec2_fft.h + codec2_ofdm.h + defines.h + fdmdv_internal.h + fdv_arm_math.h + fmfsk.h + freedv_data_channel.h + freedv_filter.h + freedv_vhf_framing.h + gp_interleaver.h + interldpc.h + kiss_fft.h + kiss_fftr.h + lbfreedv.h + modem_probe.h + mpdecode_core.h + ofdm_internal.h +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CODEC2_INCLUDE_DIR} +) + +add_definitions(-DQT_SHARED) + +add_library(freedv SHARED + ${freedv_SOURCES} +) + +target_link_libraries(freedv + ${CODEC2_LIBRARIES} +) + +install(TARGETS freedv DESTINATION lib) \ No newline at end of file diff --git a/libfreedv/codec2_cohpsk.h b/libfreedv/codec2_cohpsk.h new file mode 100644 index 000000000..f3b086feb --- /dev/null +++ b/libfreedv/codec2_cohpsk.h @@ -0,0 +1,76 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: codec2_cohpsk.h + AUTHOR......: David Rowe + DATE CREATED: March 2015 + + Functions that implement a coherent PSK FDM modem. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2015 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 . +*/ + +#ifndef __CODEC2_COHPSK__ +#define __CODEC2_COHPSK__ + +#define COHPSK_BITS_PER_FRAME 56 /* hard coded for now */ +#define COHPSK_NC 7 /* hard coded for now */ +#define COHPSK_NOM_SAMPLES_PER_FRAME 600 +#define COHPSK_MAX_SAMPLES_PER_FRAME 625 +#define COHPSK_RS 75 +#define COHPSK_FS 7500 /* note this is a wierd + value to get an integer + oversampling rate */ +#define COHPSK_CLIP 6.5 /* hard clipping for Nc*Nc=14 to reduce PAPR */ + +#include "codec2/comp.h" +#include "modem_stats.h" + +namespace FreeDV +{ + +struct COHPSK; + +extern const int test_bits_coh[]; + +struct COHPSK *cohpsk_create(void); +void cohpsk_destroy(struct COHPSK *coh); +void cohpsk_mod(struct COHPSK *cohpsk, COMP tx_fdm[], int tx_bits[], int nbits); +void cohpsk_clip(COMP tx_fdm[], float clip_thresh, int n); +void cohpsk_demod(struct COHPSK *cohpsk, float rx_bits[], int *sync, COMP rx_fdm[], int *nin_frame); +void cohpsk_get_demod_stats(struct COHPSK *cohpsk, struct MODEM_STATS *stats); +void cohpsk_set_verbose(struct COHPSK *coh, int verbose); +void cohpsk_get_test_bits(struct COHPSK *coh, int rx_bits[]); +void cohpsk_put_test_bits(struct COHPSK *coh, int *state, short error_pattern[], + int *bit_errors, char rx_bits[], int channel); +int cohpsk_error_pattern_size(void); +void cohpsk_set_frame(struct COHPSK *coh, int frame); +void fdmdv_freq_shift_coh(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, float Fs, + COMP *foff_phase_rect, int nin); + +void cohpsk_set_freq_est_mode(struct COHPSK *coh, int used_simple_mode); + +/* used for accessing upper and lower bits before diversity combination */ + +float *cohpsk_get_rx_bits_lower(struct COHPSK *coh); +float *cohpsk_get_rx_bits_upper(struct COHPSK *coh); +void cohpsk_set_carrier_ampl(struct COHPSK *coh, int c, float ampl); + +} // FreeDV + +#endif diff --git a/libfreedv/codec2_fdmdv.h b/libfreedv/codec2_fdmdv.h new file mode 100644 index 000000000..1a1f5fb7a --- /dev/null +++ b/libfreedv/codec2_fdmdv.h @@ -0,0 +1,110 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: codec2_fdmdv.h + AUTHOR......: David Rowe + DATE CREATED: April 14 2012 + + A 1400 bit/s (nominal) Frequency Division Multiplexed Digital Voice + (FDMDV) modem. Used for digital audio over HF SSB. See + README_fdmdv.txt for more information, and fdmdv_mod.c and + fdmdv_demod.c for example usage. + + The name codec2_fdmdv.h is used to make it unique when "make + installed". + + References: + + [1] http://n1su.com/fdmdv/FDMDV_Docs_Rel_1_4b.pdf + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2012 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 . +*/ + +#ifndef __FDMDV__ +#define __FDMDV__ + +/* set up the calling convention for DLL function import/export for + WIN32 cross compiling */ + +#ifdef __CODEC2_WIN32__ +#ifdef __CODEC2_BUILDING_DLL__ +#define CODEC2_WIN32SUPPORT __declspec(dllexport) __stdcall +#else +#define CODEC2_WIN32SUPPORT __declspec(dllimport) __stdcall +#endif +#else +#define CODEC2_WIN32SUPPORT +#endif + +#include "codec2/comp.h" +#include "modem_stats.h" + +#define FDMDV_NC 14 /* default number of data carriers */ +#define FDMDV_NC_MAX 20 /* maximum number of data carriers */ +#define FDMDV_BITS_PER_FRAME 28 /* 20ms frames, for nominal 1400 bit/s */ +#define FDMDV_NOM_SAMPLES_PER_FRAME 160 /* modulator output samples/frame and nominal demod samples/frame */ + /* at 8000 Hz sample rate */ +#define FDMDV_MAX_SAMPLES_PER_FRAME 200 /* max demod samples/frame, use this to allocate storage */ +#define FDMDV_SCALE 1000 /* suggested scaling for 16 bit shorts */ +#define FDMDV_FCENTRE 1500 /* Centre frequency, Nc/2 carriers below this, Nc/2 carriers above (Hz) */ + +/* 8 to 48 kHz sample rate conversion */ + +#define FDMDV_OS 2 /* oversampling rate */ +#define FDMDV_OS_TAPS_16K 48 /* number of OS filter taps at 16kHz */ +#define FDMDV_OS_TAPS_8K (FDMDV_OS_TAPS_16K/FDMDV_OS) /* number of OS filter taps at 8kHz */ + +namespace FreeDV +{ + +/* FDMDV states and stats structures */ + +struct FDMDV; + +struct FDMDV * fdmdv_create(int Nc); +void fdmdv_destroy(struct FDMDV *fdmdv_state); +void fdmdv_use_old_qpsk_mapping(struct FDMDV *fdmdv_state); +int fdmdv_bits_per_frame(struct FDMDV *fdmdv_state); +float fdmdv_get_fsep(struct FDMDV *fdmdv_state); +void fdmdv_set_fsep(struct FDMDV *fdmdv_state, float fsep); + +void fdmdv_mod(struct FDMDV *fdmdv_state, COMP tx_fdm[], int tx_bits[], int *sync_bit); +void fdmdv_demod(struct FDMDV *fdmdv_state, int rx_bits[], int *reliable_sync_bit, COMP rx_fdm[], int *nin); + +void fdmdv_get_test_bits(struct FDMDV *fdmdv_state, int tx_bits[]); +int fdmdv_error_pattern_size(struct FDMDV *fdmdv_state); +void fdmdv_put_test_bits(struct FDMDV *f, int *sync, short error_pattern[], int *bit_errors, int *ntest_bits, int rx_bits[]); + +void fdmdv_get_demod_stats(struct FDMDV *fdmdv_state, struct MODEM_STATS *stats); + +void fdmdv_8_to_16(float out16k[], float in8k[], int n); +void fdmdv_8_to_16_short(short out16k[], short in8k[], int n); +void fdmdv_16_to_8(float out8k[], float in16k[], int n); +void fdmdv_16_to_8_short(short out8k[], short in16k[], int n); + +void fdmdv_freq_shift(COMP rx_fdm_fcorr[], COMP rx_fdm[], float foff, COMP *foff_phase_rect, int nin); + +/* debug/development function(s) */ + +void fdmdv_dump_osc_mags(struct FDMDV *f); +void fdmdv_simulate_channel(float *sig_pwr_av, COMP samples[], int nin, float target_snr); + +} // FreeDV + +#endif + diff --git a/libfreedv/codec2_fft.h b/libfreedv/codec2_fft.h new file mode 100644 index 000000000..1e269e1c9 --- /dev/null +++ b/libfreedv/codec2_fft.h @@ -0,0 +1,108 @@ +/* + * codec2_fft.h + * + * Created on: 17.09.2016 + * Author: danilo + */ + +#ifndef DRIVERS_FREEDV_CODEC2_FFT_H_ +#define DRIVERS_FREEDV_CODEC2_FFT_H_ + +#include +#include +#include +#include +#include + +namespace FreeDV +{ + +#ifdef FDV_ARM_MATH + #include "fdv_arm_math.h" +#else + #define USE_KISS_FFT +#endif + +#include "defines.h" +#include "codec2/comp.h" + + +typedef COMP codec2_fft_cpx; +#include "kiss_fftr.h" + +#ifdef USE_KISS_FFT + #include "kiss_fft.h" + typedef kiss_fftr_cfg codec2_fftr_cfg; + typedef kiss_fft_cfg codec2_fft_cfg; + typedef kiss_fft_scalar codec2_fft_scalar; +#else + typedef float32_t codec2_fft_scalar; + typedef struct { + arm_rfft_fast_instance_f32* instance; + int inverse; + } codec2_fftr_struct; + + typedef codec2_fftr_struct* codec2_fftr_cfg; + + typedef struct { + const arm_cfft_instance_f32* instance; + int inverse; + } codec2_fft_struct; + typedef codec2_fft_struct* codec2_fft_cfg; +#endif + + + +static inline void codec2_fftr(codec2_fftr_cfg cfg, codec2_fft_scalar* in, codec2_fft_cpx* out) +{ + +#ifdef USE_KISS_FFT + kiss_fftr(cfg, in, (kiss_fft_cpx*)out); +#else + arm_rfft_fast_f32(cfg->instance,in,(float*)out,cfg->inverse); + out->imag = 0; // remove out[FFT_ENC/2]->real stored in out[0].imag +#endif +} + +static inline void codec2_fftri(codec2_fftr_cfg cfg, codec2_fft_cpx* in, codec2_fft_scalar* out) +{ +#ifdef USE_KISS_FFT + kiss_fftri(cfg, (kiss_fft_cpx*)in, out); +#else + arm_rfft_fast_f32(cfg->instance,(float*)in,out,cfg->inverse); + // arm_scale_f32(out,cfg->instance->fftLenRFFT,out,cfg->instance->fftLenRFFT); +#endif + +} + +codec2_fft_cfg codec2_fft_alloc(int nfft, int inverse_fft, void* mem, size_t* lenmem); +codec2_fftr_cfg codec2_fftr_alloc(int nfft, int inverse_fft, void* mem, size_t* lenmem); +void codec2_fft_free(codec2_fft_cfg cfg); +void codec2_fftr_free(codec2_fftr_cfg cfg); + + +static inline void codec2_fft(codec2_fft_cfg cfg, codec2_fft_cpx* in, codec2_fft_cpx* out) +{ + +#ifdef USE_KISS_FFT + kiss_fft(cfg, (kiss_fft_cpx*)in, (kiss_fft_cpx*)out); +#else + memcpy(out,in,cfg->instance->fftLen*2*sizeof(float)); + arm_cfft_f32(cfg->instance,(float*)out,cfg->inverse,0); + // TODO: this is not nice, but for now required to keep changes minimal + // however, since main goal is to reduce the memory usage + // we should convert to an in place interface + // on PC like platforms the overhead of using the "inplace" kiss_fft calls + // is neglectable compared to the gain in memory usage on STM32 platforms + if (cfg->inverse) + { + arm_scale_f32((float*)out,cfg->instance->fftLen,(float*)out,cfg->instance->fftLen*2); + } +#endif +} + +void codec2_fft_inplace(codec2_fft_cfg cfg, codec2_fft_cpx* inout); + +} // FreeDV + +#endif diff --git a/libfreedv/codec2_ofdm.h b/libfreedv/codec2_ofdm.h new file mode 100644 index 000000000..21a09fbae --- /dev/null +++ b/libfreedv/codec2_ofdm.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: codec2_ofdm.h + AUTHORS.....: David Rowe & Steve Sampson + DATE CREATED: June 2017 + + External user references to the modem library. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2017 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 . +*/ + +#ifndef CODEC2_OFDM_H +#define CODEC2_OFDM_H + +/* Includes */ + +#include +#include +#include + +#include "codec2/comp.h" +#include "modem_stats.h" + +/* Defines */ + +#define OFDM_AMP_SCALE (2E5*1.1491/1.06) /* use to scale to 16 bit short */ +#define OFDM_CLIP (32767*0.35) /* experimentally derived constant to reduce PAPR to about 8dB */ + +namespace FreeDV +{ + +struct OFDM_CONFIG; +struct OFDM; + +typedef enum { + search, + trial, + synced +} State; + +typedef enum { + unsync, /* force sync state machine to lose sync, and search for new sync */ + autosync, /* falls out of sync automatically */ + manualsync /* fall out of sync only under operator control */ +} Sync; + +/* create and destroy modem states */ + +struct OFDM *ofdm_create(const struct OFDM_CONFIG * config); +void ofdm_destroy(struct OFDM *); + +/* signal processing */ + +void ofdm_mod(struct OFDM *, COMP *, const int *); +void ofdm_demod(struct OFDM *, int *, COMP *); +void ofdm_demod_shorts(struct OFDM *, int *, short *, float); +int ofdm_sync_search(struct OFDM *, COMP *); +int ofdm_sync_search_shorts(struct OFDM *, short *, float); +void ofdm_sync_state_machine(struct OFDM *, uint8_t *); + +/* getters */ + +struct OFDM_CONFIG *ofdm_get_config_param(void); +int ofdm_get_nin(struct OFDM *); +int ofdm_get_samples_per_frame(void); +int ofdm_get_max_samples_per_frame(void); +int ofdm_get_bits_per_frame(void); +void ofdm_get_demod_stats(struct OFDM *ofdm, struct MODEM_STATS *stats); + +/* option setters */ + +void ofdm_set_verbose(struct OFDM *, int); +void ofdm_set_timing_enable(struct OFDM *, bool); +void ofdm_set_foff_est_enable(struct OFDM *, bool); +void ofdm_set_phase_est_enable(struct OFDM *, bool); +void ofdm_set_off_est_hz(struct OFDM *, float); +void ofdm_set_sync(struct OFDM *, Sync); +void ofdm_set_tx_bpf(struct OFDM *, bool); + +void ofdm_print_info(struct OFDM *); + +} // FreeDV + +#endif + diff --git a/libfreedv/defines.h b/libfreedv/defines.h new file mode 100644 index 000000000..d30f06807 --- /dev/null +++ b/libfreedv/defines.h @@ -0,0 +1,120 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: defines.h + AUTHOR......: David Rowe + DATE CREATED: 23/4/93 + + Defines and structures used throughout the codec. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2009 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 . +*/ + +#ifndef __DEFINES__ +#define __DEFINES__ + +/*---------------------------------------------------------------------------*\ + + DEFINES + +\*---------------------------------------------------------------------------*/ + +/* General defines */ + +#define N_S 0.01 /* internal proc frame length in secs */ +#define TW_S 0.005 /* trapezoidal synth window overlap */ +#define MAX_AMP 160 /* maximum number of harmonics */ +#ifndef PI +#define PI 3.141592654 /* mathematical constant */ +#endif +#define TWO_PI 6.283185307 /* mathematical constant */ +#define MAX_STR 2048 /* maximum string size */ + +#define FFT_ENC 512 /* size of FFT used for encoder */ +#define FFT_DEC 512 /* size of FFT used in decoder */ +#define V_THRESH 6.0 /* voicing threshold in dB */ +#define LPC_ORD 10 /* LPC order */ +#define LPC_ORD_LOW 6 /* LPC order for lower rates */ + +/* Pitch estimation defines */ + +#define M_PITCH_S 0.0400 /* pitch analysis window in s */ +#define P_MIN_S 0.0025 /* minimum pitch period in s */ +#define P_MAX_S 0.0200 /* maximum pitch period in s */ + +namespace FreeDV +{ + +/*---------------------------------------------------------------------------*\ + + TYPEDEFS + +\*---------------------------------------------------------------------------*/ + +/* Structure to hold constants calculated at run time based on sample rate */ + +typedef struct { + int Fs; /* sample rate of this instance */ + int n_samp; /* number of samples per 10ms frame at Fs */ + int max_amp; /* maximum number of harmonics */ + int m_pitch; /* pitch estimation window size in samples */ + int p_min; /* minimum pitch period in samples */ + int p_max; /* maximum pitch period in samples */ + float Wo_min; + float Wo_max; + int nw; /* analysis window size in samples */ + int tw; /* trapezoidal synthesis window overlap */ +} C2CONST; + +/* Structure to hold model parameters for one frame */ + +typedef struct { + float Wo; /* fundamental frequency estimate in radians */ + int L; /* number of harmonics */ + float A[MAX_AMP+1]; /* amplitiude of each harmonic */ + float phi[MAX_AMP+1]; /* phase of each harmonic */ + int voiced; /* non-zero if this frame is voiced */ +} MODEL; + +/* describes each codebook */ + +struct lsp_codebook { + int k; /* dimension of vector */ + int log2m; /* number of bits in m */ + int m; /* elements in codebook */ + const float * cb; /* The elements */ +}; + +extern const struct lsp_codebook lsp_cb[]; +extern const struct lsp_codebook lsp_cbd[]; +extern const struct lsp_codebook lsp_cbvq[]; +extern const struct lsp_codebook lsp_cbjnd[]; +extern const struct lsp_codebook lsp_cbdt[]; +extern const struct lsp_codebook lsp_cbjvm[]; +extern const struct lsp_codebook lsp_cbvqanssi[]; +extern const struct lsp_codebook mel_cb[]; +extern const struct lsp_codebook ge_cb[]; +extern const struct lsp_codebook lspmelvq_cb[]; +extern const struct lsp_codebook newamp1vq_cb[]; +extern const struct lsp_codebook newamp1_energy_cb[]; +extern const struct lsp_codebook newamp2vq_cb[]; +extern const struct lsp_codebook newamp2_energy_cb[]; + +} // FreeDV + +#endif diff --git a/libfreedv/fdmdv_internal.h b/libfreedv/fdmdv_internal.h new file mode 100644 index 000000000..dceb6b222 --- /dev/null +++ b/libfreedv/fdmdv_internal.h @@ -0,0 +1,200 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: fdmdv_internal.h + AUTHOR......: David Rowe + DATE CREATED: April 16 2012 + + Header file for FDMDV internal functions, exposed via this header + file for testing. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2012 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 . +*/ + +#ifndef __FDMDV_INTERNAL__ +#define __FDMDV_INTERNAL__ + +#include "codec2/comp.h" +#include "codec2_fdmdv.h" +#include "codec2_fft.h" + +/*---------------------------------------------------------------------------*\ + + DEFINES + +\*---------------------------------------------------------------------------*/ + +#ifndef PI +#define PI 3.141592654 +#endif +#define FS 8000 /* sample rate in Hz */ +#define T (1.0/FS) /* sample period in seconds */ +#define RS 50 /* symbol rate in Hz */ +#define NC 20 /* max number of data carriers (plus one pilot in the centre) */ +#define NB 2 /* Bits/symbol for QPSK modulation */ +#define RB (NC*RS*NB) /* bit rate */ +#define M_FAC (FS/RS) /* oversampling factor */ +#define NSYM 6 /* number of symbols to filter over */ +#define NFILTER (NSYM*M_FAC) /* size of tx/rx filters at sample rate M */ + +#define FSEP 75 /* Default separation between carriers (Hz) */ + +#define NT 5 /* number of symbols we estimate timing over */ +#define P 4 /* oversample factor used for initial rx symbol filtering output */ +#define Q (M_FAC/4) /* oversample factor used for initial rx symbol filtering input */ +#define NRXDEC 31 /* number of taps in the rx decimation filter */ + +#define NPILOT_LUT (4*M_FAC) /* number of pilot look up table samples */ +#define NPILOTCOEFF 30 /* number of FIR filter coeffs in LP filter */ +#define NPILOTBASEBAND (NPILOTCOEFF+M_FAC+M_FAC/P) /* number of pilot baseband samples reqd for pilot LPF */ +#define NPILOTLPF (4*M_FAC) /* number of samples we DFT pilot over, pilot est window */ +#define MPILOTFFT 256 + +#define NSYNC_MEM 6 + +#define NRX_FDM_MEM (NFILTER+M_FAC+M_FAC/P) /* size of rx filter memory */ +#define NRXDECMEM (NRXDEC+M_FAC+M_FAC/P) /* size of rx decimation filter memory */ + +/* averaging filter coeffs */ + +#define TRACK_COEFF 0.5 +#define SNR_COEFF 0.9 /* SNR est averaging filter coeff */ + +namespace FreeDV +{ + +/*---------------------------------------------------------------------------*\ + + STRUCT for States + +\*---------------------------------------------------------------------------*/ + +struct FDMDV { + + int Nc; + float fsep; + + /* test data (test frame) states */ + + int ntest_bits; + int current_test_bit; + int *rx_test_bits_mem; + + /* Modulator */ + + int old_qpsk_mapping; + int tx_pilot_bit; + COMP prev_tx_symbols[NC+1]; + COMP tx_filter_memory[NC+1][NSYM]; + COMP phase_tx[NC+1]; + COMP freq[NC+1]; + float freq_pol[NC+1]; + + /* Pilot generation at demodulator */ + + COMP pilot_lut[NPILOT_LUT]; + int pilot_lut_index; + int prev_pilot_lut_index; + + /* freq offset estimation states */ + + codec2_fft_cfg fft_pilot_cfg; + COMP pilot_baseband1[NPILOTBASEBAND]; + COMP pilot_baseband2[NPILOTBASEBAND]; + COMP pilot_lpf1[NPILOTLPF]; + COMP pilot_lpf2[NPILOTLPF]; + COMP S1[MPILOTFFT]; + COMP S2[MPILOTFFT]; + + /* baseband to low IF carrier states */ + + COMP fbb_rect; + float fbb_pol; + COMP fbb_phase_tx; + COMP fbb_phase_rx; + + /* freq offset correction states */ + + float foff; + COMP foff_phase_rect; + float foff_filt; + + /* Demodulator */ + + COMP rxdec_lpf_mem[NRXDECMEM]; + COMP rx_fdm_mem[NRX_FDM_MEM]; + COMP phase_rx[NC+1]; + COMP rx_filter_mem_timing[NC+1][NT*P]; + float rx_timing; + COMP phase_difference[NC+1]; + COMP prev_rx_symbols[NC+1]; + + /* sync state machine */ + + int sync_mem[NSYNC_MEM]; + int fest_state; + int sync; + int timer; + + /* SNR estimation states */ + + float sig_est[NC+1]; + float noise_est[NC+1]; + + /* channel simulation */ + + float sig_pwr_av; +}; + +/*---------------------------------------------------------------------------*\ + + FUNCTION PROTOTYPES + +\*---------------------------------------------------------------------------*/ + +void bits_to_dqpsk_symbols(COMP tx_symbols[], int Nc, COMP prev_tx_symbols[], int tx_bits[], int *pilot_bit, int old_qpsk_mapping); +void tx_filter(COMP tx_baseband[NC+1][M_FAC], int Nc, COMP tx_symbols[], COMP tx_filter_memory[NC+1][NSYM]); +void fdm_upconvert(COMP tx_fdm[], int Nc, COMP tx_baseband[NC+1][M_FAC], COMP phase_tx[], COMP freq_tx[], + COMP *fbb_phase, COMP fbb_rect); +void tx_filter_and_upconvert(COMP tx_fdm[], int Nc, COMP tx_symbols[], + COMP tx_filter_memory[NC+1][NSYM], + COMP phase_tx[], COMP freq[], COMP *fbb_phase, COMP fbb_rect); +void generate_pilot_fdm(COMP *pilot_fdm, int *bit, float *symbol, float *filter_mem, COMP *phase, COMP *freq); +void generate_pilot_lut(COMP pilot_lut[], COMP *pilot_freq); +float rx_est_freq_offset(struct FDMDV *f, COMP rx_fdm[], int nin, int do_fft); +void lpf_peak_pick(float *foff, float *max, COMP pilot_baseband[], COMP pilot_lpf[], codec2_fft_cfg fft_pilot_cfg, COMP S[], int nin, int do_fft); +void fdm_downconvert(COMP rx_baseband[NC+1][M_FAC+M_FAC/P], int Nc, COMP rx_fdm[], COMP phase_rx[], COMP freq[], int nin); +void rxdec_filter(COMP rx_fdm_filter[], COMP rx_fdm[], COMP rxdec_lpf_mem[], int nin); +void rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_baseband[NC+1][M_FAC+M_FAC/P], COMP rx_filter_memory[NC+1][NFILTER], int nin); +void down_convert_and_rx_filter(COMP rx_filt[NC+1][P+1], int Nc, COMP rx_fdm[], + COMP rx_fdm_mem[], COMP phase_rx[], COMP freq[], + float freq_pol[], int nin, int dec_rate); +float rx_est_timing(COMP rx_symbols[], int Nc, + COMP rx_filt[NC+1][P+1], + COMP rx_filter_mem_timing[NC+1][NT*P], + float env[], + int nin, + int m); +float qpsk_to_bits(int rx_bits[], int *sync_bit, int Nc, COMP phase_difference[], COMP prev_rx_symbols[], COMP rx_symbols[], int old_qpsk_mapping); +void snr_update(float sig_est[], float noise_est[], int Nc, COMP phase_difference[]); +int freq_state(int *reliable_sync_bit, int sync_bit, int *state, int *timer, int *sync_mem); +float calc_snr(int Nc, float sig_est[], float noise_est[]); + +} // FreeDV + +#endif diff --git a/libfreedv/fdv_arm_math.h b/libfreedv/fdv_arm_math.h new file mode 100644 index 000000000..b50706bac --- /dev/null +++ b/libfreedv/fdv_arm_math.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: fdv_arm_math.h + AUTHOR......: David Rowe + DATE CREATED: Feb 13 2019 + + Bundles access to ARM CORTEX M specific functions which are enabled by + defining FDV_ARM_MATH + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2012 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 . +*/ + +#ifndef __FDV_ARM_MATH__ +#define __FDV_ARM_MATH__ + +#ifdef FDV_ARM_MATH + #include "arm_const_structs.h" + #define SINF(a) arm_sin_f32(a) + #define COSF(a) arm_cos_f32(a) +#else + #define SINF(a) sinf(a) + #define COSF(a) cosf(a) +#endif + +#endif diff --git a/libfreedv/fmfsk.h b/libfreedv/fmfsk.h new file mode 100644 index 000000000..0f7645f89 --- /dev/null +++ b/libfreedv/fmfsk.h @@ -0,0 +1,116 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: fmfsk.h + AUTHOR......: Brady O'Brien + DATE CREATED: 6 February 2016 + + C Implementation of 2FSK+Manchester over FM modulator/demodulator, based + on mancyfsk.m and fmfsk.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 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 . +*/ + +#ifndef __C2FMFSK_H +#define __C2FMFSK_H +#include +#include "codec2/comp.h" + +#include "modem_stats.h" + +#define FMFSK_SCALE 16383 + +namespace FreeDV +{ + +/* + * fm-me-2fsk state + */ +struct FMFSK{ + /* Static fmfsk parameters */ + int Rb; /* Manchester-encoded bitrate */ + int Rs; /* Raw modem symbol rate */ + int Fs; /* Sample rate */ + int Ts; /* Samples-per-symbol */ + int N; /* Sample processing buffer size */ + int nsym; /* Number of raw modem symbols processed per demod call */ + int nbit; /* Number of bits spit out per demod call */ + int nmem; /* Number of samples kept around between demod calls */ + + /* State kept by demod */ + int nin; /* Number of samples to be demod-ed the next cycle */ + int lodd; /* Last integrated sample for odd bitstream generation */ + float * oldsamps; /* Memory of old samples to make clock-offset-tolerance possible */ + + /* Stats generated by demod */ + float norm_rx_timing; /* RX Timing, used to calculate clock offset */ + int ppm; /* Clock offset in parts-per-million */ + float snr_mean; + + /* Modem stat structure */ + struct MODEM_STATS * stats; +}; + +/* + * Create a new fmfsk modem instance. + * + * int Fs - sample rate + * int Rb - non-manchester bitrate + * returns - new struct FMFSK on sucess, NULL on failure + */ +struct FMFSK * fmfsk_create(int Fs,int Rb); + +/* + * Destroys an fmfsk modem and deallocates memory + */ +void fmfsk_destroy(struct FMFSK *fmfsk); + +/* + * Deposit demod statistics into a MODEM_STATS struct + */ +void fmfsk_get_demod_stats(struct FMFSK *fmfsk,struct MODEM_STATS *stats); + +/* + * Returns the number of samples that must be fed to fmfsk_demod the next + * cycle + */ +uint32_t fmfsk_nin(struct FMFSK *fmfsk); + +/* + * Modulates nbit bits into N samples to be sent through an FM radio + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * float mod_out[] - Buffer for N samples of modulated FMFSK + * uint8_t tx_bits[] - Buffer containing Nbits unpacked bits + */ +void fmfsk_mod(struct FMFSK *fmfsk, float fmfsk_out[],uint8_t bits_in[]); + + +/* + * Demodulate some number of FMFSK samples. The number of samples to be + * demodulated can be found by calling fmfsk_nin(). + * + * struct FMFSK *fsk - FMFSK config/state struct, set up by fsk_create + * uint8_t rx_bits[] - Buffer for nbit unpacked bits to be written + * float fsk_in[] - nin samples of modualted FMFSK from an FM radio + */ +void fmfsk_demod(struct FMFSK *fmfsk, uint8_t rx_bits[],float fmfsk_in[]); + +} // FreeDV + +#endif diff --git a/libfreedv/freedv_api.cpp b/libfreedv/freedv_api.cpp new file mode 100644 index 000000000..b856008bd --- /dev/null +++ b/libfreedv/freedv_api.cpp @@ -0,0 +1,2538 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_api.c + AUTHOR......: David Rowe + DATE CREATED: August 2014 + + Library of API functions that implement FreeDV "modes", useful for + embedding FreeDV in other 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef TT +#if defined(__APPLE__) +#include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) +#include +#else +#include +#endif /* __APPLE__ */ +#endif + +#include "fsk.h" +#include "fmfsk.h" +#include "codec2/codec2.h" +#include "codec2_fdmdv.h" +#include "fdmdv_internal.h" +#include "codec2/golay23.h" +#include "codec2/varicode.h" +#include "libfreedv.h" +#include "freedv_api_internal.h" +#include "freedv_vhf_framing.h" +#include "codec2/comp_prim.h" +#include "freedv_filter.h" + +#include "codec2_ofdm.h" +#include "ofdm_internal.h" +#include "mpdecode_core.h" +#include "gp_interleaver.h" +#include "interldpc.h" + +#define VERSION 12 /* 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. */ +/* + * 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 + */ + +/* experimentally derived fudge factors to normalise power across modes */ + +#define NORM_PWR_COHPSK 1.74 +#define NORM_PWR_FSK 0.193 +#define NORM_PWR_OFDM 1.00 + +namespace FreeDV +{ + +static struct OFDM_CONFIG *ofdm_config; + +static int ofdm_bitsperframe; +static int ofdm_nuwbits; +static int ofdm_ntxtbits; + +static const char *statemode[] = { + "search", + "trial", + "synced" +}; + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: freedv_open + AUTHOR......: David Rowe + DATE CREATED: 3 August 2014 + + Call this first to initialise. Returns NULL if initialisation fails + (e.g. out of memory or mode not supported). + +\*---------------------------------------------------------------------------*/ + +struct freedv *freedv_open(int mode) { + return freedv_open_advanced(mode, NULL); +} + +struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) { + struct freedv *f; + int Nc, codec2_mode, nbit, nbyte; + + if ((mode != FREEDV_MODE_1600) && (mode != FREEDV_MODE_700) && + (mode != FREEDV_MODE_700B) && (mode != FREEDV_MODE_2400A) && + (mode != FREEDV_MODE_2400B) && (mode != FREEDV_MODE_800XA) && + (mode != FREEDV_MODE_700C) && (mode != FREEDV_MODE_700D) ) + return NULL; + + f = (struct freedv*) malloc(sizeof(struct freedv)); + if (f == NULL) + return NULL; + + f->mode = mode; + f->verbose = 0; + f->test_frames = f->smooth_symbols = 0; + f->freedv_put_error_pattern = NULL; + f->error_pattern_callback_state = NULL; + f->n_protocol_bits = 0; + f->frames = 0; + + /* Init states for this mode, and set up samples in/out -----------------------------------------*/ + + if (mode == FREEDV_MODE_1600) { + f->snr_squelch_thresh = 2.0; + f->squelch_en = 1; + Nc = 16; + f->tx_sync_bit = 0; + codec2_mode = CODEC2_MODE_1300; + f->fdmdv = fdmdv_create(Nc); + if (f->fdmdv == NULL) + return NULL; + golay23_init(); + f->nin = FDMDV_NOM_SAMPLES_PER_FRAME; + f->n_nom_modem_samples = 2*FDMDV_NOM_SAMPLES_PER_FRAME; + f->n_nat_modem_samples = f->n_nom_modem_samples; + f->n_max_modem_samples = FDMDV_NOM_SAMPLES_PER_FRAME+FDMDV_MAX_SAMPLES_PER_FRAME; + f->modem_sample_rate = FS; + nbit = fdmdv_bits_per_frame(f->fdmdv); + f->fdmdv_bits = (int*) malloc(nbit*sizeof(int)); + if (f->fdmdv_bits == NULL) + return NULL; + nbit = 2*fdmdv_bits_per_frame(f->fdmdv); + f->tx_bits = (int*) malloc(nbit*sizeof(int)); + f->rx_bits = (int*) malloc(nbit*sizeof(int)); + if ((f->tx_bits == NULL) || (f->rx_bits == NULL)) { + if (f->tx_bits != NULL) { + free(f->tx_bits); + f->tx_bits = NULL; + } + if (f->rx_bits != NULL) { + free(f->rx_bits); + f->rx_bits = NULL; + } + return NULL; + } + f->evenframe = 0; + f->sz_error_pattern = fdmdv_error_pattern_size(f->fdmdv); + } + + if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C)) { + f->snr_squelch_thresh = 0.0; + f->squelch_en = 0; + switch(mode) { + case FREEDV_MODE_700: + codec2_mode = CODEC2_MODE_700; + break; + case FREEDV_MODE_700B: + codec2_mode = CODEC2_MODE_700B; + break; + case FREEDV_MODE_700C: + codec2_mode = CODEC2_MODE_700C; + break; + default: + assert(0); + } + + f->cohpsk = cohpsk_create(); + f->nin = 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 * FS / COHPSK_FS; // number of samples after native samples are interpolated to 8000 sps + f->n_max_modem_samples = COHPSK_MAX_SAMPLES_PER_FRAME * FS / COHPSK_FS + 1; + f->modem_sample_rate = FS; /* note wierd sample rate tamed by interpolator */ + f->clip = 1; + nbit = COHPSK_BITS_PER_FRAME; + f->tx_bits = (int*) malloc(nbit*sizeof(int)); + if (f->tx_bits == NULL) + return NULL; + f->sz_error_pattern = cohpsk_error_pattern_size(); + } + + if (mode == FREEDV_MODE_700D) { + /* + TODO: + [ ] how to set up interleaver, prob init time option best, as many arrays depend on it + [ ] clip option? Haven't tried clipping OFDM waveform yet + [ ] support for uncoded and coded error patterns + */ + + f->snr_squelch_thresh = 0.0; + f->squelch_en = 0; + codec2_mode = CODEC2_MODE_700C; + + if ((ofdm_config = (struct OFDM_CONFIG *) calloc(1, sizeof (struct OFDM_CONFIG))) == NULL) { + if (f->tx_bits != NULL) { + free(f->tx_bits); + f->tx_bits = NULL; + } + if (f->rx_bits != NULL) { + free(f->rx_bits); + f->rx_bits = NULL; + } + return NULL; + } + + f->ofdm = ofdm_create(ofdm_config); + free(ofdm_config); + + /* Get a copy of the actual modem config */ + ofdm_config = ofdm_get_config_param(); + + ofdm_bitsperframe = ofdm_get_bits_per_frame(); + ofdm_nuwbits = (ofdm_config->ns - 1) * ofdm_config->bps - ofdm_config->txtbits; + ofdm_ntxtbits = ofdm_config->txtbits; + + f->ldpc = (struct LDPC*) malloc(sizeof(struct LDPC)); + + if (f->ldpc == NULL) { + return NULL; + } + + set_up_hra_112_112(f->ldpc, ofdm_config); + int coded_syms_per_frame = f->ldpc->coded_syms_per_frame; + + if (adv == NULL) { + f->interleave_frames = 1; + } else { + assert((adv->interleave_frames >= 0) && (adv->interleave_frames <= 16)); + f->interleave_frames = adv->interleave_frames; + } + + f->modem_frame_count_tx = f->modem_frame_count_rx = 0; + + f->codeword_symbols = (COMP*) malloc(sizeof(COMP)*f->interleave_frames*coded_syms_per_frame); + + if (f->codeword_symbols == NULL) {return NULL;} + + f->codeword_amps = (float*) malloc(sizeof(float)*f->interleave_frames*coded_syms_per_frame); + + if (f->codeword_amps == NULL) {free(f->codeword_symbols); return NULL;} + + for (int i=0; iinterleave_frames*coded_syms_per_frame; i++) { + f->codeword_symbols[i].real = 0.0; + f->codeword_symbols[i].imag = 0.0; + f->codeword_amps[i] = 0.0; + } + + f->nin = ofdm_get_samples_per_frame(); + f->n_nat_modem_samples = ofdm_get_samples_per_frame(); + f->n_nom_modem_samples = ofdm_get_samples_per_frame(); + f->n_max_modem_samples = ofdm_get_max_samples_per_frame(); + f->modem_sample_rate = ofdm_config->fs; + f->clip = 0; + + nbit = f->sz_error_pattern = ofdm_bitsperframe; + + f->tx_bits = NULL; /* not used for 700D */ + + if (f->interleave_frames > 1) { + /* only allocate this array for interleaver sizes > 1 to save memory on SM1000 port */ + f->mod_out = (COMP*) malloc(sizeof(COMP)*f->interleave_frames*f->n_nat_modem_samples); + if (f->mod_out == NULL) { + if (f->codeword_symbols != NULL) { free(f->codeword_symbols); } + return NULL; + } + + for (int i=0; iinterleave_frames*f->n_nat_modem_samples; i++) { + f->mod_out[i].real = 0.0; + f->mod_out[i].imag = 0.0; + } + } + +#ifndef __EMBEDDED__ + /* tx BPF on by default, can't see any reason we'd want this off */ + ofdm_set_tx_bpf(f->ofdm, 1); +#endif + } + + if ((mode == FREEDV_MODE_2400A) || (mode == FREEDV_MODE_2400B)) { + + /* Set up the C2 mode */ + codec2_mode = CODEC2_MODE_1300; + /* Set the number of protocol bits */ + f->n_protocol_bits = 20; + f->sz_error_pattern = 0; + f->ext_vco = 0; + } + + if (mode == FREEDV_MODE_2400A) { + /* Create the framer|deframer */ + f->deframer = fvhff_create_deframer(FREEDV_VHF_FRAME_A,0); + if(f->deframer == NULL) + return NULL; + + f->fsk = fsk_create_hbr(48000,1200,10,4,1200,1200); + + /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ + f->tx_bits = (int*) malloc(f->fsk->Nbits*sizeof(uint8_t)); + + if(f->fsk == NULL){ + fvhff_destroy_deframer(f->deframer); + return NULL; + } + + f->n_nom_modem_samples = f->fsk->N; + f->n_max_modem_samples = f->fsk->N + (f->fsk->Ts); + f->n_nat_modem_samples = f->fsk->N; + f->nin = fsk_nin(f->fsk); + f->modem_sample_rate = 48000; + f->modem_symbol_rate = 1200; + /* Malloc something to appease freedv_init and freedv_destroy */ + f->codec_bits = (int*) malloc(1); + } + + if (mode == FREEDV_MODE_2400B) { + /* Create the framer|deframer */ + f->deframer = fvhff_create_deframer(FREEDV_VHF_FRAME_A,1); + if(f->deframer == NULL) { + if (f->codec_bits != NULL) { free(f->codec_bits); } + return NULL; + } + + f->fmfsk = fmfsk_create(48000,2400); + + if(f->fmfsk == NULL){ + fvhff_destroy_deframer(f->deframer); + return NULL; + } + /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ + f->tx_bits = (int*) malloc(f->fmfsk->nbit*sizeof(uint8_t)); + + f->n_nom_modem_samples = f->fmfsk->N; + f->n_max_modem_samples = f->fmfsk->N + (f->fmfsk->Ts); + f->n_nat_modem_samples = f->fmfsk->N; + f->nin = fmfsk_nin(f->fmfsk); + f->modem_sample_rate = 48000; + /* Malloc something to appease freedv_init and freedv_destroy */ + f->codec_bits = (int*) malloc(1); + } + + if (mode == FREEDV_MODE_800XA) { + /* Create the framer|deframer */ + f->deframer = fvhff_create_deframer(FREEDV_HF_FRAME_B,0); + if(f->deframer == NULL) + return NULL; + + f->fsk = fsk_create_hbr(8000,400,10,4,800,400); + fsk_set_nsym(f->fsk,32); + + /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ + f->tx_bits = (int*) malloc(f->fsk->Nbits*sizeof(uint8_t)); + + if(f->fsk == NULL){ + fvhff_destroy_deframer(f->deframer); + return NULL; + } + + f->n_nom_modem_samples = f->fsk->N; + f->n_max_modem_samples = f->fsk->N + (f->fsk->Ts); + f->n_nat_modem_samples = f->fsk->N; + f->nin = fsk_nin(f->fsk); + f->modem_sample_rate = 8000; + f->modem_symbol_rate = 400; + + /* Malloc something to appease freedv_init and freedv_destroy */ + f->codec_bits = (int*) malloc(1); + + f->n_protocol_bits = 0; + codec2_mode = CODEC2_MODE_700C; + fsk_stats_normalise_eye(f->fsk, 0); + f->sz_error_pattern = 0; + } + + /* Init test frame states */ + + f->test_frames_diversity = 1; + f->test_frame_sync_state = 0; + f->test_frame_sync_state_upper = 0; + f->total_bits = 0; + f->total_bit_errors = 0; + f->total_bits_coded = 0; + f->total_bit_errors_coded = 0; + + /* Init Codec 2 for this FreeDV mode ----------------------------------------------------*/ + + f->codec2 = codec2_create(codec2_mode); + if (f->codec2 == NULL) + return NULL; + + /* work out how many codec 2 frames per mode frame, and number of + bytes of storage for packed and unpacket bits. TODO: do we really + need to work in packed bits at all? It's messy, chars would probably + be OK.... */ + + if ((mode == FREEDV_MODE_1600) || (mode == FREEDV_MODE_2400A) || (mode == FREEDV_MODE_2400B)) { + f->n_speech_samples = codec2_samples_per_frame(f->codec2); + f->n_codec_bits = codec2_bits_per_frame(f->codec2); + nbit = f->n_codec_bits; + nbyte = (nbit + 7) / 8; + } else if (mode == FREEDV_MODE_800XA) { + f->n_speech_samples = 2*codec2_samples_per_frame(f->codec2); + f->n_codec_bits = codec2_bits_per_frame(f->codec2); + nbit = f->n_codec_bits; + nbyte = (nbit + 7) / 8; + nbyte = nbyte*2; + nbit = 8*nbyte; + f->n_codec_bits = nbit; + } else if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C)) { + f->n_speech_samples = 2*codec2_samples_per_frame(f->codec2); + f->n_codec_bits = 2*codec2_bits_per_frame(f->codec2); + nbit = f->n_codec_bits; + nbyte = 2*((codec2_bits_per_frame(f->codec2) + 7) / 8); + } else /* mode == FREEDV_MODE_700D */ { + + /* should be exactly an integer number ofCodec 2 frames in a OFDM modem frame */ + + assert((f->ldpc->data_bits_per_frame % codec2_bits_per_frame(f->codec2)) == 0); + + int Ncodec2frames = f->ldpc->data_bits_per_frame/codec2_bits_per_frame(f->codec2); + + f->n_speech_samples = Ncodec2frames*codec2_samples_per_frame(f->codec2); + f->n_codec_bits = f->interleave_frames*Ncodec2frames*codec2_bits_per_frame(f->codec2); + nbit = codec2_bits_per_frame(f->codec2); + nbyte = (nbit + 7) / 8; + nbyte = nbyte*Ncodec2frames*f->interleave_frames; + f->nbyte_packed_codec_bits = nbyte; + + //fprintf(stderr, "Ncodec2frames: %d n_speech_samples: %d n_codec_bits: %d nbit: %d nbyte: %d\n", + // Ncodec2frames, f->n_speech_samples, f->n_codec_bits, nbit, nbyte); + + f->packed_codec_bits_tx = (unsigned char*) malloc(nbyte*sizeof(char)); + if (f->packed_codec_bits_tx == NULL) return(NULL); + f->codec_bits = NULL; + } + + f->packed_codec_bits = (unsigned char*) malloc(nbyte*sizeof(char)); + if (f->packed_codec_bits == NULL) return(NULL); + + if (mode == FREEDV_MODE_1600) + f->codec_bits = (int*) malloc(nbit*sizeof(int)); + + if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C)) + f->codec_bits = (int*) malloc(COHPSK_BITS_PER_FRAME*sizeof(int)); + + /* Note: VHF Framer/deframer goes directly from packed codec/vc/proto bits to filled frame */ + + if (f->packed_codec_bits == NULL) { + if (f->codec_bits != NULL) {free(f->codec_bits); f->codec_bits = NULL; } + return NULL; + } + + /* Sample rate conversion for modes using COHPSK */ + + if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C) ) { + 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)); + } else { + f->ptFilter7500to8000 = NULL; + f->ptFilter8000to7500 = NULL; + } + + /* Varicode low bit rate text states */ + + varicode_decode_init(&f->varicode_dec_states, 1); + f->nvaricode_bits = 0; + f->varicode_bit_index = 0; + f->freedv_get_next_tx_char = NULL; + f->freedv_put_next_rx_char = NULL; + f->freedv_put_next_proto = NULL; + f->freedv_get_next_proto = NULL; + f->total_bit_errors = 0; + + return f; +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: freedv_close + AUTHOR......: David Rowe + DATE CREATED: 3 August 2014 + + Frees up memory. + +\*---------------------------------------------------------------------------*/ + +void freedv_close(struct freedv *freedv) { + assert(freedv != NULL); + + free(freedv->packed_codec_bits); + free(freedv->codec_bits); + free(freedv->tx_bits); + if (freedv->mode == FREEDV_MODE_1600) + fdmdv_destroy(freedv->fdmdv); + if ((freedv->mode == FREEDV_MODE_700) || (freedv->mode == FREEDV_MODE_700B) || (freedv->mode == FREEDV_MODE_700C)) + cohpsk_destroy(freedv->cohpsk); + if (freedv->mode == FREEDV_MODE_700D) { + free(freedv->packed_codec_bits_tx); + if (freedv->interleave_frames > 1) + free(freedv->mod_out); + free(freedv->codeword_symbols); + free(freedv->codeword_amps); + free(freedv->ldpc); + ofdm_destroy(freedv->ofdm); + } + if ((freedv->mode == FREEDV_MODE_2400A) || (freedv->mode == FREEDV_MODE_800XA)){ + fsk_destroy(freedv->fsk); + fvhff_destroy_deframer(freedv->deframer); + } + + if (freedv->mode == FREEDV_MODE_2400B){ + fmfsk_destroy(freedv->fmfsk); + fvhff_destroy_deframer(freedv->deframer); + } + + codec2_destroy(freedv->codec2); + if (freedv->ptFilter8000to7500) { + quisk_filt_destroy(freedv->ptFilter8000to7500); + free(freedv->ptFilter8000to7500); + freedv->ptFilter8000to7500 = NULL; + } + if (freedv->ptFilter7500to8000) { + quisk_filt_destroy(freedv->ptFilter7500to8000); + free(freedv->ptFilter7500to8000); + freedv->ptFilter7500to8000 = NULL; + } + free(freedv); +} + +/*---------------------------------------------------------------------------*\ + + 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 freedv_tx.c for an example. + + speech_in[] is sampled at 8000 Hz, and the user must supply a block + of exactly freedv_get_n_speech_samples(). The speech_in[] level + should be such that the peak speech level is between +/- 16384 and + +/- 32767. + + The FDM modem signal mod_out[] is sampled at 8000 Hz and is + freedv_get_n_nom_modem_samples() long. mod_out[] will be scaled + such that the peak level is just less than +/-32767. + + The complex-valued output can directly drive an I/Q modulator to + produce a single sideband signal. To generate the other sideband, + take the complex conjugate of mod_out[]. + + The FreeDV 1600 modem has a high crest factor (around 12dB), however + the energy and duration of the peaks is small. FreeDV 1600 is + usually operated at a "backoff" of 8dB. Adjust the power amplifier + drive so that the average power is 8dB less than the peak power of + the PA. For example, on a radio rated at 100W PEP for SSB, the + average FreeDV power is typically 20W. + + The FreeDV 700 modem has a crest factor of about 8dB (with + f->clip=1, the default), so if your PA can handle it, it can be + driven harder than FreeDV 1600. Caution - some PAs cannot handle a + high continuous power. A conservative level is 20W average for a + 100W PEP rated PA. + +\*---------------------------------------------------------------------------*/ + +/* real-valued short sample output, useful for going straight to DAC */ + +/* TX routines for 2400 FSK modes, after codec2 encoding */ +static void freedv_tx_fsk_voice(struct freedv *f, short mod_out[]) { + int i; + float *tx_float; /* To hold on to modulated samps from fsk/fmfsk */ + uint8_t vc_bits[2]; /* Varicode bits for 2400 framing */ + uint8_t proto_bits[3]; /* Prococol bits for 2400 framing */ + + /* Frame for 2400A/B */ + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B){ + /* Get varicode bits for TX and possibly ask for a new char */ + /* 2 bits per 2400A/B frame, so this has to be done twice */ + for(i=0;i<2;i++){ + if (f->nvaricode_bits) { + vc_bits[i] = f->tx_varicode_bits[f->varicode_bit_index++]; + f->nvaricode_bits--; + } + + 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, 1); + f->varicode_bit_index = 0; + } + } + } + + /* If the API user hasn't set up message callbacks, don't bother with varicode bits */ + if(f->freedv_get_next_proto != NULL){ + (*f->freedv_get_next_proto)(f->proto_callback_state,(char*)proto_bits); + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),proto_bits,vc_bits); + }else if(f->freedv_get_next_tx_char != NULL){ + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,vc_bits); + }else { + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); + } + /* Frame for 800XA */ + }else if(f->mode == FREEDV_MODE_800XA){ + fvhff_frame_bits(FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); + } + + /* Allocate floating point buffer for FSK mod */ + tx_float = (float*) malloc(sizeof(float)*f->n_nom_modem_samples); + + /* do 4fsk mod */ + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ + if (f->ext_vco) { + fsk_mod_ext_vco(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = (short)tx_float[i]; + } + } + else { + fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = (short)(tx_float[i]*FSK_SCALE*NORM_PWR_FSK); + } + } + /* do me-fsk mod */ + }else if(f->mode == FREEDV_MODE_2400B){ + fmfsk_mod(f->fmfsk,tx_float,(uint8_t*)(f->tx_bits)); + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = (short)(tx_float[i]*FMFSK_SCALE); + } + } + + free(tx_float); +} + +/* TX routines for 2400 FSK modes, after codec2 encoding */ +static void freedv_comptx_fsk_voice(struct freedv *f, COMP mod_out[]) { + int i; + float *tx_float; /* To hold on to modulated samps from fsk/fmfsk */ + uint8_t vc_bits[2]; /* Varicode bits for 2400 framing */ + uint8_t proto_bits[3]; /* Prococol bits for 2400 framing */ + + /* Frame for 2400A/B */ + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B){ + /* Get varicode bits for TX and possibly ask for a new char */ + /* 2 bits per 2400A/B frame, so this has to be done twice */ + for(i=0;i<2;i++){ + if (f->nvaricode_bits) { + vc_bits[i] = f->tx_varicode_bits[f->varicode_bit_index++]; + f->nvaricode_bits--; + } + + 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, 1); + f->varicode_bit_index = 0; + } + } + } + + /* If the API user hasn't set up message callbacks, don't bother with varicode bits */ + if(f->freedv_get_next_proto != NULL){ + (*f->freedv_get_next_proto)(f->proto_callback_state,(char*)proto_bits); + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),proto_bits,vc_bits); + }else if(f->freedv_get_next_tx_char != NULL){ + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,vc_bits); + }else { + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); + } + /* Frame for 800XA */ + }else if(f->mode == FREEDV_MODE_800XA){ + fvhff_frame_bits(FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); + } + + /* Allocate floating point buffer for FSK mod */ + tx_float = (float*) malloc(sizeof(float)*f->n_nom_modem_samples); + + /* do 4fsk mod */ + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ + fsk_mod_c(f->fsk,mod_out,(uint8_t*)(f->tx_bits)); + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = fcmult(NORM_PWR_FSK,mod_out[i]); + } + /* do me-fsk mod */ + }else if(f->mode == FREEDV_MODE_2400B){ + fmfsk_mod(f->fmfsk,tx_float,(uint8_t*)(f->tx_bits)); + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i].real = (tx_float[i]); + } + } + + free(tx_float); +} + + +/* TX routines for 2400 FSK modes, data channel */ +static void freedv_tx_fsk_data(struct freedv *f, short mod_out[]) { + int i; + float *tx_float; /* To hold on to modulated samps from fsk/fmfsk */ + + if (f->mode != FREEDV_MODE_800XA) + fvhff_frame_data_bits(f->deframer, FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits)); + else + fvhff_frame_data_bits(f->deframer, FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits)); + + /* Allocate floating point buffer for FSK mod */ + tx_float = (float*) malloc(sizeof(float)*f->n_nom_modem_samples); + + /* do 4fsk mod */ + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ + fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = (short)(tx_float[i]*FSK_SCALE); + } + /* do me-fsk mod */ + }else if(f->mode == FREEDV_MODE_2400B){ + fmfsk_mod(f->fmfsk,tx_float,(uint8_t*)(f->tx_bits)); + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = (short)(tx_float[i]*FMFSK_SCALE); + } + } + + free(tx_float); +} + +void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { + assert(f != NULL); + COMP *tx_fdm = new COMP[f->n_nom_modem_samples]; + int i; + assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || + (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C) || + (f->mode == FREEDV_MODE_700D) || + (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || + (f->mode == FREEDV_MODE_800XA)); + + /* 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 ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ + /* 800XA has two codec frames per modem frame */ + if(f->mode == FREEDV_MODE_800XA){ + codec2_encode(f->codec2, &f->packed_codec_bits[0], &speech_in[ 0]); + codec2_encode(f->codec2, &f->packed_codec_bits[4], &speech_in[320]); + }else{ + codec2_encode(f->codec2, f->packed_codec_bits, speech_in); + } + freedv_tx_fsk_voice(f, mod_out); + } else{ + freedv_comptx(f, tx_fdm, speech_in); + for(i=0; in_nom_modem_samples; i++) + mod_out[i] = tx_fdm[i].real; + } + + delete[] tx_fdm; +} + +/* complex valued output, useful for suitable for single sided freq shifting */ + +static void freedv_comptx_fdmdv_1600(struct freedv *f, COMP mod_out[]) { + int bit, byte, i, j; + int bits_per_codec_frame, bits_per_modem_frame; + int data, codeword1, data_flag_index; + COMP *tx_fdm = new COMP[f->n_nat_modem_samples]; + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bits_per_modem_frame = fdmdv_bits_per_frame(f->fdmdv); + + /* unpack bits, MSB first */ + + bit = 7; byte = 0; + for(i=0; icodec_bits[i] = (f->packed_codec_bits[byte] >> bit) & 0x1; + bit--; + if (bit < 0) { + bit = 7; + byte++; + } + } + + // spare bit in frame that codec defines. Use this 1 + // bit/frame to send txt messages + + data_flag_index = codec2_get_spare_bit_index(f->codec2); + + if (f->nvaricode_bits) { + f->codec_bits[data_flag_index] = f->tx_varicode_bits[f->varicode_bit_index++]; + f->nvaricode_bits--; + } + + 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, 1); + f->varicode_bit_index = 0; + } + } + + /* Protect first 12 out of first 16 excitation bits with (23,12) Golay Code: + + 0,1,2,3: v[0]..v[1] + 4,5,6,7: MSB of pitch + 11,12,13,14: MSB of energy + + */ + + data = 0; + for(i=0; i<8; i++) { + data <<= 1; + data |= f->codec_bits[i]; + } + for(i=11; i<15; i++) { + data <<= 1; + data |= f->codec_bits[i]; + } + codeword1 = golay23_encode(data); + + /* now pack output frame with parity bits at end to make them + as far apart as possible from the data they protect. Parity + bits are LSB of the Golay codeword */ + + for(i=0; itx_bits[i] = f->codec_bits[i]; + for(j=0; itx_bits[i] = (codeword1 >> (10-j)) & 0x1; + } + f->tx_bits[i] = 0; /* spare bit */ + + /* optionally overwrite with test frames */ + + if (f->test_frames) { + fdmdv_get_test_bits(f->fdmdv, f->tx_bits); + fdmdv_get_test_bits(f->fdmdv, &f->tx_bits[bits_per_modem_frame]); + //fprintf(stderr, "test frames on tx\n"); + } + + /* modulate even and odd frames */ + + fdmdv_mod(f->fdmdv, tx_fdm, f->tx_bits, &f->tx_sync_bit); + assert(f->tx_sync_bit == 1); + + fdmdv_mod(f->fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &f->tx_bits[bits_per_modem_frame], &f->tx_sync_bit); + assert(f->tx_sync_bit == 0); + + assert(2*FDMDV_NOM_SAMPLES_PER_FRAME == f->n_nom_modem_samples); + + for(i=0; in_nom_modem_samples; i++) + mod_out[i] = fcmult(FDMDV_SCALE, tx_fdm[i]); + + delete[] tx_fdm; +} + +static void freedv_comptx_700(struct freedv *f, COMP mod_out[]) { + int bit, byte, i, j, k; + int bits_per_codec_frame, bits_per_modem_frame; + int data_flag_index, nspare; + COMP *tx_fdm = new COMP[f->n_nat_modem_samples]; + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bits_per_modem_frame = COHPSK_BITS_PER_FRAME; + + byte = 0; + for (j=0; jcodec_bits[j+i] = (f->packed_codec_bits[byte] >> bit) & 0x1; + bit--; + if (bit < 0) { + bit = 7; + byte++; + } + } + if (bit != 7) + byte++; + + // spare bits in frame that codec defines. Use these spare + // bits/frame to send txt messages + + switch(f->mode) { + case FREEDV_MODE_700: + nspare = 2; + break; + case FREEDV_MODE_700B: + nspare = 1; // Just one spare bit for FREEDV_MODE_700B + break; + case FREEDV_MODE_700C: + nspare = 0; // and no spare bits for 700C atm + break; + default: + nspare = 0; + assert(0); + } + + data_flag_index = codec2_get_spare_bit_index(f->codec2); + + for(k=0; knvaricode_bits) { + f->codec_bits[j+data_flag_index+k] = f->tx_varicode_bits[f->varicode_bit_index++]; + //fprintf(stderr, "%d %d\n", j+data_flag_index+k, f->codec_bits[j+data_flag_index+k]); + f->nvaricode_bits--; + } + 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, 1); + f->varicode_bit_index = 0; + } + } + } + } + /* optionally ovwerwrite the codec bits with test frames */ + + if (f->test_frames) { + cohpsk_get_test_bits(f->cohpsk, f->codec_bits); + } + + /* cohpsk modulator */ + + cohpsk_mod(f->cohpsk, tx_fdm, f->codec_bits, COHPSK_BITS_PER_FRAME); + if (f->clip) + cohpsk_clip(tx_fdm, COHPSK_CLIP, COHPSK_NOM_SAMPLES_PER_FRAME); + for(i=0; in_nat_modem_samples; i++) + mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR_COHPSK, tx_fdm[i]); + i = quisk_cfInterpDecim((std::complex *)mod_out, f->n_nat_modem_samples, f->ptFilter7500to8000, 16, 15); + //assert(i == f->n_nom_modem_samples); + // Caution: assert fails if f->n_nat_modem_samples * 16.0 / 15.0 is not an integer + + delete[] tx_fdm; + +} + +/* + Ok so when interleaved, we take the interleaver length of input samples, + and output that many modem samples, e.g. for interleaver of length 4: + + record input speech 1234 + freedv tx | + play modem sig 1234 + record modem sig 1234 + freedv_rx | + play output speech 1234 + time axis --------->123456789012----> + + So a sample of input speech at time 1 is ouput at time 9. We assume + the freedv_tx and freedv_rx and propogation time over channel (from + when a modem signal is played at the HF tx to when it is recorded at + the HF rx) is zero. + + The freedv tx interface ouputs n_nom_modem_samples, which a single + OFDM modem frame, 112 payload bits or 4 speech codec frames. So + this function must always have 1280 speech samples as input, and + 1280 modem samples as output, regradless of interleaver_frames. For + interleaver_frames > 1, we need to buffer samples. +*/ + +static void freedv_comptx_700d(struct freedv *f, COMP mod_out[]) { + int bit, byte, i, j, k; + int nspare; + + int data_bits_per_frame = f->ldpc->data_bits_per_frame; + int bits_per_interleaved_frame = f->interleave_frames*data_bits_per_frame; + uint8_t *tx_bits = new uint8_t[bits_per_interleaved_frame]; + int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + + byte = 0; + for (j=0; jpacked_codec_bits_tx[byte] >> bit) & 0x1; + bit--; + if (bit < 0) { + bit = 7; + byte++; + } + } + if (bit != 7) + byte++; + } + + assert(byte <= f->nbyte_packed_codec_bits); + + // Generate Varicode txt bits. Txt bits in OFDM frame come just + // after Unique Word (UW). Txt bits aren't protected by FEC, and need to be + // added to each frame after interleaver as done it's thing + + nspare = ofdm_ntxtbits*f->interleave_frames; + uint8_t *txt_bits = new uint8_t[nspare]; + + for(k=0; knvaricode_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, 1); + f->varicode_bit_index = 0; + } + } + if (f->nvaricode_bits) { + txt_bits[k] = f->tx_varicode_bits[f->varicode_bit_index++]; + f->nvaricode_bits--; + } + } + + /* optionally replace codec payload bits with test frames known to rx */ + + if (f->test_frames) { + uint8_t *payload_data_bits = new uint8_t[data_bits_per_frame]; + ofdm_generate_payload_data_bits(payload_data_bits, data_bits_per_frame); + + for (j=0; jinterleave_frames; j++) { + for(i=0; i *tx_sams = new std::complex[f->interleave_frames*f->n_nat_modem_samples]; + COMP asam; + + ofdm_ldpc_interleave_tx(f->ofdm, f->ldpc, tx_sams, tx_bits, txt_bits, f->interleave_frames, ofdm_config); + + for(i=0; iinterleave_frames*f->n_nat_modem_samples; i++) { + asam.real = tx_sams[i].real(); + asam.imag = tx_sams[i].imag(); + mod_out[i] = fcmult(OFDM_AMP_SCALE*NORM_PWR_OFDM, asam); + } + + if (f->clip) { + //fprintf(stderr, "clip "); + cohpsk_clip(mod_out, OFDM_CLIP, f->interleave_frames*f->n_nat_modem_samples); + } + + delete[] tx_sams; + delete[] txt_bits; + delete[] tx_bits; +} + + + +void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { + assert(f != NULL); + + assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || + (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C) || + (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || + (f->mode == FREEDV_MODE_700D)); + + if (f->mode == FREEDV_MODE_1600) { + codec2_encode(f->codec2, f->packed_codec_bits, speech_in); + freedv_comptx_fdmdv_1600(f, mod_out); + } + + + int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + int bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + int i,j; + + /* all these modes need to pack a bunch of codec frames into one modem frame */ + + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { + int codec_frames = f->n_codec_bits / bits_per_codec_frame; + + for (j=0; jcodec2, f->packed_codec_bits + j * bytes_per_codec_frame, speech_in); + speech_in += codec2_samples_per_frame(f->codec2); + } + freedv_comptx_700(f, mod_out); + } + + /* special treatment due to interleaver */ + + if (f->mode == FREEDV_MODE_700D) { + int data_bits_per_frame = f->ldpc->data_bits_per_frame; + int codec_frames = data_bits_per_frame / bits_per_codec_frame; + + //fprintf(stderr, "modem_frame_count_tx: %d dec_frames: %d bytes offset: %d\n", + // f->modem_frame_count_tx, codec_frames, (f->modem_frame_count_tx*codec_frames)*bytes_per_codec_frame); + + /* buffer up bits until we get enough encoded bits for interleaver */ + + for (j=0; jcodec2, f->packed_codec_bits_tx + (f->modem_frame_count_tx*codec_frames+j)*bytes_per_codec_frame, speech_in); + speech_in += codec2_samples_per_frame(f->codec2); + } + + + /* Only use extra local buffer if needed for interleave > 1 */ + if (f->interleave_frames == 1) { + freedv_comptx_700d(f, mod_out); + } else { + /* call modulate function when we have enough frames to run interleaver */ + assert((f->modem_frame_count_tx >= 0) && + (f->modem_frame_count_tx < f->interleave_frames)); + f->modem_frame_count_tx++; + if (f->modem_frame_count_tx == f->interleave_frames) { + freedv_comptx_700d(f, f->mod_out); + //fprintf(stderr, " calling freedv_comptx_700d()\n"); + f->modem_frame_count_tx = 0; + } + /* output n_nom_modem_samples at a time from modulated buffer */ + for(i=0; in_nat_modem_samples; i++) { + mod_out[i] = + f->mod_out[f->modem_frame_count_tx * f->n_nat_modem_samples+i]; + } + } + + } + + /* 2400 A and B are handled by the real-mode TX */ + if((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B)){ + codec2_encode(f->codec2, f->packed_codec_bits, speech_in); + freedv_comptx_fsk_voice(f,mod_out); + } +} + +void freedv_codectx(struct freedv *f, short mod_out[], unsigned char *packed_codec_bits) { + assert(f != NULL); + COMP *tx_fdm = new COMP[f->n_nom_modem_samples]; + int bits_per_codec_frame; + int bytes_per_codec_frame; + int codec_frames; + int i; + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + codec_frames = f->n_codec_bits / bits_per_codec_frame; + + memcpy(f->packed_codec_bits, packed_codec_bits, bytes_per_codec_frame * codec_frames); + + switch(f->mode) { + case FREEDV_MODE_1600: + freedv_comptx_fdmdv_1600(f, tx_fdm); + break; + case FREEDV_MODE_700: + case FREEDV_MODE_700B: + case FREEDV_MODE_700C: + freedv_comptx_700(f, tx_fdm); + break; + case FREEDV_MODE_700D: { + /* special treatment due to interleaver */ + int data_bits_per_frame = f->ldpc->data_bits_per_frame; + int codec_frames = data_bits_per_frame / bits_per_codec_frame; + int j; + + /* buffer up bits until we get enough encoded bits for interleaver */ + + for (j=0; jpacked_codec_bits_tx + (f->modem_frame_count_tx*codec_frames+j)*bytes_per_codec_frame, packed_codec_bits, bytes_per_codec_frame); + packed_codec_bits += bytes_per_codec_frame; + } + + /* call modulate function when we have enough frames to run interleaver */ + + assert((f->modem_frame_count_tx >= 0) && (f->modem_frame_count_tx < f->interleave_frames)); + f->modem_frame_count_tx++; + if (f->modem_frame_count_tx == f->interleave_frames) { + freedv_comptx_700d(f, f->mod_out); + f->modem_frame_count_tx = 0; + } + + /* output n_nom_modem_samples at a time from modulated buffer */ + for(i=0; in_nat_modem_samples; i++) { + mod_out[i] = f->mod_out[f->modem_frame_count_tx*f->n_nat_modem_samples+i].real; + } + + return; /* output is already real */ + } + case FREEDV_MODE_2400A: + case FREEDV_MODE_2400B: + case FREEDV_MODE_800XA: + freedv_tx_fsk_voice(f, mod_out); + return; /* output is already real */ + } + /* convert complex to real */ + for(i=0; in_nom_modem_samples; i++) + mod_out[i] = tx_fdm[i].real; + + delete[] tx_fdm; +} + +void freedv_datatx (struct freedv *f, short mod_out[]){ + assert(f != NULL); + if (f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B || f->mode == FREEDV_MODE_800XA) { + freedv_tx_fsk_data(f, mod_out); + } +} + +int freedv_data_ntxframes (struct freedv *f){ + assert(f != NULL); + if (f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B) { + if (f->deframer->fdc) + return freedv_data_get_n_tx_frames(f->deframer->fdc, 8); + } else if (f->mode == FREEDV_MODE_800XA) { + if (f->deframer->fdc) + return freedv_data_get_n_tx_frames(f->deframer->fdc, 6); + } + return 0; +} + +int freedv_nin(struct freedv *f) { + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) + // For mode 700, the input rate is 8000 sps, but the modem rate is 7500 sps + // For mode 700, 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; +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: freedv_rx + AUTHOR......: David Rowe + DATE CREATED: 3 August 2014 + + Takes a frame of samples from the radio receiver, demodulates and + decodes them, producing a frame of decoded speech samples. See + freedv_rx.c for an example. + + demod_in[] is a block of received samples sampled at 8000 Hz. + 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. + + 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 8000 Hz. You should ALWAYS check + the return value of freedv_rx(), and read EXACTLY that number of + speech samples from speech_out[]. + + 1600 and 700D mode: When out of sync, the number of output speech + samples returned will be freedv_nin(). When in sync to a valid + FreeDV 1600 signal, the number of output speech samples will + alternate between freedv_get_n_speech_samples() and 0. + + 700 .. 700C modes: The number of output speech samples returned will + always be freedv_get_n_speech_samples(), regardless of sync. + + 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 out of sync, 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. Setting the squelch with freedv_set_squelch_en(1) will + return zero-valued samples instead. + +\*---------------------------------------------------------------------------*/ + + +// short version + +int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { + assert(f != NULL); + int i; + int nin = freedv_nin(f); + + assert(nin <= f->n_max_modem_samples); + + /* FSK RX happens in real floats, so convert to those and call their demod here */ + if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA) ){ + float *rx_float = new float[f->n_max_modem_samples]; + for(i=0; imode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || + (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { + + float gain = 1.0; + + /* FDM RX happens with complex samps, so do that */ + COMP *rx_fdm = new COMP[f->n_max_modem_samples]; + for(i=0; imode == FREEDV_MODE_700D) { + float gain = 2.0; /* keep levels the same as Octave simulations and C unit tests for real signals */ + return freedv_shortrx(f, speech_out, demod_in, gain); + } + + return 0; /* should never get here */ +} + + +// float input samples version +int freedv_comprx_fsk(struct freedv *f, COMP demod_in[], int *valid) { + /* Varicode and protocol bits */ + uint8_t vc_bits[2]; + uint8_t proto_bits[3]; + short vc_bit; + int i; + int n_ascii; + char ascii_out; + + if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ + fsk_demod(f->fsk,(uint8_t*)f->tx_bits,demod_in); + f->nin = fsk_nin(f->fsk); + float EbNodB = f->fsk->stats->snr_est; /* fsk demod actually estimates Eb/No */ + f->snr_est = EbNodB + 10.0*log10f(800.0/3000.0); /* so convert to SNR Rb=800, noise B=3000 */ + //fprintf(stderr," %f %f\n", EbNodB, f->snr_est); + } else{ + /* 2400B needs real input samples */ + int n = fmfsk_nin(f->fmfsk); + float *demod_in_float = new float[n]; + for(i=0; ifmfsk,(uint8_t*)f->tx_bits,demod_in_float); + delete[] demod_in_float; + f->nin = fmfsk_nin(f->fmfsk); + } + + if(fvhff_deframe_bits(f->deframer,f->packed_codec_bits,proto_bits,vc_bits,(uint8_t*)f->tx_bits)){ + /* Decode varicode text */ + for(i=0; i<2; i++){ + /* Note: deframe_bits spits out bits in uint8_ts while varicode_decode expects shorts */ + vc_bit = vc_bits[i]; + n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, &vc_bit, 1, 1); + if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { + (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); + } + } + /* Pass proto bits on down if callback is present */ + if( f->freedv_put_next_proto != NULL){ + (*f->freedv_put_next_proto)(f->proto_callback_state,(char*)proto_bits); + } + *valid = 1; + + /* squelch if if sync but SNR too low */ + if (f->squelch_en && (f->snr_est < f->snr_squelch_thresh)) { + *valid = 0; + } + } else { + /* squelch if out of sync, or echo input of squelch off */ + if (f->squelch_en) + *valid = 0; + else + *valid = -1; + } + f->sync = f->deframer->state; + f->stats.sync = f->deframer->state; + + return f->n_speech_samples; +} + +int freedv_floatrx(struct freedv *f, short speech_out[], float demod_in[]) { + assert(f != NULL); + int i; + int nin = freedv_nin(f); + + assert(nin <= f->n_max_modem_samples); + + COMP *rx_fdm = new COMP[f->n_max_modem_samples]; + for(i=0; icodec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + nout = f->n_speech_samples; + + COMP *ademod_in = new COMP[f->nin]; + for(i=0; inin; i++) + ademod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); + + bits_per_fdmdv_frame = fdmdv_bits_per_frame(f->fdmdv); + + nin_prev = f->nin; + fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, ademod_in, &f->nin); + fdmdv_get_demod_stats(f->fdmdv, &f->stats); + f->sync = f->fdmdv->sync; + f->snr_est = f->stats.snr_est; + + if (reliable_sync_bit == 1) { + f->evenframe = 1; + } + + if (f->stats.sync) { + if (f->evenframe == 0) { + memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); + nout = 0; + *valid = 0; + } + else { + memcpy(&f->rx_bits[bits_per_fdmdv_frame], f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); + + if (f->test_frames == 0) { + recd_codeword = 0; + for(i=0; i<8; i++) { + recd_codeword <<= 1; + recd_codeword |= (f->rx_bits[i] & 0x1); + } + for(i=11; i<15; i++) { + recd_codeword <<= 1; + recd_codeword |= (f->rx_bits[i] & 0x1); + } + for(i=bits_per_codec_frame; irx_bits[i] & 0x1); + } + codeword1 = golay23_decode(recd_codeword); + f->total_bit_errors += golay23_count_errors(recd_codeword, codeword1); + f->total_bits += 23; + + //codeword1 = recd_codeword; + //fprintf(stderr, "received codeword1: 0x%x decoded codeword1: 0x%x\n", recd_codeword, codeword1); + + for(i=0; icodec_bits[i] = f->rx_bits[i]; + + for(i=0; i<8; i++) { + f->codec_bits[i] = (codeword1 >> (22-i)) & 0x1; + } + for(i=8,j=11; i<12; i++,j++) { + f->codec_bits[j] = (codeword1 >> (22-i)) & 0x1; + } + + // extract txt msg data bit ------------------------------------------------------------ + + data_flag_index = codec2_get_spare_bit_index(f->codec2); + abit[0] = f->codec_bits[data_flag_index]; + + n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, abit, 1, 1); + if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { + (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); + } + + // reconstruct missing bit we steal for data bit and decode speech + + codec2_rebuild_spare_bit(f->codec2, f->codec_bits); + + // pack bits, MSB received first + + bit = 7; + byte = 0; + memset(f->packed_codec_bits, 0, bytes_per_codec_frame); + for(i=0; ipacked_codec_bits[byte] |= (f->codec_bits[i] << bit); + bit--; + if(bit < 0) { + bit = 7; + byte++; + } + } + *valid = 1; + } + else { + int test_frame_sync, bit_errors, ntest_bits, k; + short *error_pattern = new short[fdmdv_error_pattern_size(f->fdmdv)]; + + for(k=0; k<2; k++) { + /* test frames, so lets sync up to the test frames and count any errors */ + + fdmdv_put_test_bits(f->fdmdv, &test_frame_sync, error_pattern, &bit_errors, &ntest_bits, &f->rx_bits[k*bits_per_fdmdv_frame]); + + if (test_frame_sync == 1) { + f->test_frame_sync_state = 1; + f->test_frame_count = 0; + } + + if (f->test_frame_sync_state) { + if (f->test_frame_count == 0) { + f->total_bit_errors += bit_errors; + f->total_bits += ntest_bits; + if (f->freedv_put_error_pattern != NULL) { + (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, fdmdv_error_pattern_size(f->fdmdv)); + } + } + f->test_frame_count++; + if (f->test_frame_count == 4) + f->test_frame_count = 0; + } + + //fprintf(stderr, "test_frame_sync: %d test_frame_sync_state: %d bit_errors: %d ntest_bits: %d\n", + // test_frame_sync, f->test_frame_sync_state, bit_errors, ntest_bits); + } + + delete[] error_pattern; + } + + + /* squelch if beneath SNR threshold or test frames enabled */ + + if ((f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) || f->test_frames) { + //fprintf(stderr,"squelch %f %f !\n", f->stats.snr_est, f->snr_squelch_thresh); + *valid = 0; + } + + nout = f->n_speech_samples; + + } + + /* note this freewheels if reliable sync dissapears on bad channels */ + + if (f->evenframe) + f->evenframe = 0; + else + f->evenframe = 1; + //fprintf(stderr,"%d\n", f->evenframe); + + } /* if (sync) .... */ + else { + /* if not in sync pass through analog samples */ + /* this lets us "hear" whats going on, e.g. during tuning */ + + //fprintf(stderr, "out of sync\n"); + + if (f->squelch_en == 0) { + *valid = -1; + } + else { + *valid = 0; + } + //fprintf(stderr, "%d %d %d\n", nin_prev, speech_out[0], speech_out[nin_prev-1]); + nout = nin_prev; + } + delete[] ademod_in; + return nout; +} + +static int freedv_comprx_700(struct freedv *f, COMP demod_in_8kHz[], int *valid) { + int bits_per_codec_frame, bytes_per_codec_frame; + int i, j, bit, byte, nout, k; + int data_flag_index, n_ascii, nspare; + short abit[1]; + char ascii_out; + float rx_bits[COHPSK_BITS_PER_FRAME]; /* soft decn rx bits */ + int sync; + int frames; + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + frames = f->n_codec_bits / bits_per_codec_frame; + nout = f->n_speech_samples; + + // echo samples back out as default (say if sync not found) + *valid = -1; + + // quisk_cfInterpDecim() modifies input data so lets make a copy just in case there + // is no sync and we need to echo inout to output + + COMP *demod_in = new COMP[freedv_nin(f)]; + for(i=0; i *)demod_in, freedv_nin(f), f->ptFilter8000to7500, 15, 16); + //if (i != f->nin) + // printf("freedv_comprx decimation: input %d output %d\n", freedv_nin(f), i); + + for(i=0; inin; i++) + demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); + + cohpsk_demod(f->cohpsk, rx_bits, &sync, demod_in, &f->nin); + + f->sync = sync; + cohpsk_get_demod_stats(f->cohpsk, &f->stats); + f->snr_est = f->stats.snr_est; + + memset(f->packed_codec_bits, 0, bytes_per_codec_frame * frames); + + if (sync) { + + if (f->test_frames == 0) { + data_flag_index = codec2_get_spare_bit_index(f->codec2); + + /* optional smoothing of codec symbols */ + + if (f->smooth_symbols) { + + for(i=0; imode) { + case FREEDV_MODE_700: + nspare = 2; + break; + case FREEDV_MODE_700B: + nspare = 1; // Just one spare bit for FREEDV_MODE_700B + break; + case FREEDV_MODE_700C: + nspare = 0; // and no spare bits for 700C atm + break; + default: + nspare = 0; + assert(0); + } + + for(k=0; kvaricode_dec_states, &ascii_out, abit, 1, 1); + if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { + (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); + } + } + + /* pack bits, MSB received first */ + + bit = 7; + for(i=0; ipacked_codec_bits[byte] |= ((rx_bits[j+i] < 0.0) << bit); + bit--; + if (bit < 0) { + bit = 7; + byte++; + } + } + if (bit != 7) + byte++; + + if (f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) { + *valid = 0; + } + *valid = 1; + } + nout = f->n_speech_samples; + } + else { + //fprintf(stderr, " freedv_api: f->test_frames_diversity: %d\n", f->test_frames_diversity); + + 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; icohpsk, &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; icohpsk, &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; icohpsk, &f->test_frame_sync_state_upper, &error_pattern[COHPSK_BITS_PER_FRAME], &bit_errors_upper, rx_bits_char, 1); + // fprintf(stderr, " freedv_api: f->test_frame_sync_state: %d f->test_frame_sync_state_upper: %d\n", + // f->test_frame_sync_state, f->test_frame_sync_state_upper); + + /* 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); + } + } + + } + + *valid = 0; + nout = f->n_speech_samples; + } + + } + + /* no valid FreeDV signal - squelch output */ + + if (sync == 0) { + nout = freedv_nin(f); + if (f->squelch_en) { + *valid = 0; + } + } + delete[] demod_in; + return nout; +} + +/* + TODO: + [X] in testframe mode count coded and uncoded errors + [X] freedv getter for modem and interleaver sync + [X] rms level the same as fdmdv + [X] way to stay in sync and not resync automatically + [X] SNR est, maybe from pilots, cohpsk have an example? + [X] work out how to handle return of multiple interleaved frames over time + [ ] error pattern support? + [ ] deal with out of sync returning nin samples, listening to analog audio when out of sync +*/ + +static int freedv_comprx_700d(struct freedv *f, short demod_in_8kHz[], float gain, int *valid) { + int bits_per_codec_frame, bytes_per_codec_frame; + int i, j, bit, byte, nout, k; + int n_ascii; + char ascii_out; + int frames; + struct OFDM *ofdm = f->ofdm; + struct LDPC *ldpc = f->ldpc; + + int data_bits_per_frame = ldpc->data_bits_per_frame; + int coded_bits_per_frame = ldpc->coded_bits_per_frame; + int coded_syms_per_frame = ldpc->coded_syms_per_frame; + int interleave_frames = f->interleave_frames; + COMP *codeword_symbols = f->codeword_symbols; + float *codeword_amps = f->codeword_amps; + int *rx_bits = new int[ofdm_bitsperframe]; + short *txt_bits = new short[ofdm_ntxtbits]; + COMP *payload_syms = new COMP[coded_syms_per_frame]; + float *payload_amps = new float[coded_syms_per_frame]; + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + frames = f->n_codec_bits / bits_per_codec_frame; + + // pass through is too noisey .... + //nout = f->n_speech_samples; + nout = 0; + + int Nerrs_raw = 0; + int Nerrs_coded = 0; + int iter = 0; + int parityCheckCount = 0; + uint8_t *rx_uw = new uint8_t[ofdm_nuwbits]; + + float new_gain = gain / OFDM_AMP_SCALE; + + /* echo samples back out as default (say if sync not found) */ + + *valid = 1; + f->sync = f->stats.sync = 0; + + /* TODO estimate this properly from signal */ + + float EsNo = 3.0; + + /* looking for modem sync */ + + if (ofdm->sync_state == search) { + ofdm_sync_search_shorts(f->ofdm, demod_in_8kHz, new_gain); + } + + /* OK modem is in sync */ + + if ((ofdm->sync_state == synced) || (ofdm->sync_state == trial)) + { + ofdm_demod_shorts(ofdm, rx_bits, demod_in_8kHz, new_gain); + ofdm_disassemble_modem_frame(ofdm, rx_uw, payload_syms, payload_amps, txt_bits); + + f->sync = 1; + ofdm_get_demod_stats(f->ofdm, &f->stats); + f->snr_est = f->stats.snr_est; + + assert((ofdm_nuwbits+ofdm_ntxtbits+coded_bits_per_frame) == ofdm_bitsperframe); + + /* now we need to buffer for de-interleaving -------------------------------------*/ + + /* shift interleaved symbol buffers to make room for new symbols */ + + for(i=0, j=coded_syms_per_frame; jsync_state_interleaver == synced) && (ofdm->frame_count_interleaver == interleave_frames)) { + ofdm->frame_count_interleaver = 0; + + if (f->test_frames) { + int *tmp = new int[interleave_frames]; + Nerrs_raw = count_uncoded_errors(ldpc, ofdm_config, tmp, interleave_frames, codeword_symbols_de); + f->total_bit_errors += Nerrs_raw; + f->total_bits += ofdm_bitsperframe*interleave_frames; + delete[] tmp; + } + + memset(f->packed_codec_bits, 0, bytes_per_codec_frame * frames); + byte = 0; f->modem_frame_count_rx = 0; + + for (j=0; jmean_amp, coded_syms_per_frame); + iter = run_ldpc_decoder(ldpc, out_char, llr, &parityCheckCount); + + if (f->test_frames) { + uint8_t *payload_data_bits = new uint8_t[data_bits_per_frame]; + ofdm_generate_payload_data_bits(payload_data_bits, data_bits_per_frame); + Nerrs_coded = count_errors(payload_data_bits, out_char, data_bits_per_frame); + f->total_bit_errors_coded += Nerrs_coded; + f->total_bits_coded += data_bits_per_frame; + delete[] payload_data_bits; + } else { + + /* a frame of valid Codec 2 bits, pack into Codec 2 frame */ + + for (i=0; ipacked_codec_bits[byte] |= (out_char[i+k] << bit); + bit--; + if (bit < 0) { + bit = 7; + byte++; + } + } + if (bit != 7) + byte++; + } + + } + } /* for interleave frames ... */ + + /* make sure we don't overrun packed byte array */ + + assert(byte <= f->nbyte_packed_codec_bits); + + nout = f->n_speech_samples; + + if (f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) { + *valid = 0; + } + + } /* if interleaver synced ..... */ + + /* If modem is synced we can decode txt bits */ + + for(k=0; kvaricode_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); + } + } + + /* estimate uncoded BER from UW. Coded bit errors could + probably be estimated as half of all failed LDPC parity + checks */ + + for(i=0; itx_uw[i]) { + f->total_bit_errors++; + } + } + f->total_bits += ofdm_nuwbits; + + delete[] out_char; + delete[] llr; + delete[] codeword_amps_de; + delete[] codeword_symbols_de; + } /* if modem synced .... */ + else + { + *valid = -1; + } + + /* iterate state machine and update nin for next call */ + + f->nin = ofdm_get_nin(ofdm); + //fprintf(stderr, "nin: %d\n", ofdm_get_nin(ofdm)); + ofdm_sync_state_machine(ofdm, rx_uw); + + if (f->verbose && (ofdm->last_sync_state == search)) { + fprintf(stderr, "%3d st: %-6s euw: %2d %1d f: %5.1f ist: %-6s %2d eraw: %3d ecdd: %3d iter: %3d pcc: %3d vld: %d, nout: %4d\n", + f->frames++, statemode[ofdm->last_sync_state], ofdm->uw_errors, ofdm->sync_counter, + (double)ofdm->foff_est_hz, + statemode[ofdm->last_sync_state_interleaver], ofdm->frame_count_interleaver, + Nerrs_raw, Nerrs_coded, iter, parityCheckCount, *valid, nout); + } + + /* no valid FreeDV signal - squelch output */ + + bool sync = ((ofdm->sync_state == synced) || (ofdm->sync_state == trial)); + if (sync == false) { + if (f->squelch_en == true) { + *valid = 0; + } + //f->snr_est = 0.0; + } + + //fprintf(stderr, "sync: %d valid: %d snr: %3.2f\n", f->sync, *valid, f->snr_est); + + delete[] rx_uw; + delete[] payload_amps; + delete[] payload_syms; + delete[] txt_bits; + delete[] rx_bits; + return nout; +} + +/* Original version for all but 700D */ +int freedv_comprx(struct freedv *f, short speech_out[], COMP demod_in[]) { + assert(f != NULL); + int bits_per_codec_frame, bytes_per_codec_frame; + int i, nout = 0; + int valid = 0; + + assert(f->nin <= f->n_max_modem_samples); + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + + if (f->mode == FREEDV_MODE_1600) { + nout = freedv_comprx_fdmdv_1600(f, demod_in, &valid); + } + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { + nout = freedv_comprx_700(f, demod_in, &valid); + } + + if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ + nout = freedv_comprx_fsk(f, demod_in, &valid); + } + + if (valid == 0) { + //fprintf(stderr, "squelch nout: %d\n", nout); + + /* squelch */ + + for (i = 0; i < nout; i++) + speech_out[i] = 0; + } + else if (valid < 0) { + /* we havent got sync so play audio from radio. This might + not work for all modes due to nin bouncing about */ + for (i = 0; i < nout; i++) + speech_out[i] = demod_in[i].real; + } + else { + /* decoded audio to play */ + + int frames = f->n_codec_bits / bits_per_codec_frame; + //fprintf(stderr, "frames: %d\n", frames); + for (i = 0; i < frames; i++) { + codec2_decode(f->codec2, speech_out, f->packed_codec_bits + i * bytes_per_codec_frame); + speech_out += codec2_samples_per_frame(f->codec2); + } + } + + //fprintf(stderr,"freedv_nin(f): %d nout: %d valid: %d\n", freedv_nin(f), nout, valid); + return nout; +} + +/* 700D version */ +int freedv_shortrx(struct freedv *f, short speech_out[], short demod_in[], float gain) { + assert(f != NULL); + int bits_per_codec_frame, bytes_per_codec_frame; + int i, nout = 0; + int valid = 0; + + assert(f->nin <= f->n_max_modem_samples); + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + + if (f->mode == FREEDV_MODE_700D) { + nout = freedv_comprx_700d(f, demod_in, gain, &valid); + } + + if (valid == 0) { + //fprintf(stderr, "squelch nout: %d\n", nout); + + /* squelch */ + + for (i = 0; i < nout; i++) + speech_out[i] = 0; + } + else if (valid < 0) { + /* we havent got sync so play audio from radio. This might + not work for all modes due to nin bouncing about */ + for (i = 0; i < nout; i++) + speech_out[i] = demod_in[i]; + } + else { + /* decoded audio to play */ + + int data_bits_per_frame = f->ldpc->data_bits_per_frame; + int frames = data_bits_per_frame/bits_per_codec_frame; + + nout = 0; + if (f->modem_frame_count_rx < f->interleave_frames) { + nout = f->n_speech_samples; + //fprintf(stderr, "modem_frame_count_rx: %d nout: %d\n", f->modem_frame_count_rx, nout); + for (i = 0; i < frames; i++) { + codec2_decode(f->codec2, speech_out, f->packed_codec_bits + (i + frames*f->modem_frame_count_rx)* bytes_per_codec_frame); + speech_out += codec2_samples_per_frame(f->codec2); + } + f->modem_frame_count_rx++; + } + } + + return nout; +} + + +int freedv_codecrx(struct freedv *f, unsigned char *packed_codec_bits, short demod_in[]) +{ + assert(f != NULL); + int i; + int nin = freedv_nin(f); + int valid; + int ret = 0; + int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + + assert(nin <= f->n_max_modem_samples); + + if (f->mode != FREEDV_MODE_700D) { + COMP *rx_fdm = new COMP[f->n_max_modem_samples]; + + for(i=0; imode == FREEDV_MODE_1600) { + freedv_comprx_fdmdv_1600(f, rx_fdm, &valid); + } + + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { + freedv_comprx_700(f, rx_fdm, &valid); + } + + if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ + freedv_comprx_fsk(f, rx_fdm, &valid); + } + + delete[] rx_fdm; + } + + int bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + + if (f->mode == FREEDV_MODE_700D) { + freedv_comprx_700d(f, demod_in, 1.0, &valid); + + int data_bits_per_frame = f->ldpc->data_bits_per_frame; + int frames = data_bits_per_frame/bits_per_codec_frame; + + if (valid == 1 && f->modem_frame_count_rx < f->interleave_frames) { + for (i = 0; i < frames; i++) { + memcpy(packed_codec_bits, f->packed_codec_bits + (i + frames*f->modem_frame_count_rx)* bytes_per_codec_frame, bytes_per_codec_frame); + packed_codec_bits += bytes_per_codec_frame; + ret += bytes_per_codec_frame; + } + f->modem_frame_count_rx++; + } + return ret; + } + + if (valid == 1) { + int codec_frames = f->n_codec_bits / bits_per_codec_frame; + + memcpy(packed_codec_bits, f->packed_codec_bits, bytes_per_codec_frame * codec_frames); + ret = bytes_per_codec_frame * codec_frames; + } + + 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_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 (f->mode != FREEDV_MODE_800XA) { + f->freedv_put_next_rx_char = rx; + f->freedv_get_next_tx_char = tx; + f->callback_state = state; + } +} + +/*---------------------------------------------------------------------------*\ + + FUNCTION....: freedv_set_callback_protocol + AUTHOR......: Brady OBrien + DATE CREATED: 21 February 2016 + + 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 (f->mode != FREEDV_MODE_800XA) { + 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 + + 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 ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ + 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 + + 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 ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ + 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 (f->mode == FREEDV_MODE_1600) + fdmdv_get_demod_stats(f->fdmdv, &f->stats); + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) + cohpsk_get_demod_stats(f->cohpsk, &f->stats); + if (f->mode == FREEDV_MODE_700D) { + ofdm_get_demod_stats(f->ofdm, &f->stats); + } + if (f->mode == FREEDV_MODE_2400B) { + fmfsk_get_demod_stats(f->fmfsk, &f->stats); + } + if (sync) *sync = f->stats.sync; + if (snr_est) *snr_est = f->stats.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. + +\*---------------------------------------------------------------------------*/ + +// Set integers +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, int 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_clip (struct freedv *f, int val) {f->clip = 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;} + + +/* Band Pass Filter to cleanup OFDM tx waveform, only supported by FreeDV 700D */ + +void freedv_set_tx_bpf(struct freedv *f, int val) { + if (f->mode == FREEDV_MODE_700D) { + ofdm_set_tx_bpf(f->ofdm, val); + } +} + + +void freedv_set_verbose(struct freedv *f, int verbosity) { + f->verbose = verbosity; + if (f->mode == FREEDV_MODE_700D) { + ofdm_set_verbose(f->ofdm, f->verbose); + } +} + +// Set floats +void freedv_set_snr_squelch_thresh (struct freedv *f, float val) {f->snr_squelch_thresh = val;} + +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 *freedv, int c, float ampl) { + assert(freedv->mode == FREEDV_MODE_700C); + cohpsk_set_carrier_ampl(freedv->cohpsk, c, ampl); +} + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: freedv_set_alt_modem_samp_rate + AUTHOR......: Brady O'Brien + DATE CREATED: 25 June 2016 + + Attempt to set the alternative sample rate on the modem side of the api. Only + a few alternative sample rates are supported. Please see below. + + 2400A - 48000, 96000 + 2400B - 48000, 96000 + + TODO: Implement 2400B rate changing, allow other rate changing. + + +\*---------------------------------------------------------------------------*/ + +int freedv_set_alt_modem_samp_rate(struct freedv *f, int samp_rate){ + if(f->mode == FREEDV_MODE_2400A){ + if(samp_rate == 24000 || samp_rate == 48000 || samp_rate == 96000){ + fsk_destroy(f->fsk); + f->fsk = fsk_create_hbr(samp_rate,1200,10,4,1200,1200); + + free(f->tx_bits); + /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ + f->tx_bits = (int*) malloc(f->fsk->Nbits*sizeof(uint8_t)); + + f->n_nom_modem_samples = f->fsk->N; + f->n_max_modem_samples = f->fsk->N + (f->fsk->Ts); + f->n_nat_modem_samples = f->fsk->N; + f->nin = fsk_nin(f->fsk); + f->modem_sample_rate = samp_rate; + return 0; + }else + return -1; + }else if(f->mode == FREEDV_MODE_2400B){ + if(samp_rate == 48000 || samp_rate == 96000){ + return -1; + }else + return -1; + } + return -1; +} + + +/*---------------------------------------------------------------------------* \ + + FUNCTIONS...: freedv_set_sync + AUTHOR......: David Rowe + DATE CREATED: May 2018 + + Extended control of sync state machines, especially for FreeDV 700D. + This mode is required to acquire sync up at very low SNRS. This is + difficult to implement, for example we may get a false sync, or the + state machine may fall out of sync by mistake during a long fade. + + So with this API call we allow some operator assistance. + + Ensure this is called inthe same thread as freedv_rx(). + +\*---------------------------------------------------------------------------*/ + +void freedv_set_sync(struct freedv *freedv, Sync sync_cmd) { + assert (freedv != NULL); + + if (freedv->mode == FREEDV_MODE_700D) { + ofdm_set_sync(freedv->ofdm, sync_cmd); + } + +} + +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. It is possible to write a macro using ## for + this, but I wasn't sure it would be 100% portable. + +\*---------------------------------------------------------------------------*/ + +// Get integers +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_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_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_sync (struct freedv *f) {return f->stats.sync;} + +int freedv_get_sync_interleaver(struct freedv *f) { + if (f->mode == FREEDV_MODE_700D) { + return f->ofdm->sync_state_interleaver == synced; + } + + return 0; +} + +int freedv_get_sz_error_pattern(struct freedv *f) +{ + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { + /* 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 floats + +struct CODEC2 *freedv_get_codec2 (struct freedv *f){return f->codec2;} +int freedv_get_n_codec_bits (struct freedv *f){return f->n_codec_bits;} + +// Get modem status + +void freedv_get_modem_extended_stats(struct freedv *f, struct MODEM_STATS *stats) +{ + if (f->mode == FREEDV_MODE_1600) + fdmdv_get_demod_stats(f->fdmdv, stats); + + if ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_800XA)) { + fsk_get_demod_stats(f->fsk, stats); + float EbNodB = stats->snr_est; /* fsk demod actually estimates Eb/No */ + stats->snr_est = EbNodB + 10.0*log10f(800.0/3000.0); /* so convert to SNR Rb=800, noise B=3000 */ + } + + if (f->mode == FREEDV_MODE_2400B) { + fmfsk_get_demod_stats(f->fmfsk, stats); + } + + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { + cohpsk_get_demod_stats(f->cohpsk, stats); + } + + if (f->mode == FREEDV_MODE_700D) { + ofdm_get_demod_stats(f->ofdm, stats); + } + +} + +} // FreeDV + diff --git a/libfreedv/freedv_api_internal.h b/libfreedv/freedv_api_internal.h new file mode 100644 index 000000000..17a8bc4da --- /dev/null +++ b/libfreedv/freedv_api_internal.h @@ -0,0 +1,154 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_api_internal.h + AUTHOR......: David Rowe + DATE CREATED: August 2014 + + This declares the structure freedv. A pointer to this structure is + returned by the FreeDV API freedv_open() function. The pointer is used + by the other FreeDV API functions declared in freedv_api.h. This + structure is intended to be internal to the FreeDV API. The public + functions are declared in freedv_api.h. Changes to this structure + are expected. Changes (except additions) to freedv_api.h are + discouraged. + +\*---------------------------------------------------------------------------*/ + +/* + 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 . +*/ + +#ifndef __FREEDV_API_INTERNAL__ +#define __FREEDV_API_INTERNAL__ + +#include "codec2/varicode.h" +#include "fsk.h" +#include "fmfsk.h" +#include "codec2_cohpsk.h" +#include "codec2_fdmdv.h" + +namespace FreeDV +{ + +struct freedv { + int mode; + + /* states for various modems we support */ + + struct CODEC2 *codec2; + struct FDMDV *fdmdv; + struct COHPSK *cohpsk; + struct FSK *fsk; + struct FMFSK *fmfsk; + struct OFDM *ofdm; + struct LDPC *ldpc; + struct MODEM_STATS stats; + + struct freedv_vhf_deframer * deframer; // Extracts frames from VHF stream + + struct quisk_cfFilter * ptFilter7500to8000; // Filters to change to/from 7500 and 8000 sps for 700 .... 700C + struct quisk_cfFilter * ptFilter8000to7500; + + int n_speech_samples; // number of speech samples we need for each freedv_tx() call + // num of speech samples output by freedv_rx() call + int n_nom_modem_samples; // size of tx and most rx modem sample buffers + int n_max_modem_samples; // make your rx modem sample buffers this big + int n_nat_modem_samples; // tx modem sample block length as used by the modem before interpolation to output + // usually the same as n_nom_modem_samples, except for 700..700C + int modem_sample_rate; // Caller is responsible for meeting this + int modem_symbol_rate; // Useful for ext_vco operation on 2400A and 800XA + int clip; // non-zero for cohpsk modem output clipping for low PAPR + + unsigned char *packed_codec_bits; + unsigned char *packed_codec_bits_tx; // for 700D we separate packed bits to maintain state due to interleaving + int nbyte_packed_codec_bits; // keep track of size of above arrays in 700D + int *codec_bits; + int *tx_bits; + int *fdmdv_bits; + int *rx_bits; + int n_codec_bits; // number of codec bits in a frame + + int tx_sync_bit; + int smooth_symbols; + int frames; + + /* test frame states -------------------------------------------------------------------------*/ + + int *ptest_bits_coh; + int *ptest_bits_coh_end; + + int test_frames; // set this baby for 1 to tx/rx test frames to look at bit error stats + int test_frames_diversity; // 1 -> used combined carriers for error counting on 700 waveforms + int test_frame_sync_state; + int test_frame_sync_state_upper; // when test_frames_diveristy==0 we need extra states for upper carriers + int test_frame_count; + int total_bits; + int total_bit_errors; + int total_bits_coded; + int total_bit_errors_coded; + int sz_error_pattern; + + /* optional user defined function to pass error pattern when a test frame is received */ + + void *error_pattern_callback_state; + void (*freedv_put_error_pattern)(void *error_pattern_callback_state, short error_pattern[], int sz_error_pattern); + + /* Misc ---------------------------------------------------------------------------------------------*/ + + int sync; + int evenframe; + float snr_est; + float snr_squelch_thresh; + int squelch_en; + int nin; + int verbose; + int ext_vco; /* 2400A/800XA use external VCO flag */ + + /* Varicode txt channel states ----------------------------------------------------------------------*/ + + struct VARICODE_DEC varicode_dec_states; + short tx_varicode_bits[VARICODE_MAX_BITS]; + int nvaricode_bits; + int varicode_bit_index; + + /* interleaved LDPC OFDM states ---------------------------------------------------------------------*/ + + int interleave_frames; // number of OFDM modem frames in interleaver, e.g. 1,2,4,8,16 + COMP *codeword_symbols; + float *codeword_amps; + int modem_frame_count_tx; // modem frame counter for tx side + int modem_frame_count_rx; // modem frame counter for rx side + COMP *mod_out; // output buffer of intereaved frames + + /* user defined function ptrs to produce and consume ASCII + characters using aux txt channel */ + + char (*freedv_get_next_tx_char)(void *callback_state); + void (*freedv_put_next_rx_char)(void *callback_state, char c); + void *callback_state; + + /* user defined functions to produce and consume protocol bits */ + /* Protocol bits are packed MSB-first */ + void (*freedv_put_next_proto)(void *callback_state, char *proto_bits_packed); + void (*freedv_get_next_proto)(void *callback_state, char *proto_bits_packed); + void *proto_callback_state; + int n_protocol_bits; +}; + +} // FreeDV + +#endif + diff --git a/libfreedv/freedv_data_channel.h b/libfreedv/freedv_data_channel.h new file mode 100644 index 000000000..7e2f142ae --- /dev/null +++ b/libfreedv/freedv_data_channel.h @@ -0,0 +1,75 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_data_channel.h + AUTHOR......: Jeroen Vreeken + DATE CREATED: 03 March 2016 + + Data channel for ethernet like packets in freedv VHF frames. + Currently designed for- + * 2 control bits per frame + * 4 byte counter bits per frame + * 64 bits of data per frame +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 Jeroen Vreeken + + 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 . +*/ + +#ifndef _FREEDV_DATA_CHANNEL_H +#define _FREEDV_DATA_CHANNEL_H + +#include + +#define FREEDV_DATA_CHANNEL_PACKET_MAX 2048 + +namespace FreeDV +{ + +typedef void (*freedv_data_callback_rx)(void *, unsigned char *packet, size_t size); +typedef void (*freedv_data_callback_tx)(void *, unsigned char *packet, size_t *size); + +struct freedv_data_channel { + freedv_data_callback_rx cb_rx; + void *cb_rx_state; + freedv_data_callback_tx cb_tx; + void *cb_tx_state; + + unsigned char rx_header[8]; + unsigned char packet_rx[FREEDV_DATA_CHANNEL_PACKET_MAX + 2]; + int packet_rx_cnt; + + unsigned char tx_header[8]; + unsigned char packet_tx[FREEDV_DATA_CHANNEL_PACKET_MAX + 2]; + int packet_tx_cnt; + size_t packet_tx_size; +}; + + +struct freedv_data_channel *freedv_data_channel_create(void); +void freedv_data_channel_destroy(struct freedv_data_channel *fdc); + +void freedv_data_set_cb_rx(struct freedv_data_channel *fdc, freedv_data_callback_rx cb, void *state); +void freedv_data_set_cb_tx(struct freedv_data_channel *fdc, freedv_data_callback_tx cb, void *state); + +void freedv_data_channel_rx_frame(struct freedv_data_channel *fdc, unsigned char *data, size_t size, int from_bit, int bcast_bit, int crc_bit, int end_bits); +void freedv_data_channel_tx_frame(struct freedv_data_channel *fdc, unsigned char *data, size_t size, int *from_bit, int *bcast_bit, int *crc_bit, int *end_bits); + +void freedv_data_set_header(struct freedv_data_channel *fdc, unsigned char *header); +int freedv_data_get_n_tx_frames(struct freedv_data_channel *fdc, size_t size); + +} // FreeDV + +#endif /* _FREEDV_DATA_CHANNEL_H */ diff --git a/libfreedv/freedv_filter.h b/libfreedv/freedv_filter.h new file mode 100644 index 000000000..88be51449 --- /dev/null +++ b/libfreedv/freedv_filter.h @@ -0,0 +1,48 @@ +/* + Copyright (C) 2018 James C. Ahlstrom + + 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 . +*/ + +#ifndef __FILTER__ +#define __FILTER__ + +#include + +namespace FreeDV +{ + +struct quisk_cfFilter { // Structure to hold the static data for FIR filters + float * dCoefs; // real filter coefficients + std::complex * cpxCoefs; // complex filter coefficients + int nBuf; // dimension of cBuf + int nTaps; // dimension of dSamples, cSamples, dCoefs + int decim_index; // index of next sample for decimation + std::complex * cSamples; // storage for old samples + std::complex * ptcSamp; // next available position in cSamples + std::complex * cBuf; // auxillary buffer for interpolation +} ; + +extern int quisk_cfInterpDecim(std::complex *, int, struct quisk_cfFilter *, int, int); +extern void quisk_filt_cfInit(struct quisk_cfFilter *, float *, int); +extern void quisk_filt_destroy(struct quisk_cfFilter *); +extern void quisk_cfTune(struct quisk_cfFilter *, float); +extern void quisk_ccfFilter(std::complex *, std::complex *, int, struct quisk_cfFilter *); + +extern float quiskFilt120t480[480]; +extern float filtP550S750[160]; + +} // FreeDV + +#endif diff --git a/libfreedv/freedv_vhf_framing.h b/libfreedv/freedv_vhf_framing.h new file mode 100644 index 000000000..6f7dc3604 --- /dev/null +++ b/libfreedv/freedv_vhf_framing.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_vhf_framing.h + AUTHOR......: Brady O'Brien + DATE CREATED: 11 February 2016 + + Framer and deframer for VHF FreeDV modes 'A' and 'B' + Currently designed for- + * 40ms ota modem frames + * 40ms Codec2 1300 frames + * 52 bits of Codec2 per frame + * 16 bits of unique word per frame + * 28 'spare' bits per frame + * - 4 spare bits at front and end of frame (8 total) for padding + * - 20 'protocol' bits, either for higher layers of 'protocol' or + * - 18 'protocol' bits and 2 vericode sidechannel bits +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 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 . +*/ + +#ifndef _FREEDV_VHF_FRAMING_H +#define _FREEDV_VHF_FRAMING_H + +#include "freedv_data_channel.h" + +/* Standard frame type */ +#define FREEDV_VHF_FRAME_A 1 /* 2400A/B Frame */ +#define FREEDV_HF_FRAME_B 2 /* 800XA Frame */ +#define FREEDV_VHF_FRAME_AT 3 /* 4800T Frame */ + +namespace FreeDV +{ + +struct freedv_vhf_deframer { + int ftype; /* Type of frame to be looking for */ + int state; /* State of deframer */ + uint8_t * bits; /* Bits currently being decanted */ + uint8_t * invbits; /* Inversion of bits currently being decanted, for FMFSK */ + + int bitptr; /* Pointer into circular bit buffer */ + int miss_cnt; /* How many UWs have been missed */ + int last_uw; /* How many bits since the last UW? */ + int frame_size; /* How big is a frame? */ + int uw_size; /* How big is the UW */ + int on_inv_bits; /* Are we using the inverted bits? */ + int sym_size; /* How many bits in a modem symbol */ + + float ber_est; /* Bit error rate estimate */ + int total_uw_bits; /* Total RX-ed bits of UW */ + int total_uw_err; /* Total errors in UW bits */ + + struct freedv_data_channel *fdc; +}; + +/* Init and allocate memory for a freedv-vhf framer/deframer */ +struct freedv_vhf_deframer * fvhff_create_deframer(uint8_t frame_type,int enable_bit_flip); + +/* Get size of various frame parameters */ +/* Frame size in bits */ +int fvhff_get_frame_size(struct freedv_vhf_deframer * def); +/* Codec2 size in bytes */ +int fvhff_get_codec2_size(struct freedv_vhf_deframer * def); +/* Protocol bits in bits */ +int fvhff_get_proto_size(struct freedv_vhf_deframer * def); +/* Varicode bits in bits */ +int fvhff_get_varicode_size(struct freedv_vhf_deframer * def); + +/* Free the memory used by a freedv-vhf framer/deframer */ +void fvhff_destroy_deframer(struct freedv_vhf_deframer * def); + +/* Place codec and other bits into a frame */ +void fvhff_frame_bits(int frame_type,uint8_t bits_out[],uint8_t codec2_in[],uint8_t proto_in[],uint8_t vc_in[]); +void fvhff_frame_data_bits(struct freedv_vhf_deframer * def, int frame_type,uint8_t bits_out[]); + +/* Find and extract frames from a stream of bits */ +int fvhff_deframe_bits(struct freedv_vhf_deframer * def,uint8_t codec2_out[],uint8_t proto_out[],uint8_t vc_out[],uint8_t bits_in[]); + +/* Is the de-framer synchronized? */ +int fvhff_synchronized(struct freedv_vhf_deframer * def); + +/* Search for a complete UW in a buffer of bits */ +std::size_t fvhff_search_uw(const uint8_t bits[],size_t nbits, + const uint8_t uw[], std::size_t uw_len, + std::size_t * delta_out, std::size_t bits_per_sym); + +} // FreeDV + +#endif //_FREEDV_VHF_FRAMING_H diff --git a/libfreedv/fsk.h b/libfreedv/fsk.h new file mode 100644 index 000000000..412d904e0 --- /dev/null +++ b/libfreedv/fsk.h @@ -0,0 +1,209 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: fsk.h + AUTHOR......: Brady O'Brien + DATE CREATED: 6 January 2016 + + C Implementation of 2FSK/4FSK modulator/demodulator, based on octave/fsk_horus.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 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 . +*/ + + +#ifndef __C2FSK_H +#define __C2FSK_H +#include +#include "codec2/comp.h" +#include "kiss_fftr.h" +#include "modem_stats.h" + +#define MODE_2FSK 2 +#define MODE_4FSK 4 + +#define MODE_M_MAX 4 + +#define FSK_SCALE 16383 + +namespace FreeDV +{ + +struct FSK { + /* Static parameters set up by fsk_init */ + int Ndft; /* buffer size for freq offset est fft */ + int Fs; /* sample freq */ + int N; /* processing buffer size */ + int Rs; /* symbol rate */ + int Ts; /* samples per symbol */ + int Nmem; /* size of extra mem for timing adj */ + int P; /* oversample rate for timing est/adj */ + int Nsym; /* Number of symbols spat out in a processing frame */ + int Nbits; /* Number of bits spat out in a processing frame */ + int f1_tx; /* f1 for modulator */ + int fs_tx; /* Space between TX freqs for modulatosr */ + int mode; /* 2FSK or 4FSK */ + int est_min; /* Minimum frequency for freq. estimator */ + int est_max; /* Maximum frequency for freq. estimaotr */ + int est_space; /* Minimum frequency spacing for freq. estimator */ + float* hann_table; /* Precomputed or runtime computed hann window table */ + + /* Parameters used by demod */ + COMP phi_c[MODE_M_MAX]; + + kiss_fft_cfg fft_cfg; /* Config for KISS FFT, used in freq est */ + float norm_rx_timing; /* Normalized RX timing */ + + COMP* samp_old; /* Tail end of last batch of samples */ + int nstash; /* How many elements are in there */ + + float* fft_est; /* Freq est FFT magnitude */ + + /* Memory used by demod but not important between demod frames */ + + /* Parameters used by mod */ + COMP tx_phase_c; /* TX phase, but complex */ + + /* Statistics generated by demod */ + float EbNodB; /* Estimated EbNo in dB */ + float f_est[MODE_M_MAX];/* Estimated frequencies */ + float ppm; /* Estimated PPM clock offset */ + + /* Parameters used by mod/demod and driving code */ + int nin; /* Number of samples to feed the next demod cycle */ + int burst_mode; /* enables/disables 'burst' mode */ + + /* modem statistic struct */ + struct MODEM_STATS *stats; + int normalise_eye; /* enables/disables normalisation of eye diagram */ +}; + +/* + * Create an FSK config/state struct from a set of config parameters + * + * int Fs - Sample frequency + * int Rs - Symbol rate + * int tx_f1 - '0' frequency + * int tx_fs - frequency spacing + */ +struct FSK * fsk_create(int Fs, int Rs, int M, int tx_f1, int tx_fs); + +/* + * Create an FSK config/state struct from a set of config parameters + * + * int Fs - Sample frequency + * int Rs - Symbol rate + * int tx_f1 - '0' frequency + * int tx_fs - frequency spacing + */ +struct FSK * fsk_create_hbr(int Fs, int Rs, int P, int M, int tx_f1, int tx_fs); + +/* + * Set a new number of symbols per processing frame + */ +void fsk_set_nsym(struct FSK *fsk,int nsym); + +/* + * Set the minimum and maximum frequencies at which the freq. estimator can find tones + */ +void fsk_set_est_limits(struct FSK *fsk,int fmin, int fmax); + +/* + * Clear the estimator states + */ +void fsk_clear_estimators(struct FSK *fsk); + +/* + * Fills MODEM_STATS struct with demod statistics + */ +void fsk_get_demod_stats(struct FSK *fsk, struct MODEM_STATS *stats); + +/* + * Destroy an FSK state struct and free it's memory + * + * struct FSK *fsk - FSK config/state struct to be destroyed + */ +void fsk_destroy(struct FSK *fsk); + +/* + * Modulates Nsym bits into N samples + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * float fsk_out[] - Buffer for N samples of modulated FSK + * uint8_t tx_bits[] - Buffer containing Nbits unpacked bits + */ +void fsk_mod(struct FSK *fsk, float fsk_out[], uint8_t tx_bits[]); + +/* + * Modulates Nsym bits into N samples + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * float fsk_out[] - Buffer for N samples of "voltage" used to modulate an external VCO + * uint8_t tx_bits[] - Buffer containing Nbits unpacked bits + */ +void fsk_mod_ext_vco(struct FSK *fsk, float vco_out[], uint8_t tx_bits[]); + +/* + * Modulates Nsym bits into N complex samples + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * comp fsk_out[] - Buffer for N samples of modulated FSK + * uint8_t tx_bits[] - Buffer containing Nbits unpacked bits + */ +void fsk_mod_c(struct FSK *fsk, COMP fsk_out[], uint8_t tx_bits[]); + + +/* + * Returns the number of samples needed for the next fsk_demod() cycle + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * returns - number of samples to be fed into fsk_demod next cycle + */ +uint32_t fsk_nin(struct FSK *fsk); + + +/* + * Demodulate some number of FSK samples. The number of samples to be + * demodulated can be found by calling fsk_nin(). + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * uint8_t rx_bits[] - Buffer for Nbits unpacked bits to be written + * float fsk_in[] - nin samples of modualted FSK + */ +void fsk_demod(struct FSK *fsk, uint8_t rx_bits[],COMP fsk_in[]); + +/* + * Demodulate some number of FSK samples. The number of samples to be + * demodulated can be found by calling fsk_nin(). + * + * struct FSK *fsk - FSK config/state struct, set up by fsk_create + * float rx_bits[] - Buffer for Nbits soft decision bits to be written + * float fsk_in[] - nin samples of modualted FSK + */ +void fsk_demod_sd(struct FSK *fsk, float rx_bits[],COMP fsk_in[]); + +/* enables/disables normalisation of eye diagram samples */ + +void fsk_stats_normalise_eye(struct FSK *fsk, int normalise_enable); + +/* Set the FSK modem into burst demod mode */ + +void fsk_enable_burst_mode(struct FSK *fsk,int nsyms); + +} // FreeSV + +#endif diff --git a/libfreedv/gp_interleaver.h b/libfreedv/gp_interleaver.h new file mode 100644 index 000000000..40cbad4ac --- /dev/null +++ b/libfreedv/gp_interleaver.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: gp_interleaver.h + AUTHOR......: David Rowe + DATE CREATED: April 2018 + + Golden Prime Interleaver. My interprestation of "On the Analysis and + Design of Good Algebraic Interleavers", Xie et al,eq (5). + + See also octvae/gp_interleaver.m + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2018 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 . +*/ + +#ifndef __GP_INTERLEAVER__ +#define __GP_INTERLEAVER__ + +#include "codec2/comp.h" + +namespace FreeDV +{ + +void gp_interleave_comp(COMP interleaved_frame[], COMP frame[], int Nbits); +void gp_deinterleave_comp(COMP frame[], COMP interleaved_frame[], int Nbits); +void gp_interleave_float(float frame[], float interleaved_frame[], int Nbits); +void gp_deinterleave_float(float interleaved_frame[], float frame[], int Nbits); + +} // FreeDV + +#endif diff --git a/libfreedv/interldpc.h b/libfreedv/interldpc.h new file mode 100644 index 000000000..76de9c567 --- /dev/null +++ b/libfreedv/interldpc.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: interldpc.h + AUTHOR......: David Rowe + DATE CREATED: April 2018 + + Helper functions for interleaved LDPC modems. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2018 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 . +*/ + +#ifndef __INTERLDPC__ +#define __INTERLDPC__ + +#include + +#include "codec2/comp.h" +#include "mpdecode_core.h" +#include "ofdm_internal.h" + +namespace FreeDV +{ + +/* CRC type function, used to compare QPSK vectors when debugging */ + +COMP test_acc(COMP v[], int n); +void printf_n(COMP v[], int n); +void set_up_hra_112_112(struct LDPC *ldpc, struct OFDM_CONFIG *); +void ldpc_encode_frame(struct LDPC *ldpc, int codeword[], unsigned char tx_bits_char[]); +void qpsk_modulate_frame(COMP tx_symbols[], int codeword[], int n); +void interleaver_sync_state_machine(struct OFDM *ofdm, struct LDPC *ldpc, struct OFDM_CONFIG *config, + COMP codeword_symbols_de[], + float codeword_amps_de[], + float EsNo, int interleave_frames, + int *inter, int *parityCheckCount, int *Nerrs_coded); +int count_uncoded_errors(struct LDPC *ldpc, struct OFDM_CONFIG *config, int Nerrs_raw[], int interleave_frames, COMP codeword_symbols_de[]); +int count_errors(uint8_t tx_bits[], uint8_t rx_bits[], int n); +void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, std::complex tx_sams[], uint8_t tx_bits[], uint8_t txt_bits[], int interleave_frames, struct OFDM_CONFIG *config); +void build_modulated_uw(struct OFDM *ofdm, std::complex tx_symbols[], uint8_t txt_bits[], struct OFDM_CONFIG *config); + +} // FreeDV + +#endif diff --git a/libfreedv/kiss_fft.h b/libfreedv/kiss_fft.h new file mode 100644 index 000000000..ca4933aae --- /dev/null +++ b/libfreedv/kiss_fft.h @@ -0,0 +1,122 @@ +#ifndef FREEDV_KISS_FFT_H +#define FREEDV_KISS_FFT_H + +#include +#include +#include +#include + + +/* + ATTENTION! + If you would like a : + -- a utility that will handle the caching of fft objects + -- real-only (no imaginary time component ) FFT + -- a multi-dimensional FFT + -- a command-line utility to perform ffts + -- a command-line utility to perform fast-convolution filtering + + Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c + in the tools/ directory. +*/ + +#ifdef USE_SIMD +# include +# define kiss_fft_scalar __m128 +#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) +#define KISS_FFT_FREE _mm_free +#else +#define KISS_FFT_MALLOC malloc +#define KISS_FFT_FREE free +#endif + + +#ifdef FIXED_POINT +#include +# if (FIXED_POINT == 32) +# define kiss_fft_scalar int32_t +# else +# define kiss_fft_scalar int16_t +# endif +#else +# ifndef kiss_fft_scalar +/* default is float */ +# define kiss_fft_scalar float +# endif +#endif + +namespace FreeDV +{ + +typedef struct { + kiss_fft_scalar r; + kiss_fft_scalar i; +}kiss_fft_cpx; + +typedef struct kiss_fft_state* kiss_fft_cfg; + +/* + * kiss_fft_alloc + * + * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. + * + * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); + * + * The return value from fft_alloc is a cfg buffer used internally + * by the fft routine or NULL. + * + * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. + * The returned value should be free()d when done to avoid memory leaks. + * + * The state can be placed in a user supplied buffer 'mem': + * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, + * then the function places the cfg in mem and the size used in *lenmem + * and returns mem. + * + * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), + * then the function returns NULL and places the minimum cfg + * buffer size in *lenmem. + * */ + +kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); + +/* + * kiss_fft(cfg,in_out_buf) + * + * Perform an FFT on a complex input buffer. + * for a forward FFT, + * fin should be f[0] , f[1] , ... ,f[nfft-1] + * fout will be F[0] , F[1] , ... ,F[nfft-1] + * Note that each element is complex and can be accessed like + f[k].r and f[k].i + * */ +void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); + +/* + A more generic version of the above function. It reads its input from every Nth sample. + * */ +void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); + +/* If kiss_fft_alloc allocated a buffer, it is one contiguous + buffer and can be simply free()d when no longer needed*/ +#define kiss_fft_free free + +/* + Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up + your compiler output to call this before you exit. +*/ +void kiss_fft_cleanup(void); + + +/* + * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) + */ +int kiss_fft_next_fast_size(int n); + +/* for real ffts, we need an even size */ +#define kiss_fftr_next_fast_size_real(n) \ + (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) + +} // FreeDV + +#endif // FREEDV_KISS_FFT_H diff --git a/libfreedv/kiss_fftr.h b/libfreedv/kiss_fftr.h new file mode 100644 index 000000000..916c1ff63 --- /dev/null +++ b/libfreedv/kiss_fftr.h @@ -0,0 +1,44 @@ +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" + +namespace FreeDV +{ + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points +*/ + +void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); +/* + input freqdata has nfft/2+1 complex points + output timedata has nfft scalar points +*/ + +} // FreeDV + +#define kiss_fftr_free free + +#endif diff --git a/libfreedv/libfreedv.h b/libfreedv/libfreedv.h new file mode 100644 index 000000000..06b0a3c2a --- /dev/null +++ b/libfreedv/libfreedv.h @@ -0,0 +1,186 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// freedv_api.h replacement // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + + +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_api.h + AUTHOR......: David Rowe + DATE CREATED: August 2014 + + Library of API functions that implement FreeDV "modes", useful for + embedding FreeDV in other programs. Please see the documentation + for each function in freedv_api.c, and the sample 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 . +*/ + +#ifndef LIBFREEDV_LIBFREEDV_H_ +#define LIBFREEDV_LIBFREEDV_H_ + +// This declares a single-precision (float) complex number +#include +#include + +#include "codec2/comp.h" +#include "codec2_ofdm.h" + +#define FREEDV_MODE_1600 0 +#define FREEDV_MODE_700 1 +#define FREEDV_MODE_700B 2 +#define FREEDV_MODE_2400A 3 +#define FREEDV_MODE_2400B 4 +#define FREEDV_MODE_800XA 5 +#define FREEDV_MODE_700C 6 +#define FREEDV_MODE_700D 7 + +/* operator control of 700D state machine */ + +#define FREEDV_SYNC_UNSYNC 0 /* force sync state machine to lose sync, and search for new sync */ +#define FREEDV_SYNC_AUTO 1 /* falls out of sync automatically */ +#define FREEDV_SYNC_MANUAL 2 /* fall out of sync only under operator control */ + + +namespace FreeDV +{ + +struct freedv; + +/* advanced freedv open options rqd by some modes */ + +struct freedv_advanced { + int interleave_frames; +}; + +/* Called when text message char is decoded */ +typedef void (*freedv_callback_rx)(void *, char); +/* Called when new text message char is needed */ +typedef char (*freedv_callback_tx)(void *); +typedef void (*freedv_calback_error_pattern) + (void *error_pattern_callback_state, short error_pattern[], int sz_error_pattern); + +/* Protocol bits are packed MSB-first */ +/* Called when a frame containing protocol data is decoded */ +typedef void (*freedv_callback_protorx)(void *, char *); +/* Called when a frame containing protocol data is to be sent */ +typedef void (*freedv_callback_prototx)(void *, char *); + +/* Data packet callbacks */ +/* Called when a packet has been received */ +typedef void (*freedv_callback_datarx)(void *, unsigned char *packet, std::size_t size); +/* Called when a new packet can be send */ +typedef void (*freedv_callback_datatx)(void *, unsigned char *packet, std::size_t *size); + + +/*---------------------------------------------------------------------------*\ + + FreeDV API functions + +\*---------------------------------------------------------------------------*/ + +// open, close ---------------------------------------------------------------- + +struct freedv *freedv_open(int mode); +struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv); +void freedv_close (struct freedv *freedv); + +// Transmit ------------------------------------------------------------------- + +void freedv_tx (struct freedv *freedv, short mod_out[], short speech_in[]); +void freedv_comptx (struct freedv *freedv, COMP mod_out[], short speech_in[]); +void freedv_codectx (struct freedv *f, short mod_out[], unsigned char *packed_codec_bits); +void freedv_datatx (struct freedv *f, short mod_out[]); +int freedv_data_ntxframes (struct freedv *freedv); + +// Receive ------------------------------------------------------------------- + +int freedv_nin (struct freedv *freedv); +int freedv_rx (struct freedv *freedv, short speech_out[], short demod_in[]); +int freedv_shortrx (struct freedv *freedv, short speech_out[], short demod_in[], float gain); +int freedv_floatrx (struct freedv *freedv, short speech_out[], float demod_in[]); +int freedv_comprx (struct freedv *freedv, short speech_out[], COMP demod_in[]); +int freedv_codecrx (struct freedv *freedv, unsigned char *packed_codec_bits, short demod_in[]); + +// Set parameters ------------------------------------------------------------ + +void freedv_set_callback_txt (struct freedv *freedv, freedv_callback_rx rx, freedv_callback_tx tx, void *callback_state); +void freedv_set_callback_protocol (struct freedv *freedv, freedv_callback_protorx rx, freedv_callback_prototx tx, void *callback_state); +void freedv_set_callback_data (struct freedv *freedv, freedv_callback_datarx datarx, freedv_callback_datatx datatx, void *callback_state); +void freedv_set_test_frames (struct freedv *freedv, int test_frames); +void freedv_set_test_frames_diversity (struct freedv *freedv, int test_frames_diversity); +void freedv_set_smooth_symbols (struct freedv *freedv, int smooth_symbols); +void freedv_set_squelch_en (struct freedv *freedv, int squelch_en); +void freedv_set_snr_squelch_thresh (struct freedv *freedv, float snr_squelch_thresh); +void freedv_set_clip (struct freedv *freedv, int val); +void freedv_set_total_bit_errors (struct freedv *freedv, int val); +void freedv_set_total_bits (struct freedv *freedv, int val); +void freedv_set_callback_error_pattern (struct freedv *freedv, freedv_calback_error_pattern cb, void *state); +void freedv_set_varicode_code_num (struct freedv *freedv, int val); +void freedv_set_data_header (struct freedv *freedv, unsigned char *header); +int freedv_set_alt_modem_samp_rate (struct freedv *freedv, int samp_rate); +void freedv_set_carrier_ampl (struct freedv *freedv, int c, float ampl); +void freedv_set_sync (struct freedv *freedv, Sync sync_cmd); +void freedv_set_verbose (struct freedv *freedv, int verbosity); +void freedv_set_tx_bpf (struct freedv *freedv, int val); +void freedv_set_ext_vco (struct freedv *f, int val); + +// Get parameters ------------------------------------------------------------------------- + +struct MODEM_STATS; +int freedv_get_version(void); +int freedv_get_mode (struct freedv *freedv); +void freedv_get_modem_stats (struct freedv *freedv, int *sync, float *snr_est); +void freedv_get_modem_extended_stats(struct freedv *freedv, struct MODEM_STATS *stats); +int freedv_get_test_frames (struct freedv *freedv); +int freedv_get_n_speech_samples (struct freedv *freedv); +int freedv_get_modem_sample_rate (struct freedv *freedv); +int freedv_get_modem_symbol_rate (struct freedv *freedv); +int freedv_get_n_max_modem_samples (struct freedv *freedv); +int freedv_get_n_nom_modem_samples (struct freedv *freedv); +int freedv_get_total_bits (struct freedv *freedv); +int freedv_get_total_bit_errors (struct freedv *freedv); +int freedv_get_total_bits_coded (struct freedv *freedv); +int freedv_get_total_bit_errors_coded(struct freedv *freedv); +int freedv_get_sync (struct freedv *freedv); +int freedv_get_sync_interleaver (struct freedv *freedv); +struct FSK * freedv_get_fsk (struct freedv *f); +struct CODEC2 *freedv_get_codec2 (struct freedv *freedv); +int freedv_get_n_codec_bits (struct freedv *freedv); +int freedv_get_sz_error_pattern (struct freedv *freedv); +int freedv_get_protocol_bits (struct freedv *freedv); + +} // namespace FreeDV + +#endif /* LIBFREEDV_LIBFREEDV_H_ */ diff --git a/libfreedv/modem_probe.h b/libfreedv/modem_probe.h new file mode 100644 index 000000000..e5ba6ac42 --- /dev/null +++ b/libfreedv/modem_probe.h @@ -0,0 +1,134 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: modem_probe.h + AUTHOR......: Brady O'Brien + DATE CREATED: 9 January 2016 + + Library to easily extract debug traces from modems during development + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2016 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 . +*/ + +#ifndef __MODEMPROBE_H +#define __MODEMPROBE_H +#include +#include +#include +#include "comp.h" + +namespace FreeDV +{ + +#ifdef MODEMPROBE_ENABLE + +/* Internal functions */ +void modem_probe_init_int(char *modname, char *runname); +void modem_probe_close_int(); + +void modem_probe_samp_i_int(char * tracename,int samp[],size_t cnt); +void modem_probe_samp_f_int(char * tracename,float samp[],size_t cnt); +void modem_probe_samp_c_int(char * tracename,COMP samp[],size_t cnt); + +/* + * Init the probe library. + * char *modname - Name of the modem under test + * char *runname - Name/path of the file data is dumped to + */ +static inline void modem_probe_init(char *modname,char *runname){ + modem_probe_init_int(modname,runname); +} + +/* + * Dump traces to a file and clean up + */ +static inline void modem_probe_close(){ + modem_probe_close_int(); +} + +/* + * Save some number of int samples to a named trace + * char *tracename - name of trace being saved to + * int samp[] - int samples + * size_t cnt - how many samples to save + */ +static inline void modem_probe_samp_i(char *tracename,int samp[],size_t cnt){ + modem_probe_samp_i_int(tracename,samp,cnt); +} + +/* + * Save some number of float samples to a named trace + * char *tracename - name of trace being saved to + * float samp[] - int samples + * size_t cnt - how many samples to save + */ +static inline void modem_probe_samp_f(char *tracename,float samp[],size_t cnt){ + modem_probe_samp_f_int(tracename,samp,cnt); +} + +/* + * Save some number of complex samples to a named trace + * char *tracename - name of trace being saved to + * COMP samp[] - int samples + * size_t cnt - how many samples to save + */ +static inline void modem_probe_samp_c(char *tracename,COMP samp[],size_t cnt){ + modem_probe_samp_c_int(tracename,samp,cnt); +} + +/* + * Save some number of complex samples to a named trace + * char *tracename - name of trace being saved to + * float complex samp[] - int samples + * size_t cnt - how many samples to save + */ +static inline void modem_probe_samp_cft(char *tracename,complex float samp[],size_t cnt){ + modem_probe_samp_c_int(tracename,(COMP*)samp,cnt); +} + +#else + +static inline void modem_probe_init(char *modname,char *runname){ + return; +} + +static inline void modem_probe_close(){ + return; +} + +static inline void modem_probe_samp_i(char *name,int samp[],size_t sampcnt){ + return; +} + +static inline void modem_probe_samp_f(char *name,float samp[],size_t cnt){ + return; +} + +static inline void modem_probe_samp_c(char *name,COMP samp[],size_t cnt){ + return; +} + +static inline void modem_probe_samp_cft(char *name,complex float samp[],size_t cnt){ + return; +} + +#endif + +} // FreeDV + +#endif diff --git a/libfreedv/modem_stats.h b/libfreedv/modem_stats.h new file mode 100644 index 000000000..2fb5afd60 --- /dev/null +++ b/libfreedv/modem_stats.h @@ -0,0 +1,79 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: modem_stats.h + AUTHOR......: David Rowe + DATE CREATED: June 2015 + + Common structure for returning demod stats from fdmdv and cohpsk modems. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2015 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 . +*/ + +#ifndef __FREEDV_MODEM_STATS__ +#define __FREEDV_MODEM_STATS__ + +#include "codec2/comp.h" +#include "kiss_fft.h" + +#define MODEM_STATS_NC_MAX 20 +#define MODEM_STATS_NR_MAX 8 +#define MODEM_STATS_ET_MAX 8 +#define MODEM_STATS_EYE_IND_MAX 160 +#define MODEM_STATS_NSPEC 512 +#define MODEM_STATS_MAX_F_HZ 4000 +#define MODEM_STATS_MAX_F_EST 4 + +namespace FreeDV +{ + +struct MODEM_STATS { + int Nc; + float snr_est; /* estimated SNR of rx signal in dB (3 kHz noise BW) */ + COMP rx_symbols[MODEM_STATS_NR_MAX][MODEM_STATS_NC_MAX+1]; + /* latest received symbols, for scatter plot */ + int nr; /* number of rows in rx_symbols */ + int sync; /* demod sync state */ + float foff; /* estimated freq offset in Hz */ + float rx_timing; /* estimated optimum timing offset in samples */ + float clock_offset; /* Estimated tx/rx sample clock offset in ppm */ + float sync_metric; /* number between 0 and 1 indicating quality of sync */ + + /* eye diagram traces */ + /* Eye diagram plot -- first dim is trace number, second is the trace idx */ + float rx_eye[MODEM_STATS_ET_MAX][MODEM_STATS_EYE_IND_MAX]; + int neyetr; /* How many eye traces are plotted */ + int neyesamp; /* How many samples in the eye diagram */ + + /* optional for FSK modems - est tone freqs */ + + float f_est[MODEM_STATS_MAX_F_EST]; + + /* Buf for FFT/waterfall */ + + float fft_buf[2*MODEM_STATS_NSPEC]; + kiss_fft_cfg fft_cfg; +}; + +void modem_stats_open(struct MODEM_STATS *f); +void modem_stats_close(struct MODEM_STATS *f); +void modem_stats_get_rx_spectrum(struct MODEM_STATS *f, float mag_spec_dB[], COMP rx_fdm[], int nin); + +} // FreeDV + +#endif diff --git a/libfreedv/mpdecode_core.h b/libfreedv/mpdecode_core.h new file mode 100644 index 000000000..550f0df7e --- /dev/null +++ b/libfreedv/mpdecode_core.h @@ -0,0 +1,51 @@ +/* + FILE...: mpdecode_core.h + AUTHOR.: David Rowe + CREATED: Sep 2016 + + C-callable core functions for MpDecode, so they can be used for + Octave and C programs. Also some convenience functions to help use + the C-callable LDPC decoder in C programs. +*/ + +#ifndef __MPDECODE_CORE__ +#define __MPDECODE_CORE__ + +#include + +#include "codec2/comp.h" + +namespace FreeDV +{ + +struct LDPC { + int max_iter; + int dec_type; + int q_scale_factor; + int r_scale_factor; + int CodeLength; + int NumberParityBits; + int NumberRowsHcols; + int max_row_weight; + int max_col_weight; + int data_bits_per_frame; + int coded_bits_per_frame; + int coded_syms_per_frame; + uint16_t *H_rows; + uint16_t *H_cols; +}; + +void encode(struct LDPC *ldpc, unsigned char ibits[], unsigned char pbits[]); + +int run_ldpc_decoder(struct LDPC *ldpc, uint8_t out_char[], float input[], int *parityCheckCount); + +void sd_to_llr(float llr[], double sd[], int n); +void Demod2D(float symbol_likelihood[], COMP r[], COMP S_matrix[], float EsNo, float fading[], float mean_amp, int number_symbols); +void Somap(float bit_likelihood[], float symbol_likelihood[], int number_symbols); +void symbols_to_llrs(float llr[], COMP rx_qpsk_symbols[], float rx_amps[], float EsNo, float mean_amp, int nsyms); + +void ldpc_print_info(struct LDPC *ldpc); + +} // FreeDV + +#endif diff --git a/libfreedv/ofdm_internal.h b/libfreedv/ofdm_internal.h new file mode 100644 index 000000000..6dcd3f823 --- /dev/null +++ b/libfreedv/ofdm_internal.h @@ -0,0 +1,137 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: ofdm_internal.h + AUTHORS.....: David Rowe & Steve Sampson + DATE CREATED: June 2017 + + OFDM Internal definitions. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2017 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 . + */ + +#ifndef OFDM_INTERNAL_H +#define OFDM_INTERNAL_H + +#include +#include +#include + +#include "codec2_ofdm.h" +#include "freedv_filter.h" +#include "fdv_arm_math.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846f +#endif + +#define TAU (2.0f * M_PI) +#define ROT45 (M_PI / 4.0f) + +#define cmplx(value) (COSF(value) + SINF(value) * I) +#define cmplxconj(value) (COSF(value) + SINF(value) * -I) + +namespace FreeDV +{ + +/* + * Contains user configuration for OFDM modem + */ + +struct OFDM_CONFIG { + float tx_centre; /* TX Centre Audio Frequency */ + float rx_centre; /* RX Centre Audio Frequency */ + float fs; /* Sample Frequency */ + float rs; /* Modulation Symbol Rate */ + float ts; /* symbol duration */ + float tcp; /* Cyclic Prefix duration */ + float ofdm_timing_mx_thresh; + + int nc; /* Number of carriers */ + int ns; /* Number of Symbol frames */ + int bps; /* Bits per Symbol */ + int txtbits; /* number of auxiliary data bits */ + int ftwindowwidth; +}; + +struct OFDM { + std::complex *pilot_samples; + std::complex *rxbuf; + std::complex *pilots; + std::complex **rx_sym; + std::complex *rx_np; + + float *rx_amp; + float *aphase_est_pilot_log; + + uint8_t *tx_uw; + + State sync_state; + State last_sync_state; + State sync_state_interleaver; + State last_sync_state_interleaver; + + Sync sync_mode; + + struct quisk_cfFilter *ofdm_tx_bpf; + + std::complex foff_metric; + + float foff_est_gain; + float foff_est_hz; + float timing_mx; + float coarse_foff_est_hz; + float timing_norm; + float sig_var; + float noise_var; + float mean_amp; + + int clock_offset_counter; + int verbose; + int sample_point; + int timing_est; + int timing_valid; + int nin; + int uw_errors; + int sync_counter; + int frame_count; + + int frame_count_interleaver; + + bool sync_start; + bool sync_end; + bool timing_en; + bool foff_est_en; + bool phase_est_en; + bool tx_bpf_en; +}; + +/* function headers exposed for LDPC work */ + +std::complex qpsk_mod(int *); +void qpsk_demod(std::complex, int *); +void ofdm_txframe(struct OFDM *, std::complex *, std::complex []); +void ofdm_assemble_modem_frame(struct OFDM *, uint8_t [], uint8_t [], uint8_t []); +void ofdm_assemble_modem_frame_symbols(std::complex [], COMP [], uint8_t []); +void ofdm_disassemble_modem_frame(struct OFDM *, uint8_t [], COMP [], float [], short []); +void ofdm_rand(uint16_t [], int); +void ofdm_generate_payload_data_bits(uint8_t payload_data_bits[], int data_bits_per_frame); + +} // FreeDV + +#endif