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