UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/freedv/fm.c

291 lines
8.7 KiB
C
Raw Normal View History

2022-08-24 08:41:00 +02:00
/*---------------------------------------------------------------------------*\
FILE........: fm.c
AUTHOR......: David Rowe
DATE CREATED: February 2015
Functions that implement analog FM modulation and demodulation, see
also octave/fm.m.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2015 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*---------------------------------------------------------------------------*\
DEFINES
\*---------------------------------------------------------------------------*/
#define FILT_MEM 200
/*---------------------------------------------------------------------------*\
INCLUDES
\*---------------------------------------------------------------------------*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "codec2_fm.h"
#include "fm_fir_coeff.h"
#include "comp_prim.h"
/*---------------------------------------------------------------------------*\
FUNCTIONS
\*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*\
FUNCTION....: fm_create
AUTHOR......: David Rowe
DATE CREATED: 24 Feb 2015
Create and initialise an instance of the "modem". Returns a pointer
to the modem states or NULL on failure. One set of states is
sufficient for a full duplex modem.
\*---------------------------------------------------------------------------*/
struct FM *fm_create(int nsam)
{
struct FM *fm;
fm = (struct FM*)malloc(sizeof(struct FM));
if (fm == NULL)
return NULL;
fm->rx_bb = (COMP*)malloc(sizeof(COMP)*(FILT_MEM+nsam));
assert(fm->rx_bb != NULL);
fm->rx_bb_filt_prev.real = 0.0;
fm->rx_bb_filt_prev.imag = 0.0;
fm->lo_phase.real = 1.0;
fm->lo_phase.imag = 0.0;
fm->tx_phase = 0;
fm->rx_dem_mem = (float*)malloc(sizeof(float)*(FILT_MEM+nsam));
assert(fm->rx_dem_mem != NULL);
fm->nsam = nsam;
return fm;
}
void fm_destroy(struct FM *fm_states)
{
free(fm_states->rx_bb);
free(fm_states->rx_dem_mem);
free(fm_states);
}
/*---------------------------------------------------------------------------*\
FUNCTION....: fm_demod
AUTHOR......: David Rowe
DATE CREATED: 24 Feb 2015
Demodulate a FM signal to baseband audio.
\*---------------------------------------------------------------------------*/
void fm_demod(struct FM *fm_states, float rx_out[], float rx[])
{
float Fs = fm_states->Fs;
float fc = fm_states->fc;
float wc = 2*M_PI*fc/Fs;
float fd = fm_states->fd;
float wd = 2*M_PI*fd/Fs;
COMP *rx_bb = fm_states->rx_bb + FILT_MEM;
COMP wc_rect, rx_bb_filt, rx_bb_diff;
float rx_dem;
/*
float acc;
*/
float *rx_dem_mem = fm_states->rx_dem_mem + FILT_MEM;
int nsam = fm_states->nsam;
float mag;
int i,k;
wc_rect.real = cosf(wc); wc_rect.imag = -sinf(wc);
for(i=0; i<nsam; i++) {
/* down to complex baseband */
fm_states->lo_phase = cmult(fm_states->lo_phase, wc_rect);
rx_bb[i] = fcmult(rx[i], fm_states->lo_phase);
/* input FIR filter */
rx_bb_filt.real = 0.0; rx_bb_filt.imag = 0.0;
for(k=0; k<FILT_MEM/2; k++) {
rx_bb_filt.real += rx_bb[i-k].real * bin[k+FILT_MEM/4];
rx_bb_filt.imag += rx_bb[i-k].imag * bin[k+FILT_MEM/4];
}
//rx_bb_filt = rx_bb[i];
//printf("%f %f %f\n", rx[i], wc_rect.real, wc_rect.imag);
//printf("%f %f %f\n", rx[i], fm_states->lo_phase.real, fm_states->lo_phase.imag);
//printf("%f %f %f\n", rx[i], rx_bb[i].real, rx_bb[i].imag);
//printf("%f %f\n", rx_bb_filt.real, rx_bb_filt.imag);
/*
Differentiate first, in rect domain, then find angle, this
puts signal on the positive side of the real axis and helps
atan2() behaive.
*/
rx_bb_diff = cmult(rx_bb_filt, cconj(fm_states->rx_bb_filt_prev));
fm_states->rx_bb_filt_prev = rx_bb_filt;
rx_dem = atan2f(rx_bb_diff.imag, rx_bb_diff.real);
/* limit maximum phase jumps, to remove static type noise at low SNRs */
if (rx_dem > wd)
rx_dem = wd;
if (rx_dem < -wd)
rx_dem = -wd;
rx_dem *= (1/wd);
//printf("%f %f\n", rx_bb_diff.real, rx_bb_diff.imag);
rx_dem_mem[i] = rx_dem;
/*
acc = 0;
for(k=0; k<FILT_MEM; k++) {
acc += rx_dem_mem[i-k] * bout[k];
}
*/
rx_out[i] = rx_dem;
}
/* update filter memories */
rx_bb -= FILT_MEM;
rx_dem_mem -= FILT_MEM;
for(i=0; i<FILT_MEM; i++) {
rx_bb[i] = rx_bb[i+nsam];
rx_dem_mem[i] = rx_dem_mem[i+nsam];
}
/* normalise digital oscillator as the magnitude can drift over time */
mag = cabsolute(fm_states->lo_phase);
fm_states->lo_phase.real /= mag;
fm_states->lo_phase.imag /= mag;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: fm_mod
AUTHOR......: Brady O'Brien
DATE CREATED: Sept. 10 2015
Modulate an FM signal from a baseband modulating signal
struct FM *fm - FM state structure. Can be reused from fm_demod.
float tx_in[] - nsam baseband samples to be modulated
float tx_out[] - nsam samples in which to place the modulated FM
\*---------------------------------------------------------------------------*/
void fm_mod(struct FM *fm_states, float tx_in[], float tx_out[]) {
float Fs = fm_states->Fs; //Sampling freq
float fc = fm_states->fc; //Center freq
float wc = 2*M_PI*fc/Fs; //Center freq in rads/samp
float fd = fm_states->fd; //Max deviation in cycles/samp
float wd = 2*M_PI*fd/Fs; //Max deviation in rads/samp
int nsam = fm_states->nsam; //Samples per batch of modulation
float tx_phase = fm_states->tx_phase; //Transmit phase in rads
float w; //Temp variable for phase of VFO during loop
int i;
//Go through the samples, spin the oscillator, and generate some FM
for(i=0; i<nsam; i++){
w = wc + wd*tx_in[i]; //Calculate phase of VFO
tx_phase += w; //Spin TX oscillator
//TODO: Add pre-emphasis and pre-emph AGC for voice
//Make sure tx_phase stays from 0 to 2PI.
//If tx_phase goes above 4PI, It's because fc+fd*tx_in[i] is way too large for the sample
// rate.
if(tx_phase > 2*M_PI)
tx_phase -= 2*M_PI;
tx_out[i] = cosf(tx_phase);
}
//Save phase back into state struct
fm_states->tx_phase = tx_phase;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: fm_mod
AUTHOR......: Brady O'Brien
DATE CREATED: Sept. 10 2015
Modulate an FM signal from a baseband modulating signal. Output signal is
in complex domain
struct FM *fm - FM state structure. Can be reused from fm_demod.
float tx_in[] - nsam baseband samples to be modulated
COMP tx_out[] - nsam samples in which to place the modulated FM
\*---------------------------------------------------------------------------*/
void fm_mod_comp(struct FM *fm_states, float tx_in[], COMP tx_out[]){
float Fs = fm_states->Fs; //Sampling freq
float fc = fm_states->fc; //Center freq
float wc = 2*M_PI*fc/Fs; //Center freq in rads/samp
float fd = fm_states->fd; //Max deviation in cycles/samp
float wd = 2*M_PI*fd/Fs; //Max deviation in rads/samp
int nsam = fm_states->nsam; //Samples per batch of modulation
float tx_phase = fm_states->tx_phase; //Transmit phase in rads
float w; //Temp variable for phase of VFO during loop
int i;
//Go through the samples, spin the oscillator, and generate some FM
for(i=0; i<nsam; i++){
w = wc + wd*tx_in[i]; //Calculate phase of VFO
tx_phase += w; //Spin TX oscillator
//TODO: Add pre-emphasis and pre-emph AGC for voice
//Make sure tx_phase stays from 0 to 2PI.
//If tx_phase goes above 4PI, It's because fc+fd*tx_in[i] is way too large for the sample
// rate.
if(tx_phase > 2*M_PI)
tx_phase -= 2*M_PI;
tx_out[i].real = cosf(tx_phase);
tx_out[i].imag = sinf(tx_phase);
}
//Save phase back into state struct
fm_states->tx_phase = tx_phase;
}