mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-29 20:10:22 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			2540 lines
		
	
	
		
			92 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2540 lines
		
	
	
		
			92 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
|   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 <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdint.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <math.h>
 | |
| 
 | |
| #ifdef TT
 | |
| #if defined(__APPLE__)
 | |
| #include <malloc/malloc.h>
 | |
| #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
 | |
| #include <sys/malloc.h>
 | |
| #else
 | |
| #include <malloc.h>
 | |
| #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 "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:
 | |
|             codec2_mode = CODEC2_MODE_700C;
 | |
|             fprintf(stderr, "FreeDV::freedv_open_advanced: unknown mode default to FREEDV_MODE_700C");
 | |
|         }
 | |
| 
 | |
|         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; i<f->interleave_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; i<f->interleave_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; i<f->n_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; i<f->n_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; i<f->n_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; i<f->n_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; i<f->n_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; i<f->n_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; i<f->n_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; i<f->n_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; i<bits_per_codec_frame; i++) {
 | |
|         f->codec_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; i<bits_per_codec_frame; i++)
 | |
|         f->tx_bits[i] = f->codec_bits[i];
 | |
|     for(j=0; i<bits_per_codec_frame+11; i++,j++) {
 | |
|         f->tx_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; i<f->n_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; j<bits_per_modem_frame; j+=bits_per_codec_frame) {
 | |
| 
 | |
|         /* unpack bits, MSB first */
 | |
| 
 | |
|         bit = 7;
 | |
|         for(i=0; i<bits_per_codec_frame; i++) {
 | |
|             f->codec_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;
 | |
|             fprintf(stderr, "FreeDV::freedv_comptx_700: unknown mode default to nspare = 0");
 | |
|         }
 | |
| 
 | |
|         data_flag_index = codec2_get_spare_bit_index(f->codec2);
 | |
| 
 | |
|         for(k=0; k<nspare; k++) {
 | |
|             if (f->nvaricode_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; i<f->n_nat_modem_samples; i++)
 | |
|         mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR_COHPSK, tx_fdm[i]);
 | |
|     i = quisk_cfInterpDecim((std::complex<float> *)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; j<bits_per_interleaved_frame; j+=bits_per_codec_frame) {
 | |
| 
 | |
|         /* unpack bits, MSB first */
 | |
| 
 | |
|         bit = 7;
 | |
|         for(i=0; i<bits_per_codec_frame; i++) {
 | |
|             tx_bits[j+i] = (f->packed_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; k<nspare; k++) {
 | |
|         if (f->nvaricode_bits == 0) {
 | |
|             /* get new char and encode */
 | |
|             char s[2];
 | |
|             if (f->freedv_get_next_tx_char != NULL) {
 | |
|                 s[0] = (*f->freedv_get_next_tx_char)(f->callback_state);
 | |
|                 f->nvaricode_bits = varicode_encode(f->tx_varicode_bits, s, VARICODE_MAX_BITS, 1, 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; j<f->interleave_frames; j++) {
 | |
|             for(i=0; i<data_bits_per_frame; i++) {
 | |
|                 tx_bits[j*data_bits_per_frame + i] = payload_data_bits[i];
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         delete[] payload_data_bits;
 | |
|     }
 | |
| 
 | |
|     /* OK now ready to LDPC encode, interleave, and OFDM modulate */
 | |
| 
 | |
|     std::complex<float> *tx_sams = new std::complex<float>[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; i<f->interleave_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; j<codec_frames; j++) {
 | |
|             codec2_encode(f->codec2, 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; j<codec_frames; j++) {
 | |
|             codec2_encode(f->codec2, 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; i<f->n_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; j<codec_frames; j++) {
 | |
|                 memcpy(f->packed_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; i<f->n_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; i<f->n_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; i<nin; i++) {
 | |
|             rx_float[i] = ((float)demod_in[i]);
 | |
|         }
 | |
|         int rc = freedv_floatrx(f,speech_out,rx_float);
 | |
|         delete[] rx_float;
 | |
|         return rc;
 | |
|     }
 | |
| 
 | |
|     if ( (f->mode == 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; i<nin; i++) {
 | |
|             rx_fdm[i].real = gain*(float)demod_in[i];
 | |
|             rx_fdm[i].imag = 0.0;
 | |
|         }
 | |
|         int rc = freedv_comprx(f, speech_out, rx_fdm);
 | |
|         delete[] rx_fdm;
 | |
|         return rc;
 | |
|     }
 | |
| 
 | |
|     if (f->mode == 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; i<n; i++) {
 | |
|             demod_in_float[i] = demod_in[i].real;
 | |
|         }
 | |
|         fmfsk_demod(f->fmfsk,(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; i<nin; i++) {
 | |
|         rx_fdm[i].real = demod_in[i];
 | |
|         rx_fdm[i].imag = 0;
 | |
|     }
 | |
| 
 | |
|     int rc = freedv_comprx(f, speech_out, rx_fdm);
 | |
|     delete[] rx_fdm;
 | |
|     return rc;
 | |
| }
 | |
| 
 | |
| // complex input samples version
 | |
| 
 | |
| static int freedv_comprx_fdmdv_1600(struct freedv *f, COMP demod_in[], int *valid) {
 | |
|     int                 bits_per_codec_frame, bytes_per_codec_frame, bits_per_fdmdv_frame;
 | |
|     int                 i, j, bit, byte, nin_prev, nout;
 | |
|     int                 recd_codeword, codeword1, data_flag_index, n_ascii;
 | |
|     short               abit[1];
 | |
|     char                ascii_out;
 | |
|     int                 reliable_sync_bit;
 | |
| 
 | |
|     bits_per_codec_frame  = codec2_bits_per_frame(f->codec2);
 | |
|     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; i<f->nin; 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; i<bits_per_codec_frame+11; i++) {
 | |
|                     recd_codeword <<= 1;
 | |
|                     recd_codeword |= (f->rx_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; i<bits_per_codec_frame; i++)
 | |
|                     f->codec_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; i<bits_per_codec_frame; i++) {
 | |
|                     f->packed_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<freedv_nin(f); i++)
 | |
|         demod_in[i] = demod_in_8kHz[i];
 | |
| 
 | |
|     i = quisk_cfInterpDecim((std::complex<float> *)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; i<f->nin; 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; i<bits_per_codec_frame; i++) {
 | |
|                     rx_bits[i] += rx_bits[i+bits_per_codec_frame];
 | |
|                     rx_bits[i+bits_per_codec_frame] = rx_bits[i];
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             byte = 0;
 | |
|             for (j=0; j<COHPSK_BITS_PER_FRAME; j+=bits_per_codec_frame) {
 | |
| 
 | |
|                 /* extract txt msg data bit(s) */
 | |
| 
 | |
|                 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;
 | |
|                     fprintf(stderr, "FreeDV::freedv_comprx_700: unknown mode default to nspare = 0");
 | |
|                 }
 | |
| 
 | |
|                 for(k=0; k<nspare; k++)  {
 | |
|                     abit[0] = rx_bits[data_flag_index+j+k] < 0.0;
 | |
| 
 | |
|                     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);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 /* pack bits, MSB received first */
 | |
| 
 | |
|                 bit = 7;
 | |
|                 for(i=0; i<bits_per_codec_frame; i++) {
 | |
|                     f->packed_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; i<COHPSK_BITS_PER_FRAME; i++)
 | |
|                     rx_bits_char[i] = rx_bits[i] < 0.0;
 | |
|                 cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state, error_pattern, &bit_errors, rx_bits_char, 0);
 | |
|                 if (f->test_frame_sync_state) {
 | |
|                     f->total_bit_errors += bit_errors;
 | |
|                     f->total_bits       += COHPSK_BITS_PER_FRAME;
 | |
|                     if (f->freedv_put_error_pattern != NULL) {
 | |
|                         (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, COHPSK_BITS_PER_FRAME);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 /* calculate error pattern on uncombined carriers - test mode to spot any carrier specific issues like
 | |
|                    tx passband filtering */
 | |
| 
 | |
|                 short error_pattern[2*COHPSK_BITS_PER_FRAME];
 | |
|                 char  rx_bits_char[COHPSK_BITS_PER_FRAME];
 | |
|                 int   bit_errors_lower, bit_errors_upper;
 | |
| 
 | |
|                 /* lower group of carriers */
 | |
| 
 | |
|                 float *rx_bits_lower = cohpsk_get_rx_bits_lower(f->cohpsk);
 | |
|                 for(i=0; i<COHPSK_BITS_PER_FRAME; i++) {
 | |
|                     rx_bits_char[i] = rx_bits_lower[i] < 0.0;
 | |
|                 }
 | |
|                 cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state, error_pattern, &bit_errors_lower, rx_bits_char, 0);
 | |
| 
 | |
|                 /* upper group of carriers */
 | |
| 
 | |
|                 float *rx_bits_upper = cohpsk_get_rx_bits_upper(f->cohpsk);
 | |
|                 for(i=0; i<COHPSK_BITS_PER_FRAME; i++) {
 | |
|                     rx_bits_char[i] = rx_bits_upper[i] < 0.0;
 | |
|                 }
 | |
|                 cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state_upper, &error_pattern[COHPSK_BITS_PER_FRAME], &bit_errors_upper, rx_bits_char, 1);
 | |
|                 //                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; j<interleave_frames*coded_syms_per_frame; i++,j++) {
 | |
|             codeword_symbols[i] = codeword_symbols[j];
 | |
|             codeword_amps[i] = codeword_amps[j];
 | |
|         }
 | |
| 
 | |
|         /* newest symbols at end of buffer (uses final i from last loop), note we
 | |
|            change COMP formats from what modem uses internally */
 | |
| 
 | |
|         for(i=(interleave_frames-1)*coded_syms_per_frame,j=0; i<interleave_frames*coded_syms_per_frame; i++,j++) {
 | |
|             codeword_symbols[i] = payload_syms[j];
 | |
|             codeword_amps[i]    = payload_amps[j];
 | |
|          }
 | |
| 
 | |
|         /* run de-interleaver */
 | |
| 
 | |
|         COMP  *codeword_symbols_de = new COMP[interleave_frames*coded_syms_per_frame];
 | |
|         float *codeword_amps_de = new float[interleave_frames*coded_syms_per_frame];
 | |
|         gp_deinterleave_comp (codeword_symbols_de, codeword_symbols, interleave_frames*coded_syms_per_frame);
 | |
|         gp_deinterleave_float(codeword_amps_de   , codeword_amps   , interleave_frames*coded_syms_per_frame);
 | |
| 
 | |
|         float *llr = new float[coded_bits_per_frame];
 | |
|         uint8_t *out_char = new uint8_t[coded_bits_per_frame];
 | |
| 
 | |
|         interleaver_sync_state_machine(ofdm, ldpc, ofdm_config, codeword_symbols_de, codeword_amps_de, EsNo,
 | |
|                                        interleave_frames, &iter, &parityCheckCount, &Nerrs_coded);
 | |
| 
 | |
|         if ((ofdm->sync_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; j<interleave_frames; j++) {
 | |
|                 symbols_to_llrs(llr, &codeword_symbols_de[j*coded_syms_per_frame],
 | |
|                                 &codeword_amps_de[j*coded_syms_per_frame],
 | |
|                                 EsNo, ofdm->mean_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; i<data_bits_per_frame; i+=bits_per_codec_frame) {
 | |
| 
 | |
|                         /* pack bits, MSB received first */
 | |
| 
 | |
|                         bit = 7;
 | |
|                         for(k=0; k<bits_per_codec_frame; k++) {
 | |
|                             f->packed_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; k<ofdm_ntxtbits; k++)  {
 | |
|             //fprintf(stderr, "txt_bits[%d] = %d\n", k, rx_bits[i]);
 | |
|             n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, &txt_bits[k], 1, 1);
 | |
|             if (n_ascii && (f->freedv_put_next_rx_char != NULL)) {
 | |
|                 (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* estimate uncoded BER from UW.  Coded bit errors could
 | |
|            probably be estimated as half of all failed LDPC parity
 | |
|            checks */
 | |
| 
 | |
|         for(i=0; i<ofdm_nuwbits; i++) {
 | |
|             if (rx_uw[i] != ofdm->tx_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; i<nin; i++) {
 | |
|             rx_fdm[i].real = (float)demod_in[i];
 | |
|             rx_fdm[i].imag = 0.0;
 | |
|         }
 | |
| 
 | |
|         if (f->mode == 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
 | |
| 
 |