first commit

This commit is contained in:
2022-08-24 08:39:13 +02:00
commit e663300109
2491 changed files with 2231094 additions and 0 deletions
+595
View File
@@ -0,0 +1,595 @@
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include <assert.h>
#include "uhsdr_board_config.h"
#include "audio_agc.h"
#include "audio_driver.h" // ADC_CLIP_WARN_THRESHOLD
#include "uhsdr_math.h"
#define AGC_WDSP_RB_SIZE ((AUDIO_SAMPLE_RATE/1000)*4) // max buffer size based on max sample rate to be supported
// this translates to 192 at 48k SPS. We have FM using the AGC at full sampling speed
agc_wdsp_params_t agc_wdsp_conf;
typedef struct
{
// AGC
//#define MAX_SAMPLE_RATE (24000.0)
//#define MAX_N_TAU (8)
//#define MAX_TAU_ATTACK (0.01)
//#define RB_SIZE (int) (MAX_SAMPLE_RATE * MAX_N_TAU * MAX_TAU_ATTACK + 1)
//int8_t AGC_mode = 2;
int pmode;// = 1; // if 0, calculate magnitude by max(|I|, |Q|), if 1, calculate sqrtf(I*I+Q*Q)
float32_t out_sample[2];
float32_t abs_out_sample;
float32_t tau_attack;
float32_t tau_decay;
int n_tau;
float32_t max_gain;
float32_t var_gain;
float32_t fixed_gain; // = 1.0;
float32_t max_input;
float32_t out_targ;
float32_t tau_fast_backaverage;
float32_t tau_fast_decay;
float32_t pop_ratio;
//uint8_t hang_enable;
float32_t tau_hang_backmult;
float32_t hangtime;
float32_t hang_thresh;
float32_t tau_hang_decay;
float32_t ring[2 * AGC_WDSP_RB_SIZE]; //192]; //96];
float32_t abs_ring[AGC_WDSP_RB_SIZE];// 192 //96]; // abs_ring is half the size of ring
//assign constants
int ring_buffsize; // = 96;
//do one-time initialization
int out_index; // = -1;
float32_t ring_max; // = 0.0;
float32_t volts; // = 0.0;
float32_t save_volts; // = 0.0;
float32_t fast_backaverage; // = 0.0;
float32_t hang_backaverage; // = 0.0;
int hang_counter; // = 0;
uint8_t decay_type; // = 0;
uint8_t state; // = 0;
int attack_buffsize;
uint32_t in_index;
float32_t attack_mult;
float32_t decay_mult;
float32_t fast_decay_mult;
float32_t fast_backmult;
float32_t onemfast_backmult;
float32_t out_target;
float32_t min_volts;
float32_t inv_out_target;
float32_t tmp;
float32_t slope_constant;
float32_t inv_max_input;
float32_t hang_level;
float32_t hang_backmult;
float32_t onemhang_backmult;
float32_t hang_decay_mult;
bool remove_dc;
float32_t sample_rate;
bool initialised;
} agc_variables_t;
agc_variables_t agc_wdsp;
/**
* Sets the basic initial values for the WDSP AGC
* Call only once at startup!
*/
void AudioAgc_AgcWdsp_Init()
{
// the values below are all loaded from
// EEPROM, which happens BEFORE we get here
// so there is no point in setting these.
// agc_wdsp_conf.mode = 2;
// agc_wdsp_conf.slope = 70;
// agc_wdsp_conf.hang_enable = 0;
// agc_wdsp_conf.tau_decay[0] = 4000;
// agc_wdsp_conf.tau_decay[1] = 2000;
// agc_wdsp_conf.tau_decay[2] = 500;
// agc_wdsp_conf.tau_decay[3] = 250;
// agc_wdsp_conf.tau_decay[4] = 50;
// agc_wdsp_conf.thresh = 20;
// agc_wdsp_conf.tau_hang_decay = 500;
// these are not stored in volatile memory
// so let us initialize them here.
agc_wdsp_conf.hang_time = 500;
agc_wdsp_conf.hang_thresh = 45;
agc_wdsp_conf.action = 0;
agc_wdsp_conf.switch_mode = 1;
agc_wdsp_conf.hang_action = 0;
agc_wdsp_conf.tau_decay[5] = 1; // this is the OFF-Mode
}
/**
* Initializes the AGC data structures, has to be called when switching modes, filter changes
*
* @param sample_rate audio sample rate
* @param remove_dc Should be set for AM demodulation (AM,SAM,DSB) If set to true, remove DC in output
*/
void AudioAgc_SetupAgcWdsp(float32_t sample_rate, bool remove_dc)
{
// this is a quick and dirty hack
// it initialises the AGC variables once again,
// if the decimation rate is changed
// this should prevent confusion between the distance of in_index and out_index variables
// because these are freshly initialised
// in_index and out_index have a distance of 48 (sample rate 12000) or 96 (sample rate 24000)
// so that has to be defined very well when filter from 4k8 to 5k0 (changing decimation rate from 4 to 2)
agc_wdsp.remove_dc = remove_dc;
if(agc_wdsp.sample_rate != sample_rate)
{
agc_wdsp.initialised = false; // force initialisation
agc_wdsp.sample_rate = sample_rate; // remember decimation rate for next time
}
// Start variables taken from wdsp
// RXA.c !!!!
/*
0.001, // tau_attack
0.250, // tau_decay
4, // n_tau
10000.0, // max_gain
1.5, // var_gain
1000.0, // fixed_gain
1.0, // max_input
1.0, // out_target
0.250, // tau_fast_backaverage
0.005, // tau_fast_decay
5.0, // pop_ratio
1, // hang_enable
0.500, // tau_hang_backmult
0.250, // hangtime
0.250, // hang_thresh
0.100); // tau_hang_decay
*/
// one time initialization
if(!agc_wdsp.initialised)
{
/*
*
* //assign constants
a->ring_buffsize = RB_SIZE;
//do one-time initialization
a->out_index = -1;
a->ring_max = 0.0;
a->volts = 0.0;
a->save_volts = 0.0;
a->fast_backaverage = 0.0;
a->hang_backaverage = 0.0;
a->hang_counter = 0;
a->decay_type = 0;
a->state = 0;
a->ring = (double *)malloc0(RB_SIZE * sizeof(complex));
a->abs_ring = (double *)malloc0(RB_SIZE * sizeof(double));
loadWcpAGC(a);
*
*
* */
agc_wdsp.ring_buffsize = AGC_WDSP_RB_SIZE; //192; //96;
//do one-time initialization
agc_wdsp.out_index = -1; //agc_wdsp.ring_buffsize; // or -1 ??
agc_wdsp.fixed_gain = 1.0;
agc_wdsp.ring_max = 0.0;
agc_wdsp.volts = 0.0;
agc_wdsp.save_volts = 0.0;
agc_wdsp.fast_backaverage = 0.0;
agc_wdsp.hang_backaverage = 0.0;
agc_wdsp.hang_counter = 0;
agc_wdsp.decay_type = 0;
agc_wdsp.state = 0;
for(int idx = 0; idx < AGC_WDSP_RB_SIZE; idx++)
{
agc_wdsp.ring[idx * 2 + 0] = 0.0;
agc_wdsp.ring[idx * 2 + 1] = 0.0;
agc_wdsp.abs_ring[idx] = 0.0;
}
agc_wdsp.tau_attack = 0.001; // tau_attack
// tau_decay = agc_wdsp_conf.tau_decay / 1000.0; // 0.250; // tau_decay
agc_wdsp.n_tau = 4; // n_tau
// max_gain = 1000.0; // 1000.0; determines the AGC threshold = knee level
// max_gain is powf (10.0, (float32_t)agc_wdsp_conf.thresh / 20.0);
// fixed_gain = ads.agc_rf_gain; //0.7; // if AGC == OFF, this gain is used
agc_wdsp.max_input = (float32_t)ADC_CLIP_WARN_THRESHOLD; // which is 4096 at the moment
//32767.0; // maximum value of 16-bit audio // 1.0; //
agc_wdsp.out_targ = (float32_t)ADC_CLIP_WARN_THRESHOLD; // 4096, tweaked, so that volume when switching between the two AGCs remains equal
//12000.0; // target value of audio after AGC
agc_wdsp.tau_fast_backaverage = 0.250; // tau_fast_backaverage
agc_wdsp.tau_fast_decay = 0.005; // tau_fast_decay
agc_wdsp.pop_ratio = 5.0; // pop_ratio
// hang_enable = 0; // hang_enable
agc_wdsp.tau_hang_backmult = 0.500; // tau_hang_backmult
agc_wdsp.initialised = true;
}
// var_gain = 32.0; // slope of the AGC --> this is 10 * 10^(slope / 20) --> for 10dB slope, this is 30.0
agc_wdsp.var_gain = pow10f((float32_t)agc_wdsp_conf.slope / 20.0 / 10.0); // 10^(slope / 200)
// hangtime = 0.250; // hangtime
agc_wdsp.hangtime = (float32_t)agc_wdsp_conf.hang_time / 1000.0;
// hang_thresh = 0.250; // hang_thresh
// tau_hang_decay = 0.100; // tau_hang_decay
//calculate internal parameters
if(agc_wdsp_conf.switch_mode)
{
switch (agc_wdsp_conf.mode)
{
case 5: //agcOFF
break;
case 1: //agcLONG
agc_wdsp.hangtime = 2.000;
// agc_wdsp_conf.tau_decay = 2000;
// hang_thresh = 1.0;
// agc_wdsp_conf.hang_enable = 1;
break;
case 2: //agcSLOW
agc_wdsp.hangtime = 1.000;
// hang_thresh = 1.0;
// agc_wdsp_conf.tau_decay = 500;
// agc_wdsp_conf.hang_enable = 1;
break;
case 3: //agcMED
// hang_thresh = 1.0;
agc_wdsp.hangtime = 0.250;
// agc_wdsp_conf.tau_decay = 250;
break;
case 4: //agcFAST
// hang_thresh = 1.0;
agc_wdsp.hangtime = 0.100;
// agc_wdsp_conf.tau_decay = 50;
break;
case 0: //agcFrank --> very long
// agc_wdsp_conf.hang_enable = 0;
// hang_thresh = 0.300; // from which level on should hang be enabled
agc_wdsp.hangtime = 3.000; // hang time, if enabled
agc_wdsp.tau_hang_backmult = 0.500; // time constant exponential averager
// agc_wdsp_conf.tau_decay = 4000; // time constant decay long
agc_wdsp.tau_fast_decay = 0.05; // tau_fast_decay
agc_wdsp.tau_fast_backaverage = 0.250; // time constant exponential averager
break;
default:
break;
}
agc_wdsp_conf.switch_mode = 0;
}
// float32_t noise_offset = 10.0 * log10f(fhigh - rxa[channel].nbp0.p->flow)
// * size / rate);
// max_gain = out_target / var_gain * powf (10.0, (thresh + noise_offset) / 20.0));
agc_wdsp.tau_hang_decay = (float32_t)agc_wdsp_conf.tau_hang_decay / 1000.0;
agc_wdsp.tau_decay = (float32_t)agc_wdsp_conf.tau_decay[agc_wdsp_conf.mode] / 1000.0;
agc_wdsp.max_gain = pow10f ((float32_t)agc_wdsp_conf.thresh / 20.0);
agc_wdsp.fixed_gain = agc_wdsp.max_gain / 10.0;
// attack_buff_size is 48 for sample rate == 12000 and
// 96 for sample rate == 24000
// 192 for sample rate == 48000
agc_wdsp.attack_buffsize = ceilf(sample_rate * agc_wdsp.n_tau * agc_wdsp.tau_attack);
agc_wdsp.in_index = agc_wdsp.attack_buffsize + agc_wdsp.out_index; // attack_buffsize + out_index can be more than 2x ring_bufsize !!!
agc_wdsp.in_index %= agc_wdsp.ring_buffsize; // need to keep this within the index boundaries
agc_wdsp.attack_mult = 1.0 - expf(-1.0 / (sample_rate * agc_wdsp.tau_attack));
agc_wdsp.decay_mult = 1.0 - expf(-1.0 / (sample_rate * agc_wdsp.tau_decay));
agc_wdsp.fast_decay_mult = 1.0 - expf(-1.0 / (sample_rate * agc_wdsp.tau_fast_decay));
agc_wdsp.fast_backmult = 1.0 - expf(-1.0 / (sample_rate * agc_wdsp.tau_fast_backaverage));
agc_wdsp.onemfast_backmult = 1.0 - agc_wdsp.fast_backmult;
agc_wdsp.out_target = agc_wdsp.out_targ * (1.0 - expf(-(float32_t)agc_wdsp.n_tau)) * 0.9999;
// out_target = out_target * (1.0 - expf(-(float32_t)n_tau)) * 0.9999;
agc_wdsp.min_volts = agc_wdsp.out_target / (agc_wdsp.var_gain * agc_wdsp.max_gain);
agc_wdsp.inv_out_target = 1.0 / agc_wdsp.out_target;
float32_t tmpA = log10f(agc_wdsp.out_target / (agc_wdsp.max_input * agc_wdsp.var_gain * agc_wdsp.max_gain));
if (tmpA == 0.0)
{
tmpA = 1e-16;
}
agc_wdsp.slope_constant = (agc_wdsp.out_target * (1.0 - 1.0 / agc_wdsp.var_gain)) / tmpA;
agc_wdsp.inv_max_input = 1.0 / agc_wdsp.max_input;
if (agc_wdsp.max_input > agc_wdsp.min_volts)
{
float32_t convert
= pow10f ((float32_t)agc_wdsp_conf.hang_thresh / 20.0);
float32_t tmpB = (convert - agc_wdsp.min_volts) / (agc_wdsp.max_input - agc_wdsp.min_volts);
if(tmpB < 1e-8)
{
tmpB = 1e-8;
}
agc_wdsp.hang_thresh = 1.0 + 0.125 * log10f (tmpB);
}
else
{
agc_wdsp.hang_thresh = 1.0;
}
float32_t tmpC = pow10f ((agc_wdsp.hang_thresh - 1.0) / 0.125);
agc_wdsp.hang_level = (agc_wdsp.max_input * tmpC + (agc_wdsp.out_target /
(agc_wdsp.var_gain * agc_wdsp.max_gain)) * (1.0 - tmpC)) * 0.637;
agc_wdsp.hang_backmult = 1.0 - expf(-1.0 / (sample_rate * agc_wdsp.tau_hang_backmult));
agc_wdsp.onemhang_backmult = 1.0 - agc_wdsp.hang_backmult;
agc_wdsp.hang_decay_mult = 1.0 - expf(-1.0 / (sample_rate * agc_wdsp.tau_hang_decay));
}
/**
*
* @param blockSize
* @param agcbuffer a pointer to the list of buffers of size blockSize containing the audio data
* @param num_channels
*/
void AudioAgc_RunAgcWdsp(int16_t blockSize, float32_t (*agcbuffer)[AUDIO_BLOCK_SIZE], const bool use_stereo )
{
// Be careful: the original source code has no comments,
// all comments added by DD4WH, February 2017: comments could be wrong, misinterpreting or highly misleading!
//
if (agc_wdsp_conf.mode == 5) // AGC OFF
{
for (uint16_t i = 0; i < blockSize; i++)
{
agcbuffer[0][i] = agcbuffer[0][i] * agc_wdsp.fixed_gain;
if (use_stereo)
{
agcbuffer[1][i] = agcbuffer[1][i] * agc_wdsp.fixed_gain;
}
}
return;
}
for (uint16_t i = 0; i < blockSize; i++)
{
if (++agc_wdsp.out_index >= agc_wdsp.ring_buffsize)
{
agc_wdsp.out_index -= agc_wdsp.ring_buffsize;
}
if (++agc_wdsp.in_index >= agc_wdsp.ring_buffsize)
{
agc_wdsp.in_index -= agc_wdsp.ring_buffsize;
}
agc_wdsp.out_sample[0] = agc_wdsp.ring[2 * agc_wdsp.out_index];
if(use_stereo)
{
agc_wdsp.out_sample[1] = agc_wdsp.ring[2 * agc_wdsp.out_index + 1];
}
agc_wdsp.abs_out_sample = agc_wdsp.abs_ring[agc_wdsp.out_index];
agc_wdsp.ring[2 * agc_wdsp.in_index] = agcbuffer[0][i];
if(use_stereo)
{
agc_wdsp.ring[2 * agc_wdsp.in_index + 1] = agcbuffer[1][i];
}
agc_wdsp.abs_ring[agc_wdsp.in_index] = fabsf(agcbuffer[0][i]);
if(use_stereo)
{
if(agc_wdsp.abs_ring[agc_wdsp.in_index] < fabsf(agcbuffer[1][i]))
{
agc_wdsp.abs_ring[agc_wdsp.in_index] = fabsf(agcbuffer[1][i]);
}
}
agc_wdsp.fast_backaverage = agc_wdsp.fast_backmult * agc_wdsp.abs_out_sample + agc_wdsp.onemfast_backmult * agc_wdsp.fast_backaverage;
agc_wdsp.hang_backaverage = agc_wdsp.hang_backmult * agc_wdsp.abs_out_sample + agc_wdsp.onemhang_backmult * agc_wdsp.hang_backaverage;
if(agc_wdsp.hang_backaverage > agc_wdsp.hang_level)
{
agc_wdsp_conf.hang_action = 1;
}
else
{
agc_wdsp_conf.hang_action = 0;
}
if ((agc_wdsp.abs_out_sample >= agc_wdsp.ring_max) && (agc_wdsp.abs_out_sample > 0.0))
{
agc_wdsp.ring_max = 0.0;
int k = agc_wdsp.out_index;
for (uint16_t j = 0; j < agc_wdsp.attack_buffsize; j++)
{
if (++k == agc_wdsp.ring_buffsize)
{
k = 0;
}
if (agc_wdsp.abs_ring[k] > agc_wdsp.ring_max)
{
agc_wdsp.ring_max = agc_wdsp.abs_ring[k];
}
}
}
if (agc_wdsp.abs_ring[agc_wdsp.in_index] > agc_wdsp.ring_max)
{
agc_wdsp.ring_max = agc_wdsp.abs_ring[agc_wdsp.in_index];
}
if (agc_wdsp.hang_counter > 0)
{
--agc_wdsp.hang_counter;
}
switch (agc_wdsp.state)
{
case 0: // starting point after ATTACK
{
if (agc_wdsp.ring_max >= agc_wdsp.volts)
{ // ATTACK
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.attack_mult;
}
else
{ // DECAY
if (agc_wdsp.volts > agc_wdsp.pop_ratio * agc_wdsp.fast_backaverage)
{ // short time constant detector
agc_wdsp.state = 1;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.fast_decay_mult;
}
else
{ // hang AGC enabled and being activated
if (agc_wdsp_conf.hang_enable && (agc_wdsp.hang_backaverage > agc_wdsp.hang_level))
{
agc_wdsp.state = 2;
agc_wdsp.hang_counter = (int)(agc_wdsp.hangtime * agc_wdsp.sample_rate);
agc_wdsp.decay_type = 1;
}
else
{// long time constant detector
agc_wdsp.state = 3;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.decay_mult;
agc_wdsp.decay_type = 0;
}
}
}
break;
}
case 1: // short time constant decay
{
if (agc_wdsp.ring_max >= agc_wdsp.volts)
{ // ATTACK
agc_wdsp.state = 0;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.attack_mult;
}
else
{
if (agc_wdsp.volts > agc_wdsp.save_volts)
{// short time constant detector
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.fast_decay_mult;
}
else
{
if (agc_wdsp.hang_counter > 0)
{
agc_wdsp.state = 2;
}
else
{
if (agc_wdsp.decay_type == 0)
{// long time constant detector
agc_wdsp.state = 3;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.decay_mult;
}
else
{ // hang time constant
agc_wdsp.state = 4;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.hang_decay_mult;
}
}
}
}
break;
}
case 2: // Hang is enabled and active, hang counter still counting
{ // ATTACK
if (agc_wdsp.ring_max >= agc_wdsp.volts)
{
agc_wdsp.state = 0;
agc_wdsp.save_volts = agc_wdsp.volts;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.attack_mult;
}
else
{
if (agc_wdsp.hang_counter == 0)
{ // hang time constant
agc_wdsp.state = 4;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.hang_decay_mult;
}
}
break;
}
case 3: // long time constant decay in progress
{
if (agc_wdsp.ring_max >= agc_wdsp.volts)
{ // ATTACK
agc_wdsp.state = 0;
agc_wdsp.save_volts = agc_wdsp.volts;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.attack_mult;
}
else
{ // DECAY
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.decay_mult;
}
break;
}
case 4: // hang was enabled and counter has counted to zero --> hang decay
{
if (agc_wdsp.ring_max >= agc_wdsp.volts)
{ // ATTACK
agc_wdsp.state = 0;
agc_wdsp.save_volts = agc_wdsp.volts;
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.attack_mult;
}
else
{ // HANG DECAY
agc_wdsp.volts += (agc_wdsp.ring_max - agc_wdsp.volts) * agc_wdsp.hang_decay_mult;
}
break;
}
}
if (agc_wdsp.volts < agc_wdsp.min_volts)
{
agc_wdsp.volts = agc_wdsp.min_volts; // no AGC action is taking place
agc_wdsp_conf.action = 0;
}
else
{
// LED indicator for AGC action
agc_wdsp_conf.action = 1;
}
float32_t vo = Math_log10f_fast(agc_wdsp.inv_max_input * agc_wdsp.volts);
if(vo > 0.0)
{
vo = 0.0;
}
float32_t mult = (agc_wdsp.out_target - agc_wdsp.slope_constant * vo) / agc_wdsp.volts;
agcbuffer[0][i] = agc_wdsp.out_sample[0] * mult;
if(use_stereo)
{
agcbuffer[1][i] = agc_wdsp.out_sample[1] * mult;
}
}
if(agc_wdsp.remove_dc)
{
static float32_t wold[2] = { 0.0, 0.0 };
// eliminate DC in the audio after the AGC
for(uint16_t i = 0; i < blockSize; i++)
{
float32_t w = agcbuffer[0][i] + wold[0] * 0.9999; // yes, I want a superb bass response ;-)
agcbuffer[0][i] = w - wold[0];
wold[0] = w;
if(use_stereo)
{
float32_t w = agcbuffer[1][i] + wold[1] * 0.9999; // yes, I want a superb bass response ;-)
agcbuffer[1][i] = w - wold[1];
wold[1] = w;
}
}
}
}
+46
View File
@@ -0,0 +1,46 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**--------------------------------------------------------------------------------**
** **
** Description: Code for automatic gain control based on the WDSP Lib **
** Licence: GNU GPLv3 **
************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __AUDIO_AGC_H
#define __AUDIO_AGC_H
#include "uhsdr_board_config.h"
#include "uhsdr_types.h"
// these are the control parameter
// for external runtime configuration
// of the AGC
typedef struct
{
uint8_t mode;
uint8_t slope;
uint8_t hang_enable;
int thresh;
int hang_thresh;
int hang_time;
uint8_t action;
uint8_t switch_mode;
uint8_t hang_action;
int tau_decay[6];
int tau_hang_decay;
} agc_wdsp_params_t;
extern agc_wdsp_params_t agc_wdsp_conf;
void AudioAgc_RunAgcWdsp(int16_t blockSize, float32_t (*agcbuffer)[AUDIO_BLOCK_SIZE], const bool use_stereo );
void AudioAgc_SetupAgcWdsp(float32_t sample_rate, bool remove_dc);
void AudioAgc_AgcWdsp_Init(void);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,78 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**--------------------------------------------------------------------------------**
** **
** Description: Code for fast convolution filtering **
** Licence: GNU GPLv3 **
************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __AUDIO_CONVOLUTION_H
#define __AUDIO_CONVOLUTION_H
#include "arm_math.h"
//#include "softdds.h"
#include "uhsdr_hw_i2s.h"
#include "uhsdr_board.h"
#include "codec.h"
#include "audio_driver.h"
#include "radio_management.h"
#include "usbd_audio_if.h"
#define SAMPLE_BUFFER_SIZE 256
#define SAMPLE_BUFFER_NUM 4
#define SAMPLE_BUFFER_FIFO_SIZE (SAMPLE_BUFFER_NUM + 1)
extern arm_fir_decimate_instance_f32 DECIMATE_RX_I;
extern arm_fir_decimate_instance_f32 DECIMATE_RX_Q;
//extern AudioDriverBuffer adb;
#ifdef USE_CONVOLUTION
#define FFT_CONVOLUTION_SIZE 256
#define CONVOLUTION_MAX_NO_OF_BLOCKS 8
#define CONVOLUTION_MAX_NO_OF_COEFFS 2048
#endif
#ifdef USE_CONVOLUTION
typedef struct
{
// for convolution filtering
float32_t i_buffer_convolution[FFT_CONVOLUTION_SIZE / 2];
float32_t q_buffer_convolution[FFT_CONVOLUTION_SIZE / 2];
float32_t fmask[CONVOLUTION_MAX_NO_OF_BLOCKS][FFT_CONVOLUTION_SIZE * 2];
float32_t fftin[FFT_CONVOLUTION_SIZE * 2];
float32_t fftout[CONVOLUTION_MAX_NO_OF_BLOCKS][FFT_CONVOLUTION_SIZE * 2];
float32_t ifftout[FFT_CONVOLUTION_SIZE * 2];
float32_t accum[FFT_CONVOLUTION_SIZE * 2];
float32_t a_buffer[2][FFT_CONVOLUTION_SIZE / 2]; // for convolution, we need an output buffer of 128 samples
} ConvolutionBuffers;
typedef struct
{
// for convolution filtering
int nc; // no. of coefficients
int size; // no. of input samples
int nfor; // no. of blocks in the convolution
int buffidx; // buffer pointer
int DF; // decimation factor
float32_t impulse[CONVOLUTION_MAX_NO_OF_COEFFS * 2]; // impulse response has real and imaginary components
float32_t maskgen[FFT_CONVOLUTION_SIZE * 2];
} ConvolutionBuffersShared;
extern ConvolutionBuffersShared cbs;
void AudioDriver_CalcConvolutionFilterCoeffs (int N, float32_t f_low, float32_t f_high, float32_t samplerate, int wintype, int rtype, float32_t scale);
void AudioDriver_RxProcessorConvolution(AudioSample_t * const src, AudioSample_t * const dst, const uint16_t blockSize);
void convolution_handle(void);
#endif
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,682 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __AUDIO_DRIVER_H
#define __AUDIO_DRIVER_H
#include "uhsdr_board_config.h"
#include "uhsdr_types.h"
#include "arm_math.h"
#include "softdds.h"
#include "audio_filter.h"
#define IQ_SAMPLE_RATE_F ((float32_t)IQ_SAMPLE_RATE)
#define AUDIO_SAMPLE_RATE_F ((float32_t)AUDIO_SAMPLE_RATE)
#if defined(USE_32_IQ_BITS)
typedef int32_t iq_data_t;
#else
typedef int16_t iq_data_t;
#endif
#if defined(USE_32_AUDIO_BITS)
typedef int32_t audio_data_t;
#else
typedef int16_t audio_data_t;
#endif
typedef struct {
__packed audio_data_t l;
__packed audio_data_t r;
} AudioSample_t;
typedef struct {
__packed iq_data_t l;
__packed iq_data_t r;
} IqSample_t;
#ifdef USE_CONVOLUTION
typedef struct {
COMP samples[SAMPLE_BUFFER_SIZE];
} Sample_Buffer;
#endif
// -----------------------------
// FFT buffer, this is double the size of the length of the FFT used for spectrum display and waterfall spectrum
#ifdef USE_FFT_1024
#define FFT_IQ_BUFF_LEN 1024
#else
#define FFT_IQ_BUFF_LEN 512
#endif
#define SPEC_BUFF_LEN (FFT_IQ_BUFF_LEN/2)
// twice the number of samples in the each iq block buffer
// (which is half of the total dma buffer, since in each interrupt we get half of the total dma buffer)
#define IQ_BUFSZ (2*IQ_BLOCK_SIZE)
#define AUDIO_BUFSZ (2*AUDIO_BLOCK_SIZE)
// Audio filter
#define FIR_RXAUDIO_BLOCK_SIZE IQ_BLOCK_SIZE
#define FIR_RXAUDIO_NUM_TAPS 16 // maximum number of taps in the decimation and interpolation FIR filters
#define IIR_RXAUDIO_BLOCK_SIZE IQ_BLOCK_SIZE
#define IIR_RXAUDIO_NUM_STAGES_MAX 12 // we use a maximum stage number of 10 at the moment, so this is 12 just to be safe
//
#define CODEC_DEFAULT_GAIN 0x1F // Gain of line input to start with
#define ADC_CLIP_WARN_THRESHOLD 4096 // This is at least 12dB below the clipping threshold of the A/D converter itself
//
//
//
#define SCALING_FACTOR_IQ_PHASE_ADJUST 2000.0
#define SCALING_FACTOR_IQ_AMPLITUDE_ADJUST 2731.0
#define SAM_PLL_HILBERT_STAGES 7
#ifdef USE_TWO_CHANNEL_AUDIO
#define NUM_AUDIO_CHANNELS 2
#else
#define NUM_AUDIO_CHANNELS 1
#endif
typedef struct
{
// for SAM demodulation
// DX adjustments: zeta = 0.15, omegaN = 100.0
// very stable, but does not lock very fast
// standard settings: zeta = 1.0, omegaN = 250.0
// maybe user can choose between slow (DX), medium, fast SAM PLL
// zeta / omegaN
// DX = 0.2, 70
// medium 0.6, 200
// fast 1.2, 500
//pll
float32_t omega_min; // (2.0 * 3.141592653589793f * pll_fmin * DF / IQ_SAMPLE_RATE_F);
float32_t omega_max; //(2.0 * 3.141592653589793f * pll_fmax * DF / IQ_SAMPLE_RATE_F);
float32_t g1; //(1.0 - exp(-2.0 * omegaN * zeta * DF / IQ_SAMPLE_RATE_F));
float32_t g2; //(- g1 + 2.0 * (1 - exp(- omegaN * zeta * DF / IQ_SAMPLE_RATE_F)
// * cosf(omegaN * DF / IQ_SAMPLE_RATE_F * sqrtf(1.0 - zeta * zeta))));
//fade leveler
float32_t mtauR; //(exp(- DF / (IQ_SAMPLE_RATE_F * tauR))); //0.99948;
float32_t onem_mtauR;
float32_t mtauI; //(exp(- DF / (IQ_SAMPLE_RATE_F * tauI))); //0.99999255955;
float32_t onem_mtauI;
} demod_sam_param_t;
typedef struct
{
float32_t teta1;
float32_t teta2;
float32_t teta3;
float32_t teta1_old;
float32_t teta2_old;
float32_t teta3_old;
float32_t M_c1;
float32_t M_c2;
} iq_correction_data_t;
typedef float32_t audio_block_t[AUDIO_BLOCK_SIZE];
typedef float32_t iq_block_t[IQ_BLOCK_SIZE];
typedef struct
{
iq_block_t i_buffer;
iq_block_t q_buffer;
} iq_buffer_t;
typedef struct
{
// Stereo buffers
iq_buffer_t iq_buf;
float32_t agc_valbuf[IQ_BLOCK_SIZE]; // holder for "running" AGC value
audio_block_t a_buffer[2];
demod_sam_param_t sam;
iq_correction_data_t iq_corr;
} AudioDriverBuffer;
typedef struct
{
float sql_avg; // averaged squelch level (for FM)
bool squelched; // TRUE if FM receiver audio is to be squelched
float subaudible_tone_gen_freq; // frequency, in Hz, of currently-selected subaudible tone for generation
soft_dds_t subaudible_tone_dds;
soft_dds_t tone_burst_dds;
bool tone_burst_active; // this is TRUE if the tone burst is actively being generated
//
float subaudible_tone_det_freq; // frequency, in Hz, of currently-selected subaudible tone for detection
bool subaudible_tone_detected; // TRUE if subaudible tone has been detected
Goertzel goertzel[3];
#define FM_HIGH 0
#define FM_LOW 1
#define FM_CTR 2
} fm_conf_t;
typedef enum
{
SAM_SIDEBAND_BOTH = 0,
SAM_SIDEBAND_LSB,
SAM_SIDEBAND_USB,
#ifdef USE_TWO_CHANNEL_AUDIO
SAM_SIDEBAND_STEREO,
#endif
SAM_SIDEBAND_MAX
} sam_sideband_t;
typedef struct
{
#define DSP_NR_ENABLE 0x01 // DSP NR mode is on (| 1)
#define DSP_NR_POSTAGC_ENABLE 0x02 // DSP NR is to occur post AGC (| 2)
#define DSP_NOTCH_ENABLE 0x04 // DSP Notch mode is on (| 4)
#define DSP_NB_ENABLE 0x08 // DSP is to be displayed on screen instead of NB (| 8)
#define DSP_MNOTCH_ENABLE 0x10 // Manual Notch enabled
#define DSP_MPEAK_ENABLE 0x20 // Manual Peak enabled
#define DSP_ANR_ENABLE 0x40 // DSP ANR (Leak LMS) mode is on
uint8_t active; // Used to hold various aspects of DSP mode selection
uint8_t mode; // holds the mode chosen in the DSP
uint16_t mode_mask; // holds the DSP mode mask (to be chosen by virtual dsp keyboard)
uint8_t active_toggle; // holder used on the press-hold of button G2 to "remember" the previous setting
uint8_t nr_strength; // "Strength" of DSP Noise reduction - to be converted to "Mu" factor
#if defined (USE_LMS_AUTONOTCH)
uint8_t notch_numtaps;
uint8_t notch_mu;
// mu adjust of notch DSP LMS
uint8_t notch_delaybuf_len; // size of DSP notch delay buffer
#endif
uint8_t inhibit; // if != 0, DSP (NR, Notch) functions are inhibited. Used during power-up and switching
uint8_t nb_setting;
ulong notch_frequency; // frequency of the manual notch filter
ulong peak_frequency; // frequency of the manual peak filter
int bass_gain; // gain of the low shelf EQ filter
int treble_gain; // gain of the high shelf EQ filter
// int tx_bass_gain; // gain of the TX low shelf EQ filter
// int tx_treble_gain; // gain of the TX high shelf EQ filter
int tx_eq_gain[5]; // gain of the TX 5-bands EQ filter
} dsp_params_t;
// Audio driver publics
typedef struct AudioDriverState
{
//
// Lock audio filter flag
//
volatile bool af_disabled; // if TRUE, audio filtering is disabled (used during filter bandwidth changing, etc.)
volatile bool tx_filter_adjusting; // used to disable TX I/Q filter during phase adjustment
float codec_gain_calc; // spectrum gain value
bool adc_clip; // used to display warning in s meter
bool adc_half_clip; // used to control input gain and spectrum gain
bool adc_quarter_clip; // used to control input gain and spectrum gain
float peak_audio; // used for audio metering to detect the peak audio level
float alc_val; // "live" transmitter ALC value
float alc_decay; // decay rate (speed) of ALC
uchar decimation_rate; // current decimation/interpolation rate
uint32_t decimated_freq; // resulting decimated sample frequency (used in iq and audio processing)
fm_conf_t fm_conf; // configuration parameters for the fm demodulator
soft_dds_t beep; // this is the actively-used DDS tone word for the radio's beep generator
float beep_loudness_factor; // this is used to set the beep loudness
/* SAM */
// sam related output variables
int carrier_freq_offset;
// sam related configuration parameters, stored in config memory
int pll_fmax_int;
int zeta_int; // zeta * 100
int omegaN_int;
uint8_t fade_leveler; // boolean
// sam related operation parameters, not stored in config memory
sam_sideband_t sam_sideband; // 0 = both, 1 = LSB, 2 = USB
/* IQ Balance */
float32_t iq_phase_balance_rx;
float32_t iq_phase_balance_tx[IQ_TRANS_NUM];
ulong snap_carrier_freq; // used for passing the estimated carrier freq in SNAP mode to the print routine in UI_Driver
bool CW_signal; // if CW decoder is enabled and carrier snap is wanted, this indicates whenever a pulse is received
// only in that case, the carrier frequency is estimated and the display refreshed
} AudioDriverState;
void AudioManagement_CalcIQPhaseAdjust(uint32_t freq);
// S meter public
typedef struct SMeter
{
// configurable ALPHA = 1 - e^(-T/Tau)
// we use alpha config value scaling of 100, i.e. 100 => alpha = 1.00
// this construct permits us to use a single configuration store for both
// it looks rather complex but this is necessary to ensure type safety checks are working
union {
uint16_t alphaCombined;
struct
{
uint8_t DecayAlpha;
uint8_t AttackAlpha;
} alphaSplit;
} config;
// first/upper 8 bits is AttackAlpha
// second/lower 8 bits is DecayAlpha
#define SMETER_ALPHA_ATTACK_DEFAULT 50
#define SMETER_ALPHA_DECAY_DEFAULT 5
#define SMETER_ALPHA_MIN 1 // used for both alphas
#define SMETER_ALPHA_MAX 100 // used for both alphas
// averaged values, used for display
float32_t dbm;
float32_t dbmhz;
// current measurements, used for averaging
float32_t dbm_cur;
float32_t dbmhz_cur;
// internal variables for dbm low pass calculation
float32_t AttackAvedbm;
float32_t DecayAvedbm;
float32_t AttackAvedbmhz;
float32_t DecayAvedbmhz;
uint32_t s_count; // number of S steps, used for display and CAT level
} SMeter;
#define MAX_BASS 20
#define MIN_BASS -20
#define MAX_TREBLE 20
#define MIN_TREBLE -20
//#define MAX_TX_BASS 5
//#define MIN_TX_BASS -20
//#define MAX_TX_TREBLE 5
//#define MIN_TX_TREBLE -20
#define MAX_TX_EQ 20
#define MIN_TX_EQ -20
#define MIN_PEAK_NOTCH_FREQ 200
//
// AGC Time constants
// C. Turner, KA7OEI
//
#define AGC_KNEE 1000//4000 // ADC "knee" threshold for AGC action
//
#define AGC_KNEE_REF 1000
#define AGC_VAL_MAX_REF 131072//4096
#define POST_AGC_GAIN_SCALING_REF 1.333
#define AGC_ATTACK 0.033 // Attack time multiplier for AGC
//
#define AGC_FAST_DECAY 0.0002 // Decay rate multiplier for "Fast" AGC
#define AGC_MED_DECAY 0.00006 // Decay rate multiplier for "Medium" AGC
#define AGC_SLOW_DECAY 0.00001 // Decay rate for multiplier "Slow" AGC
//
#define AGC_ATTACK_FM 0.0033 // Attack time for FM (S-meter reading only)
#define AGC_DECAY_FM 0.0333 // Decay time for FM (S-meter reading only)
//
#define AGC_VAL_MIN 0.02 // Minimum AGC gain multiplier (e.g. gain reduction of 34dB)
//#define AGC_VAL_MAX 4096//1024 // Maximum AGC gain multiplier (e.g. gain multiplication of 60dB)
#define AGC_PREFILTER_MAXGAIN 5 // Scaling factor for RF gain adjustment (e.g. factor by which RFG will be multiplied to yield actual RFG multiplier
#define AGC_PREFILTER_MINGAIN 0.5 // Minimum "RFG" gain multiplier (e.g. gain reduction of 6 dB)
//
#define AGC_PREFILTER_HISIG_THRESHOLD 0.1 // Threshold at which adjustment of RFGAIN (pre-filter) gain adjustment will occur
#define AGC_PREFILTER_LOWSIG_THRESHOLD 1.0 // Threshold at which adjustment of RFGAIN (pre-filter) gain adjustment will occur
#define AGC_PREFILTER_ATTACK_RATE 0.0002 // Attack rate for RFG reduction
#define AGC_PREFILTER_DECAY_RATE 0.000002 // Decay rate for RFG gain recovery
//
#define AGC_PREFILTER_MAX_SIGNAL 1 // maximum level of pre-filtered signal
//
#define POST_AGC_GAIN_SCALING 1.333//0.333 // Used to rescale the post-filter audio level to a value suitable for the codec. This sets the line level output
// to approx. 1000mV peak-peak.
//
#define POST_AGC_GAIN_SCALING_DECIMATE_4 3.46 // Used to scale audio from the decimation/interpolation-by-4 process (based square root of decimation factor)
//
#define POST_AGC_GAIN_SCALING_DECIMATE_2 (POST_AGC_GAIN_SCALING_DECIMATE_4 * 0.6) // Scales audio from decimation/interpolation-by-2 process
//
#define AM_SCALING 1.0 // was 2.0 // Amount of gain multiplication to apply to audio and AGC to make recovery equal to that of SSB
#define AM_AUDIO_SCALING 1.4 // was 1.4 // Additional correction factor applied to audio demodulation to make amplitude equal to that of SSB demodulation
//
//#define AGC_GAIN_CAL 155000.0//22440 // multiplier value (linear, not DB) to calibrate the S-Meter reading to the AGC value
//
#define AUTO_RFG_DECREASE_LOCKOUT 1
#define AUTO_RFG_INCREASE_TIMER 5//10
//
//#define AGC_SLOW 0 // Mode setting for slow AGC
//#define AGC_MED 1 // Mode setting for medium AGC
//#define AGC_FAST 2 // Mode setting for fast AGC
//#define AGC_CUSTOM 3 // Mode setting for custom AGC
//#define AGC_OFF 4 // Mode setting for AGC off
//#define AGC_MAX_MODE 4 // Maximum for mode setting for AGC
//#define AGC_DEFAULT AGC_MED // Default!
//
//#define AGC_CUSTOM_MAX 30 // Maximum (slowest) setting for custom AGC
//#define AGC_CUSTOM_DEFAULT 12 // Default custom AGC setting (approx. equal to "medium")
//#define AGC_CUSTOM_FAST_WARNING 2 // Value at or below which setting the custom AGC is likely to degrade audio
//
#define MAX_RF_GAIN_MAX 30 // Maximum setting for "Max RF gain"
#define MAX_RF_GAIN_DEFAULT 10
// Noise blanker constants
#define MAX_NB_SETTING 15
#define NB_WARNING1_SETTING 7 // setting at or above which NB warning1 (yellow) is given
#define NB_WARNING2_SETTING 12 // setting at or above which NB warning2 (orange) is given
#define NB_WARNING3_SETTING 15 // setting at or above which NB warning3 (red) is given
// Values used for "custom" AGC settings
#define LINE_OUT_SCALING_FACTOR 10 // multiplication of audio for fixed LINE out level (nominally 1vpp)
//
#define LINE_IN_GAIN_RESCALE 20 // multiplier for line input gain
#define MIC_GAIN_RESCALE 2 // divisor for microphone gain setting
// ALC (Auto Level Control) for transmitter, constants
#define ALC_VAL_MAX 1 // Maximum ALC Value is 1 (e.g. it can NEVER amplify)
#define ALC_VAL_MIN 0.001 // Minimum ALC Value - it can provide up to 60dB of attenuation
#define ALC_ATTACK 0.1//0.033 // Attack time for the ALC's gain control
#define ALC_KNEE 30000 // The audio value threshold for the ALC operation
// Decay (release time) for ALC/Audio compressor
#define ALC_DECAY_MAX 20 // Maximum (slowest) setting for ALC decay
#define ALC_DECAY_DEFAULT 10 // Default custom ALC setting (approx. equal to AGC "medium")
// Audio post-filter (pre-alc) gain adjust. This effectively sets the min/max compression level.
#define ALC_POSTFILT_GAIN_MIN 1
#define ALC_POSTFILT_GAIN_MAX 25
#define ALC_POSTFILT_GAIN_DEFAULT 1
//
#define SSB_ALC_GAIN_CORRECTION 1.00 // This scales the output of the ALC for 100% modulation for SSB transmission
//
#define SSB_GAIN_COMP 1.133 // This compensates for slight differences in gain processing in the SSB algorithm (empirically derived)
//
#define AM_GAIN_COMP 1.133 // This compensates for slight differences in gain processing in the AM algorithm (empirically derived)
//
#define FREEDV_GAIN_COMP (20*SSB_GAIN_COMP)
// The following are calibration constants for AM (transmitter) modulation, carefully adjusted for proper D/A scaling to set
// maximum possible 95-100% AM modulation depth.
//
#define AM_ALC_GAIN_CORRECTION 0.23 // This scales the output of the ALC for 100% modulation with respect to the carrier level
#define AM_CARRIER_LEVEL 5100 // This sets the AM carrier level in DAC units, scaled for proper ALC operation and 100% modulation
//
// DO NOT change the above unless you know *EXACTLY* what you are doing! If you screw with these numbers, you WILL wreck the
// AM modulation!!! (No, I'm not kidding!)
// FM TX/RX
#define NUM_SUBAUDIBLE_TONES 56
#define FM_SUBAUDIBLE_TONE_OFF 0
// FM TX
#define FM_TONE_BURST_MAX 2
extern uint32_t fm_tone_burst_freq[FM_TONE_BURST_MAX+1];
#define FM_TONE_BURST_OFF 0
#define FM_TONE_BURST_DURATION 100 // duration, in 100ths of a second, of the tone burst
// FM RX
#define FM_SQUELCH_MAX 20 // maximum setting for FM squelch
#define FM_SQUELCH_DEFAULT 12 // default setting for FM squelch
#define FM_SUBAUDIBLE_GOERTZEL_WINDOW 400 // this sets the overall number of samples involved in the Goertzel decode windows (this value * "size/2")
#define MIN_BEEP_FREQUENCY 200 // minimum beep frequency in Hz
#define MAX_BEEP_FREQUENCY 3000 // maximum beep frequency in Hz
#define DEFAULT_BEEP_FREQUENCY 1000 // default beep frequency in Hz
#define BEEP_DURATION 2 // duration of beep in 100ths of a second
#define MAX_BEEP_LOUDNESS 21 // maximum setting for beep loudness
#define DEFAULT_BEEP_LOUDNESS 10 // default loudness for the keyboard beep
// Factors used in audio compressor adjustment limits
//
#define TX_AUDIO_COMPRESSION_MIN -1 // -1 = OFF
#define TX_AUDIO_COMPRESSION_MAX 13 // 0 = least compression, 12 = most, 13 = EEPROM values ("CUS" = CUSTOM) - custom selected by user
#define TX_AUDIO_COMPRESSION_SV 13
#define TX_AUDIO_COMPRESSION_DEFAULT 2
//
//
#define RX_DECIMATION_RATE_8KHZ 6 // Decimation/Interpolation rate in receive function for 8 kHz sample rate
#define RX_DECIMATION_RATE_12KHZ 4 // Decimation/Interpolation rate in receive function for 12 kHz sample rate
#define RX_DECIMATION_RATE_24KHZ 2 // Decimation/Interpolation rate in receive function for 24 kHz sample rate
#define RX_DECIMATION_RATE_48KHZ 1 // Deimcation/Interpolation rate in receive function for 48 kHz sample rate (e.g. no decimation!)
#define DSP_NR_STRENGTH_MIN 1
#define DSP_NR_STRENGTH_MAX 200 // Maximum menu setting for DSP "Strength"
#define DSP_NR_STRENGTH_STEP 5
#define DSP_NR_STRENGTH_DEFAULT 160 // Default setting
#ifdef USE_LMS_AUTONOTCH
//
// Automatic Notch Filter
//
#define LMS_NOTCH_DELAYBUF_SIZE_MAX 512
//
#define DSP_NOTCH_NUMTAPS_MAX 64//128
#define DSP_NOTCH_NUMTAPS_MIN 64
#define DSP_NOTCH_NUMTAPS_DEFAULT 64//96
//
#define DSP_NOTCH_BUFLEN_MIN 128//64//48 // minimum length of decorrelation buffer for the notch filter FIR
#define DSP_NOTCH_BUFLEN_MAX 128//192 // maximum decorrelation buffer length for the notch filter FIR
#define DSP_NOTCH_DELAYBUF_DEFAULT 128//104 // default decorrelation buffer length for the notch filter FIR
//
#define DSP_NOTCH_MU_MAX 40//40 // maximum "strength" (convergence) setting for the notch
#define DSP_NOTCH_MU_DEFAULT 10//25 // default convergence setting for the notch
#endif
typedef enum
{
DSP_SWITCH_OFF = 0,
DSP_SWITCH_NR,
DSP_SWITCH_ANR,
DSP_SWITCH_NOTCH,
DSP_SWITCH_NR_AND_NOTCH,
DSP_SWITCH_NOTCH_MANUAL,
DSP_SWITCH_PEAK_FILTER,
DSP_SWITCH_MAX, // bass & treble not used here
DSP_SWITCH_BASS = 98,
DSP_SWITCH_TREBLE = 99,
} dsp_mode_t;
typedef enum
{
ModeVK_SSB = 0,
ModeVK_CW,
ModeVK_AM,
ModeVK_SAM,
ModeVK_FM,
ModeVK_RTTY,
ModeVK_BPSK,
ModeVK_FDV,
ModeVK_SWITCH_MAX,
} ModeVK_t;
#define DSP_SWITCH_MODEMASK_ENABLE_MASK ((1<<DSP_SWITCH_MAX)-1)
#define DSP_SWITCH_MODEMASK_ENABLE_DEFAULT ((1<<DSP_SWITCH_MAX)-1)
#define DSP_SWITCH_MODEMASK_ENABLE_DSPOFF (1<<DSP_SWITCH_OFF)
//
#define AUDIO_DELAY_BUFSIZE (IQ_BUFSZ)*5 // Size of AGC delaying audio buffer - Must be a multiple of IQ_BUFSZ.
// This is divided by the decimation rate so that the time delay is constant.
#define FREQ_IQ_CONV_MODE_OFF 0 // No frequency conversion
#define FREQ_IQ_CONV_P6KHZ 1 // LO is 6KHz above receive frequency in RX mode
#define FREQ_IQ_CONV_M6KHZ 2 // LO is 6KHz below receive frequency in RX mode
#define FREQ_IQ_CONV_P12KHZ 3 // LO is 12KHz above receive frequency in RX mode
#define FREQ_IQ_CONV_M12KHZ 4 // LO is 12KHz below receive frequency in RX mode
#define FREQ_IQ_CONV_SLIDE 5 // LO slide frequency in RX mode
//
#define FREQ_IQ_CONV_MODE_DEFAULT FREQ_IQ_CONV_M12KHZ //FREQ_IQ_CONV_MODE_OFF
#define FREQ_IQ_CONV_MODE_MAX 5
// Public Audio
extern AudioDriverState ads;
extern SMeter sm;
extern AudioDriverBuffer adb;
typedef struct SnapCarrier
{
bool snap;
} SnapCarrier;
extern SnapCarrier sc;
#ifdef USE_LEAKY_LMS
#define LEAKYLMSDLINE_SIZE 256 //512 // was 256 //2048 // dline_size
// 1024 funktioniert nicht
typedef struct
{// Automatic noise reduction
// Variable-leak LMS algorithm
// taken from (c) Warren Pratts wdsp library 2016
// GPLv3 licensed
// #define DLINE_SIZE 256 //512 //2048 // dline_size
int16_t n_taps; // = 64; //64; // taps
int16_t delay; // = 16; //16; // delay
int dline_size; // = LEAKYLMSDLINE_SIZE;
//int ANR_buff_size = FFT_length / 2.0;
int position;// = 0;
float32_t two_mu;// = 0.0001; typical: 0.001 to 0.000001 = 1000 to 1 -> div by 1000000 // two_mu --> "gain"
uint32_t two_mu_int;
float32_t gamma;// = 0.1; typical: 1.000 to 0.001 = 1000 to 1 -> div by 1000 // gamma --> "leakage"
uint32_t gamma_int;
float32_t lidx;// = 120.0; // lidx
float32_t lidx_min;// = 0.0; // lidx_min
float32_t lidx_max;// = 200.0; // lidx_max
float32_t ngamma;// = 0.001; // ngamma
float32_t den_mult;// = 6.25e-10; // den_mult
float32_t lincr;// = 1.0; // lincr
float32_t ldecr;// = 3.0; // ldecr
//int ANR_mask = ANR_dline_size - 1;
int mask;// = DLINE_SIZE - 1;
int in_idx;// = 0;
float32_t d [LEAKYLMSDLINE_SIZE];
float32_t w [LEAKYLMSDLINE_SIZE];
uint8_t on;// = 0;
uint8_t notch;// = 0;
} lLMS;
extern lLMS leakyLMS;
#endif
// TODO: Discuss to drop 16 bit I2S support. Would simplify the incoming/outgoing data handling. We could
// align all scalings accordingly and would not have most of the ugly stuff below
// FIXME: This is ugly: The STM32F4 returns 32bit reads from 16 bit peripherals such as the SPI/I2S
// with the two half words in "mixed endian" instead of the wanted "little endian". This is documented in
// the data sheet, so the only thing we can do is to swap the halfwords. This is in fact a single ror16 operation
// if the compiler is smart enough to detect what we want.
// these constants are used to adjust the 32 bit integer samples to represent the same levels as if we sample 16 bit integers,
// effectively "shifting" them down or up.
// FIXME: switch to 16 bit extended mode for 16 bit samples will eliminate the need for this at the expense of
// using the same DMA memory (two times the memory true 16 bit values take, in our case this is 2*(2*(IQ_BLOCK_SIZE*2samples*2bytes) = 512 bytes)
#ifdef USE_32_IQ_BITS
#define IQ_BIT_SHIFT 16
#define IQ_BIT_SCALE_DOWN (0.0000152587890625)
#else
#define IQ_BIT_SHIFT 0
#define IQ_BIT_SCALE_DOWN (1.0)
#endif
#define IQ_BIT_SCALE_UP (1<<IQ_BIT_SHIFT)
#define IQ_BIT_SCALE_DOWN24 (0.0039)
#ifdef USE_32_AUDIO_BITS
#define AUDIO_BIT_SHIFT 16
#define AUDIO_BIT_SCALE_DOWN (0.0000152587890625)
#else
#define AUDIO_BIT_SHIFT 0
#define AUDIO_BIT_SCALE_DOWN (1.0)
#endif
#define AUDIO_BIT_SCALE_UP (1<<AUDIO_BIT_SHIFT)
// we have to swap them only if we are having 32bit values from/to I2S and an STM32F4
#if defined(STM32F4) && defined(USE_32_IQ_BITS)
static inline int32_t I2S_correctHalfWord(const int32_t word)
{
uint32_t uWord = (uint32_t)word;
return uWord >> 16 | uWord << 16;
}
static inline int16_t I2S_IqSample_2_Int16(const iq_data_t sample) { return (I2S_correctHalfWord(sample)) >> IQ_BIT_SHIFT; }
static inline int16_t I2S_AudioSample_2_Int16(const iq_data_t sample) { return (I2S_correctHalfWord(sample)) >> IQ_BIT_SHIFT; }
static inline iq_data_t I2S_Int16_2_IqSample(const int16_t sample) { return (I2S_correctHalfWord(sample << IQ_BIT_SHIFT)); }
static inline iq_data_t I2S_Int16_2_AudioSample(const int16_t sample) { return (I2S_correctHalfWord(sample << IQ_BIT_SHIFT)); }
#else
#define I2S_correctHalfWord(a) (a)
#if defined(USE_32_IQ_BITS)
static inline int16_t I2S_IqSample_2_Int16(const iq_data_t sample) { return sample >> 16; }
static inline iq_data_t I2S_Int16_2_IqSample(const int16_t sample) { return sample << 16; }
#else
static inline int16_t I2S_IqSample_2_Int16(const iq_data_t sample) { return sample; }
static inline iq_data_t I2S_Int16_2_IqSample(const int16_t sample) { return sample; }
#endif
#if defined(USE_32_AUDIO_BITS)
static inline int16_t I2S_AudioSample_2_Int16(const iq_data_t sample) { return sample >> 16; }
static inline iq_data_t I2S_Int16_2_AudioSample(const int16_t sample) { return sample << 16; }
#else
static inline int16_t I2S_AudioSample_2_Int16(const iq_data_t sample) { return sample; }
static inline iq_data_t I2S_Int16_2_AudioSample(const int16_t sample) { return sample; }
#endif
#endif
// Exports
void AudioDriver_Init(void);
void AudioDriver_SetProcessingChain(uint8_t dmod_mode, bool reset_dsp_nr);
int32_t AudioDriver_GetTranslateFreq(void);
void AudioDriver_SetSamPllParameters (void);
void AudioDriver_I2SCallback(AudioSample_t *audio, IqSample_t *iq, AudioSample_t *audioDst, int16_t size);
void AudioDriver_CalcPeakEQ(float32_t coeffs[5], float32_t f0, float32_t q, float32_t gain, float32_t FS);
void AudioDriver_CalcLowShelf(float32_t coeffs[5], float32_t f0, float32_t S, float32_t gain, float32_t FS);
void AudioDriver_CalcHighShelf(float32_t coeffs[5], float32_t f0, float32_t S, float32_t gain, float32_t FS);
void AudioDriver_CalcBandpass(float32_t coeffs[5], float32_t f0, float32_t FS);
void AudioDriver_SetBiquadCoeffs(float32_t* coeffsTo,const float32_t* coeffsFrom);
void AudioDriver_IQPhaseAdjust(uint16_t txrx_mode, float32_t* i_buffer, float32_t* q_buffer, const uint16_t blockSize);
void AudioDriver_AgcWdsp_Set(void);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,182 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef DRIVERS_AUDIO_AUDIO_FILTER_H_
#define DRIVERS_AUDIO_AUDIO_FILTER_H_
#include "uhsdr_types.h"
#include "arm_math.h"
// TODO: Decide if we switch to use this struct
typedef struct
{
arm_fir_instance_f32 I;
arm_fir_instance_f32 Q;
} FIR_IQ_t;
extern arm_fir_instance_f32 Fir_Tx_Hilbert_Q;
extern arm_fir_instance_f32 Fir_Tx_Hilbert_I;
extern arm_fir_instance_f32 Fir_Rx_Hilbert_Q;
extern arm_fir_instance_f32 Fir_Rx_Hilbert_I;
extern arm_fir_instance_f32 Fir_TxFreeDV_Interpolate_Q;
extern arm_fir_instance_f32 Fir_TxFreeDV_Interpolate_I;
extern arm_fir_decimate_instance_f32 DECIMATE_RX_I;
extern arm_fir_decimate_instance_f32 DECIMATE_RX_Q;
void AudioFilter_SetRxHilbertAndDecimationFIR(uint8_t dmod_mode);
void AudioFilter_SetTxHilbertFIR(void);
enum
{
AUDIO_100HZ = 0,
AUDIO_200HZ,
// AUDIO_300HZ = 0,
AUDIO_300HZ,
AUDIO_500HZ,
AUDIO_1P4KHZ,
AUDIO_1P6KHZ,
AUDIO_1P8KHZ,
AUDIO_2P1KHZ,
AUDIO_2P3KHZ,
AUDIO_2P5KHZ,
AUDIO_2P7KHZ,
AUDIO_2P9KHZ,
AUDIO_3P2KHZ,
AUDIO_3P4KHZ,
AUDIO_3P6KHZ,
AUDIO_3P8KHZ,
AUDIO_4P0KHZ,
AUDIO_4P2KHZ,
AUDIO_4P4KHZ,
AUDIO_4P6KHZ,
AUDIO_4P8KHZ,
AUDIO_5P0KHZ,
AUDIO_5P5KHZ,
AUDIO_6P0KHZ,
AUDIO_6P5KHZ,
AUDIO_7P0KHZ,
AUDIO_7P5KHZ,
AUDIO_8P0KHZ,
AUDIO_8P5KHZ,
AUDIO_9P0KHZ,
AUDIO_9P5KHZ,
AUDIO_10P0KHZ,
AUDIO_OFF,
AUDIO_FILTER_NUM
};
enum
{
FILTER_MODE_CW = 0,
FILTER_MODE_SSB,
FILTER_MODE_AM,
FILTER_MODE_FM,
FILTER_MODE_SAM,
FILTER_MODE_MAX
};
#define AUDIO_DEFAULT_FILTER AUDIO_2P3KHZ
//
// use below to define the lowest-used filter number
//
#define AUDIO_MIN_FILTER 0
//
// use below to define the highest-used filter number-1
//
#define AUDIO_MAX_FILTER (AUDIO_FILTER_NUM-1)
typedef struct FilterDescriptor_s
{
const uint8_t id;
const char* const name;
const uint16_t width;
} FilterDescriptor;
extern const FilterDescriptor FilterInfo[AUDIO_FILTER_NUM];
extern uint16_t filterpath_mode_map[FILTER_MODE_MAX];
typedef struct FilterPathDescriptor_s
{
const uint8_t id;
const char* name;
const uint16_t mode;
const uint8_t filter_select_id;
// arm_fir_instance_f32*
const uint16_t FIR_numTaps;
const float *FIR_I_coeff_file;
// arm_fir_instance_f32*
const float *FIR_Q_coeff_file;
// arm_fir_decimate_instance_f32*
const arm_fir_decimate_instance_f32* dec;
const uint8_t sample_rate_dec;
const arm_iir_lattice_instance_f32* pre_instance;
const arm_fir_interpolate_instance_f32* interpolate;
const arm_iir_lattice_instance_f32* iir_instance;
// const arm_biquad_casd_df1_inst_f32* notch_instance;
const uint16_t offset; // how much offset in Hz has the center frequency of the filter from base frequency.
// remark: this IS in fact IDENTICAL to the CENTRE FREQUENCY of the filter
// For most non-CW filters 0 is okay,
// in this case bandwidth/2 is being used here.
} FilterPathDescriptor;
//#define AUDIO_FILTER_PATH_NUM 87
#define AUDIO_FILTER_PATH_NUM 107
extern const FilterPathDescriptor FilterPathInfo[AUDIO_FILTER_PATH_NUM];
enum
{
PATH_ALL_APPLICABLE = 0,
PATH_NEXT_BANDWIDTH = 1,
PATH_SAME_BANDWITH =2,
PATH_UP = 4,
PATH_DOWN = 8,
PATH_USE_RULES = 16,
PATH_LAST_USED_IN_MODE = 32,
PATH_DONT_STORE = 64,
};
uint8_t AudioFilter_NextApplicableFilterPath(const uint16_t query, const uint16_t filter_mode, const uint8_t current_path);
bool AudioFilter_IsApplicableFilterPath(const uint16_t query, const uint16_t filter_mode, const uint8_t filter_path);
void AudioFilter_GetNamesOfFilterPath(uint16_t filter_path,const char** filter_names);
uint16_t AudioFilter_GetFilterModeFromDemodMode(uint8_t dmod_mode);
uint8_t AudioFilter_NextApplicableFilter(void);
void AudioFilter_SetDefaultMemories(void);
typedef struct
{
int a;
float32_t b;
float32_t sin;
float32_t cos;
float32_t r;
float32_t buf[3];
} Goertzel;
void AudioFilter_CalcGoertzel(Goertzel* g, float32_t freq, const uint32_t size, const float goertzel_coeff, float32_t samplerate);
void AudioFilter_GoertzelInput(Goertzel* goertzel, float32_t in);
float32_t AudioFilter_GoertzelEnergy(Goertzel* goertzel);
#endif /* DRIVERS_AUDIO_AUDIO_FILTER_H_ */
@@ -0,0 +1,480 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
#include "uhsdr_board.h"
#include "audio_management.h"
#include "math.h"
#include "audio_driver.h"
#include "softdds.h"
#include "fm_subaudible_tone_table.h"
#include "radio_management.h"
/**
* Calculate the ALC Decay for the Tx Voice Compressor
*/
void AudioManagement_CalcALCDecay()
{
// calculate ALC decay (release) time constant - this needs to be moved to its own function (and the one in "ui_menu.c")
ads.alc_decay = pow10f(-((((float32_t)ts.alc_decay_var)+35.0)/10.0));
}
// make sure the frequencies below match the order of iq_freq_enum_t !
#define AUDIO_M_IQ_ADJUST_INIT(freqVal) { .freq = freqVal , .adj = { .rx = { IQ_BALANCE_OFF, IQ_BALANCE_OFF }, .tx = { { IQ_BALANCE_OFF, IQ_BALANCE_OFF }, { IQ_BALANCE_OFF,IQ_BALANCE_OFF } } } },
freq_adjust_point_t iq_adjust[IQ_FREQ_NUM+1] =
{
AUDIO_M_IQ_ADJUST_INIT( 3600000)
AUDIO_M_IQ_ADJUST_INIT(14100000)
AUDIO_M_IQ_ADJUST_INIT(21100000)
AUDIO_M_IQ_ADJUST_INIT(28100000)
AUDIO_M_IQ_ADJUST_INIT(29650000)
AUDIO_M_IQ_ADJUST_INIT(52000000)
AUDIO_M_IQ_ADJUST_INIT( 1900000)
AUDIO_M_IQ_ADJUST_INIT( 7100000)
AUDIO_M_IQ_ADJUST_INIT(10100000)
AUDIO_M_IQ_ADJUST_INIT(18100000)
AUDIO_M_IQ_ADJUST_INIT(24900000)
// this must be last
AUDIO_M_IQ_ADJUST_INIT(0)
};
static int32_t AudioManagement_GetBalanceValFromStruct(freq_adjust_point_t* point, iq_adjust_params_t what)
{
int32_t retval = IQ_BALANCE_OFF;
switch(what)
{
case IQ_RX_GAIN:
retval = point->adj.rx.gain;
break;
case IQ_RX_PHASE:
retval = point->adj.rx.phase;
break;
case IQ_TX_TRANS_ON_GAIN:
retval = point->adj.tx[IQ_TRANS_ON].gain;
break;
case IQ_TX_TRANS_OFF_GAIN:
retval = point->adj.tx[IQ_TRANS_OFF].gain;
break;
case IQ_TX_TRANS_ON_PHASE:
retval = point->adj.tx[IQ_TRANS_ON].phase;
break;
case IQ_TX_TRANS_OFF_PHASE:
retval = point->adj.tx[IQ_TRANS_OFF].phase;
break;
}
return retval;
}
/**
*
* @param freq for which the two closest points shall be found
* @return idx of lower frequency point, please note that the higher frequency point may be the stop value
*/
static uint32_t AudioManagement_GetNextValid(freq_adjust_point_t* points, uint32_t idx, iq_adjust_params_t what)
{
if(!(ts.expflags2 & EXPFLAGS2_IMPROV_IQ_ADJ)) // regular I/Q adj.
{
// while (points[idx].freq != 0)
while (idx <= 5)
{
int32_t val = AudioManagement_GetBalanceValFromStruct(&points[idx], what);
if (val != IQ_BALANCE_OFF)
{
break;
}
idx++;
}
}
else // advance I/Q adj.
{
uint8_t iq_adv_adj_order[] = {6, 0, 7, 8, 1, 9, 2, 10, 3, 4, 5};
uint8_t i = 0;
while (i <= 10)
{
if(idx == iq_adv_adj_order[i])
{
break;
}
i++;
}
int32_t val;
while (i <= 10)
{
val = AudioManagement_GetBalanceValFromStruct(&points[(iq_adv_adj_order[i])], what);
if (val != IQ_BALANCE_OFF)
{
break;
}
i++;
}
idx = iq_adv_adj_order[i];
}
return idx;
}
static void AudioManagement_FindFreqRange(freq_adjust_point_t* points, uint32_t freq, iq_adjust_params_t what, float32_t* adj_low_ptr, float32_t* adj_high_ptr, float32_t* freq_low_ptr, float32_t* freq_high_ptr )
{
// AudioManagement_GetBalanceValFromStruct(&points[idx], what);
uint32_t idx = AudioManagement_GetNextValid(points, 0, what);
uint32_t previous_idx = idx;
uint32_t before_previous_idx = idx;
uint32_t valid_points = 0;
bool found_match = false;
if(!(ts.expflags2 & EXPFLAGS2_IMPROV_IQ_ADJ))
{
// searching for the frist valid calibration point just higher than this frequency
// while (points[idx].freq != 0)
while (idx <= 5)
{
valid_points++;
if (freq < points[idx].freq)
{
// we found one valid calibration point which is higher.
// now we need to see if there is a second point if possible.
// if the idx is not equal previous_idx, there was a valid lower frequency calibration point
// which is nice otherwise we try to find one more higher point
if (previous_idx == idx)
{
// we are lower than the lowest entry, now check if there is one more valid calibration point
idx = AudioManagement_GetNextValid(points, idx+1, what);
// if (points[idx].freq == 0) // stop entry
if (idx == 5) // stop entry
{
// no more valid points, too bad
idx = previous_idx; // we keep both points identical then
}
}
found_match = true;
break;
}
before_previous_idx = previous_idx;
previous_idx = idx;
idx = AudioManagement_GetNextValid(points, idx+1, what);
}
}
else // Advanced I/Q just. We assume that advanced alignment is only needed by advanced users. We work on all alignment points.
{
uint8_t iq_adv_adj_order[] = {6, 0, 7, 8, 1, 9, 2, 10, 3, 4, 5};
uint8_t i;
uint8_t previous_i = 0;
if(freq <= points[(iq_adv_adj_order[0])].freq)
{
i = previous_i = 0;
}
else if(freq >= points[(iq_adv_adj_order[10])].freq)
{
i = previous_i = 10;
}
else
{
i = 1;
while (i <= 9)
{
if(freq == points[(iq_adv_adj_order[i])].freq)
{
previous_i = i;
break;
}
else if(freq < points[(iq_adv_adj_order[i])].freq)
{
previous_i = (i - 1);
break;
}
i++;
}
}
previous_idx = iq_adv_adj_order[previous_i];
idx = iq_adv_adj_order[i];
}
if(!(ts.expflags2 & EXPFLAGS2_IMPROV_IQ_ADJ))
{
if (found_match == false)
{
if (valid_points > 1)
{
// we had two or more valid points, but all are below or equal the frequency
// just use the last two valid points, idx is not a valid point but the stop entry
idx = previous_idx;
previous_idx = before_previous_idx;
}
else
{
// if we had not a single valid calibration point, too bad
// idx is same as previous_idx and both point to the last "stop" entry
// nothing to be done now, but we handle it like the next case, makes no difference
// we had only one valid point, and that must be in previous_idx
// no problem, we use it for all frequencies
idx = previous_idx;
}
}
}
*adj_high_ptr = AudioManagement_GetBalanceValFromStruct(&points[idx], what);
*adj_low_ptr = AudioManagement_GetBalanceValFromStruct(&points[previous_idx], what);
*freq_high_ptr = points[idx].freq;
*freq_low_ptr = points[previous_idx].freq;
}
static float AudioManagement_CalcAdjustInFreqRangeHelperNew(freq_adjust_point_t* points, iq_adjust_params_t what, float32_t freq, float32_t scaling)
{
float32_t adj_low, adj_high;
float32_t freq_low, freq_high;
AudioManagement_FindFreqRange(points, freq, what, &adj_low, &adj_high, &freq_low, &freq_high);
if (adj_high == IQ_BALANCE_OFF)
{
if (adj_low == IQ_BALANCE_OFF)
{
// no data available, set both to zero
adj_high = adj_low = 0.0;
}
else
{
// we use the low value for both
adj_high = adj_low;
}
} else if (adj_low == IQ_BALANCE_OFF)
{
// we use the high value for both
adj_low = adj_high;
}
float32_t adj_delta = (freq_high != freq_low)? (adj_high - adj_low) / (freq_high - freq_low) * (freq - freq_low) : 0;
return (adj_delta + adj_low)/scaling;
// get current gain adjustment setting USB and other modes
}
static void AudioManagement_CalcIqGainAdjustVarHelper(volatile iq_float_t* var, float32_t adj)
{
var->i = 1 + adj;
var->q = 1 - adj;
}
void AudioManagement_CalcIqPhaseGainAdjust(float freq)
{
//
// the phase adjustment is done by mixing a little bit of I into Q or vice versa
// this is justified because the phase shift between two signals of equal frequency can
// be regulated by adjusting the amplitudes of the two signals!
ads.iq_phase_balance_rx = AudioManagement_CalcAdjustInFreqRangeHelperNew(
iq_adjust,
IQ_RX_PHASE,
freq,
SCALING_FACTOR_IQ_PHASE_ADJUST);
// please note that the RX adjustments for gain are negative
// and the adjustments for TX (in the function AudioManagement_CalcTxIqGainAdj) are positive
float32_t adj_i_rx = -AudioManagement_CalcAdjustInFreqRangeHelperNew(
iq_adjust,
IQ_RX_GAIN,
freq,
SCALING_FACTOR_IQ_AMPLITUDE_ADJUST);
AudioManagement_CalcIqGainAdjustVarHelper(&ts.rx_adj_gain_var,adj_i_rx);
ads.iq_phase_balance_tx[IQ_TRANS_ON] = AudioManagement_CalcAdjustInFreqRangeHelperNew(
iq_adjust,
IQ_TX_TRANS_ON_PHASE,
freq,
SCALING_FACTOR_IQ_PHASE_ADJUST);
ads.iq_phase_balance_tx[IQ_TRANS_OFF] = AudioManagement_CalcAdjustInFreqRangeHelperNew(
iq_adjust,
IQ_TX_TRANS_OFF_PHASE,
freq,
SCALING_FACTOR_IQ_PHASE_ADJUST);
AudioManagement_CalcIqGainAdjustVarHelper(&ts.tx_adj_gain_var[IQ_TRANS_ON],AudioManagement_CalcAdjustInFreqRangeHelperNew(
iq_adjust,
IQ_TX_TRANS_ON_GAIN,
freq,
SCALING_FACTOR_IQ_AMPLITUDE_ADJUST));
AudioManagement_CalcIqGainAdjustVarHelper(
&ts.tx_adj_gain_var[IQ_TRANS_OFF],
AudioManagement_CalcAdjustInFreqRangeHelperNew(
iq_adjust,
IQ_TX_TRANS_OFF_GAIN,
freq,
SCALING_FACTOR_IQ_AMPLITUDE_ADJUST));
}
//*----------------------------------------------------------------------------
typedef struct AlcParams_s
{
uint32_t tx_postfilt_gain;
uint32_t alc_decay;
} AlcParams;
static const AlcParams alc_params[] =
{
{ 1, 15},
{ 2, 12},
{ 4, 10},
{ 6, 9},
{ 7, 8},
{ 8, 7},
{ 10, 6},
{ 12, 5},
{ 15, 4},
{ 17, 3},
{ 20, 2},
{ 25, 1},
{ 25, 0},
};
/**
* @brief Set TX audio compression settings (gain and ALC decay rate) based on user setting
*/
void AudioManagement_CalcTxCompLevel()
{
int16_t tx_comp_level = ts.tx_comp_level;
if (TX_AUDIO_COMPRESSION_MIN < tx_comp_level && tx_comp_level < TX_AUDIO_COMPRESSION_SV)
{
ts.alc_tx_postfilt_gain_var = alc_params[ts.tx_comp_level].tx_postfilt_gain; // restore "pristine" EEPROM values
ts.alc_decay_var = alc_params[ts.tx_comp_level].alc_decay;
}
else if (tx_comp_level == TX_AUDIO_COMPRESSION_SV) // get the speech compressor setting
{
// read saved values from EEPROM
ts.alc_tx_postfilt_gain_var = ts.alc_tx_postfilt_gain; // restore "pristine" EEPROM values
ts.alc_decay_var = ts.alc_decay;
}
else
{
ts.alc_tx_postfilt_gain_var = 4;
ts.alc_decay_var = 10;
}
AudioManagement_CalcALCDecay();
}
#include "fm_subaudible_tone_table.h"
//
//*----------------------------------------------------------------------------
//* Function Name : UiCalcSubaudibleGenFreq
//* Object : Calculate frequency word for subaudible tone generation [KA7OEI October, 2015]
//* Input Parameters :
//* Output Parameters :
//* Functions called :
//*----------------------------------------------------------------------------
void AudioManagement_CalcSubaudibleGenFreq(float32_t freq)
{
ads.fm_conf.subaudible_tone_gen_freq = freq; // look up tone frequency (in Hz)
softdds_setFreqDDS(&ads.fm_conf.subaudible_tone_dds, ads.fm_conf.subaudible_tone_gen_freq,ts.samp_rate,false);
}
#define FM_GOERTZEL_HIGH 1.04 // ratio of "high" detect frequency with respect to center
#define FM_GOERTZEL_LOW 0.95 // ratio of "low" detect frequency with respect to center
/**
* @brief Calculate frequency word for subaudible tone, call after change of detection frequency [KA7OEI October, 2015]
*/
void AudioManagement_CalcSubaudibleDetFreq(float32_t freq)
{
const uint32_t size = AUDIO_BLOCK_SIZE;
ads.fm_conf.subaudible_tone_det_freq = freq; // look up tone frequency (in Hz)
if (freq > 0)
{
// Calculate Goertzel terms for tone detector(s)
AudioFilter_CalcGoertzel(&ads.fm_conf.goertzel[FM_HIGH], ads.fm_conf.subaudible_tone_det_freq, FM_SUBAUDIBLE_GOERTZEL_WINDOW*size,FM_GOERTZEL_HIGH, IQ_SAMPLE_RATE);
AudioFilter_CalcGoertzel(&ads.fm_conf.goertzel[FM_LOW], ads.fm_conf.subaudible_tone_det_freq, FM_SUBAUDIBLE_GOERTZEL_WINDOW*size,FM_GOERTZEL_LOW, IQ_SAMPLE_RATE);
AudioFilter_CalcGoertzel(&ads.fm_conf.goertzel[FM_CTR], ads.fm_conf.subaudible_tone_det_freq, FM_SUBAUDIBLE_GOERTZEL_WINDOW*size,1.0, IQ_SAMPLE_RATE);
}
}
uint32_t fm_tone_burst_freq[FM_TONE_BURST_MAX+1] = { 0, 1750, 2135 };
//
//
//*----------------------------------------------------------------------------
//* Function Name : UiLoadToneBurstMode
//* Object : Load tone burst mode [KA7OEI October, 2015]
//* Input Parameters :
//* Output Parameters :
//* Functions called :
//*----------------------------------------------------------------------------
void AudioManagement_LoadToneBurstMode()
{
uint16_t frequency = 0;
if (ts.fm_tone_burst_mode <= FM_TONE_BURST_MAX)
{
frequency = fm_tone_burst_freq[ts.fm_tone_burst_mode];
}
softdds_setFreqDDS(&ads.fm_conf.tone_burst_dds, frequency, ts.samp_rate, false);
}
/**
* @brief Generates the sound for a given beep frequency, call after change of beep freq or before beep [KA7OEI October, 2015]
*/
void AudioManagement_KeyBeepPrepare()
{
softdds_setFreqDDS(&ads.beep, ts.beep_frequency,ts.samp_rate,false);
float32_t calc = (float)(ts.beep_loudness-1); // range 0-20
calc /= 2; // range 0-10
calc *= calc; // range 0-100
calc += 3; // range 3-103
ads.beep_loudness_factor = calc / 400; // range from 0.0075 to 0.2575 - multiplied by DDS output
}
/**
* @brief Tell audio driver to make beeping sound [KA7OEI October, 2015]
*/
void AudioManagement_KeyBeep()
{
if((ts.flags2 & FLAGS2_KEY_BEEP_ENABLE) && (ads.beep.step > 0)) // is beep enabled and frequency non-zero?
{
ads.beep.acc = 0; // force reset of accumulator to start at zero to minimize "click" caused by an abrupt voltage transition at startup
ts.beep_timing = BEEP_DURATION * (IQ_INTERRUPT_FREQ / 100); // set duration of beep and activate it
}
}
void AudioManagement_SetSidetoneForDemodMode(uint8_t dmod_mode, bool tune_mode)
{
float tonefreq[2] = {0.0, 0.0};
switch(dmod_mode)
{
case DEMOD_CW:
tonefreq[0] = tune_mode?CW_SIDETONE_FREQ_DEFAULT:ts.cw_sidetone_freq;
break;
case DEMOD_DIGI:
if (ts.digital_mode == DigitalMode_RTTY || ts.digital_mode == DigitalMode_BPSK)
{
tonefreq[0] = tune_mode?CW_SIDETONE_FREQ_DEFAULT:ts.cw_sidetone_freq;
}
break;
default:
tonefreq[0] = tune_mode?SSB_TUNE_FREQ:0.0;
if (tune_mode && ts.tune_tone_mode == TUNE_TONE_TWO)
{ // ARRL Standard is 700Hz and 1900Hz
// so I temporarily changed this to SSB_TUNE_FREQ + 1200, DD4WH 2016_07_14
// --> TWO_TONE = 750Hz and 1950Hz
// tonefreq[1] = tune_mode?(SSB_TUNE_FREQ+600):0.0;
tonefreq[1] = SSB_TUNE_FREQ+1200;
}
}
softdds_configRunIQ(tonefreq,ts.samp_rate,0);
}
@@ -0,0 +1,101 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef DRIVERS_AUDIO_AUDIO_MANAGEMENT_H_
#define DRIVERS_AUDIO_AUDIO_MANAGEMENT_H_
#include "uhsdr_types.h"
void AudioManagement_CalcIqPhaseGainAdjust(float32_t freq);
void AudioManagement_CalcTxCompLevel(void);
void AudioManagement_CalcNB_AGC(void);
void AudioManagement_CalcAGCVals(void);
void AudioManagement_CalcRFGain(void);
void AudioManagement_CalcALCDecay(void);
void AudioManagement_CalcAGCDecay(void);
void AudioManagement_SetSidetoneForDemodMode(uint8_t dmod_mode, bool tune_mode);
void AudioManagement_LoadToneBurstMode(void);
void AudioManagement_CalcSubaudibleGenFreq(float32_t freq); // load/set current FM subaudible tone settings for generation
void AudioManagement_CalcSubaudibleDetFreq(float32_t freq);
void AudioManagement_KeyBeep(void);
void AudioManagement_KeyBeepPrepare(void);
#define IQ_BALANCE_OFF -128 // Minimum setting for IQ balance
#define IQ_BALANCE_MAX 127 // Maximum setting for IQ balance
#define MIN_IQ_GAIN_BALANCE IQ_BALANCE_OFF // Minimum setting for IQ gain balance
#define MAX_IQ_GAIN_BALANCE IQ_BALANCE_MAX // Maximum setting for IQ gain balance
#define MIN_IQ_PHASE_BALANCE IQ_BALANCE_OFF // Minimum setting for IQ phase balance
#define MAX_IQ_PHASE_BALANCE IQ_BALANCE_MAX // Maximum setting for IQ phase balance
typedef struct
{
int32_t phase;
int32_t gain;
} iq_adjust_balance_t;
typedef enum
{
IQ_RX_GAIN,
IQ_RX_PHASE,
IQ_TX_TRANS_ON_GAIN,
IQ_TX_TRANS_OFF_GAIN,
IQ_TX_TRANS_ON_PHASE,
IQ_TX_TRANS_OFF_PHASE,
} iq_adjust_params_t;
typedef struct {
iq_adjust_balance_t rx;
iq_adjust_balance_t tx[2];
} iq_adjust_vals_t;
typedef struct
{
int32_t freq;
iq_adjust_vals_t adj;
} freq_adjust_point_t;
// this data structure(s) must have at least 2 elements (including the stop element)
// corresponding frequencies are stored in const array iq_adjust_freq
typedef enum {
IQ_80M = 0,
IQ_20M,
IQ_15M,
IQ_10M,
IQ_10M_UP,
IQ_6M,
IQ_160M,
IQ_40M,
IQ_30M,
IQ_17M,
IQ_12M,
IQ_FREQ_NUM
} iq_freq_enum_t;
// the order here must match the order in the structure below!
extern freq_adjust_point_t iq_adjust[];
// For advance I/Q jusify
//const uint8_t iq_adv_adj_order[] = {6, 0, 7, 8, 1, 9, 2, 10, 3, 4, 5};
#endif /* DRIVERS_AUDIO_AUDIO_MANAGEMENT_H_ */
File diff suppressed because it is too large Load Diff
+139
View File
@@ -0,0 +1,139 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __AUDIO_NR_H
#define __AUDIO_NR_H
#include "uhsdr_board_config.h"
#ifdef USE_ALTERNATE_NR
// this contains our shared buffer structure, since FreeDV and NR are mutually exclusive
// they can use the same set of buffers for exchanging data between audio interrupt and
// user code
#include "freedv_uhsdr.h"
#define NR_FFT_SIZE 128
// for the mcHF, we would uncomment all those four // !
//#if defined(STM32F7) || defined(STM32H7)
#define NR_FFT_L_2 (NR_FFT_SIZE * 2) // for NR FFT size 256
//#else
//#define NR_FFT_L_2 (NR_FFT_SIZE) // for NR FFT size 128
//#endif
// we need another struct, because of the need for strict allocation of memory for users of the
// mcHF hardware with small RAM (192 kb)
typedef struct NoiseReduction2 // declaration
{
float32_t Hk[NR_FFT_L_2 / 2]; // gain factors
float32_t X[NR_FFT_L_2 / 2][2]; // magnitudes of the current and the last FFT bins
//float32_t X[NR_FFT_L/2]; // magnitudes of the current and the last FFT bins
// float32_t long_tone_gain[NR_FFT_L_2 / 2];
// float32_t long_tone[NR_FFT_L_2 / 2][2];
// int VAD_delay;
// int VAD_duration; //takes the duration of the last vowel
// uint32_t VAD_crash_detector; // this is counted upwards during speech detection, if noise is detected, it is reset to zero
// if it exceeds a certain limit, noise estimate is done irrespective of the VAD value
// this helps to get the noise estimate out of a very low position --> "VAD crash"
// uint8_t VAD_type; // 0 = Sohn et al. VAD, 1 = Esch & Vary 2009 VAD
bool notch_change; // indicates that notch filter has to be changed
// uint32_t long_tone_counter[NR_FFT_L_2 / 2]; // holds the notch index for every bin, the higher, the more notchworthy is a bin
uint8_t notch1_bin; // frequency bin where notch filter 1 has to work
uint8_t max_bin; // holds the bin number of the strongest persistent tone during tone detection
float32_t long_tone_max; // power value of the strongest persistent tone, used for max search
bool notch1_active; // is notch1 active?
bool notch2_active; // is notch21 active?
bool notch3_active; // is notch3 active?
bool notch4_active; // is notch4 active?
//float32_t tax; // for NR devel2: noise output smoothing time constant = -tinc/ln(0.8)
//int16_t tax_int;
//float32_t tap; // for NR devel2: speech prob smoothing time constant = -tinc/ln(0.9) tinc = frame time (5.33ms)
//int16_t tap_int;
int16_t asnr; // for NR devel2: active SNR in dB
float32_t ax;
float32_t ap;
float32_t xih1;
float32_t xih1r;
float32_t pfac;
int16_t snr_prio_min_int;
float32_t snr_prio_min;
int16_t NN;// for musical noise reduction
int16_t width;// for musical noise reduction
float32_t pre_power;// for musical noise reduction
float32_t post_power;// for musical noise reduction
float32_t power_ratio; // for musical noise reduction
float32_t power_threshold;
int16_t power_threshold_int;
} NoiseReduction2;
extern NoiseReduction2 NR2; // declaration, definition is in audio_nr.c
typedef struct
{
bool enable; // enable spectral noise reduction
uint8_t first_time; // set to 1 for initialization of the NR variables
float32_t alpha; // alpha smoothing constant for spectral noise reduction
float32_t beta; // beta smoothing constant for spectral noise reduction
// bool gain_smooth_enable; // enable gain smoothing
// float32_t gain_smooth_alpha; // smoothing constant for gain smoothing in the spectral noise reduction
// bool long_tone_enable; // enable elimination of long tones in the spectral NR algorithm
// float32_t long_tone_alpha; // time constant for long tone detection
// int16_t long_tone_thresh; // threshold for long tone detection
// bool long_tone_reset; // used to reset gains of the long tone detection to 1.0
// int16_t vad_delay; // how many frames to delay the noise estimate after VAD has detected NOISE
// int16_t mode;
// float32_t vad_thresh; // threshold for voice activity detector in spectral noise reduction
bool fft_256_enable; // debugging: enable FFT256 instead of FFT128 for spectral NR
uint16_t NR_FFT_L; // resulting FFT length: 128 or 256
uint8_t NR_FFT_LOOP_NO;
bool NR_decimation_enable; // set to true, if we want to use another decimation step for the spectral NR leading to 6ksps sample rate
bool NR_decimation_active; // set to true if the current buffer content is "double decimated" down to 6khz, set by the buffer producer
} audio_nr_params_t;
extern audio_nr_params_t nr_params;
void NR_Init(void);
void AudioNr_Prepare(void);
void AudioNr_HandleNoiseReduction(void);
void AudioNr_ActivateAutoNotch(uint8_t notch1_bin, bool notch1_active);
int NR_in_buffer_peek(NR_Buffer** c_ptr);
int NR_in_buffer_remove(NR_Buffer** c_ptr);
/* no room left in the buffer returns 0 */
int NR_in_buffer_add(NR_Buffer* c);
void NR_in_buffer_reset(void);
int8_t NR_in_has_data(void);
int32_t NR_in_has_room(void);
int NR_out_buffer_peek(NR_Buffer** c_ptr);
int NR_out_buffer_remove(NR_Buffer** c_ptr);
/* no room left in the buffer returns 0 */
int NR_out_buffer_add(NR_Buffer* c);
void NR_out_buffer_reset(void);
int8_t NR_out_has_data(void);
int32_t NR_out_has_room(void);
#endif
#endif
@@ -0,0 +1,159 @@
/************************************************************************************
** **
** UHSDR Firmware **
** O Belousov - R1CBU 2021 **
** **
**--------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "uhsdr_board.h"
#include "audio_reverb.h"
//#define CF0 2595
//#define CF1 2241
//#define CF2 2911
//#define CF3 3234
#define DECIM 5
//#define AP0 480
//#define AP1 161
//#define AP2 46
#define CF0 (3460 / DECIM)
#define CF1 (2988 / DECIM)
#define CF2 (3882 / DECIM)
#define CF3 (4312 / DECIM)
#define AP0 (480 / DECIM)
#define AP1 (161 / DECIM)
#define AP2 (46 / DECIM)
static float32_t cfbuf0[CF0];
static float32_t cfbuf1[CF1];
static float32_t cfbuf2[CF2];
static float32_t cfbuf3[CF3];
static float32_t apbuf0[AP0];
static float32_t apbuf1[AP1];
static float32_t apbuf2[AP2];
typedef struct {
float32_t *buf;
uint16_t index;
uint16_t delay;
float32_t gain;
} item_t;
static float32_t wet0 = 1.0f;
static float32_t wet1 = 0.0f;
static float32_t result = 0.0f;
static uint8_t decim_count = 0;
static item_t cf0, cf1, cf2, cf3;
static item_t ap0, ap1, ap2;
static float32_t CalcComb(float32_t in, item_t *comb) {
float32_t readback = comb->buf[comb->index];
float32_t new = readback * comb->gain + in;
comb->buf[comb->index] = new;
comb->index++;
if (comb->index > comb->delay)
comb->index = 0;
return readback;
}
static float32_t CalcAllPass(float32_t in, item_t *allpass) {
float32_t reedback = allpass->buf[allpass->index] - allpass->gain*in;
float32_t new = reedback*allpass->gain + in;
allpass->buf[allpass->index] = new;
allpass->index++;
if (allpass->index > allpass->delay)
allpass->index = 0;
return reedback;
}
void AudioReverb_Init(void) {
cf0.buf = (float32_t*) &cfbuf0; cf0.gain = 0.805f;
cf1.buf = (float32_t*) &cfbuf1; cf1.gain = 0.827f;
cf2.buf = (float32_t*) &cfbuf2; cf2.gain = 0.783f;
cf3.buf = (float32_t*) &cfbuf3; cf3.gain = 0.764f;
ap0.buf = (float32_t*) &apbuf0; ap0.gain = 0.7f;
ap1.buf = (float32_t*) &apbuf1; ap1.gain = 0.7f;
ap2.buf = (float32_t*) &apbuf2; ap2.gain = 0.7f;
wet0 = 1.0f;
wet1 = 0.0f;
result = 0.0f;
decim_count = 0;
// AudioReverb_SetDelay(1.0f);
// AudioReverb_SetWet(0.5f);
AudioReverb_SetDelay();
AudioReverb_SetWet();
}
//void AudioReverb_SetDelay(float32_t x) {
// cf0.delay = (uint16_t) (x * CF0);
// cf1.delay = (uint16_t) (x * CF1);
// cf2.delay = (uint16_t) (x * CF2);
// cf3.delay = (uint16_t) (x * CF3);
//
// ap0.delay = (uint16_t) (x * AP0);
// ap1.delay = (uint16_t) (x * AP1);
// ap2.delay = (uint16_t) (x * AP2);
//}
void AudioReverb_SetDelay()
{
float32_t x = ts.reverb_delay / 100.f;
cf0.index = 0; cf0.delay = (uint16_t) (x * CF0);
cf1.index = 0; cf1.delay = (uint16_t) (x * CF1);
cf2.index = 0; cf2.delay = (uint16_t) (x * CF2);
cf3.index = 0; cf3.delay = (uint16_t) (x * CF3);
ap0.index = 0; ap0.delay = (uint16_t) (x * AP0);
ap1.index = 0; ap1.delay = (uint16_t) (x * AP1);
ap2.index = 0; ap2.delay = (uint16_t) (x * AP2);
}
//void AudioReverb_SetWet(float32_t x) {
void AudioReverb_SetWet()
{
float32_t x = ts.reverb_gain / 100.0f;
wet0 = x;
wet1 = 1.0f - x;
}
float32_t AudioReverb_Calc(float32_t in)
{
// float32_t new = (CalcComb(in, &cf0) + CalcComb(in, &cf1) + CalcComb(in, &cf2) + CalcComb(in, &cf3)) / 4.0f;
decim_count++;
if (decim_count >= DECIM)
{
result = (CalcComb(in, &cf0) + CalcComb(in, &cf1) + CalcComb(in, &cf2) + CalcComb(in, &cf3)) / 4.0f;
result = CalcAllPass(result, &ap0);
result = CalcAllPass(result, &ap1);
result = CalcAllPass(result, &ap2);
// new = CalcAllPass(new, &ap0);
// new = CalcAllPass(new, &ap1);
// new = CalcAllPass(new, &ap2);
decim_count = 0;
}
// return wet0 * new + wet1 * in;
return wet0 * result + wet1 * in;
}
@@ -0,0 +1,26 @@
/*************************************************************************************
** **
** UHSDR Firmware **
** O Belousov - R1CBU 2021 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
*************************************************************************************/
#ifndef AUDIO_AUDIO_REVERB_H_
#define AUDIO_AUDIO_REVERB_H_
#include "uhsdr_types.h"
extern void AudioReverb_Init(void);
//extern void AudioReverb_SetDelay(float32_t x);
//extern void AudioReverb_SetWet(float32_t x);
extern void AudioReverb_SetDelay();
extern void AudioReverb_SetWet();
float32_t AudioReverb_Calc(float32_t in);
#endif /* AUDIO_AUDIO_REVERB_H_ */
+702
View File
@@ -0,0 +1,702 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
// Common
#include "uhsdr_board.h"
#include "audio_driver.h"
#include "radio_management.h"
#include <stdio.h>
#include "uhsdr_hw_i2c.h"
#include "codec.h"
#include "uhsdr_mcu.h"
// I2C addresses
#define W8731_ADDR_0 0x1A // CS = 0, MODE to GND
#define W8731_ADDR_1 0x1B // CS = 1, MODE to GND
// The 7 bits Codec address (sent through I2C interface)
#define CODEC_ADDRESS (W8731_ADDR_0<<1)
// Registers
#define W8731_LEFT_LINE_IN 0x00 // 0000000
#define W8731_RIGHT_LINE_IN 0x01 // 0000001
#define W8731_LEFT_HEADPH_OUT 0x02 // 0000010
#define W8731_RIGHT_HEADPH_OUT 0x03 // 0000011
#define W8731_ANLG_AU_PATH_CNTR 0x04 // 0000100
#define W8731_DIGI_AU_PATH_CNTR 0x05 // 0000101
#define W8731_POWER_DOWN_CNTR 0x06 // 0000110
#define W8731_DIGI_AU_INTF_FORMAT 0x07 // 0000111
#define W8731_SAMPLING_CNTR 0x08 // 0001000
#define W8731_ACTIVE_CNTR 0x09 // 0001001
#define W8731_RESET 0x0F // 0001111
// -------------------------------------------------
//#define W8731_DEEMPH_CNTR 0x06 // WM8731 codec De-emphasis enabled
#define W8731_DEEMPH_CNTR 0x00 // WM8731 codec De-emphasis disabled
#define W8731_HEADPH_OUT_ZCEN 0x0080 // bit 7 W8731_LEFT_HEADPH_OUT / W8731_RIGHT_HEADPH_OUT
#define W8731_HEADPH_OUT_HPBOTH 0x0100 // bit 8 W8731_LEFT_HEADPH_OUT / W8731_RIGHT_HEADPH_OUT
#define W8731_LINE_IN_LRBOTH 0x0100 // bit 8 W8731_LEFT_LINE_IN_OUT / W8731_RIGHT_LINE_IN
#define W8731_ANLG_AU_PATH_CNTR_DACSEL (0x10)
#define W8731_ANLG_AU_PATH_CNTR_INSEL_MIC (0x04)
#define W8731_ANLG_AU_PATH_CNTR_INSEL_LINE (0x00)
#define W8731_ANLG_AU_PATH_CNTR_MUTEMIC (0x02)
#define W8731_ANLG_AU_PATH_CNTR_MICBBOOST (0x01)
#define W8731_DIGI_AU_INTF_FORMAT_PHILIPS 0x02
#define W8731_DIGI_AU_INTF_FORMAT_PCM 0x00
#define W8731_DIGI_AU_INTF_FORMAT_16B (0x0 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_20B (0x1 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_24B (0x2 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_32B (0x3 << 2)
#define W8731_DIGI_AU_INTF_FORMAT_I2S_PROTO W8731_DIGI_AU_INTF_FORMAT_PHILIPS
#define W8731_POWER_DOWN_CNTR_POWEROFF (0x80)
#define W8731_POWER_DOWN_CNTR_CLKOUTPD (0x40)
#define W8731_POWER_DOWN_CNTR_OSCPD (0x20)
#define W8731_POWER_DOWN_CNTR_OUTPD (0x10)
#define W8731_POWER_DOWN_CNTR_DACPD (0x08)
#define W8731_POWER_DOWN_CNTR_ADCPD (0x04)
#define W8731_POWER_DOWN_CNTR_MICPD (0x02)
#define W8731_POWER_DOWN_CNTR_LINEPD (0x01)
#define W8731_SAMPLING_CNTR_BOSR (0x0002)
#define W8731_SAMPLING_CNTR_96K (0x0007 << 2)
#define W8731_SAMPLING_CNTR_48K (0x0000 << 2)
#define W8731_SAMPLING_CNTR_32K (0x0006 << 2)
#define W8731_SAMPLING_CNTR_8K (0x0003 << 2)
#define W8731_VOL_MAX 0x50
#define W8731_POWER_DOWN_CNTR_MCHF_ALL_ON (W8731_POWER_DOWN_CNTR_CLKOUTPD|W8731_POWER_DOWN_CNTR_OSCPD)
// all on but osc and out, since we don't need it, clock comes from STM
#define W8731_POWER_DOWN_CNTR_MCHF_MIC_OFF (W8731_POWER_DOWN_CNTR_CLKOUTPD|W8731_POWER_DOWN_CNTR_OSCPD|W8731_POWER_DOWN_CNTR_MICPD)
typedef struct
{
bool present;
} mchf_codec_t;
__IO mchf_codec_t mchf_codecs[CODEC_NUM];
// FIXME: for now we use 32bits transfer size, does not change the ADC/DAC resolution
// which is 24 bits in any case. We should reduce finally to 24bits (which requires also the I2S/SAI peripheral to
// use 24bits)
#if defined(USE_32_IQ_BITS)
#define IQ_WORD_SIZE WORD_SIZE_32
#else
#define IQ_WORD_SIZE WORD_SIZE_16
#endif
#if defined(USE_32_AUDIO_BITS)
#define AUDIO_WORD_SIZE WORD_SIZE_32
#else
#define AUDIO_WORD_SIZE WORD_SIZE_16
#endif
#ifdef UI_BRD_OVI40
#include "dac.h"
/**
* @brief controls volume on "external" PA via DAC
* @param vol volume in range of 0 to CODEC_SPEAKER_MAX_VOLUME
*/
static void AudioPA_Volume(uint8_t vol)
{
uint32_t lv = vol>CODEC_SPEAKER_MAX_VOLUME?CODEC_SPEAKER_MAX_VOLUME:vol;
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R, (lv * 4095)/CODEC_SPEAKER_MAX_VOLUME);
}
/**
* @brief controls sound delivery on "external" PA via DAC
* @param enable true == amplification, false == powerdown
*/
static void AudioPA_Enable(bool enable)
{
if (enable)
{
GPIO_SetBits(AUDIO_PA_EN_PIO,AUDIO_PA_EN);
}
else
{
GPIO_ResetBits(AUDIO_PA_EN_PIO,AUDIO_PA_EN);
}
}
#endif
/**
* @brief writes 16 bit data word to codec register
* @returns I2C error code
*/
static uint32_t Codec_WriteRegister(I2C_HandleTypeDef* hi2c, uint8_t RegisterAddr, uint16_t RegisterValue)
{
// Assemble 2-byte data in WM8731 format
uint8_t Byte1 = ((RegisterAddr<<1)&0xFE) | ((RegisterValue>>8)&0x01);
uint8_t Byte2 = RegisterValue&0xFF;
return UhsdrHw_I2C_WriteRegister(hi2c, CODEC_ADDRESS, Byte1, 1, Byte2);
}
static uint32_t Codec_ResetCodec(I2C_HandleTypeDef* hi2c, uint32_t AudioFreq, CodecSampleWidth_t word_size)
{
uint32_t retval = HAL_OK;
retval = Codec_WriteRegister(hi2c, W8731_RESET, 0);
// Reset register
if( retval == HAL_OK)
{
// Reg 00: Left Line In (0dB, mute off)
Codec_WriteRegister(hi2c, W8731_LEFT_LINE_IN,0x001F);
// Reg 01: Right Line In (0dB, mute off)
Codec_WriteRegister(hi2c, W8731_RIGHT_LINE_IN,0x001F);
// Reg 02: Left Headphone out (0dB)
//Codec_WriteRegister(0x02,0x0079);
// Reg 03: Right Headphone out (0dB)
//Codec_WriteRegister(0x03,0x0079);
// Reg 04: Analog Audio Path Control (DAC sel, ADC line, Mute Mic)
Codec_WriteRegister(hi2c, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL |
W8731_ANLG_AU_PATH_CNTR_INSEL_LINE |
W8731_ANLG_AU_PATH_CNTR_MUTEMIC);
// Reg 05: Digital Audio Path Control(all filters disabled)
// De-emphasis control, bx11x - 48kHz
// bx00x - off
// DAC soft mute b1xxx - mute on
// b0xxx - mute off
//
Codec_WriteRegister(hi2c, W8731_DIGI_AU_PATH_CNTR,W8731_DEEMPH_CNTR);
// Reg 06: Power Down Control (Clk off, Osc off, Mic off))
Codec_WriteRegister(hi2c, W8731_POWER_DOWN_CNTR,W8731_POWER_DOWN_CNTR_MCHF_MIC_OFF);
// Reg 07: Digital Audio Interface Format (i2s, 16/32 bit, slave)
uint16_t size_reg_val;
switch(word_size)
{
case WORD_SIZE_32:
size_reg_val = W8731_DIGI_AU_INTF_FORMAT_32B;
break;
case WORD_SIZE_24:
size_reg_val = W8731_DIGI_AU_INTF_FORMAT_24B;
break;
case WORD_SIZE_16:
default:
size_reg_val = W8731_DIGI_AU_INTF_FORMAT_16B;
break;
}
Codec_WriteRegister(hi2c, W8731_DIGI_AU_INTF_FORMAT,W8731_DIGI_AU_INTF_FORMAT_I2S_PROTO|size_reg_val);
// Reg 08: Sampling Control (Normal, 256x, 48k ADC/DAC)
// master clock: 12.288 Mhz
uint16_t samp_reg_val;
switch (AudioFreq)
{
case 32000:
samp_reg_val = W8731_SAMPLING_CNTR_32K;
break;
case 8000:
samp_reg_val = W8731_SAMPLING_CNTR_8K;
break;
case 96000:
samp_reg_val = W8731_SAMPLING_CNTR_96K;
break;
case 48000:
default:
samp_reg_val = W8731_SAMPLING_CNTR_48K;
break;
}
Codec_WriteRegister(hi2c, W8731_SAMPLING_CNTR,samp_reg_val);
// Reg 09: Active Control
// and now we start the Codec Digital Interface
Codec_WriteRegister(hi2c, W8731_ACTIVE_CNTR,0x0001);
}
return retval;
}
#ifdef SDR_AMBER
#ifndef UI_BRD_MCHF
uint32_t Codec_Reset_CS4270_Codec()
{
uint32_t retval = HAL_OK;
// uint32_t AudioFreq = ts.samp_rate;
// CodecSampleWidth_t word_size = IQ_WORD_SIZE;
GPIO_ResetBits(BAND3_PIO, BAND3); // this port is using for CS4270 codec RESET in TRX Amber
non_os_delay(); // we can't use HAL_Delay here
GPIO_SetBits(BAND3_PIO, BAND3);
retval = Board_AmberCS4270_Write(0x02, 0x01); // total codec powerdown
if(retval != HAL_OK)
{
return retval;
}
non_os_delay();
retval = Board_AmberCS4270_Write(0x02, 0x00); // total codec powerup
if(retval == HAL_OK)
{
// Board_AmberCS4270_Write(0x02, 0x02); // DAC powerdown, we need RESET just on RX
Board_AmberCS4270_Write(0x03, 0x00); // slave, divide 1
Board_AmberCS4270_Write(0x04, 0x09); // I2S for DAC & ADC
Board_AmberCS4270_Write(0x05, 0x80); // DACs single volume
Board_AmberCS4270_Write(0x06, 0x00); // Automute off
Board_AmberCS4270_Write(0x07, 0x00); // DACs max volume
}
return retval;
}
#endif
#endif
/**
* @brief initializes codec
* @param AudioFreq sample rate in Hertz
* @param word_size should be set to WORD_SIZE_16, since we have not yet implemented any other word_size
*/
uint32_t Codec_Reset(uint32_t AudioFreq)
{
uint32_t retval;
#ifdef UI_BRD_MCHF
retval = Codec_ResetCodec(CODEC_I2C, AudioFreq, IQ_WORD_SIZE);
#else
ts.codecWM8731_Audio_present = false;
retval = Codec_ResetCodec(CODEC_ANA_I2C, AudioFreq, AUDIO_WORD_SIZE);
if (retval == 0)
{
ts.codecWM8731_Audio_present = true;
mchf_codecs[1].present = true;
retval = Codec_ResetCodec(CODEC_IQ_I2C, AudioFreq, IQ_WORD_SIZE);
}
#endif
if (retval == 0)
{
mchf_codecs[0].present = true;
#ifdef UI_BRD_OVI40
AudioPA_Enable(true);
#endif
Codec_VolumeSpkr(0); // mute speaker
Codec_VolumeLineOut(ts.txrx_mode); // configure lineout according to mode
}
#if defined(SDR_AMBER) && defined(UI_BRD_OVI40)
else
{
ts.codecCS4270_present = Codec_Reset_CS4270_Codec() == HAL_OK;
if(ts.codecCS4270_present)
{
AudioPA_Enable(true);
}
}
#endif
return retval;
}
/**
* @brief Call this if the twin peaks happen, this restarts the I2S audio stream and it may fix the issue
*/
void Codec_RestartI2S(void)
{
#if !defined(UI_BRD_MCHF) && defined(SDR_AMBER)
if(ts.codecCS4270_present)
{
Codec_Reset_CS4270_Codec();
}
else
{
#endif
// Reg 09: Active Control
Codec_WriteRegister(CODEC_IQ_I2C, W8731_ACTIVE_CNTR,0x0000);
non_os_delay(); // we can't use HAL_Delay here, since our audio interrupt has higher priority which stops the ticks.
// Reg 09: Active Control
Codec_WriteRegister(CODEC_IQ_I2C, W8731_ACTIVE_CNTR,0x0001);
#if !defined(UI_BRD_MCHF) && defined(SDR_AMBER)
}
#endif
}
/**
* @brief This enables the microphone if in TX and sets gain, does nothing in RX or if audio_source is not microphone
* @param txrx_mode the mode for which it should be configured
*/
void Codec_SwitchMicTxRxMode(uint8_t txrx_mode)
{
// only adjust the hardware if in TX txrx_mode with mic selected (it will kill RX otherwise!)
if(txrx_mode == TRX_MODE_TX && ts.tx_audio_source == TX_AUDIO_MIC)
{
// Set up microphone gain and adjust mic boost accordingly
// Reg 04: Analog Audio Path Control (DAC sel, ADC Mic, Mic on)
// non_os_delay();
if(ts.tx_gain[TX_AUDIO_MIC] > 50) // actively adjust microphone gain and microphone boost
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL |
W8731_ANLG_AU_PATH_CNTR_INSEL_MIC|
W8731_ANLG_AU_PATH_CNTR_MICBBOOST); // mic boost on
ts.tx_mic_gain_mult = (ts.tx_gain[TX_AUDIO_MIC] - 35)/3; // above 50, rescale software amplification
}
else
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL |
W8731_ANLG_AU_PATH_CNTR_INSEL_MIC); // mic boost off
ts.tx_mic_gain_mult = ts.tx_gain[TX_AUDIO_MIC];
}
}
}
static bool is_microphone_active(void)
{
return ts.tx_audio_source == TX_AUDIO_MIC && (ts.dmod_mode != DEMOD_CW && is_demod_rtty() == false && is_demod_psk() == false);
}
/**
* @brief sets certain settings in preparation for smooth TX switching, call before actual switch function is called
* @param current_txrx_mode the current mode, not the future mode (this is assumed to be TRX_MODE_TX)
*/
void Codec_PrepareTx(uint8_t current_txrx_mode)
{
Codec_LineInGainAdj(0); // yes - momentarily mute LINE IN audio if in LINE IN mode until we have switched to TX
bool uses_mic_input = is_microphone_active();
if (uses_mic_input) // we are in MIC IN mode
{
ts.tx_mic_gain_mult = 0; // momentarily set the mic gain to zero while we go to TX
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL
| W8731_ANLG_AU_PATH_CNTR_INSEL_LINE
| W8731_ANLG_AU_PATH_CNTR_MUTEMIC);
// Mute the microphone with the CODEC (this does so without a CLICK) and remain/switch line in on
Codec_WriteRegister(CODEC_ANA_I2C, W8731_POWER_DOWN_CNTR,
W8731_POWER_DOWN_CNTR_MCHF_ALL_ON);
// now we power on all amps including the mic preamp and bias
}
// Is translate mode active and we have NOT already muted the audio output?
if ((ts.iq_freq_mode) && (current_txrx_mode == TRX_MODE_RX))
{
Codec_VolumeSpkr(0);
Codec_VolumeLineOut(TRX_MODE_TX); // yes - mute the audio codec to suppress an approx. 6 kHz chirp when going in to TX mode
}
if (uses_mic_input)
{
HAL_Delay(10);
// pause an instant because the codec chip has its own delay before tasks complete when we use the microphone input!
// otherwise audible noise will be transmitted
}
}
/**
* @brief setups up the codec according to tx/rx mode and selected sources
* @param txrx_mode the mode for which it should be configured
*
*/
void Codec_SwitchTxRxMode(uint8_t txrx_mode)
{
// First step - mute sound
Codec_VolumeSpkr(0);
Codec_VolumeLineOut(txrx_mode);
if(txrx_mode == TRX_MODE_RX)
{
// Mute line input
Codec_LineInGainAdj(0);
// Reg 04: Analog Audio Path Control (DAC sel, ADC line, Mute Mic)
Codec_WriteRegister(CODEC_ANA_I2C, W8731_ANLG_AU_PATH_CNTR,
W8731_ANLG_AU_PATH_CNTR_DACSEL|
W8731_ANLG_AU_PATH_CNTR_INSEL_LINE|
W8731_ANLG_AU_PATH_CNTR_MUTEMIC);
// Reg 06: Power Down Control (Clk off, Osc off, Mic Off)
// COMMENT: It would be tempting to set bit 1 "MICPD" of "W8731_POWER_DOWN_CTR" to zero to disable mic power down
// and maintain microphone bias during receive, but this seems to cause problems on receive (e.g. deafness) even
// if the microphone is muted and "mic boost" is disabled. (KA7OEI 20151030)
Codec_WriteRegister(CODEC_ANA_I2C, W8731_POWER_DOWN_CNTR,W8731_POWER_DOWN_CNTR_MCHF_MIC_OFF); // turn off mic bias
}
else // It is transmit
{
if(RadioManagement_UsesTxSidetone())
{
Codec_TxSidetoneSetgain(txrx_mode); // set sidetone level
}
else // Not CW or TUNE mode
{
// Select source or leave it as it is
// PHONE out is muted, normal exit routed to TX modulator
// input audio is routed via 4066 switch
if(ts.tx_audio_source == TX_AUDIO_MIC)
{
// now enabled the analog path according to gain settings
// with or without boost
Codec_SwitchMicTxRxMode(txrx_mode);
}
else if (ts.tx_audio_source != TX_AUDIO_DIG || ts.tx_audio_source != TX_AUDIO_DIGIQ)
{
// we change gain only if it is not a digital tx input source
Codec_LineInGainAdj(ts.tx_gain[ts.tx_audio_source]);
// set LINE input gain if in LINE in mode
}
}
}
}
/**
* @brief calculates and sets sidetone gain based on tx power factor
*
* This calculates the relative level of the sidetone and sets the headphone gain appropriately
* to keep the sidetone level more or less the same.
* This seems to be slightly "off", particularly at the extremes of high and low
* transmit power levels - this needs to be looked into...
*
*/
void Codec_TxSidetoneSetgain(uint8_t txrx_mode)
{
// Note that this function is called from places OTHER than Codec_RX_TX(), above!
if(txrx_mode == TRX_MODE_TX) // bail out if not in transmit mode
{
float32_t vcalc = 0;
if(ts.cw_sidetone_gain) // calculate if the sidetone gain is non-zero
{
float32_t pf = ts.tx_power_factor; // get TX scaling power factor
if ( pf == 0 )
{
pf = 0.001; // Almost zero but prevent from NoNe (1/0) in the next equation.
}
float32_t signal_level_db = 10* log10f(1/(pf*pf));
// we invert the square of power_factor (aka the signal energy)
// since we are calculating attenuation of the original signal (assuming normalization to 1.0)
// get the log
// and multiple with 10 to convert to deciBels
float32_t sidetone_level_db = 6.0 *((float32_t)ts.cw_sidetone_gain-5);
// get the sidetone gain (level) setting
// offset by # of dB the desired sidetone gain
vcalc = signal_level_db + sidetone_level_db; // add the calculated gain to the desired sidetone gain
if(vcalc > 127) // enforce limits of calculation to range of attenuator
{
vcalc = 127;
}
else if (vcalc < 0)
{
vcalc = 0;
}
}
Codec_VolumeSpkr(vcalc/5); // divide by 5 to convert decibel to volume control steps
}
}
/**
* @brief audio volume control in TX and RX modes for speaker [left headphone]
* @param vol speaker / headphone volume in range [0 - CODEC_SPEAKER_MAX_VOLUME], unit is dB, 0 represents muting, one increment represents 5db
*/
void Codec_VolumeSpkr(uint8_t vol)
{
#ifdef UI_BRD_MCHF
uint32_t lv = vol*5>W8731_VOL_MAX?W8731_VOL_MAX:vol*5;
// limit max value to 80
lv += 0x2F; // volume offset, all lower values including 0x2F represent muting
// Reg 02: Speaker - variable volume, change at zero crossing in order to prevent audible clicks
// Codec_WriteRegister(W8731_LEFT_HEADPH_OUT,lv); // (lv | W8731_HEADPH_OUT_ZCEN));
Codec_WriteRegister(CODEC_ANA_I2C, W8731_LEFT_HEADPH_OUT,(lv | W8731_HEADPH_OUT_ZCEN));
#else
// external PA Control
AudioPA_Volume(vol);
#endif
}
/**
* @brief audio volume control in TX and RX modes for lineout [right headphone]
*
* At RX Lineout is always on with constant level (control via ts.lineout_gain)
* At TX only if no frequency translation is active AND TX lineout mute is not set OR in CW
* (because we send always without frequency translation in CW)
*
* @param txrx_mode txrx for which volume is to be set
*/
void Codec_VolumeLineOut(uint8_t txrx_mode)
{
uint16_t lov = ts.lineout_gain + 0x2F;
// Selectively mute "Right Headphone" output (LINE OUT) depending on transceiver configuration
#ifdef UI_BRD_MCHF
// only needed if we have MCHF_UI which shares IQ and Audio on same codec
// we can only "listen to the transmit output if we are send
if (
(txrx_mode == TRX_MODE_TX)
&&
((ts.flags1 & FLAGS1_MUTE_LINEOUT_TX) || (ts.iq_freq_mode && ts.dmod_mode != DEMOD_CW))
)
{
// at CW we transmit without translation, no matter what the iq_freq_mode for RX is
// is translate mode active OR translate mode OFF but LINE OUT to be muted during transmit
Codec_WriteRegister(CODEC_ANA_I2C, W8731_RIGHT_HEADPH_OUT,0); // yes - mute LINE OUT during transmit
}
else // receive mode - LINE OUT always enabled
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_RIGHT_HEADPH_OUT,lov); // value selected for 0.5VRMS at AGC setting
}
#elif defined(UI_BRD_OVI40)
UNUSED(txrx_mode);
// we have a special shared lineout/headphone on the OVI40.
// And since we have a dedidacted IQ codec, there is no need to switch of the lineout or headphones here
Codec_WriteRegister(CODEC_ANA_I2C, W8731_RIGHT_HEADPH_OUT, lov | W8731_HEADPH_OUT_ZCEN | W8731_HEADPH_OUT_HPBOTH ); // value selected for 0.5VRMS at AGC setting
#endif
}
/**
* @brief mute the Codecs Digital to Analog Converter Output
* @param state true -> mute, false -> unmute
*/
void Codec_MuteDAC(bool state)
{
if(state)
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_DIGI_AU_PATH_CNTR,(W8731_DEEMPH_CNTR|0x08)); // mute
}
else
{
Codec_WriteRegister(CODEC_ANA_I2C, W8731_DIGI_AU_PATH_CNTR,(W8731_DEEMPH_CNTR)); // mute off
}
}
/**
* @brief Sets the Codec WM8371 line input gain for both channels
* @param gain in range of [0-255]
*/
static void Codec_InGainAdj(I2C_HandleTypeDef* hi2c, uint16_t gain)
{
// Use Reg 00: Left Line In, set flag to adjust gain of both channels simultaneously
Codec_WriteRegister(hi2c, W8731_LEFT_LINE_IN, gain | W8731_LINE_IN_LRBOTH);
}
/**
* @brief Sets the Codec WM8371 line input gain for both audio in channels
* @param gain in range of [0-255]
*/
void Codec_LineInGainAdj(uint8_t gain)
{
Codec_InGainAdj(CODEC_ANA_I2C, gain);
}
/**
* @brief Sets the Codec WM8371 line input gain for IQ in (both channels)
* @param gain in range of [0-255]
*/
void Codec_IQInGainAdj(uint8_t gain)
{
#ifdef SDR_AMBER
if(ts.codecCS4270_present) // simulation of level control at the ADC input, which is absent in the CS4270 codec
{
float32_t gain_t;
switch(gain)
{
case 0: gain_t = 0.0188; break; // -34.5 dB
case 1: gain_t = 0.0224; break; // -33.0 dB
case 2: gain_t = 0.0266; break; // -31.5 dB
case 3: gain_t = 0.0316; break; // -30.0 dB
case 4: gain_t = 0.0376; break; // -28.5 dB
case 5: gain_t = 0.0447; break; // -27.0 dB
case 6: gain_t = 0.0531; break; // -25.5 dB
case 7: gain_t = 0.0631; break; // -24.0 dB
case 8: gain_t = 0.0750; break; // -22.5 dB
case 9: gain_t = 0.0891; break; // -21.0 dB
case 10: gain_t = 0.1060; break; // -19.5 dB
case 11: gain_t = 0.1260; break; // -18.0 dB
case 12: gain_t = 0.1500; break; // -16.5 dB
case 13: gain_t = 0.1780; break; // -15.0 dB
case 14: gain_t = 0.2110; break; // -13.5 dB
case 15: gain_t = 0.2510; break; // -12.0 dB
case 16: gain_t = 0.2990; break; // -10.5 dB
case 17: gain_t = 0.3550; break; // -9.0 dB
case 18: gain_t = 0.4220; break; // -7.5 dB
case 19: gain_t = 0.5010; break; // -6.0 dB
case 20: gain_t = 0.5960; break; // -4.5 dB
case 21: gain_t = 0.7080; break; // -3.0 dB
case 22: gain_t = 0.8410; break; // -1.5 dB
case 23: gain_t = 1.0000; break; // 0 dB
case 24: gain_t = 1.1900; break; // 1.5 dB
case 25: gain_t = 1.4100; break; // 3.0 dB
case 26: gain_t = 1.6800; break; // 4.5 dB
case 27: gain_t = 2.0000; break; // 6.0 dB
case 28: gain_t = 2.3700; break; // 7.5 dB
case 29: gain_t = 2.8200; break; // 9.0 dB
case 30: gain_t = 3.3500; break; // 10.5 dB
case 31: gain_t = 4.0000; break; // 12.0 dB
}
ts.rf_gain_codecCS4270 = gain_t;
}
else
{
#endif
Codec_InGainAdj(CODEC_IQ_I2C, gain);
#ifdef SDR_AMBER
}
#endif
}
/**
* @brief Checks if all codec resources are available for switching
* It basically checks if the I2C is currently in use
* This function must be called before changing the oscillator in interrupts
* otherwise deadlocks may happen
* @return true if it is safe to call codec functions in an interrupt
*/
bool Codec_ReadyForIrqCall(void)
{
return (CODEC_ANA_I2C->Lock == HAL_UNLOCKED) && (CODEC_IQ_I2C->Lock == HAL_UNLOCKED);
}
@@ -0,0 +1,58 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __CODEC_H
#define __CODEC_H
typedef enum
{
WORD_SIZE_16 = 0,
WORD_SIZE_24 = 2,
WORD_SIZE_32 = 3,
} CodecSampleWidth_t;
#include "ui_configuration.h"
#ifdef UI_BRD_MCHF
#define CODEC_SPEAKER_MAX_VOLUME 16
#else
#define CODEC_SPEAKER_MAX_VOLUME AUDIO_GAIN_MAX
#endif
// --------------------------------------------------
/**
* @brief setups up the codec according to tx/rx mode and selected sources
* @param txrx_mode the mode for which it should be configured
*
*/
void Codec_SwitchTxRxMode(uint8_t txrx_mode);
void Codec_MCUInterfaceInit(uint32_t AudioFreq);
void Codec_VolumeLineOut(uint8_t txrx_mode);
void Codec_VolumeSpkr(uint8_t vol);
void Codec_LineInGainAdj(uint8_t gain);
void Codec_IQInGainAdj(uint8_t gain);
void Codec_MuteDAC(bool state);
uint32_t Codec_Reset(uint32_t AudioFreq);
void Codec_RestartI2S(void);
void Codec_TxSidetoneSetgain(uint8_t mode);
void Codec_SwitchMicTxRxMode(uint8_t mode);
void Codec_PrepareTx(uint8_t current_txrx_mode);
bool Codec_ReadyForIrqCall(void);
#endif
#ifdef SDR_AMBER
uint32_t Codec_Reset_CS4270_Codec(void);
#endif
@@ -0,0 +1,250 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
// Common
#include "uhsdr_board_config.h"
#include "uhsdr_board.h"
#include "profiling.h"
#include "uhsdr_hw_i2s.h"
#include "audio_driver.h"
#ifdef UI_BRD_MCHF
#include "i2s.h"
#endif
#ifdef UI_BRD_OVI40
#include "sai.h"
#endif
typedef struct
{
IqSample_t out[2*IQ_BLOCK_SIZE];
IqSample_t in[2*IQ_BLOCK_SIZE];
} dma_iq_buffer_t;
typedef struct
{
AudioSample_t out[2*AUDIO_BLOCK_SIZE];
AudioSample_t in[2*AUDIO_BLOCK_SIZE];
} dma_audio_buffer_t;
// we do something tricky here:
// if we have a single codec both buffers are in fact the same, so we use a union
// if we have two codecs we use a struct hence two separate buffers
typedef
#if CODEC_NUM == 1
union
#else
struct
#endif
{
dma_iq_buffer_t iq_buf;
dma_audio_buffer_t audio_buf;
} I2S_DmaBuffers_t;
static __UHSDR_DMAMEM I2S_DmaBuffers_t dma;
void UhsdrHwI2s_Codec_ClearTxDmaBuffer()
{
memset((void*)&dma.iq_buf.out, 0, sizeof(dma.iq_buf.out));
}
// #define PROFILE_APP
static void MchfHw_Codec_HandleBlock(uint16_t which)
{
#ifdef PROFILE_EVENTS
// we stop during interrupt
// at the end we start again
#ifdef PROFILE_APP
profileCycleCount_stop();
#else
profileTimedEventStart(ProfileAudioInterrupt);
#endif
#endif
#ifdef EXEC_PROFILING
// Profiling pin (high level)
GPIOE->BSRRL = GPIO_Pin_10;
#endif
ts.audio_int_counter++; // generating a time base for encoder handling
// Transfer complete interrupt
// Point to 2nd half of buffers
const size_t sz = IQ_BLOCK_SIZE;
const uint16_t offset = which == 0?sz:0;
AudioSample_t *audio;
IqSample_t *iq;
if (ts.txrx_mode != TRX_MODE_TX)
{
iq = &dma.iq_buf.in[offset];
audio = &dma.audio_buf.out[offset];
}
else
{
audio = &dma.audio_buf.in[offset];
iq = &dma.iq_buf.out[offset];
}
AudioSample_t *audioDst = &dma.audio_buf.out[offset];
// Handle
AudioDriver_I2SCallback(audio, iq, audioDst, sz);
#ifdef EXEC_PROFILING
// Profiling pin (low level)
GPIOE->BSRRH = GPIO_Pin_10;
#endif
#ifdef PROFILE_EVENTS
// we stopped during interrupt
// now we start again
#ifdef PROFILE_APP
profileCycleCount_start();
#else
profileTimedEventStop(ProfileAudioInterrupt);
#endif
#endif
}
#ifdef UI_BRD_MCHF
/**
* @brief HAL Handler for Codec DMA Interrupt
*/
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
MchfHw_Codec_HandleBlock(0);
}
/**
* @brief HAL Handler for Codec DMA Interrupt
*/
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
MchfHw_Codec_HandleBlock(1);
}
#endif
#ifdef UI_BRD_OVI40
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hi2s)
{
if (hi2s == &hsai_BlockA2)
{
MchfHw_Codec_HandleBlock(0);
}
}
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hi2s)
{
if (hi2s == &hsai_BlockA2)
{
MchfHw_Codec_HandleBlock(1);
}
}
#endif
#if defined(UI_BRD_OVI40) && defined(USE_32_IQ_BITS) && defined(USE_32_AUDIO_BITS)
static void UhsdrHWI2s_Sai32Bits(SAI_HandleTypeDef* hsai)
{
hsai->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hsai->hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
HAL_DMA_Init(hsai->hdmarx);
HAL_SAI_InitProtocol(hsai, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2);
}
static void UhsdrHWI2s_Sai24Bits(SAI_HandleTypeDef* hsai)
{
hsai->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hsai->hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
HAL_DMA_Init(hsai->hdmarx);
HAL_SAI_InitProtocol(hsai, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_24BIT, 2);
}
#endif
static void UhsdrHwI2s_SetBitWidth(void)
{
#if defined(USE_32_IQ_BITS)
#if defined(UI_BRD_MCHF)
hi2s3.Init.DataFormat = I2S_DATAFORMAT_32B;
HAL_I2S_Init(&hi2s3);
#endif
#if defined(UI_BRD_OVI40)
if(ts.codecCS4270_present)
{
UhsdrHWI2s_Sai24Bits(&hsai_BlockA2);
UhsdrHWI2s_Sai24Bits(&hsai_BlockB2);
}
else
{
UhsdrHWI2s_Sai32Bits(&hsai_BlockA2);
UhsdrHWI2s_Sai32Bits(&hsai_BlockB2);
}
#endif
#endif
#if defined(USE_32_AUDIO_BITS)
#if defined(UI_BRD_OVI40)
UhsdrHWI2s_Sai32Bits(&hsai_BlockA1);
UhsdrHWI2s_Sai32Bits(&hsai_BlockB1);
#endif
#endif
}
void UhsdrHwI2s_Codec_StartDMA()
{
UhsdrHwI2s_SetBitWidth();
#ifdef UI_BRD_MCHF
HAL_I2SEx_TransmitReceive_DMA(&hi2s3,(uint16_t*)dma.iq_buf.out,(uint16_t*)dma.iq_buf.in,sizeof(dma.iq_buf.in)/sizeof(dma.iq_buf.in[0].l));
#endif
#ifdef UI_BRD_OVI40
// we clean the buffers since we don't know if we are in a "cleaned" memory segement
memset((void*)&dma.audio_buf,0,sizeof(dma.audio_buf));
memset((void*)&dma.iq_buf,0,sizeof(dma.iq_buf));
HAL_SAI_Receive_DMA(&hsai_BlockA1,(uint8_t*)dma.audio_buf.in,sizeof(dma.audio_buf.in)/sizeof(dma.audio_buf.in[0].l));
HAL_SAI_Transmit_DMA(&hsai_BlockB1,(uint8_t*)dma.audio_buf.out,sizeof(dma.audio_buf.out)/sizeof(dma.audio_buf.out[0].l));
HAL_SAI_Receive_DMA(&hsai_BlockA2,(uint8_t*)dma.iq_buf.in,sizeof(dma.iq_buf.in)/sizeof(dma.iq_buf.in[0].l));
HAL_SAI_Transmit_DMA(&hsai_BlockB2,(uint8_t*)dma.iq_buf.out,sizeof(dma.iq_buf.out)/sizeof(dma.iq_buf.out[0].l));
#endif
}
void UhsdrHwI2s_Codec_StopDMA(void)
{
#ifdef UI_BRD_MCHF
HAL_I2S_DMAStop(&hi2s3);
#endif
#ifdef UI_BRD_OVI40
HAL_SAI_DMAStop(&hsai_BlockA1);
HAL_SAI_DMAStop(&hsai_BlockB1);
HAL_SAI_DMAStop(&hsai_BlockA2);
HAL_SAI_DMAStop(&hsai_BlockB2);
#endif
}
@@ -0,0 +1,26 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __MCHF_HW_I2S_H
#define __MCHF_HW_I2S_H
#include "uhsdr_board_config.h"
void UhsdrHwI2s_Codec_StartDMA(void);
void UhsdrHwI2s_Codec_StopDMA(void);
void UhsdrHwI2s_Codec_ClearTxDmaBuffer(void);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,58 @@
/*
* cw_decoder.h
*
* Created on: 07.09.2017
* Author: danilo
*/
#ifndef AUDIO_CW_CW_DECODER_H_
#define AUDIO_CW_CW_DECODER_H_
#define CW_DECODER_BLOCKSIZE_MIN 8
#define CW_DECODER_BLOCKSIZE_MAX 128
#define CW_DECODER_BLOCKSIZE_DEFAULT 88
#define CW_DECODER_THRESH_MIN 1000
#define CW_DECODER_THRESH_MAX 50000
#define CW_DECODER_THRESH_DEFAULT 32000
#define CW_DECODER_FLAGS_DEFAULT (0b110001) // NOISECANCEL_ENABLE, SNAP_ENABLE, SHOW_CW_LED
typedef struct
{
float32_t sampling_freq;
float32_t target_freq;
// float32_t speed;
uint8_t speed;
// uint8_t average;
uint32_t thresh;
uint8_t blocksize;
// uint8_t AGC_enable;
union
{
struct
{
uint16_t noisecancel_enable:1;
uint16_t spikecancel:2;
#define CW_SPIKECANCEL_MODE_OFF 0
#define CW_SPIKECANCEL_MODE_SPIKE 1
#define CW_SPIKECANCEL_MODE_SHORT 2
uint16_t use_3_goertzels:1;
uint16_t snap_enable:1;
uint16_t show_CW_LED:1; // menu choice whether the user wants the CW LED indicator to be working or not
};
uint16_t flags;
};
} cw_config_t;
extern cw_config_t cw_decoder_config;
void CwDecode_RxProcessor(float32_t * const src, int16_t blockSize);
void CwDecode_Filter_Set(void);
//void CW_Decoder_WPM_display_erase(void);
void CwDecoder_WpmDisplayUpdate(bool force_update);
void CwDecoder_WpmDisplayClearOrPrepare(bool prepare);
#endif /* AUDIO_CW_CW_DECODER_H_ */
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __CW_GEN_H
#define __CW_GEN_H
#include "arm_math.h"
void CwGen_Init(void);
void CwGen_PrepareTx(void);
void CwGen_SetSpeed(void);
bool CwGen_Process( float32_t *i_buffer, float32_t *q_buffer, uint32_t size );
// As showed tests calling them (CwGen_DahIRQ & CwGen_DitIRQ) is matter only for Ultimatic mode.
// After change code to work in this mode w/o them, these extra dependencies with low-level layers could be removed.
void CwGen_DahIRQ(void);
void CwGen_DitIRQ(void);
uint8_t CwGen_CharacterIdFunc( uint32_t );
#endif
@@ -0,0 +1,146 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**--------------------------------------------------------------------------------**
** **
** Description: Please provide one **
** Licence: GNU GPLv3 **
************************************************************************************/
#include <assert.h>
#include "ui_driver.h" // for pushing into UI buffer
#include "uhsdr_digi_buffer.h"
#define DIGIMODES_TX_BUFFER_SIZE 128
static __IO uint8_t digimodes_tx_buffer[DIGIMODES_TX_BUFFER_SIZE];
static __IO int32_t digimodes_tx_buffer_head = 0;
static __IO int32_t digimodes_tx_tail = 0;
static __IO uint32_t active_consumer = 0;
// keeps prev consumer to restore it by calling DigiModes_Restore_BufferConsumer();
static __IO uint32_t prev_consumer = CW;
void DigiModes_Restore_BufferConsumer()
{
active_consumer = prev_consumer;
}
/*
* The UI consumer has higher priority to protect it by changing modes.
* UI should release buffer by calling DigiModes_Restore_BufferConsumer()
*/
digi_buff_consumer_t DigiModes_Set_BufferConsumer( digi_buff_consumer_t consumer )
{
assert((consumer & RTTY) || (consumer & BPSK) || (consumer & CW) || (consumer & UI));
if ( active_consumer == UI ) // <- UI has higher priority
{
prev_consumer = consumer; // in case we change mode
return prev_consumer;
}
prev_consumer = active_consumer;
active_consumer = consumer;
return prev_consumer;
}
uint8_t DigiModes_TxBufferHasData()
{
int32_t len = digimodes_tx_buffer_head - digimodes_tx_tail;
return len < 0 ? ( len + DIGIMODES_TX_BUFFER_SIZE ) : len;
}
bool DigiModes_TxBufferRemove( uint8_t* c_ptr, digi_buff_consumer_t consumer )
{
assert( c_ptr );
assert((consumer & RTTY) || (consumer & BPSK) || (consumer & CW) || (consumer & UI));
bool retval = false;
if ( consumer == active_consumer && digimodes_tx_buffer_head != digimodes_tx_tail)
{
*c_ptr = digimodes_tx_buffer[digimodes_tx_tail];
digimodes_tx_tail = (digimodes_tx_tail + 1) % DIGIMODES_TX_BUFFER_SIZE;
// push removed char to UI buffer to display it on the screen.
// characters for UI and CW prints when they add to digi_buffer
if ( active_consumer == RTTY || active_consumer == BPSK )
{
UiDriver_TextMsgPutChar( *c_ptr );
}
retval = true;
}
return retval;
}
/* no room left in the buffer returns 0 */
int32_t DigiModes_TxBufferPutChar( uint8_t c, digi_buff_consumer_t source )
{
assert((source & CW) || (source & KeyBoard) || (source & UI));
int32_t ret = 0;
/*
* In case when source and consumer are equal we just
* print on the screen without putting into digi_buffer
* to prevent from infinite loop
* ( CW working as a source and receiver at the same time )
*/
if ( active_consumer == source )
{
if(!(ts.dmod_mode == DEMOD_CW && !ts.cw_decoder_enable))
{
UiDriver_TextMsgPutChar( c );
}
}
else
{
int32_t next_head = (digimodes_tx_buffer_head + 1) % DIGIMODES_TX_BUFFER_SIZE;
if (next_head != digimodes_tx_tail)
{
/* there is room */
digimodes_tx_buffer[digimodes_tx_buffer_head] = c;
digimodes_tx_buffer_head = next_head;
/*
* If the consumer is UI we print on the screen
* right the way on the inputing text into buffer
*/
if ( active_consumer == UI )
{
if(!(ts.dmod_mode == DEMOD_CW && !ts.cw_decoder_enable))
{
UiDriver_TextMsgPutChar( c );
}
}
ret ++;
}
}
return ret;
}
void DigiModes_TxBufferPutSign( const char* s, digi_buff_consumer_t source )
{
assert( s );
assert((source & CW) || (source & KeyBoard));
DigiModes_TxBufferPutChar( '<', source );
DigiModes_TxBufferPutChar( s[0], source );
DigiModes_TxBufferPutChar( s[1], source );
DigiModes_TxBufferPutChar( '>', source );
}
void DigiModes_TxBufferReset()
{
digimodes_tx_tail = digimodes_tx_buffer_head;
}
#if defined(_UNIT_TEST_)
uint32_t DigiModes_TxBufferGetCurrentConsumer( void )
{
return active_consumer;
}
uint32_t DigiModes_TxBufferGetPrevConsumer( void )
{
return prev_consumer;
}
#endif
@@ -0,0 +1,63 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**--------------------------------------------------------------------------------**
** **
** Description: Please provide one **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __UHSDR_DIGI_BUFFER_H
#define __UHSDR_DIGI_BUFFER_H
#include "uhsdr_types.h"
typedef enum
{
RTTY = 1,
BPSK = 2,
CW = 4,
UI = 8,
KeyBoard = 16, // <- keyboard should be used as a source only
} digi_buff_consumer_t;
#ifdef __cplusplus
extern "C" {
#endif
uint8_t DigiModes_TxBufferHasData(void);
/*
* The Digi buffer has multi consumers, some of them trying to get an entry
* from the buffer at the same time.
* to organize it, the allowed consumer for buffer should be set before using it.
*
* @return - previous consumer
*/
digi_buff_consumer_t DigiModes_Set_BufferConsumer( digi_buff_consumer_t consumer );
void DigiModes_Restore_BufferConsumer(void);
/*
* @return - true if element was removed from buffer and available in c_ptr
* false if there is no elements in buffer or this consumer not
* allowed to remove elements from buffer.
*/
bool DigiModes_TxBufferRemove( uint8_t* c_ptr, digi_buff_consumer_t consumer );
int32_t DigiModes_TxBufferPutChar( uint8_t c, digi_buff_consumer_t source );
void DigiModes_TxBufferPutSign( const char* s, digi_buff_consumer_t source );
void DigiModes_TxBufferReset( );
#if defined(_UNIT_TEST_)
uint32_t DigiModes_TxBufferGetCurrentConsumer( void );
uint32_t DigiModes_TxBufferGetPrevConsumer( void );
#endif
#ifdef __cplusplus
}
#endif
#endif // __UHSDR_DIGI_BUFFER_H
@@ -0,0 +1,174 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
#ifndef _filters_h_
#define _filters_h_
#include "uhsdr_board.h"
#include "arm_math.h"
#define IQ_RX_BLOCK_SIZE IQ_BLOCK_SIZE
#define IQ_RX_NUM_TAPS 89
#define IQ_RX_NUM_TAPS_HI 199
#define IQ_RX_NUM_TAPS_LO 31
//#define IQ_RX_NUM_TAPS_HI 121
//#define IQ_RX_NUM_TAPS_MAX 121
#define IQ_RX_NUM_TAPS_MAX 199
//#define IQ_RX_NUM_TAPS_HI 499
//#define IQ_RX_NUM_TAPS_MAX 499
#define FIR_TX_FREEDV_INTERPOLATE_NUM_TAPS 30
typedef struct {
const float32_t* i;
const float32_t* q;
const int num_taps;
} IQ_FilterDescriptor;
extern const arm_fir_decimate_instance_f32 FirRxDecimate;
extern const arm_fir_decimate_instance_f32 FirRxDecimate_sideband_supp;
extern const arm_fir_decimate_instance_f32 FirZoomFFTDecimate[6];
extern const arm_fir_decimate_instance_f32 FirRxDecimateMinLPF;
extern const arm_fir_interpolate_instance_f32 FirRxInterpolate;
extern const arm_fir_interpolate_instance_f32 FirRxInterpolate_4_5k;
extern const arm_fir_interpolate_instance_f32 FirRxInterpolate_4_10k;
extern const arm_fir_interpolate_instance_f32 FirRxInterpolate10KHZ;
extern const arm_fir_instance_f32 Fir_TxFreeDV_Interpolate;
extern const float Fir_Rx_FreeDV_Interpolate_Coeffs[24];
extern const float iq_rx_am_10k_coeffs[IQ_RX_NUM_TAPS];
extern const float iq_rx_am_2k3_coeffs[IQ_RX_NUM_TAPS];
extern const float iq_rx_am_3k6_coeffs[IQ_RX_NUM_TAPS];
extern const float iq_rx_am_5k_coeffs[IQ_RX_NUM_TAPS];
extern const float iq_rx_am_4k5_coeffs[IQ_RX_NUM_TAPS];
extern const float iq_rx_am_6k_coeffs[IQ_RX_NUM_TAPS];
extern const float iq_rx_am_7k5_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_10k_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_3k6_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_4k5_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_5k_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_6k_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_7k5_coeffs[IQ_RX_NUM_TAPS];
//extern const float i_rx_coeffs[IQ_NUM_TAPS];
extern const float i_rx_wow_coeffs[IQ_RX_NUM_TAPS_HI];
extern const float i_rx_new_coeffs[IQ_RX_NUM_TAPS_HI];
extern const float i_rx_FREEDV_700D_coeffs[IQ_RX_NUM_TAPS];
extern const float q_rx_FREEDV_700D_coeffs[IQ_RX_NUM_TAPS];
extern const float i_rx_FREEDV_700D_F4_coeffs[IQ_RX_NUM_TAPS_LO];
extern const float q_rx_FREEDV_700D_F4_coeffs[IQ_RX_NUM_TAPS_LO];
extern const float q_rx_new_coeffs[IQ_RX_NUM_TAPS_HI];
extern const float q_rx_wow_coeffs[IQ_RX_NUM_TAPS_HI];
//extern const float q_rx_coeffs[IQ_NUM_TAPS];
extern const float q_rx_10k_coeffs[IQ_RX_NUM_TAPS];
extern const float q_rx_3k6_coeffs[IQ_RX_NUM_TAPS];
extern const float q_rx_4k5_coeffs[IQ_RX_NUM_TAPS];
extern const float q_rx_5k_coeffs[IQ_RX_NUM_TAPS];
extern const float q_rx_6k_coeffs[IQ_RX_NUM_TAPS];
extern const float q_rx_7k5_coeffs[IQ_RX_NUM_TAPS];
extern const arm_iir_lattice_instance_f32 IIR_1k4_LPF;
extern const arm_iir_lattice_instance_f32 IIR_1k4_BPF;
//extern const arm_iir_lattice_instance_f32 IIR_1k4_SSTV;
extern const arm_iir_lattice_instance_f32 IIR_1k6_LPF;
extern const arm_iir_lattice_instance_f32 IIR_1k6_BPF;
extern const arm_iir_lattice_instance_f32 IIR_1k8_1k425;
extern const arm_iir_lattice_instance_f32 IIR_1k8_1k275;
extern const arm_iir_lattice_instance_f32 IIR_1k8_1k125;
extern const arm_iir_lattice_instance_f32 IIR_1k8_1k575;
extern const arm_iir_lattice_instance_f32 IIR_1k8_1k725;
extern const arm_iir_lattice_instance_f32 IIR_1k8_LPF;
extern const arm_iir_lattice_instance_f32 IIR_10k;
extern const arm_iir_lattice_instance_f32 IIR_10k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_15k_hpf;
extern const arm_iir_lattice_instance_f32 IIR_15k_hpf;
extern const arm_iir_lattice_instance_f32 IIR_2k1_LPF;
extern const arm_iir_lattice_instance_f32 IIR_2k1_BPF;
extern const arm_iir_lattice_instance_f32 IIR_2k3_1k412;
extern const arm_iir_lattice_instance_f32 IIR_2k3_1k275;
extern const arm_iir_lattice_instance_f32 IIR_2k3_1k562;
extern const arm_iir_lattice_instance_f32 IIR_2k3_1k712;
extern const arm_iir_lattice_instance_f32 IIR_2k3_LPF;
extern const arm_iir_lattice_instance_f32 IIR_2k5_LPF;
extern const arm_iir_lattice_instance_f32 IIR_2k5_BPF;
extern const arm_iir_lattice_instance_f32 IIR_2k7_LPF;
extern const arm_iir_lattice_instance_f32 IIR_2k7_BPF;
extern const arm_iir_lattice_instance_f32 IIR_2k9_LPF;
extern const arm_iir_lattice_instance_f32 IIR_2k9_BPF;
extern const arm_iir_lattice_instance_f32 IIR_TX_2k7;
extern const arm_iir_lattice_instance_f32 IIR_TX_WIDE_BASS;
extern const arm_iir_lattice_instance_f32 IIR_TX_WIDE_TREBLE;
extern const arm_iir_lattice_instance_f32 IIR_TX_SOPRANO;
extern const arm_iir_lattice_instance_f32 IIR_TX_eSSB_4k;
extern const arm_iir_lattice_instance_f32 IIR_TX_eSSB_5k;
extern const arm_iir_lattice_instance_f32 IIR_TX_eSSB_6k;
extern const arm_iir_lattice_instance_f32 IIR_TX_2k7_FM;
extern const arm_iir_lattice_instance_f32 IIR_3k2_LPF;
extern const arm_iir_lattice_instance_f32 IIR_3k2_BPF;
extern const arm_iir_lattice_instance_f32 IIR_3k4_LPF;
extern const arm_iir_lattice_instance_f32 IIR_3k4_BPF;
extern const arm_iir_lattice_instance_f32 IIR_3k6_LPF;
extern const arm_iir_lattice_instance_f32 IIR_3k6_BPF;
extern const arm_iir_lattice_instance_f32 IIR_3k8_LPF;
extern const arm_iir_lattice_instance_f32 IIR_3k8_BPF;
extern const arm_iir_lattice_instance_f32 IIR_100hz_500;
extern const arm_iir_lattice_instance_f32 IIR_100hz_550;
extern const arm_iir_lattice_instance_f32 IIR_100hz_600;
extern const arm_iir_lattice_instance_f32 IIR_100hz_650;
extern const arm_iir_lattice_instance_f32 IIR_100hz_700;
extern const arm_iir_lattice_instance_f32 IIR_100hz_750;
extern const arm_iir_lattice_instance_f32 IIR_100hz_800;
extern const arm_iir_lattice_instance_f32 IIR_100hz_850;
extern const arm_iir_lattice_instance_f32 IIR_100hz_900;
extern const arm_iir_lattice_instance_f32 IIR_100hz_950;
extern const arm_iir_lattice_instance_f32 IIR_200hz_500;
extern const arm_iir_lattice_instance_f32 IIR_200hz_550;
extern const arm_iir_lattice_instance_f32 IIR_200hz_600;
extern const arm_iir_lattice_instance_f32 IIR_200hz_650;
extern const arm_iir_lattice_instance_f32 IIR_200hz_700;
extern const arm_iir_lattice_instance_f32 IIR_200hz_750;
extern const arm_iir_lattice_instance_f32 IIR_200hz_800;
extern const arm_iir_lattice_instance_f32 IIR_200hz_850;
extern const arm_iir_lattice_instance_f32 IIR_200hz_900;
extern const arm_iir_lattice_instance_f32 IIR_200hz_950;
extern const arm_iir_lattice_instance_f32 IIR_300hz_750;
extern const arm_iir_lattice_instance_f32 IIR_300hz_800;
extern const arm_iir_lattice_instance_f32 IIR_300hz_850;
extern const arm_iir_lattice_instance_f32 IIR_300hz_900;
extern const arm_iir_lattice_instance_f32 IIR_300hz_950;
extern const arm_iir_lattice_instance_f32 IIR_300hz_700;
extern const arm_iir_lattice_instance_f32 IIR_300hz_650;
extern const arm_iir_lattice_instance_f32 IIR_300hz_600;
extern const arm_iir_lattice_instance_f32 IIR_300hz_550;
extern const arm_iir_lattice_instance_f32 IIR_300hz_500;
extern const arm_iir_lattice_instance_f32 IIR_3k;
extern const arm_iir_lattice_instance_f32 IIR_4k2_LPF;
extern const arm_iir_lattice_instance_f32 IIR_4k4_LPF;
extern const arm_iir_lattice_instance_f32 IIR_4k6_LPF;
extern const arm_iir_lattice_instance_f32 IIR_4k8_LPF;
extern const arm_iir_lattice_instance_f32 IIR_4k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_5k5_LPF;
extern const arm_iir_lattice_instance_f32 IIR_500hz_750;
extern const arm_iir_lattice_instance_f32 IIR_500hz_850;
extern const arm_iir_lattice_instance_f32 IIR_500hz_950;
extern const arm_iir_lattice_instance_f32 IIR_500hz_650;
extern const arm_iir_lattice_instance_f32 IIR_500hz_550;
extern const arm_iir_lattice_instance_f32 IIR_5k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_6k5_LPF;
extern const arm_iir_lattice_instance_f32 IIR_6k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_7k5_LPF;
extern const arm_iir_lattice_instance_f32 IIR_7k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_8k5_LPF;
extern const arm_iir_lattice_instance_f32 IIR_8k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_8k5_hpf;
extern const arm_iir_lattice_instance_f32 IIR_9k5_LPF;
extern const arm_iir_lattice_instance_f32 IIR_9k_LPF;
extern const arm_iir_lattice_instance_f32 IIR_aa_5k;
extern const arm_iir_lattice_instance_f32 IIR_aa_10k;
extern const arm_iir_lattice_instance_f32 IIR_aa_8k;
extern const arm_iir_lattice_instance_f32 IIR_aa_8k5;
extern const arm_iir_lattice_instance_f32 IIR_aa_9k;
extern const arm_iir_lattice_instance_f32 IIR_aa_9k5;
#endif
@@ -0,0 +1,184 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** License: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
/**************************************************************
KA7OEI, November, 2014
IMPORTANT NOTE:
This is a MINIMAL FIR Lowpass filter that is applied to the RX input
decimation. It is NOT designed to provide any significant filtering!
The majority of anti-aliasing for this decimation operation is provided BY the
Phase-Added Hilbert Transformer that PRECEDES this!
This is an absolutely minimized operation to reduce processor overhead!
Response:
DC-2370 Hz: >=-0.25dB
-1dB @ 3492 Hz
-3dB @ 5320 Hz
-6dB @ 6958 Hz
-10dB @ 8396 Hz
Notch @ 10767 Hz with -20dB points at 10000 and 11598 Hz.
Min. -7.6dB @ 16.4 kHz
-20dB > 22.9 kHz
***************************************************************/
//const arm_fir_decimate_instance_f32 FirRxDecimate =
//{
// .numTaps = 4,
// .pCoeffs = (float*) (const float[])
// {
// -0.3105711170561,
// -0.2100034101786,
// -0.2099936469089,
// -0.3105557871709
// }
//};
// this is the decimation filter used for decimating the I and the Q path
// when in CW/SSB mode and filter BW <= 3k6
// 48ksps, 43 taps
// Fc=3.6kHz 0.1500
// Kaiser Beta 2.20, Raised Cosine 0.750
const arm_fir_decimate_instance_f32 FirRxDecimate =
{
.numTaps = 43,
.pCoeffs = (float*) (const float[])
{
-596.2286796599541960E-6,-927.3655504344595780E-6,-940.6330523057968090E-6,-298.4871913266846950E-6, 0.001186311749146407, 0.003340717388857422, 0.005503534826465819, 0.006586352026602872, 0.005378691189561175, 0.001056745384724361,-0.006253091035763602,-0.015078013363837874,-0.022588392594615103,-0.025094197603292432,-0.018999101368626720,-0.001976514919694041, 0.025998282705991884, 0.062138450304126999, 0.101110564031515582, 0.136092352983227610, 0.160375863194488072, 0.169067382812500000, 0.160375863194488072, 0.136092352983227610, 0.101110564031515582, 0.062138450304126999, 0.025998282705991884,-0.001976514919694041,-0.018999101368626720,-0.025094197603292432,-0.022588392594615103,-0.015078013363837874,-0.006253091035763602, 0.001056745384724361, 0.005378691189561175, 0.006586352026602872, 0.005503534826465819, 0.003340717388857422, 0.001186311749146407,-298.4871913266846950E-6,-940.6330523057968090E-6,-927.3655504344595780E-6,-596.2286796599541960E-6
},
.pState = NULL
};
// this is an antialias lowpass filter for the first filter instance before the decimation
// 48ksps, 83 taps
// Fc=3.6kHz 0.1500
// Kaiser Beta 2.20, Raised Cosine 0.780
// --> stopband suppression > 70dB [> 5.5kHz]
const arm_fir_decimate_instance_f32 FirRxDecimate_sideband_supp =
{
.numTaps = 83,
.pCoeffs = (float*) (const float[])
{
-69.04572960222937180E-6,-137.6834398584523170E-6,-186.3970280457814400E-6,-181.9425732680219880E-6,-104.7375909552678900E-6, 37.22776946454347070E-6, 204.5969072113929600E-6, 337.6430886700526340E-6, 380.2712946166168420E-6, 308.0147076442374330E-6, 146.5458495799618390E-6,-31.48113457454075360E-6,-135.5241548808152460E-6,-104.1622729286465440E-6, 49.66097981004921280E-6, 219.8972970987902040E-6, 234.0369712091759310E-6,-72.92696579416441690E-6,-758.2621215486242360E-6,-0.001685885336453723,-0.002502896255855177,-0.002712504642547770,-0.001844951124000482, 314.9034535895095810E-6, 0.003522159070787864, 0.006982502500958138, 0.009453648049644431, 0.009558702320444503, 0.006257513701590612,-650.2918349336806610E-6,-0.010158967215772004,-0.019985981030678633,-0.026889297155700119,-0.027354668266382533,-0.018512617437158409, 950.1502355180025460E-6, 0.030152564469343023, 0.065873978372928979, 0.103000681197519150, 0.135500724389660737, 0.157707975855713750, 0.165595703121596383, 0.157707975855713750, 0.135500724389660737, 0.103000681197519150, 0.065873978372928979, 0.030152564469343023, 950.1502355180025460E-6,-0.018512617437158409,-0.027354668266382533,-0.026889297155700119,-0.019985981030678633,-0.010158967215772004,-650.2918349336806610E-6, 0.006257513701590612, 0.009558702320444503, 0.009453648049644431, 0.006982502500958138, 0.003522159070787864, 314.9034535895095810E-6,-0.001844951124000482,-0.002712504642547770,-0.002502896255855177,-0.001685885336453723,-758.2621215486242360E-6,-72.92696579416441690E-6, 234.0369712091759310E-6, 219.8972970987902040E-6, 49.66097981004921280E-6,-104.1622729286465440E-6,-135.5241548808152460E-6,-31.48113457454075360E-6, 146.5458495799618390E-6, 308.0147076442374330E-6, 380.2712946166168420E-6, 337.6430886700526340E-6, 204.5969072113929600E-6, 37.22776946454347070E-6,-104.7375909552678900E-6,-181.9425732680219880E-6,-186.3970280457814400E-6,-137.6834398584523170E-6,-69.04572960222937180E-6
},
.pState = NULL
};
// this is the decimation filter used for decimating the I and the Q path
// when in CW/SSB mode and filter BW <= 3k6
// 48ksps, 43 taps
// Fc=3.6kHz 0.1500
// Kaiser Beta 2.20, Raised Cosine 0.750
//const arm_fir_decimate_instance_f32 FirRxDecimate_sideband_supp =
//{
// .numTaps = 43,
// .pCoeffs = (float*) (const float[])
// {
// -596.2286796599541960E-6,-927.3655504344595780E-6,-940.6330523057968090E-6,-298.4871913266846950E-6, 0.001186311749146407, 0.003340717388857422, 0.005503534826465819, 0.006586352026602872, 0.005378691189561175, 0.001056745384724361,-0.006253091035763602,-0.015078013363837874,-0.022588392594615103,-0.025094197603292432,-0.018999101368626720,-0.001976514919694041, 0.025998282705991884, 0.062138450304126999, 0.101110564031515582, 0.136092352983227610, 0.160375863194488072, 0.169067382812500000, 0.160375863194488072, 0.136092352983227610, 0.101110564031515582, 0.062138450304126999, 0.025998282705991884,-0.001976514919694041,-0.018999101368626720,-0.025094197603292432,-0.022588392594615103,-0.015078013363837874,-0.006253091035763602, 0.001056745384724361, 0.005378691189561175, 0.006586352026602872, 0.005503534826465819, 0.003340717388857422, 0.001186311749146407,-298.4871913266846950E-6,-940.6330523057968090E-6,-927.3655504344595780E-6,-596.2286796599541960E-6
// }
//};
const arm_fir_decimate_instance_f32 FirZoomFFTDecimate[] =
{
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
-0.3105711170561,
-0.2100034101786,
-0.2099936469089,
-0.3105557871709
},
.pState = NULL // we don't use this directly
},
// 48ksps, 12kHz lowpass
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
475.1179397144384210E-6,
0.503905202786044337,
0.503905202786044337,
475.1179397144384210E-6
},
.pState = NULL // we don't use this directly
},
// 48ksps, 6kHz lowpass
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
0.198273254218889416,
0.298085149879260325,
0.298085149879260325,
0.198273254218889416
},
.pState = NULL // we don't use this directly
},
// 48ksps, 3kHz lowpass
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
0.199820836596682871,
0.272777397353925699,
0.272777397353925699,
0.199820836596682871
},
.pState = NULL // we don't use this directly
},
// 48ksps, 1.5kHz lowpass
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
0.199820836596682871,
0.272777397353925699,
0.272777397353925699,
0.199820836596682871
},
.pState = NULL // we don't use this directly
},
// 48ksps, 750Hz lowpass
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
0.199820836596682871,
0.272777397353925699,
0.272777397353925699,
0.199820836596682871
},
.pState = NULL // we don't use this directly
}
};
@@ -0,0 +1,58 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
/**************************************************************
KA7OEI, November, 2014
IMPORTANT NOTE:
This is a MINIMAL FIR Lowpass filter that is applied to the RX input
decimation. It is NOT designed to provide any significant filtering!
The majority anti-aliasing for this decimation operation is provided ONLY by the
Phase-Added Hilbert Transformer that precedes this!
This is an absolutely minimized operation to reduce processor overhead!
3rd order, Least Pth-Norm, 48 ksps, Fpass=10000, Fstop=15000, Wpass=10, Wstop=1
Response:
DC-10330 Hz: >=-0.5dB
-1dB @ 10900 Hz Hz
-3dB @ 13089 Hz
-6dB @ 14900 Hz
-10dB @ 16700 Hz
Notch @ 19590 Hz with -20dB points at 18510 and 20880 Hz.
Min. -13.8dB @ 23.6 kHz
***************************************************************/
const arm_fir_decimate_instance_f32 FirRxDecimateMinLPF =
{
.numTaps = 4,
.pCoeffs = (float*) (const float[])
{
-0.1848172721704,
0.135814345129,
0.5586516940301,
0.4419794765896
},
.pState = NULL
};
@@ -0,0 +1,256 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
/**************************************************************
KA7OEI, November, 2014
IMPORTANT NOTE:
This low-pass filter is designed to remove aliasing artifacts from the interpolated output.
Response (15th order FIR, Least Pth-norm: Fpass = 4000 Hz, Fstop = 8600 Hz Fsamp = 12000 Hz):
-6dB: 5075 Hz
-10dB: 5841 Hz
-20dB: 7034 Hz
-40dB: 8270 Hz
-50dB: >8550 Hz
Because the Nyquist Frequency is 6 kHz with the 12 kHz input sample rate, with filtered
audio limited to <3 kHz by the normal SSB audio filters, there will be NO aliased
content <9 kHz so the response of this low-pass filter may be relaxed to provide
a higher degree of filtering (e.g. >50dB) >8550 Hz.
In the case of the 3.6 kHz filter, the aliasing extends down no farther than 8200 Hz where the
attenuation is still at least 40dB where this, plus the effects of psychoacoustic masking
should make these artifacts inaudible.
***************************************************************/
const arm_fir_interpolate_instance_f32 FirRxInterpolate=
{
.phaseLength = 16,
.pCoeffs = (float*) (const float[])
{
0.0064096284297950057,
0.017769515865099398,
0.032257990537557804,
0.040774242726362854,
0.031402652968355385,
-0.0044545611189248744,
-0.065457689067971975,
-0.13802671944933273,
-0.20064935251937976,
-0.23328570256042488,
-0.22670802442881149,
-0.18633542013951426,
-0.1286861967088595,
-0.072726968058963559,
-0.031602889405696735,
-0.009023372039440496
},
.pState = NULL
};
/*#####################################################
*
* Interpolation filter with 4 taps
* 4 taps minimum for the ARM function to work
* two versions:
* 1.) for filter bandwidths between 3k4 and 4k8
* --> 5kHz stop frequency
* 2.) for filter bandwidths between 8k0 and 10k
* --> 10kHz stop frequency
*
* these "filters" do not filter much with 4 taps . . .
* FIR 4 taps, Fstop 5kHz / 10kHz, Rectangular
* designed with Iowa Hills FIR Filter Designer
* DD4WH 2016_03_11
*
* ####################################################
*/
const arm_fir_interpolate_instance_f32 FirRxInterpolate_4_5k=
{
.phaseLength = 4,
.pCoeffs = (float*) (const float[])
{
0.210149639888675743,
0.292367020487055873,
0.292367020487055873,
0.210149639888675743
},
.pState = NULL
};
const arm_fir_interpolate_instance_f32 FirRxInterpolate_4_10k=
{
.phaseLength = 4,
.pCoeffs = (float*) (const float[])
{
0.139302961114601442,
0.460435107346899464,
0.460435107346899464,
0.139302961114601442
},
.pState = NULL
};
// FIXME: Is this the right file for a FreeDV TX filter?
// this is meant to be an interpolation filter for FreeDV
// cutoff 2.4kHz (30 Taps)
const arm_fir_instance_f32 Fir_TxFreeDV_Interpolate =
{
.numTaps = FIR_TX_FREEDV_INTERPOLATE_NUM_TAPS,
.pCoeffs = (float*) (const float[])
{
-0.001351058376639693,
-0.003185783059285283,
-0.005575655191465650,
-0.007911042143847070,
-0.009195803961917746,
-0.008176776032790899,
-0.003590261795373249,
0.005518212389136743,
0.019475233722896626,
0.037749136460739165,
0.058883910956281677,
0.080638648048914893,
0.100323507980679211,
0.115270746696726323,
0.123339678809895686,
0.123339678809895686,
0.115270746696726323,
0.100323507980679211,
0.080638648048914893,
0.058883910956281677,
0.037749136460739165,
0.019475233722896626,
0.005518212389136743,
-0.003590261795373249,
-0.008176776032790899,
-0.009195803961917746,
-0.007911042143847070,
-0.005575655191465650,
-0.003185783059285283,
-0.001351058376639693
},
.pState = NULL
};
/*
// And here for you to try the same filter with 60 taps
// this is meant to be an interpolation filter for FreeDV
// cutoff 2.4kHz
const arm_fir_instance_f32 FirFreeDVInterpolate=
{
.numTaps = 30,
.pCoeffs = (float*) (const float[])
{
-325.7026576032276350E-6,
-371.9001810615538940E-6,
-297.4863280206030250E-6,
-42.52080605332919560E-6,
430.2851136718793440E-6,
0.001114933511882014,
0.001945133332895070,
0.002786351459208297,
0.003441665727387134,
0.003674210056274392,
0.003245972098811801,
0.001969072608194520,
-237.8878302134766050E-6,
-0.003298503514874146,
-0.006943439896700603,
-0.010701717349100807,
-0.013925451717764645,
-0.015850079401508791,
-0.015685944805004001,
-0.012730898974573298,
-0.006488430580536590,
0.003227089128632603,
0.016216448931132731,
0.031868083873548676,
0.049186001642367742,
0.066873952747489043,
0.083467736074663401,
0.097499421337832640,
0.107671414315587272,
0.113015797883873961,
0.113015797883873961,
0.107671414315587272,
0.097499421337832640,
0.083467736074663401,
0.066873952747489043,
0.049186001642367742,
0.031868083873548676,
0.016216448931132731,
0.003227089128632603,
-0.006488430580536590,
-0.012730898974573298,
-0.015685944805004001,
-0.015850079401508791,
-0.013925451717764645,
-0.010701717349100807,
-0.006943439896700603,
-0.003298503514874146,
-237.8878302134766050E-6
0.001969072608194520,
0.003245972098811801,
0.003674210056274392,
0.003441665727387134,
0.002786351459208297,
0.001945133332895070,
0.001114933511882014,
430.2851136718793440E-6,
-42.52080605332919560E-6,
-297.4863280206030250E-6,
-371.9001810615538940E-6,
-325.7026576032276350E-6
}
};
*/
const float32_t Fir_Rx_FreeDV_Interpolate_Coeffs[24] =
{
-0.017405444562935534,
-0.017108768884742161,
-0.013120768604810904,
-0.004804294719432716,
0.007973629566960488,
0.024740722876424703,
0.044407570327122774,
0.065355761534977047,
0.085619214114458830,
0.103134409504618857,
0.116021478863062078,
0.122850816384744946,
0.122850816384744946,
0.116021478863062078,
0.103134409504618857,
0.085619214114458830,
0.065355761534977047,
0.044407570327122774,
0.024740722876424703,
0.007973629566960488,
-0.004804294719432716,
-0.013120768604810904,
-0.017108768884742161,
-0.017405444562935534
};
@@ -0,0 +1,64 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
/**************************************************************
KA7OEI, November, 2014
IMPORTANT NOTE:
This low-pass filter is designed to remove aliasing artifacts from the interpolated output.
Response (15th order FIR, Least Pth-norm: Fpass = 10000 Hz, Fstop = 1200 Hz Fsamp = 24000 Hz):
Ripple: +/- 0.5dB
-6dB: 10640 Hz
-10dB: 10873 Hz
-20dB: 11240 Hz
-30dB: 11449 Hz
Notch at 11608 Hz
-34dB peak > 12000 (Ripple response at this level)
This low-pass filter is a special case for the 10 kHz passband. Due to the limited order, the ultimate response at Nyquist is
only on the order of -34dB, but this is suitable to avoid audible aliasing artifacts because of phsycoacoustic masking effects.
***************************************************************/
const arm_fir_interpolate_instance_f32 FirRxInterpolate10KHZ=
{
.phaseLength = 16,
.pCoeffs = (float*) (const float[])
{
0.05811583205356,
-0.0367094411696,
0.01940709235172,
-0.02834514773976,
-0.01544119658805,
0.00554906655524,
-0.08598762701749,
0.04147536848705,
-0.165163226107,
0.01287481670083,
-0.1999399439611,
-0.1925399994825,
-0.1329419101037,
-0.7311312926885,
0.05542105288489,
0.4430274622578
},
.pState = NULL
};
@@ -0,0 +1,127 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __I_TX_FILTER_H
#define __I_TX_FILTER_H
#define IQ_TX_BLOCK_SIZE 1
#define IQ_TX_NUM_TAPS 89
/*
* Hilbert 0 Degree, "Phase-added" bandpass filter
* Kaiser Window FIR Filter, Beta = 3.25, Raised Cosine = 9.30
* Fc = 5.23 kHz
* BW = 9.50 kHz
* 89 Taps
*
* 20140926 by KA7OEI using Iowa Hills Hilbert Filter Designer
*/
const float i_tx_coeffs[I_NUM_TAPS] =
{
0.000101250517153683,
-0.000515927338247317,
-0.00176919806883,
-0.001893116080949,
-0.000577659000336806,
3.550079658197940E-6,
-0.00172963585657513,
-0.0038100999663584,
-0.0031472032338157,
-0.000586333586637393,
-0.000494536851592284,
-0.00415653740435641,
-0.00680718523656846,
-0.00429325456879721,
-0.000273434114376999,
-0.00181389209595478,
-0.0082208278123836,
-0.0105062537188667,
-0.00469751224581168,
0.000452038013830452,
-0.00454071945888382,
-0.0142111518079221,
-0.0142255278253247,
-0.00344416232313822,
0.001565954170156727,
-0.00947713768589062,
-0.0222735621260352,
-0.0167608122370822,
0.000803648164899460,
0.002905223684809450,
-0.0179852797068097,
-0.0327457542271941,
-0.0160524441181231,
0.010701868004134297,
0.004196449024939201,
-0.0338893802111143,
-0.0479948819717114,
-0.00683680444677724,
0.036441721082929118,
0.005133250668784600,
-0.0804182627087614,
-0.0919356431907568,
0.060333107936900596,
0.291908298625711771,
0.404029117624692125,
0.291908298625728535,
0.060333107936918977,
-0.0919356431907509,
-0.0804182627087674,
0.005133250668778217,
0.036441721082930000,
-0.00683680444677221,
-0.0479948819717094,
-0.033889380211117,
0.004196449024936275,
0.010701868004134651,
-0.016052444118121,
-0.0327457542271935,
-0.0179852797068109,
0.002905223684808852,
0.000803648164900392,
-0.0167608122370813,
-0.0222735621260356,
-0.00947713768589167,
0.001565954170156743,
-0.00344416232313721,
-0.0142255278253243,
-0.0142111518079231,
-0.00454071945888512,
0.000452038013830350,
-0.00469751224581064,
-0.0105062537188658,
-0.00822082781238365,
-0.0018138920959553,
-0.000273434114377146,
-0.00429325456879692,
-0.0068071852365683,
-0.00415653740435665,
-0.000494536851592651,
-0.000586333586637503,
-0.00314720323381554,
-0.00381009996635821,
-0.00172963585657509,
3.550079658144650E-6,
-0.000577659000336875,
-0.00189311608094905,
-0.00176919806883003,
-0.000515927338247313,
0.000101250517153702
};
//static float32_t FirState_I_TX[128];
//arm_fir_instance_f32 FIR_I_TX;
#endif
@@ -0,0 +1,379 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 2020.04.27 by Y.Korenev UB8JDC using MatLAB fdatools
//
// NOTE:
// - IIR, structure is Lattice Autoregressive Moving-Average (ARMA)
// - 12 KSPS Sample Rate
// - Bandwidth 100 Hz (CW)
// - 10th order Butterworth bandpass filter
// - Single-precision floating-point
// - Coefficients importing in reverse order!
//
#define IIR_100hz_numStages 10
//
// 500 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_500 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117045402527,
-0.961479723453522,
0.995523214340210,
-0.968451142311096,
0.998828113079071,
-0.966746151447296,
0.999394357204437,
-0.966019272804260,
0.999540567398071,
-0.968152463436127
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.074513349408335e-07,
-4.139578777540010e-07,
-7.580364353998448e-07,
-5.984049948892789e-07,
-4.667455399953724e-08,
1.742693171991050e-07,
1.272666239060527e-08,
-1.451467124269357e-08,
-4.154731003680467e-10,
3.492807154614752e-10
}
};
//
// 550 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_550 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117164611816,
-0.954406321048737,
0.995446324348450,
-0.961397528648377,
0.998838007450104,
-0.959627330303192,
0.999398231506348,
-0.958896815776825,
0.999529540538788,
-0.961052417755127
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.066608419364457e-07,
-4.062592893205874e-07,
-7.287537755473750e-07,
-5.394011282078282e-07,
7.951572733588819e-09,
1.959667628170791e-07,
7.976567850676020e-09,
-1.985828923523059e-08,
-3.296478645609113e-10,
5.870664510077006e-10
}
};
//
// 600 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_600 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117105007172,
-0.946678876876831,
0.995385706424713,
-0.953669130802155,
0.998845875263214,
-0.951852679252625,
0.999400973320007,
-0.951121032238007,
0.999521076679230,
-0.953288018703461
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.057972482954028e-07,
-3.979138227805379e-07,
-6.973535846555023e-07,
-4.773115165335184e-07,
6.319763912188137e-08,
2.142628687806791e-07,
5.286769910739508e-10,
-2.597028903039700e-08,
-5.918551759798163e-11,
9.229992881110149e-10
}
};
//
// 650 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_650 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117105007172,
-0.938302576541901,
0.995337128639221,
-0.945275247097015,
0.998852193355560,
-0.943426966667175,
0.999403059482575,
-0.942695379257202,
0.999514400959015,
-0.944866895675659
},
.pvCoeffs = (float*) (const float[])
{
-1.131203308801787e-08,
-1.048611366627483e-07,
-3.889442155013967e-07,
-6.640120773226954e-07,
-4.127688271182706e-07,
1.180438644610149e-07,
2.281452537999940e-07,
-9.835012804160215e-09,
-3.262511327761786e-08,
4.808165532388387e-10,
1.369539703865996e-09
}
};
//
// 700 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_700 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117105007172,
-0.929283201694489,
0.995297670364380,
-0.936224520206451,
0.998857378959656,
-0.934354960918427,
0.999404788017273,
-0.933626711368561,
0.999508917331696,
-0.935794889926910
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.038531749486538e-07,
-3.793751943703683e-07,
-6.289155862759799e-07,
-3.464265319053084e-07,
1.714726352020080e-07,
2.366920881513579e-07,
-2.321627867729603e-08,
-3.949672944258964e-08,
1.384046655061866e-09,
1.929782778020694e-09
}
};
//
// 750 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_750 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117105007172,
-0.919626951217651,
0.995265305042267,
-0.926525056362152,
0.998861670494080,
-0.924643456935883,
0.999406158924103,
-0.923918902873993,
0.999504446983337,
-0.926079809665680
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.027740239578634e-07,
-3.692328789384192e-07,
-5.922586296946974e-07,
-2.789512905110314e-07,
2.224872162059910e-07,
2.391011832969525e-07,
-3.958798444614331e-08,
-4.616278204139235e-08,
2.746912253215328e-09,
2.592701386916474e-09
}
};
//
// 800 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_800 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117164611816,
-0.909340500831604,
0.995238363742828,
-0.916185200214386,
0.998865187168121,
-0.914298593997955,
0.999407291412354,
-0.913579702377319,
0.999500691890717,
-0.915728092193604
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.016244510765318e-07,
-3.585451509025006e-07,
-5.542443659578566e-07,
-2.110181753778306e-07,
2.701280834571662e-07,
2.347170919847486e-07,
-5.878169773154696e-08,
-5.211252940284794e-08,
4.661536934236210e-09,
3.328791242651619e-09
}
};
//
// 850 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_850 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117224216461,
-0.898430705070496,
0.995215833187103,
-0.905213057994843,
0.998868167400360,
-0.903327405452728,
0.999408245086670,
-0.902615189552307,
0.999497592449188,
-0.904747426509857
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-1.004052165853864e-07,
-3.473411993581976e-07,
-5.150816946297709e-07,
-1.433014062968141e-07,
3.134916255476128e-07,
2.230537887726314e-07,
-8.048506572322367e-08,
-5.676345793403925e-08,
7.207070940040694e-09,
4.085846327939180e-09
}
};
//
// 900 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_900 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117045402527,
-0.886905252933502,
0.995196700096130,
-0.893617093563080,
0.998870730400085,
-0.891737699508667,
0.999409019947052,
-0.891032636165619,
0.999494969844818,
-0.893145799636841
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-9.911717313570989e-08,
-3.356518618602422e-07,
-4.749855975205719e-07,
-7.647016531109330e-08,
3.517440632094804e-07,
2.038139115256854e-07,
-1.042412947072080e-07,
-5.948210812789512e-08,
1.044057107435492e-08,
4.785924101469163e-09
}
};
//
// 950 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_100hz_950 =
{
.numStages = IIR_100hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.844117164611816,
-0.874771952629089,
0.995180428028107,
-0.881405830383301,
0.998872876167297,
-0.879536986351013,
0.999409675598145,
-0.878840029239655,
0.999492764472961,
-0.880931198596954
},
.pvCoeffs = (float*) (const float[])
{
-1.131203397619629e-08,
-9.776120180049475e-08,
-3.235090559883247e-07,
-4.341745807323605e-07,
-1.117885339851910e-08,
3.841382465452625e-07,
1.769026027886866e-07,
-1.294563389819814e-07,
-5.961092597317474e-08,
1.438790597774187e-08,
5.323763208053833e-09
}
};
@@ -0,0 +1,61 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20140915 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 8th order Elliptic lowpass filter
//
//
// -6dB point: 10.0 kHz
// -20dB point: 10.18 kHz
// -40dB point: 10.46 kHz
// -60dB point: >10.88 kHz
//
//#define NCoef 8
#define IIR_10k_numStages 8
const arm_iir_lattice_instance_f32 IIR_10k =
{
.numStages = IIR_10k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3059837075323,
-0.5896555175446,
0.7331597393194,
-0.713542623059,
0.8999944361723,
-0.4831480007607,
0.9938574536212,
-0.2965660617857
},
.pvCoeffs = (float*) (const float[])
{
0.01294033742346,
0.06059508198778,
0.1442543837861,
0.184661010229,
0.1196473741182,
0.01735304263064,
-0.03003092329645,
-0.003155003081181,
0.0003259315989964
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 10000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_10k_numStages 8
const arm_iir_lattice_instance_f32 IIR_10k_LPF =
{
.numStages = IIR_10k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.184297745178662,
0.346275356901559,
0.835935186979480,
0.790382467146251,
0.986915159126712,
0.842177143803695,
0.999207130498260,
0.862058584401922
},
.pvCoeffs = (float*) (const float[])
{
0.285756161122327,
0.647395708199686,
0.191311565499666,
-0.164203854783080,
-0.0319701390832776,
0.00976034243199386,
0.00225291674041372,
-0.000117333004814757,
-4.35613343882091e-05
}
};
@@ -0,0 +1,96 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20151022 by C. Turner, KA7OEI using MatLAB fdatools
//
// This high pass filter is for the FM demodulator to extract high-frequency "triangle noise" energy
// for the squelch algorithm.
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 6th order Elliptic highpass filter
//
//
// -6dB point: 14.9 kHz
// -20dB point: 14.5 kHz
// -40dB point: 13.8 kHz
// -60dB point: 13.3 kHz
//
/*
#define IIR_15k_hpf_numStages 6
const arm_iir_lattice_instance_f32 IIR_15k_hpf = {
.numStages = IIR_15k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.513580159973,
0.7246612586998,
0.7246612586998,
0.8107542886737,
0.7534361820128,
0.9529352399472,
0.4826168836816
},
.pvCoeffs = (float*) (const float[])
{
0.01068626058243,
-0.04830879422708,
0.1047720411744,
-0.1152362519753,
0.06014978280645,
-0.003570328867624,
-0.01351144592324
}
};
*/
// Sloped version - experimental, not used
// -3dB: 20.0 kHz
// -6dB: 19.0 kHz
// -20dB: 15.6 kHz
// -40dB: 13.9 kHz
// -58dB: <13.2 kHz
#define IIR_15k_hpf_numStages 6
#define IIR_15k_numStages 6
const arm_iir_lattice_instance_f32 IIR_15k_hpf =
{
.numStages = IIR_15k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.08584448365242,
0.298455698329,
0.6057782760185,
0.7115173863477,
0.7698117813539,
0.7879540958152
},
.pvCoeffs = (float*) (const float[])
{
0.00561889294652,
-0.02629790457241,
0.0684050306275,
-0.1143219836396,
0.1169486269215,
-0.07287150832503,
0.02534743920497
}
};
@@ -0,0 +1,136 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 1400Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter 150 - 1400Hz
//
//
#define IIR_1k4_numStages 10
const arm_iir_lattice_instance_f32 IIR_1k4_LPF =
{
.numStages = IIR_1k4_numStages,
.pkCoeffs = (float*) (const float[])
{
0.513121512033492,
-0.848156725130716,
0.912905355716368,
-0.920457559093897,
0.946299472905260,
-0.874357776863127,
0.991745064593748,
-0.780050411952442,
0.999672192852107,
-0.747759230954469
},
.pvCoeffs = (float*) (const float[])
{
0.00303978190645145,
0.00957370964334359,
0.0192021255112258,
0.0205518463597444,
0.0157679250083878,
0.00641323479251947,
0.00194320223728067,
0.000177526968367495,
-0.000114188594530516,
-1.00881138288648e-06,
-1.61208577327586e-06
}
};
const arm_iir_lattice_instance_f32 IIR_1k4_BPF =
{
.numStages = IIR_1k4_numStages,
.pkCoeffs = (float*) (const float[])
{
0.544472709123517,
-0.846448469159389,
0.914323381297358,
-0.924099832753511,
0.913728327779171,
-0.906301509630351,
0.986341377918403,
-0.994060117939703,
0.999689022223705,
-0.996755741677070
},
.pvCoeffs = (float*) (const float[])
{
-0.00397159947826007,
-0.0142756966456071,
-0.0258501789268387,
-0.0249409146677880,
-0.00842585665202606,
0.00150853661679518,
0.00402678083172615,
0.000247302313425797,
-2.24256007699125e-05,
-5.01381740295598e-07,
1.79705759370541e-08
}
};
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 1100Hz - 2500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter 1100 - 2500Hz
//
//
const arm_iir_lattice_instance_f32 IIR_1k4_SSTV =
{
.numStages = IIR_1k4_numStages,
.pkCoeffs = (float*) (const float[])
{
0.506305489868821,
-0.530549016052929,
0.852752516028665,
-0.605615780849346,
0.908798485793881,
-0.609090038979443,
0.898589845278408,
-0.527324792497554,
0.972399687849265,
-0.806526221782603
},
.pvCoeffs = (float*) (const float[])
{
-0.00502583555949696,
-0.0125105344886717,
-0.00418751674186635,
0.0213049813421346,
0.0261437873033528,
-0.00847633761697014,
-0.0252946013159023,
-0.00396151687990829,
0.0111353202658218,
0.00168003453028945,
-0.000348271075749648
}
};
@@ -0,0 +1,94 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 1600Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter 150 - 1600Hz
//
//
#define IIR_1k6_numStages 10
const arm_iir_lattice_instance_f32 IIR_1k6_LPF =
{
.numStages = IIR_1k6_numStages,
.pkCoeffs = (float*) (const float[])
{
0.466488867417511,
-0.806195990652699,
0.886218044099720,
-0.895050799512214,
0.932962782640420,
-0.830295375528087,
0.990476931527691,
-0.713094807516228,
0.999609649167846,
-0.674710639139313
},
.pvCoeffs = (float*) (const float[])
{
0.00389683132359987,
0.0138226995337397,
0.0295183362623048,
0.0347590317564048,
0.0274564574556244,
0.0115668027741923,
0.00266968736496351,
0.000176086120205525,
-0.000357042283087743,
-5.34430276839977e-06,
-3.16592076707485e-06
}
};
const arm_iir_lattice_instance_f32 IIR_1k6_BPF =
{
.numStages = IIR_1k6_numStages,
.pkCoeffs = (float*) (const float[])
{
0.494195797915416,
-0.803665234575735,
0.885970320933212,
-0.904522861950696,
0.882497256186818,
-0.879441652192076,
0.985240463020393,
-0.994220286123894,
0.999679143098346,
-0.996775926313658
},
.pvCoeffs = (float*) (const float[])
{
-0.00542804202987006,
-0.0211221071946913,
-0.0411248591148432,
-0.0404574358824852,
-0.0126311821394728,
0.00603670441721448,
0.00803948053906083,
0.000449048992769978,
-4.33255842577709e-05,
-1.00733352962831e-06,
3.58686948389447e-08
}
};
@@ -0,0 +1,243 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20141202 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// *** 12 KSPS Sample Rate!
//
// Nominal 1420 Hz center-frequency Filter:
//
// 10th order Elliptic bandpass filter, 0.5db ripple
// Fpass: 582, 2250 Hz
// -6dB points: 552, 2350 Hz
// -20dB points: 500, 2525 500, 2519 Hz
// -40dB points: 428, 2830
// -60dB points: 384, 3030
//
#define IIR_1k8_numStages 10
const arm_iir_lattice_instance_f32 IIR_1k8_1k425 =
{
.numStages = IIR_1k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3633117201536,
-0.6014671698188,
0.790691903771,
-0.7552408736771,
0.8655745295849,
-0.7100967246513,
0.8371692151707,
-0.858604704342,
0.9948942198536,
-0.9504411745978
},
.pvCoeffs = (float*) (const float[])
{
-0.008451821550603,
-0.03028239739489,
-0.04383575807689,
-0.005801028198445,
0.0552803520222,
0.0409926115824,
-0.009867881161988,
-0.01912289835012,
-0.003305206206671,
0.0001846251381343,
0.0001533411723535
}
};
//
// -150 Hz, 1275 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_1k8_1k275 =
{
.numStages = IIR_1k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3615632509154,
-0.6426687049878,
0.8022155117307,
-0.8033168968408,
0.8624152554717,
-0.7373416322104,
0.8853268131748,
-0.9382217524638,
0.9974058005444,
-0.9743269012915
},
.pvCoeffs = (float*) (const float[])
{
-0.008554020969071,
-0.03290507946195,
-0.05669748976757,
-0.03112515335395,
0.03458656760459,
0.04515075118067,
0.009972736163013,
-0.008871404867662,
-0.002779915015395,
-3.149002424411e-05,
3.311102598764e-05
}
};
//
// -300 Hz, 1125 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_1k8_1k125 =
{
.numStages = IIR_1k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3613452928877,
-0.6810107467819,
0.8170984050044,
-0.8463544066134,
0.8511170107767,
-0.7821275475669,
0.9480716019822,
-0.9808799929708,
0.9989026674026,
-0.9900683804108
},
.pvCoeffs = (float*) (const float[])
{
-0.008566871148577,
-0.03494144528,
-0.06850457739963,
-0.05800592969749,
0.004948888629599,
0.03640087677665,
0.0202191966789,
-0.0005739689668037,
-0.0007471474016795,
-1.814984183384e-05,
2.538959875177e-06
}
};
//
// +150 Hz, 1575 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_1k8_1k575 =
{
.numStages = IIR_1k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3613452928877,
-0.5528878303048,
0.7775941425073,
-0.6999131353729,
0.8628162750942,
-0.6773588830313,
0.8145733229607,
-0.7494991964062,
0.9906935090308,
-0.919454179607
},
.pvCoeffs = (float*) (const float[])
{
-0.008566871148577,
-0.02836768723528,
-0.03226930906751,
0.01640676037123,
0.0677730883021,
0.02700913837121,
-0.03345776958501,
-0.02430295670566,
0.001987515414669,
0.001005716985531,
0.0003676617652895
}
};
//
// +300 Hz, 1725 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_1k8_1k725 =
{
.numStages = IIR_1k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3624364166222,
-0.5040846071146,
0.7692947989321,
-0.6409554520074,
0.8607689220576,
-0.6376837263631,
0.8178885035622,
-0.6240753437248,
0.9834623152838,
-0.8795808576955
},
.pvCoeffs = (float*) (const float[])
{
-0.008502787721249,
-0.02559366076952,
-0.0200387077241,
0.03413786956908,
0.06758199161391,
0.004017644116835,
-0.05236928020393,
-0.01897401359474,
0.01339055818279,
0.002528542455842,
0.0004151390011982
}
};
//
// low pass filter
// DD4WH 2016-02-03
//
const arm_iir_lattice_instance_f32 IIR_1k8_LPF =
{
.numStages = IIR_1k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.424104469409253,
-0.760957161720464,
0.856135458808337,
-0.865387150235557,
0.919773037078837,
-0.777255196618113,
0.989521498539331,
-0.637962725222723,
0.999554271439240,
-0.594257795338158
},
.pvCoeffs = (float*) (const float[])
{
0.00500807056613645,
0.0194715681066562,
0.0436864315353799,
0.0548773806707592,
0.0438480552375318,
0.0181134030340657,
0.00228635234567699,
-9.48681761683695e-05,
-0.000791691993306771,
-1.47032013899616e-05,
-2.00621568867149e-06
}
};
@@ -0,0 +1,379 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 2020.04.27 by Y.Korenev UB8JDC using MatLAB fdatools
//
// NOTE:
// - IIR, structure is Lattice Autoregressive Moving-Average (ARMA)
// - 12 KSPS Sample Rate
// - Bandwidth 200 Hz (CW)
// - 10th order Elliptic bandpass filter
// - Single-precision floating-point
// - Coefficients importing in reverse order!
//
#define IIR_200hz_numStages 10
//
// 500 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_500 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.963869810104370,
0.996652781963348,
-0.966875314712524,
0.998103320598602,
-0.966414153575897,
0.998208224773407,
-0.963238596916199,
0.998806416988373,
-0.974526107311249
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.411297992803156e-04,
-5.036837319494225e-05,
-5.570459325099364e-05,
1.687375879555475e-05,
3.088041239607264e-06,
-7.782410449408417e-08,
6.467502089435584e-08,
-6.034498056806115e-08,
-1.506363478398498e-09,
8.404459350686011e-10
}
};
//
// 550 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_550 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.956778883934021,
0.996626973152161,
-0.959783315658569,
0.998106360435486,
-0.959343314170837,
0.998260438442230,
-0.955788314342499,
0.998634815216065,
-0.967797458171845
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.356775458902121e-04,
-3.839765849988908e-05,
-5.141069777891971e-05,
2.077420322166290e-05,
3.698997488754685e-06,
-3.446152732067276e-07,
4.745169235320645e-08,
-8.387807781673473e-08,
-1.463176357852092e-09,
1.550584771692343e-09
}
};
//
// 600 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_600 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163441181183,
-0.949032187461853,
0.996607005596161,
-0.952028572559357,
0.998108685016632,
-0.951605141162872,
0.998302340507507,
-0.947757005691528,
0.998480916023254,
-0.960332453250885
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.297210977412760e-04,
-2.542124821047764e-05,
-4.681734571931884e-05,
2.481275623722468e-05,
4.219968559482368e-06,
-7.193478950284771e-07,
1.889994472037415e-08,
-1.111265959252705e-07,
-8.176312404906128e-10,
2.624878980839185e-09
}
};
//
// 650 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_650 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163441181183,
-0.940635085105896,
0.996591269969940,
-0.943617641925812,
0.998110592365265,
-0.943207502365112,
0.998336255550385,
-0.939133286476135,
0.998344361782074,
-0.9521484375
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.232644129544497e-04,
-1.147407965618186e-05,
-4.195252768113278e-05,
2.893220880650915e-05,
4.619471383193741e-06,
-1.213774567077053e-06,
-2.196506621032768e-08,
-1.412249019949741e-07,
7.775369237350560e-10,
4.134683262435601e-09
}
};
//
// 700 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_700 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.931593298912048,
0.996578633785248,
-0.934557318687439,
0.998112142086029,
-0.934159219264984,
0.998363912105560,
-0.929911017417908,
0.998223781585693,
-0.943260133266449
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.163121481426060e-04,
3.405127927180729e-06,
-3.684650437207893e-05,
3.307369115645997e-05,
4.866832114203135e-06,
-1.836704200286476e-06,
-7.566525539459690e-08,
-1.728796519273601e-07,
3.717674834646800e-09,
6.120295381606411e-09
}
};
//
// 750 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_750 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.921913087368012,
0.996568322181702,
-0.924854397773743,
0.998113393783569,
-0.924466073513031,
0.998386681079865,
-0.920087575912476,
0.998117566108704,
-0.933683097362518
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.088688435032964e-04,
1.917585177579895e-05,
-3.153051511617377e-05,
3.717728759511374e-05,
4.932894171361113e-06,
-2.593397084638127e-06,
-1.421764750375587e-07,
-2.043928333250733e-07,
8.421426223037543e-09,
8.569696952065442e-09
}
};
//
// 800 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_800 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163441181183,
-0.911600947380066,
0.996559858322144,
-0.914516091346741,
0.998114407062531,
-0.914137363433838,
0.998405635356903,
-0.909662127494812,
0.998024046421051,
-0.923429787158966
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-7.009397377260029e-04,
3.579455005819909e-05,
-2.603746543172747e-05,
4.118278593523428e-05,
4.790668299392564e-06,
-3.485065008135280e-06,
-2.209152398791048e-07,
-2.336709457040342e-07,
1.530131932270251e-08,
1.139929750593183e-08
}
};
//
// 850 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_850 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.900664150714874,
0.996552765369415,
-0.903549790382385,
0.998115241527557,
-0.903179645538330,
0.998421490192413,
-0.898636579513550,
0.997941672801971,
-0.912513852119446
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-6.925304187461734e-04,
5.321568824001588e-05,
-2.040095569100231e-05,
4.503029413172044e-05,
4.415991497808136e-06,
-4.508429356064880e-06,
-3.106901544924767e-07,
-2.583196021532785e-07,
2.472552118604199e-08,
1.443308850213043e-08
}
};
//
// 900 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_900 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.889109969139099,
0.996546864509583,
-0.891963183879852,
0.998116016387940,
-0.891601264476776,
0.998434960842133,
-0.887013971805573,
0.997869074344635,
-0.900947272777557
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-6.836461834609509e-04,
7.139224180718884e-05,
-1.465528839617036e-05,
4.866075323661789e-05,
3.788073172472650e-06,
-5.655422683048528e-06,
-4.097164492122829e-07,
-2.756989374574914e-07,
3.697994799267690e-08,
1.738840005316433e-08
}
};
//
// 950 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_200hz_950 =
{
.numStages = IIR_200hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.907163500785828,
-0.876946508884430,
0.996541798114777,
-0.879764497280121,
0.998116612434387,
-0.879410088062286,
0.998446464538574,
-0.874799072742462,
0.997804880142212,
-0.888742208480835
},
.pvCoeffs = (float*) (const float[])
{
-3.836439573206008e-04,
-6.742936093360186e-04,
9.027340274769813e-05,
-8.836139386403374e-06,
5.201709063840099e-05,
2.890088126150658e-06,
-6.912967364769429e-06,
-5.156420570529008e-07,
-2.830576022461173e-07,
5.222612387001391e-08,
1.986541775522710e-08
}
};
@@ -0,0 +1,95 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 2100Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter 150 - 2100Hz
//
//
#define IIR_2k1_numStages 10
const arm_iir_lattice_instance_f32 IIR_2k1_LPF =
{
.numStages = IIR_2k1_numStages,
.pkCoeffs = (float*) (const float[])
{
0.367665055229345,
-0.688450049074435,
0.805164789160803,
-0.811607062793749,
0.902007053621482,
-0.678829166852664,
0.988754839338072,
-0.511291308813728,
0.999489812346376,
-0.461573314781018
},
.pvCoeffs = (float*) (const float[])
{
0.00731063001354271,
0.0314121915186572,
0.0739715509453205,
0.0977625054614904,
0.0764634076081996,
0.0273892594750243,
-0.00364458069285592,
-0.00143162744212593,
-0.00150600852899662,
-3.00785390039450e-05,
1.90133500787143e-05
}
};
const arm_iir_lattice_instance_f32 IIR_2k1_BPF =
{
.numStages = IIR_2k1_numStages,
.pkCoeffs = (float*) (const float[])
{
0.388108085643651,
-0.684535285919172,
0.795328554327865,
-0.851745126106827,
0.786930711099640,
-0.782402423654034,
0.981748853637700,
-0.994476024273642,
0.999660553986239,
-0.996813012539810
},
.pvCoeffs = (float*) (const float[])
{
-0.0113057560156174,
-0.0493258305692081,
-0.101429131418656,
-0.0939936931949645,
-0.00933213227739606,
0.0417064397734366,
0.0245790863191006,
0.00146913585763049,
-0.000159569288036600,
-4.03203149703518e-06,
1.41762873655249e-07
}
};
@@ -0,0 +1,213 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20141202 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 12KSPS sample rate (e.g. 4x decimation)
//
// Example of the nominal 1412 Hz center-frequency Filter:
//
// 10th order Elliptic bandpass filter,
//
// Fpass: 320, 2480 Hz
// -6dB points: 299, 2602 Hz
// -20dB points: 264,2834
// -40dB points: 218,3193
// -60dB points: 194, 3424
//
#define IIR_2k3_numStages 10
//
const arm_iir_lattice_instance_f32 IIR_2k3_1k412 =
{
.numStages = IIR_2k3_numStages,
.pkCoeffs = (float*) (const float[])
{
0.2701467400288,
-0.5337350112524,
0.6958962164633,
-0.758453902622,
0.7839709967998,
-0.6094204682748,
0.8900165832056,
-0.9710784743018,
0.9983628970738,
-0.9859502653937
},
.pvCoeffs = (float*) (const float[])
{
-0.01711398845885,
-0.07116858148666,
-0.1254973880951,
-0.06367087681021,
0.08889589696012,
0.1046947051957,
0.01366010226719,
-0.008501456104369,
-0.003625645738643,
-9.123722957357e-05,
2.159311905e-05
}
};
//
// 1275Hz Center Frequency (-150 Hz)
//
const arm_iir_lattice_instance_f32 IIR_2k3_1k275 =
{
.numStages = IIR_2k3_numStages,
.pkCoeffs = (float*) (const float[])
{
0.2712876748428,
-0.5735340804203,
0.7173137797694,
-0.8175461411882,
0.7315460152375,
-0.7167786129066,
0.9767475626606,
-0.9943753177254,
0.9995379196189,
-0.9965283982769
},
.pvCoeffs = (float*) (const float[])
{
-0.01695089378394,
-0.0754815863035,
-0.1545067828949,
-0.1337099532882,
0.01250868380934,
0.08652620679791,
0.03662597834324,
0.002809508493456,
-0.0003479014294362,
-1.019626375571e-05,
3.955657094032e-07
}
};
//
// 1562Hz Center Frequency (+150 Hz)
//
const arm_iir_lattice_instance_f32 IIR_2k3_1k562 =
{
.numStages = IIR_2k3_numStages,
.pkCoeffs = (float*) (const float[])
{
0.2704722354583,
-0.4930194577195,
0.6788797346477,
-0.6985554426272,
0.8006644381482,
-0.5854106964209,
0.7723103700212,
-0.9113575606699,
0.9964165920158,
-0.9679083606539
},
.pvCoeffs = (float*) (const float[])
{
-0.01706725280562,
-0.06549445078988,
-0.09465636014278,
-0.0006279147552571,
0.1380031454132,
0.08863098032502,
-0.03386873453512,
-0.03178190420391,
-0.008447346315031,
-3.073326593073e-05,
0.0001862218852526
}
};
//
// 1712Hz Center Frequency (+300 Hz)
//
const arm_iir_lattice_instance_f32 IIR_2k3_1k712 =
{
.numStages = IIR_2k3_numStages,
.pkCoeffs = (float*) (const float[])
{
0.2717781074202,
-0.4503076240765,
0.6654292686672,
-0.6366299962827,
0.8010323775887,
-0.5867121495116,
0.6883638854733,
-0.7934171013092,
0.9929975817319,
-0.9418087297114
},
.pvCoeffs = (float*) (const float[])
{
-0.01688140653005,
-0.05893233949881,
-0.06391130117223,
0.05125151954747,
0.1565612599346,
0.0457952816904,
-0.08390530500971,
-0.04184467503704,
-0.003607973662997,
0.001058321053835,
0.0006829825631745
}
};
//
// low pass version
// DD4WH 2016-02-03
//
const arm_iir_lattice_instance_f32 IIR_2k3_LPF =
{
.numStages = IIR_2k3_numStages,
.pkCoeffs = (float*) (const float[])
{
0.334304700393179,
-0.637973146829989,
0.767879751187593,
-0.768295057186220,
0.892741024882815,
-0.599613288253562,
0.988679362407979,
-0.418706450963394,
0.999461427988613,
-0.366491706976845
},
.pvCoeffs = (float*) (const float[])
{
0.00941076764313990,
0.0423681593287898,
0.101315269975374,
0.134840512300872,
0.100835720101395,
0.0295355484872365,
-0.0131806481791514,
-0.00292265408570726,
-0.00146862219082368,
-2.39931441286178e-05,
5.02684297614079e-05
}
};
@@ -0,0 +1,94 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 2500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter 150 - 2500Hz
//
//
#define IIR_2k5_numStages 10
const arm_iir_lattice_instance_f32 IIR_2k5_LPF =
{
.numStages = IIR_2k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.304008884053488,
-0.586479754574523,
0.728707358729566,
-0.717591127840479,
0.886460005029009,
-0.509239560057348,
0.988906686305070,
-0.320749263130520,
0.999445512653576,
-0.267299520678722
},
.pvCoeffs = (float*) (const float[])
{
0.0121126909328103,
0.0563705089486983,
0.135077706371490,
0.177369645199308,
0.122940273900304,
0.0249807561603719,
-0.0272356075029574,
-0.00442102257133439,
-0.000487963028261214,
9.54073216847401e-06,
8.24834253008722e-05
}
};
const arm_iir_lattice_instance_f32 IIR_2k5_BPF =
{
.numStages = IIR_2k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.319949354758169,
-0.584143220123557,
0.699676230227154,
-0.809140966154901,
0.704873387106954,
-0.653974039723370,
0.976867907943965,
-0.994591635529659,
0.999649711777868,
-0.996834233858384
},
.pvCoeffs = (float*) (const float[])
{
-0.0194772962002087,
-0.0868514981143777,
-0.170190605680555,
-0.131302479532905,
0.0336504666821513,
0.0956282907461719,
0.0311274943500153,
0.00344191566243132,
-0.000363680054390594,
-9.63538256713858e-06,
3.36233973739775e-07
}
};
@@ -0,0 +1,92 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// IIR filter
// lattice ARMA coefficients designed with MATLAb fdatools
// DD4WH 2016-03-05
// inspired by Clint KA7OEI
//
// LPF 2k7
#define IIR_2k7_numStages 10
const arm_iir_lattice_instance_f32 IIR_2k7_LPF =
{
.numStages = IIR_2k7_numStages,
.pkCoeffs = (float*) (const float[])
{
0.276517739456119,
-0.534458119364593,
0.688627879533550,
-0.657860665637030,
0.883760854885895,
-0.408163764378125,
0.989378253422330,
-0.218531341587163,
0.999442039749756,
-0.165077404674072
},
.pvCoeffs = (float*) (const float[])
{
0.0155856864050234,
0.0740622771621721,
0.175501156733159,
0.222640088831044,
0.137215023898017,
0.0116117418712191,
-0.0432897242829734,
-0.00519209144189112,
0.00156095841010052,
6.72317701914359e-05,
9.08341518316962e-05
}
};
//
// 120 - 2700Hz bandpass, 1410Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_2k7_BPF =
{
.numStages = IIR_2k7_numStages,
.pkCoeffs = (float*) (const float[])
{
0.286204893123822,
-0.535401193365074,
0.636552242537530,
-0.801759238351553,
0.632340313225578,
-0.598674284342736,
0.984168394137399,
-0.996691132601798,
0.999768009572177,
-0.997990472561290
},
.pvCoeffs = (float*) (const float[])
{
-0.0262124858208275,
-0.117385216786601,
-0.223626900059669,
-0.159085812308114,
0.0583824351916630,
0.119904708047547,
0.0261315687651823,
0.00496617536907647,
-0.000240288448030184,
-6.41272493413780e-06,
1.37062857848147e-07
}
};
@@ -0,0 +1,95 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-03
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 2900Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
#define IIR_2k9_numStages 10
const arm_iir_lattice_instance_f32 IIR_2k9_LPF =
{
.numStages = IIR_2k9_numStages,
.pkCoeffs = (float*) (const float[])
{
0.251607461238023,
-0.482252659454225,
0.649010601492657,
-0.587098281132217,
0.884933496818835,
-0.297598183921305,
0.990030999070584,
-0.113255360948473,
0.999450459548208,
-0.0609467228813790
},
.pvCoeffs = (float*) (const float[])
{
0.0200465239002972,
0.0961515965852007,
0.222315717811456,
0.266325692682369,
0.137175400914474,
-0.0104425480557769,
-0.0563674296455557,
-0.00456438174446905,
0.00418951198974554,
0.000124467441153527,
5.62783747425286e-05
}
};
const arm_iir_lattice_instance_f32 IIR_2k9_BPF =
{
.numStages = IIR_2k9_numStages,
.pkCoeffs = (float*) (const float[])
{
0.263419613809752,
-0.487748680969863,
0.579151989014181,
-0.766691138931688,
0.644526948603595,
-0.455099813849530,
0.966822380971438,
-0.994647491908158,
0.999641053036454,
-0.996851003050428
},
.pvCoeffs = (float*) (const float[])
{
-0.0324231770435912,
-0.140665197185480,
-0.244988773730778,
-0.127152356090031,
0.127397853986530,
0.136542957067850,
0.00600155113876917,
0.00834595632603165,
-0.000738848558080582,
-2.00888540803704e-05,
6.96894635783540e-07
}
};
@@ -0,0 +1,394 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20140927 by C. Turner, KA7OEI using MatLAB fdatools
//
// This filter has relaxed band-stop properties and is designed for SSB audio TX filtering.
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 10th order Elliptic bandpass filter
// Fstop: 200, 3250 Hz
// Fpass: 300, 2700 Hz
//
// Amplitude responses - referenced to attenuation at 1.5 kHz:
// -6dB points: 257, 2830 Hz
// -10dB points: 254, 2885 Hz
// -20dB points: 246, 2988 Hz
// -30dB points: <225, >3060 Hz
//
// Low-end pre-emphasis added to offset effects of Hilbert transformer:
// >=8.0dB: @ 280 Hz
// >=7.0dB: <298 Hz
// >=6.0dB: <355 Hz
// >=5.0dB: <421 Hz
// >=4.0dB: <521 Hz
// >=3.0dB: <670 Hz
// >=2.0dB: <870 Hz
// >=1.0dB: <1130 Hz
// >=0.5dB: <1340 Hz
//
// The above counteracts the rolloff to produce an overall flat response to approx. 1dB between 275 Hz and 2500 Hz.
//
// Pole/Zero Info:
// 1: P0.98293, 0.36082; Z1, 0.40965
// 2: P0.99849, 0.;035664; Z1, 0.030945
// 3: P0.91702, 0.30422; Z1, 0.53916
// 4: P0.98884, 0.038766; Z1, 0.023267
// 5: P0.93128, 0.009238; Z (none)
//
//
#define IIR_TX_numStages 10
#define IIR_TX_2k7_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_2k7 =
{
.numStages = IIR_TX_numStages,
.pkCoeffs = (float*) (const float[])
{
0.6869176968793,
-0.9512054401742,
0.9748233658729,
-0.9886769453802,
0.9699856293614,
-0.976965264625,
0.9989051081887,
-0.9994482291916,
0.9999677314304,
-0.9993834480784
},
.pvCoeffs = (float*) (const float[])
{
-0.01873226419377,
-0.0351077314033,
-0.01181875664072,
-0.0100003360675,
-0.0005123880132686,
-0.0002827366407254,
6.374826243116e-05,
3.655128410718e-06,
-1.949876071716e-08,
-5.55178847339e-10,
3.472732518217e-12
}
};
// IIR filter
// lattice ARMA coefficients designed with MATLAb fdatools
// bandpass 50 - 2750Hz
#define IIR_TX_WIDE_BASS_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_WIDE_BASS =
{
.numStages = IIR_TX_WIDE_BASS_numStages,
.pkCoeffs = (float*) (const float[])
{
0.71896929903805900000,
-0.95942907850625200000,
0.97792311678973200000,
-0.98088701203507700000,
0.97297595879898900000,
-0.99620090645682700000,
0.99990501182910600000,
-0.99996667856688500000,
0.99998653853233900000,
-0.99998543366088200000
},
.pvCoeffs = (float*) (const float[])
{
-0.00032448877846164900,
-0.00117728102231410000,
-0.00209099021086666000,
-0.00223337754782698000,
-0.00122619886193943000,
-0.00046485618062794300,
0.00001256926158546300,
0.00000019091112216183,
-0.00000000006469610177,
-0.00000000000289012694,
1.24E-15
}
};
//
// 150 - 2850Hz bandpass
//
#define IIR_TX_WIDE_TREBLE_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_WIDE_TREBLE =
{
.numStages = IIR_TX_WIDE_TREBLE_numStages,
.pkCoeffs = (float*) (const float[])
{
0.71896929903807000000,
-0.95701929433651100000,
0.97688821391048500000,
-0.97914462752169300000,
0.97381210430161500000,
-0.98932784460632400000,
0.99918319918236100000,
-0.99964609685921500000,
0.99997374020338600000,
-0.99980049691966100000
},
.pvCoeffs = (float*) (const float[])
{
-0.00032448877846164000,
-0.00117432406256116000,
-0.00207458605133000000,
-0.00219730703764023000,
-0.00117629105002028000,
-0.00042269171278772100,
0.00003803127953775490,
0.00000159703882026720,
-0.00000000535014800171,
-0.00000000013312786485,
2.67655E-13
}
};
//
// 250 - 2950Hz bandpass
//
#define IIR_TX_SOPRANO_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_SOPRANO =
{
.numStages = IIR_TX_SOPRANO_numStages,
.pkCoeffs = (float*) (const float[])
{
0.71987365385562700,
-0.95478395730601100,
0.97597041246871300,
-0.97877331988142200,
0.97311471309818900,
-0.98013955262452800,
0.99791087029646100,
-0.99903556044544100,
0.99994405075167400,
-0.99944176350280300
},
.pvCoeffs = (float*) (const float[])
{
-0.00146489605640520,
-0.00378491009751258,
-0.00400350501493743,
-0.00341201203499246,
-0.00096809938766577,
-0.00035554457716075,
0.00007141442667891,
0.00000426101940838,
-0.00000003996117780,
-0.00000000087267111,
0.00000000000495626
}
};
//
// Filter designed 2020.05.03 by Y.Korenev UB8JDC using MatLAB fdatools
//
// NOTE:
// - IIR, structure is Lattice Autoregressive Moving-Average (ARMA)
// - 48 KSPS Sample Rate
// - 10th order Elliptic bandpass filter
// - Fstop1,2: 100, 6000 Hz
// - Single-precision floating-point
// - Coefficients importing in reverse order!
//
#define IIR_TX_eSSB_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_eSSB_6k =
{
.numStages = IIR_TX_eSSB_numStages,
.pkCoeffs = (float*) (const float[])
{
0.488251596689224,
-0.822531878948212,
0.892727732658386,
-0.927212417125702,
0.841283917427063,
-0.972716450691223,
0.999650061130524,
-0.999866247177124,
0.999989390373230,
-0.999913930892944
},
.pvCoeffs = (float*) (const float[])
{
-0.005639570765197,
-0.022809743881226,
-0.047501564025879,
-0.052696328610182,
-0.026684369891882,
-0.003642551833764,
0.002113675698638,
3.030856169061735e-05,
-3.875512533113579e-08,
-9.339896633875355e-10,
8.066186607535997e-13
}
};
//
// Filter designed 2020.06.22 by Y.Korenev UB8JDC using MatLAB fdatools
//
// NOTE:
// - IIR, structure is Lattice Autoregressive Moving-Average (ARMA)
// - 48 KSPS Sample Rate
// - 10th order Elliptic bandpass filter
// - Fstop1,2: 100, 5000 Hz
// - Single-precision floating-point
// - Coefficients importing in reverse order!
//
const arm_iir_lattice_instance_f32 IIR_TX_eSSB_5k =
{
.numStages = IIR_TX_eSSB_numStages,
.pkCoeffs = (float*) (const float[])
{
0.551112115383148,
-0.873203039169312,
0.926355421543121,
-0.946738958358765,
0.894358754158020,
-0.980076730251312,
0.999654591083527,
-0.999865353107452,
0.999989390373230,
-0.999913752079010
},
.pvCoeffs = (float*) (const float[])
{
-0.003816125448793,
-0.013906096108258,
-0.026235999539495,
-0.027811434119940,
-0.013894244097173,
-0.003450543386862,
8.077293168753386e-04,
1.270828852284467e-05,
-1.620846745709059e-08,
-3.871837017666024e-10,
3.327377436079804e-13
}
};
//
// Filter designed 2020.06.22 by Y.Korenev UB8JDC using MatLAB fdatools
//
// NOTE:
// - IIR, structure is Lattice Autoregressive Moving-Average (ARMA)
// - 48 KSPS Sample Rate
// - 10th order Elliptic bandpass filter
// - Fstop1,2: 100, 4000 Hz
// - Single-precision floating-point
// - Coefficients importing in reverse order!
//
const arm_iir_lattice_instance_f32 IIR_TX_eSSB_4k =
{
.numStages = IIR_TX_eSSB_numStages,
.pkCoeffs = (float*) (const float[])
{
0.622196376323700,
-0.917002677917481,
0.953354060649872,
-0.964468061923981,
0.935390412807465,
-0.985710978507996,
0.999658346176148,
-0.999864161014557,
0.999989271163940,
-0.999913632869721
},
.pvCoeffs = (float*) (const float[])
{
-0.002526052994654,
-0.008019731380045,
-0.012725095264614,
-0.012484691105783,
-0.005546068772674,
-0.001818265416659,
2.339449711143971e-04,
4.331307536631357e-06,
-5.518487888878099e-09,
-1.312840391953785e-10,
1.149562216251621e-13
}
};
/*
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 10th order Elliptic bandpass filter
// Fstop: 200, 3250 Hz
// Fpass: 300, 2575 Hz
// -6dB points: 285, 2698 Hz
// -10dB points: 281, 2745 Hz
// -20dB points: 270, 2845 Hz
// -30dB points: <265, >2915 Hz
//
#define IIR_TX_2k7_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_2k7 = {
.numStages = IIR_TX_numStages,
.pkCoeffs = (float*) (const float[])
{
0.6630364568624,
-0.9520893446293,
0.9760332145655,
-0.9865766256209,
0.9831633680402,
-0.9684840684235,
0.9968012184072,
-0.9991378973359,
0.9999607252996,
-0.9992727516361
},
.pvCoeffs = (float*) (const float[])
{
-0.01765590852027,
-0.03197343698045,
-0.009018268963126,
-0.007790758301124,
-0.0001813788700895,
-0.0001640724508686,
6.107606732009e-05,
4.622811990096e-06,
-3.806757115088e-08,
-5.946272640944e-10,
4.410607296057e-12
}
};
*/
@@ -0,0 +1,72 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20151024 by C. Turner, KA7OEI using MatLAB fdatools
//
// This filter is designed for FM voice transmission, removing low-frequency audio
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 10th order Elliptic bandpass filter
// Fpass: 225, 2700 Hz
//
// Amplitude responses - referenced to attenuation at top
// Filter has 0 to -2 dB ripple, below referenced to "0" dB point:
//
// -6dB points: 216, 2786 Hz
// -10dB points: 210, 2865 Hz
// -20dB points: 193, 3216 Hz
// -30dB points: 172, 3478 Hz
// -50dB points: 136, 4436 Hz
//
//
#define IIR_TX_2k7_FM_numStages 10
const arm_iir_lattice_instance_f32 IIR_TX_2k7_FM =
{
.numStages = IIR_TX_2k7_FM_numStages,
.pkCoeffs = (float*) (const float[])
{
0.795992813347,
-0.9682173169759,
0.9817510589125,
-0.9814547662913,
0.9796499689423,
-0.9856718628494,
0.9982624972485,
-0.9990433054892,
0.9999481889194,
-0.9995181953945
},
.pvCoeffs = (float*) (const float[])
{
-0.000247932512139,
-0.000812969420294,
-0.001204931403655,
-0.001166250382325,
-0.0005214249315797,
-0.0001861038674673,
2.552768869046e-05,
1.619179380559e-06,
-1.484109718886e-08,
-3.31354298817e-10,
1.62095434731e-12
}
};
@@ -0,0 +1,383 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20141202 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// *** 12 KSPS Sample Rate!
//
// Nominal 750 Hz center-frequency Filter:
//
// 10th order Least pth Norm. bandpass filter
// Fstop: 500, 990 Hz
// Fpass: 613, 885 Hz
// -6dB points: 600, 895 Hz
// -20dB points: 577, 918
// -40dB points: 529, 962
// -55dB points: 500, 990
//
#define IIR_300hz_numStages 10
const arm_iir_lattice_instance_f32 IIR_300hz_750 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8420268438333,
-0.9187411389087,
0.991915422724,
-0.9269206024542,
0.9959665495021,
-0.9250959581619,
0.9968373894352,
-0.9220079563995,
0.9954523801454,
-0.9320568982728
},
.pvCoeffs = (float*) (const float[])
{
-2.528177681235e-05,
-0.0001284428108467,
-0.0001826561498698,
-4.680324147403e-05,
-1.767058952639e-05,
2.328514864098e-05,
7.242100694031e-06,
9.825932471565e-07,
-2.827695963598e-07,
-2.141975226444e-07,
9.808568488673e-09
}
};
//
// 800 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_800 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8503130225593,
-0.9089732054814,
0.9923678320648,
-0.9163668504916,
0.9961126531134,
-0.9147111704054,
0.9969873803036,
-0.9116164217019,
0.9952780046244,
-0.9213976892508
},
.pvCoeffs = (float*) (const float[])
{
1.33337610067e-06,
7.453841937241e-05,
0.0003852357535482,
4.561173005598e-05,
2.120264803625e-05,
-2.713243679097e-05,
-9.643727863849e-06,
-9.410145539752e-07,
3.219855720009e-07,
2.507913128879e-07,
-1.339507149753e-08
}
};
//
// 850 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_850 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8619678311814,
-0.8987132900478,
0.9929800321291,
-0.9050895035197,
0.9962960753259,
-0.9035803342684,
0.9969741855608,
-0.9009035751952,
0.9955228844029,
-0.9102181260169
},
.pvCoeffs = (float*) (const float[])
{
1.015803230667e-05,
7.384809547778e-06,
0.0001261434545558,
2.625658957163e-05,
2.179481057242e-05,
-2.078867283811e-05,
-8.609229801913e-06,
-9.671863254622e-07,
2.923861090205e-07,
2.778278512409e-07,
-1.585347732762e-08
}
};
//
// 900 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_900 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.866224538039,
-0.887418184529,
0.9931785952017,
-0.8934071723653,
0.9963609345788,
-0.8919370163031,
0.9969678377825,
-0.8895111671316,
0.9955938946628,
-0.8983883276963
},
.pvCoeffs = (float*) (const float[])
{
1.027538145593e-11,
4.674412003285e-09,
8.524980293762e-06,
2.781783244349e-05,
4.556993427775e-05,
7.93734646008e-06,
-1.149365906393e-05,
-1.373938907669e-06,
-5.614114019155e-07,
1.999172180075e-07,
1.135790394447e-07
}
};
//
// 950 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_950 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8663610198931,
-0.8752991478672,
0.9931648301256,
-0.8812152134726,
0.9963683212431,
-0.8797440121644,
0.9969724019188,
-0.8774017131206,
0.99556189371,
-0.8860148099453
},
.pvCoeffs = (float*) (const float[])
{
1.712527337148e-07,
-2.14605366217e-06,
-1.282145120929e-06,
5.302451577261e-07,
3.408583271375e-05,
1.981386748243e-05,
-9.607488025739e-06,
-9.045967855846e-07,
-9.463603260528e-07,
1.034195562155e-07,
1.787682064218e-07
}
};
//
// 700 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_700 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8591958395778,
-0.9293736501052,
0.9929514387125,
-0.9360907399466,
0.996226917283,
-0.9346339462441,
0.9969482667941,
-0.931593651892,
0.9956636725664,
-0.9417304199344
},
.pvCoeffs = (float*) (const float[])
{
5.747022992099e-06,
-7.971034438356e-05,
0.0001584707032297,
1.393289629595e-05,
3.190407921738e-05,
-1.005193057782e-05,
-6.474066693983e-06,
-7.589205344351e-07,
-1.03047035546e-07,
1.28510713565e-07,
1.528611528166e-08
}
};
//
// 650 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_650 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8688818948709,
-0.9388764394749,
0.9934909324643,
-0.9448433355565,
0.9963549863567,
-0.9435572556818,
0.9969138747979,
-0.9403384327629,
0.9961526396361,
-0.9518730710222
},
.pvCoeffs = (float*) (const float[])
{
-2.242398187584e-07,
-4.638289856856e-06,
-2.352878220094e-05,
-3.080709449559e-05,
-1.553328321254e-07,
1.417306554582e-05,
5.642654818196e-07,
4.823112320132e-07,
-2.795709228696e-07,
-6.539576902733e-08,
1.737131589792e-08
}
};
//
// 600 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_600 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8688147751404,
-0.947241849841,
0.9935473550788,
-0.9532141766403,
0.9963398104956,
-0.9519739167724,
0.996893229241,
-0.9488062379632,
0.9962691170584,
-0.960137003826
},
.pvCoeffs = (float*) (const float[])
{
-1.162998232666e-08,
2.073677729923e-07,
3.52975766574e-06,
1.439908057934e-05,
1.523851473567e-05,
-1.145419163814e-05,
-1.088670924934e-06,
-5.660706791281e-07,
8.554810603011e-08,
6.021304731304e-08,
-6.066270009946e-09
}
};
//
// 550 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_550 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8541670807411,
-0.9541636396546,
0.9929236629498,
-0.9612744078615,
0.9960967701838,
-0.9599796643954,
0.9968957501619,
-0.956635152364,
0.9959046900614,
-0.9670787808963
},
.pvCoeffs = (float*) (const float[])
{
-2.136357455248e-05,
0.0001768380226792,
0.0002313081587989,
4.090518305717e-05,
2.238176395873e-05,
-9.82968044721e-06,
-3.425509918938e-06,
-5.382859003672e-07,
-2.942934157295e-08,
5.198121348388e-08,
4.257484469579e-09
}
};
//
// 500 Hz center frequency
//
const arm_iir_lattice_instance_f32 IIR_300hz_500 =
{
.numStages = IIR_300hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.8575481179465,
-0.961400641792,
0.9932114492888,
-0.9681748236979,
0.9961324948395,
-0.9670693014466,
0.9968413643764,
-0.9634005824554,
0.9964042135888,
-0.9748600915123
},
.pvCoeffs = (float*) (const float[])
{
-2.891194366311e-07,
0.0001916459337858,
0.0002267903341803,
5.926245389363e-05,
2.256777174671e-05,
-7.677432110101e-06,
-2.587424315593e-06,
-5.205542423312e-07,
-2.808725036254e-08,
3.187553831321e-08,
2.504925658148e-09
}
};
@@ -0,0 +1,95 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 3200Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter 150 - 3200Hz
//
//
#define IIR_3k2_numStages 10
const arm_iir_lattice_instance_f32 IIR_3k2_LPF =
{
.numStages = IIR_3k2_numStages,
.pkCoeffs = (float*) (const float[])
{
0.218694139928302,
-0.403838779807303,
0.594634035120532,
-0.455295219667013,
0.893598014796430,
-0.118422108607239,
0.991224652677821,
0.0476094068099549,
0.999483170165258,
0.0963103336836062
},
.pvCoeffs = (float*) (const float[])
{
0.0292173064645854,
0.139171967941396,
0.302089953179196,
0.315642874138601,
0.0977262133806149,
-0.0516806998414988,
-0.0577240236966878,
-0.000850928349123370,
0.00696050225811562,
0.000139795355880837,
-5.20544243489754e-05
}
};
const arm_iir_lattice_instance_f32 IIR_3k2_BPF =
{
.numStages = IIR_3k2_numStages,
.pkCoeffs = (float*) (const float[])
{
0.226940893639584,
-0.423427683999446,
0.469848234909221,
-0.730589423377186,
0.634876055736042,
-0.254866265688223,
0.950443823908253,
-0.994637626804822,
0.999635535930601,
-0.996861601733971
},
.pvCoeffs = (float*) (const float[])
{
-0.0465676087537323,
-0.192308251979962,
-0.291767361307907,
-0.0757211101641522,
0.218582885651641,
0.124575788231386,
-0.0337072066012442,
0.0164385733761534,
-0.00119735821375683,
-3.26543663353371e-05,
1.12878126917737e-06
}
};
@@ -0,0 +1,97 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// LPF:
// 10th order IIR Elliptic lowpass
// Fpass 3400Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// BPF:
// bandpass filter 150 - 3400Hz
//
//
#define IIR_3k4_numStages 10
const arm_iir_lattice_instance_f32 IIR_3k4_LPF =
{
.numStages = IIR_3k4_numStages,
.pkCoeffs = (float*) (const float[])
{
0.199549437319537,
-0.351164425170794,
0.565426959446253,
-0.347212945196544,
0.903076937389513,
0.00604837383458759,
0.992097069597849,
0.154906080441608,
0.999516379489912,
0.200159122143531
},
.pvCoeffs = (float*) (const float[])
{
0.0375360428960823,
0.175514272047973,
0.358379113282395,
0.327274521409256,
0.0418055459145037,
-0.0747547603431524,
-0.0419525359249555,
0.00238252032207142,
0.00672097481119971,
8.86692474596773e-05,
-0.000108177709927680
}
};
const arm_iir_lattice_instance_f32 IIR_3k4_BPF =
{
.numStages = IIR_3k4_numStages,
.pkCoeffs = (float*) (const float[])
{
0.204719622771957,
-0.386730932575186,
0.387584200592573,
-0.700090533739370,
0.652085184091423,
-0.105219888998415,
0.929614073821885,
-0.994586210742946,
0.999632199294300,
-0.996867967817777
},
.pvCoeffs = (float*) (const float[])
{
-0.0587697225218063,
-0.231747513031753,
-0.311574476801376,
-0.0140958795068678,
0.276927602806913,
0.0895259663386021,
-0.0561390299585095,
0.0254613257946724,
-0.00162532862462556,
-4.40492441641516e-05,
1.51963288604756e-06
}
};
@@ -0,0 +1,112 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-02
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 3600Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
#define IIR_3k6_numStages 10
const arm_iir_lattice_instance_f32 IIR_3k6_LPF =
{
.numStages = IIR_3k6_numStages,
.pkCoeffs = (float*) (const float[])
{
0.182604754397413,
-0.297364372108114,
0.545463598315966,
-0.221554941171137,
0.914413017285099,
0.131148813295864,
0.992992542452280,
0.260698968055509,
0.999556840879677,
0.301864824451559
},
.pvCoeffs = (float*) (const float[])
{
0.0481999578478482,
0.218712180455229,
0.412921909514823,
0.313810309656850,
-0.0329738792744796,
-0.0858389869457352,
-0.0158927513034342,
0.00483419702063959,
0.00477236245823653,
1.66508193628068e-05,
-0.000116754074820341
}
};
//
// Filter designed 20141202 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// *** 12 KSPS Sample Rate!
//
//
// 10th order Elliptic bandpass filter
// Fpass: 150, 3465 Hz
// -6dB points: 138, 3606 Hz
// -20dB points: 120, 3853 Hz
// -40dB points: 98, 4191 Hz
// -60dB points: <85, >4385 Hz
//
const arm_iir_lattice_instance_f32 IIR_3k6_BPF =
{
.numStages = IIR_3k6_numStages,
.pkCoeffs = (float*) (const float[])
{
0.1309746791958,
-0.3193816788598,
0.2931126334419,
-0.6722769249819,
0.6365603289927,
-0.03819195677896,
0.9228154008907,
-0.9953113565282,
0.9995716337798,
-0.9970944160657
},
.pvCoeffs = (float*) (const float[])
{
-0.07111514509409,
-0.2707254278064,
-0.3391742081774,
0.0230826255569,
0.3263161755735,
0.07832215297698,
-0.06606133406622,
0.03330939215314,
-0.001770560523546,
-5.405998866079e-05,
1.724784082002e-06
}
};
@@ -0,0 +1,95 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass LPF
// Fpass 3800Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
// bandpass filter BPF 150 - 3800Hz
//
//
#define IIR_3k8_numStages 10
const arm_iir_lattice_instance_f32 IIR_3k8_LPF =
{
.numStages = IIR_3k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.167955275345335,
-0.241109484666904,
0.538161432993282,
-0.0791711604545083,
0.926560053717762,
0.254092584645358,
0.993888674387578,
0.363673903035194,
0.999602803572954,
0.400290937647385
},
.pvCoeffs = (float*) (const float[])
{
0.0618636668587285,
0.269123223807235,
0.460519308028180,
0.270623345085776,
-0.114078077039268,
-0.0809257862368131,
0.0122835687057461,
0.00564462194559656,
0.00205421413782014,
-3.85276331777984e-05,
-8.31130731588567e-05
}
};
const arm_iir_lattice_instance_f32 IIR_3k8_BPF =
{
.numStages = IIR_3k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.163149581231639,
-0.334764418550643,
0.204881267601001,
-0.602380325621413,
0.738422338450555,
0.180850983309683,
0.821590504163200,
-0.994197577474067,
0.999626101957683,
-0.996879424958163
},
.pvCoeffs = (float*) (const float[])
{
-0.0919069404581591,
-0.319576688929381,
-0.305305278407872,
0.164897561830671,
0.348959586027291,
-0.0157756061640711,
-0.0440343211466503,
0.0543351559597219,
-0.00290737404995828,
-7.64322618936331e-05,
2.62841509104939e-06
}
};
@@ -0,0 +1,68 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20141202 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// *** 12 KSPS Sample Rate!
//
//
// 10th order Elliptic bandpass filter
// Fpass: 150, 2700 Hz
// -6dB points: 138, 3606 Hz
// -20dB points: 120, 3853 Hz
// -40dB points: 98, 4191 Hz
// -60dB points: <85, >4385 Hz
//
#define IIR_3k_numStages 10
const arm_iir_lattice_instance_f32 IIR_3k =
{
.numStages = IIR_3k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.00289573666141589550,
0.02895736661415895100,
0.13030814976371530000,
0.34748839936990744000,
0.60810469889733798000,
0.72972563867680562000,
0.60810469889733798000,
0.34748839936990744000,
0.13030814976371530000,
0.02895736661415895100,
0.00289573666141589550
},
.pvCoeffs = (float*) (const float[])
{
1.00000000000000000000,
0.00000000000000008232,
1.34038326769903440000,
-0.00000000000000008232,
0.54535390952381724000,
0.00000000000000009261,
0.07704116610119554600,
-0.00000000000000000322,
0.00316548154834338670,
-0.00000000000000000004,
0.00001677879772667541
}
};
@@ -0,0 +1,61 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-15
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass LPF
// Fpass 4200Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_4k2_numStages 10
const arm_iir_lattice_instance_f32 IIR_4k2_LPF =
{
.numStages = IIR_4k2_numStages,
.pkCoeffs = (float*) (const float[])
{
0.146928213236347,
-0.112037098760571,
0.573308628506104,
0.237996369839101,
0.949903541125154,
0.484079856668923,
0.995620381648115,
0.556090559967388,
0.999703984324404,
0.582944939913576
},
.pvCoeffs = (float*) (const float[])
{
0.101763006075955,
0.390529350317333,
0.507373813109782,
0.101105933174559,
-0.219699891693221,
-0.0341597848533599,
0.0407495774164026,
0.00298273384268377,
-0.00135023483300145,
-4.39812314239063e-05,
-4.24250219673017e-06
}
};
@@ -0,0 +1,58 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
// filter designed with MATLAB fdatools by DD4WH 2016-02-03
// initial idea for these filters: Clint KA7OEI
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 4400Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
#define IIR_4k4_numStages 10
const arm_iir_lattice_instance_f32 IIR_4k4_LPF =
{
.numStages = IIR_4k4_numStages,
.pkCoeffs = (float*) (const float[])
{
0.142124344207148,
-0.0322948634379783,
0.618111817382185,
0.397046008479189,
0.960113543408077,
0.587394367585518,
0.996430642116453,
0.643137617322656,
0.999755612135519,
0.665125140142304
},
.pvCoeffs = (float*) (const float[])
{
0.130420860087981,
0.458803381555481,
0.490791158211600,
-0.00438502468346513,
-0.213305874865595,
-0.00833743485729244,
0.0354832807597507,
0.00122731012273469,
-0.00136446459308992,
-2.19632806766068e-05,
7.11876663672673e-06
}
};
@@ -0,0 +1,59 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-02
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 4600Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
#define IIR_4k6_numStages 10
const arm_iir_lattice_instance_f32 IIR_4k6_LPF =
{
.numStages = IIR_4k6_numStages,
.pkCoeffs = (float*) (const float[])
{
0.143211048758245,
0.0636595308157890,
0.678384747001818,
0.544299319747716,
0.969133023383267,
0.681014534387261,
0.997187827029787,
0.722612358980166,
0.999805647018336,
0.739954454035932
},
.pvCoeffs = (float*) (const float[])
{
0.167057909773121,
0.527901313830262,
0.437747419718857,
-0.0980305392148662,
-0.167408704264879,
0.00853396918515870,
0.0231579610435375,
0.000144746230869686,
-0.000845091312013047,
-6.36386754826468e-06,
5.95501329503367e-06
}
};
@@ -0,0 +1,58 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
// filter designed with MATLAB fdatools by DD4WH 2016-02-03
// initial idea for these filters: Clint KA7OEI
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 4800Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
#define IIR_4k8_numStages 10
const arm_iir_lattice_instance_f32 IIR_4k8_LPF =
{
.numStages = IIR_4k8_numStages,
.pkCoeffs = (float*) (const float[])
{
0.153116856877746,
0.180701877352361,
0.748242393147457,
0.672766998956057,
0.976991110647331,
0.763856545049567,
0.997879618771176,
0.793537908808440,
0.999852510953044,
0.806593649345542
},
.pvCoeffs = (float*) (const float[])
{
0.213846255224293,
0.591813811815732,
0.344831798189898,
-0.157607874284224,
-0.101315055088023,
0.0140678532972274,
0.0109533232815195,
-0.000216944372851913,
-0.000345510532658189,
-3.13949510166722e-07,
2.38332090327087e-06
}
};
@@ -0,0 +1,58 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
// filter designed with MATLAB fdatools by DD4WH 2016-02-03
// initial idea for these filters: Clint KA7OEI
// 12k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass
// Fpass 4000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
#define IIR_4k_numStages 10
const arm_iir_lattice_instance_f32 IIR_4k_LPF =
{
.numStages = IIR_4k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.155876536292444,
-0.180325472152569,
0.546715175591137,
0.0762684731194015,
0.938604917720414,
0.372421515579738,
0.994769037313590,
0.462548368276089,
0.999652456699243,
0.494335639815055
},
.pvCoeffs = (float*) (const float[])
{
0.0793628035363237,
0.326663427969449,
0.494575679160066,
0.197555516079231,
-0.182792933325612,
-0.0614448809184776,
0.0331486325344859,
0.00478157010758418,
-0.000230235339343743,
-5.64895267110011e-05,
-3.66641450827987e-05
}
};
@@ -0,0 +1,207 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20141202 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// Nominal 750 Hz center-frequency Filter:
//
// *** 12 KSPS Sample Rate!
//
// 10th order Least p-th Norm. bandpass filter
// Fstop: 320, 1185 Hz
// Fpass: 525, 975 Hz
// -6dB points: 500, 998 480, 1034 Hz
// -20dB points: 454, 1041 410, 1084 Hz
// -40dB points: 370, 1125 <304, 1184 Hz
// -55dB point: <320Hz -60 dB: >1283 Hz
//
#define IIR_500hz_numStages 10
const arm_iir_lattice_instance_f32 IIR_500hz_750 =
{
.numStages = IIR_500hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.7450181244202,
-0.9084574667981,
0.9790083293996,
-0.9303082589799,
0.9883388157099,
-0.9268194870575,
0.9907447739419,
-0.919714678964,
0.9893355692757,
-0.9435646976164
},
.pvCoeffs = (float*) (const float[])
{
-1.61639285742e-07,
-2.532569769139e-06,
-1.730815165529e-05,
-6.199614625535e-05,
-0.0001162330723378,
-9.354099226825e-05,
9.396401192372e-06,
2.784282263339e-06,
8.547158547822e-06,
5.265868366272e-07,
-7.389379416329e-07
}
};
//
// 850 Hz center
//
const arm_iir_lattice_instance_f32 IIR_500hz_850 =
{
.numStages = IIR_500hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.7401338925506,
-0.8867593927342,
0.9778465129623,
-0.9094003580498,
0.9883168161429,
-0.9055217486004,
0.9903863113441,
-0.8954747103238,
0.9912950685125,
-0.9298872553011
},
.pvCoeffs = (float*) (const float[])
{
-4.410143019036e-06,
-9.236919139671e-05,
-0.0003954308979742,
-0.0003955563639255,
-0.0001628454905818,
0.0001098763251995,
6.339131935123e-05,
2.488401959762e-05,
-7.761948232174e-06,
-3.463637105875e-06,
3.023849497139e-07
}
};
//
// 950 Hz center
//
const arm_iir_lattice_instance_f32 IIR_500hz_950 =
{
.numStages = IIR_500hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.7561294931445,
-0.8664471899292,
0.9790444302334,
-0.8864086051206,
0.9889574393432,
-0.8826528972432,
0.9908739731213,
-0.8721677687198,
0.9905929406153,
-0.9077406187546
},
.pvCoeffs = (float*) (const float[])
{
-1.978764105412e-06,
-4.109388008507e-05,
-0.0002182934662175,
-0.0002429079035953,
-0.0002277987829458,
0.0001122347644774,
6.888195567177e-05,
2.855449207125e-05,
-6.041821715252e-06,
-4.915517444392e-06,
3.936206528372e-07
}
};
//
// 650 Hz center
//
const arm_iir_lattice_instance_f32 IIR_500hz_650 =
{
.numStages = IIR_500hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.7629545983553,
-0.9292246993992,
0.9812440354719,
-0.9481158800736,
0.9886964977718,
-0.9455424450397,
0.9898872346552,
-0.9387191189796,
0.9935074333889,
-0.9667663712751
},
.pvCoeffs = (float*) (const float[])
{
7.913288912416e-08,
-8.145261857675e-06,
9.724845240669e-05,
0.0002513968527392,
0.0001566426842087,
-1.692018078797e-05,
-2.026140586575e-05,
-1.410159029616e-05,
-3.517554836743e-07,
9.445267691075e-07,
6.313896488397e-08
}
};
//
// 550 Hz center
//
const arm_iir_lattice_instance_f32 IIR_500hz_550 =
{
.numStages = IIR_500hz_numStages,
.pkCoeffs = (float*) (const float[])
{
0.7444824297014,
-0.9426910512095,
0.9809095416549,
-0.9635826118077,
0.9879143935335,
-0.9619675085485,
0.9892963956729,
-0.9551745584075,
0.9937943260814,
-0.980312269594
},
.pvCoeffs = (float*) (const float[])
{
6.54693860799e-11,
-1.725286655686e-08,
1.274335799922e-06,
-2.347197649254e-05,
0.0001149607579105,
-5.20632558042e-05,
-1.133152510209e-06,
-9.492268935446e-06,
3.245552848263e-08,
5.879262051272e-07,
-1.546228266662e-08
}
};
@@ -0,0 +1,61 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass LPF
// Fpass 5500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_5k5_numStages 10
const arm_iir_lattice_instance_f32 IIR_5k5_LPF =
{
.numStages = IIR_5k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.270056112170917,
-0.521414453389724,
0.678616912945954,
-0.641289912968769,
0.883688002282309,
-0.381347496766270,
0.989526871938782,
-0.192450450475999,
0.999443061271067,
-0.139179149092653
},
.pvCoeffs = (float*) (const float[])
{
0.0165986011732628,
0.0791435170266696,
0.186630364068749,
0.233915282634195,
0.138804057676891,
0.00684091782624835,
-0.0470727830131718,
-0.00518447562091082,
0.00219719710580432,
8.29160622089417e-05,
8.64931555180915e-05
}
};
@@ -0,0 +1,61 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass LPF
// Fpass 5000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_5k_numStages 10
const arm_iir_lattice_instance_f32 IIR_5k_LPF =
{
.numStages = IIR_5k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.304008884053488,
-0.586479754574523,
0.728707358729566,
-0.717591127840479,
0.886460005029009,
-0.509239560057348,
0.988906686305070,
-0.320749263130520,
0.999445512653576,
-0.267299520678722
},
.pvCoeffs = (float*) (const float[])
{
0.0121126909328103,
0.0563705089486983,
0.135077706371490,
0.177369645199308,
0.122940273900304,
0.0249807561603719,
-0.0272356075029574,
-0.00442102257133439,
-0.000487963028261214,
9.54073216847401e-06,
8.24834253008722e-05
}
};
@@ -0,0 +1,61 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass LPF
// Fpass 6500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_6k5_numStages 10
const arm_iir_lattice_instance_f32 IIR_6k5_LPF =
{
.numStages = IIR_6k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.213702079803504,
-0.390726329295393,
0.586645549319686,
-0.429887036682493,
0.895736972595422,
-0.0875234251266855,
0.991439058220713,
0.0745020436698480,
0.999490700886464,
0.122410371569787
},
.pvCoeffs = (float*) (const float[])
{
0.0311072717160539,
0.147645149046583,
0.316095562243594,
0.320547362746078,
0.0859079982599722,
-0.0582045525665189,
-0.0550274347397058,
-3.62573178755987e-05,
0.00708241752078387,
0.000130934259420401,
-6.94896100042891e-05
}
};
@@ -0,0 +1,61 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 10th order IIR Elliptic lowpass LPF
// Fpass 6000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_6k_numStages 10
const arm_iir_lattice_instance_f32 IIR_6k_LPF =
{
.numStages = IIR_6k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.240061145139098,
-0.456135423117119,
0.629931199814858,
-0.546874535504622,
0.886954490804698,
-0.239344357593924,
0.990406694917175,
-0.0598647009288412,
0.999458839296527,
-0.00852401189342987
},
.pvCoeffs = (float*) (const float[])
{
0.0227312628074594,
0.109078946204801,
0.247843364281694,
0.285784016518540,
0.129833944671845,
-0.0238187084272364,
-0.0598880195707474,
-0.00364575842735154,
0.00541066911948504,
0.000142100013861043,
2.39142895447318e-05
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 7500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_7k5_numStages 8
const arm_iir_lattice_instance_f32 IIR_7k5_LPF =
{
.numStages = IIR_7k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.171676312284558,
-0.248915944482148,
0.554808670292421,
-0.0804708303821426,
0.937673773552786,
0.255249586500978,
0.996592749183544,
0.364810653255913
},
.pvCoeffs = (float*) (const float[])
{
0.0631810509418012,
0.273620653809899,
0.462876563831051,
0.260420336947869,
-0.123177317150119,
-0.0730805396001466,
0.0163120102308616,
0.00402985085145935,
0.00101856811686258
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 7000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_7k_numStages 8
const arm_iir_lattice_instance_f32 IIR_7k_LPF =
{
.numStages = IIR_7k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.190713577571592,
-0.319871726375826,
0.565395988442476,
-0.255306414282902,
0.924727833564385,
0.104786836584826,
0.995972585176399,
0.237878968193324
},
.pvCoeffs = (float*) (const float[])
{
0.0464388990834632,
0.211741818487612,
0.403878817111474,
0.311449064277157,
-0.0264024657273338,
-0.0789871827367324,
-0.0150701534983805,
0.00343825204070403,
0.00335955618048627
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 8500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_8k5_numStages 8
const arm_iir_lattice_instance_f32 IIR_8k5_LPF =
{
.numStages = IIR_8k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.147016458647620,
-0.0800576834677209,
0.607415025263421,
0.311944568388449,
0.961718465037911,
0.531590985304236,
0.997775367200182,
0.597158407524272
},
.pvCoeffs = (float*) (const float[])
{
0.116227477513591,
0.426616080915722,
0.501928042829669,
0.0426267579687529,
-0.221292633625496,
-0.0171136752421530,
0.0374419493102454,
0.00134067435969376,
-0.00112236216067886
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 8000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_8k_numStages 8
const arm_iir_lattice_instance_f32 IIR_8k_LPF =
{
.numStages = IIR_8k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.156730412841056,
-0.170860629143791,
0.567767507488873,
0.113051837961350,
0.950299005264617,
0.398693812045436,
0.997198510683089,
0.485296093326901
},
.pvCoeffs = (float*) (const float[])
{
0.0857789183340628,
0.345903140429045,
0.500262683274993,
0.165780339808995,
-0.198921078104878,
-0.0482542902035035,
0.0362835359961724,
0.00297622750353344,
-0.000691737718980207
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// Filter designed 20151022 by C. Turner, KA7OEI using MatLAB fdatools
//
// NOTE:
// - IIR structure is Lattice Autoregressive Moving-Average (ARMA)
// - ARM FIR/IIR algorithms require time reverse-order coefficients!!!
//
// 6th order Elliptic highpass filter
//
//
// -6dB point: 8.4 kHz
// -20dB point: 8.0 kHz
// -40dB point: 7.4 kHz
// -60dB point: <7.0 kHz
//
//#define NCoef 8
#define IIR_8k5_numStages 6
const arm_iir_lattice_instance_f32 IIR_8k5_hpf =
{
.numStages = IIR_8k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.3267431914313,
0.2827426748037,
0.6792677220421,
-0.08412750477612,
0.9796815399487,
-0.3939883111528
},
.pvCoeffs = (float*) (const float[])
{
0.07735072858385,
-0.306940854114,
0.4145882522999,
-0.09894434429254,
-0.1687647954502,
0.01867899629887,
0.02161688514499
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 9500Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_9k5_numStages 8
const arm_iir_lattice_instance_f32 IIR_9k5_LPF =
{
.numStages = IIR_9k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.154589161975431,
0.171214182840522,
0.752540038804833,
0.661446067312243,
0.979967038791770,
0.755243080342941,
0.998791846173898,
0.787213445621968
},
.pvCoeffs = (float*) (const float[])
{
0.212160913842594,
0.589885689317715,
0.348337037956110,
-0.155662625526521,
-0.103203755044854,
0.0134205375549463,
0.0105947295297542,
-0.000168431246632794,
-0.000257624900844355
}
};
@@ -0,0 +1,57 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// alternative filter designed with MATLAB fdatools by DD4WH 2016-02-18
// 24k sampling rate, Lattice ARMA structure
// 8th order IIR Elliptic lowpass LPF
// Fpass 9000Hz
// Astop 60dB
// coefficients in reverse order than that spit out by MATLAB
//
//
#define IIR_9k_numStages 8
const arm_iir_lattice_instance_f32 IIR_9k_LPF =
{
.numStages = IIR_9k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.144843368057163,
0.0311176821163535,
0.671835107699585,
0.499495871489971,
0.971603001167062,
0.651162915986589,
0.998310568870827,
0.698395145953468
},
.pvCoeffs = (float*) (const float[])
{
0.157182890700946,
0.510999353559437,
0.454077934277190,
-0.0776493551890845,
-0.180998321432113,
0.00575759072640292,
0.0249744575699347,
0.000197356170009044,
-0.000725804365039828
}
};
@@ -0,0 +1,193 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
//
// IIR filter
// lattice ARMA coefficients designed with MATLAb fdatools
// DD4WH 2016-03-05
// inspired by Clint KA7OEI
//
// LPF 5k elliptic, 60dB stopband, 48ksps
#define IIR_aa_5k_numStages 6
const arm_iir_lattice_instance_f32 IIR_aa_5k =
{
.numStages = IIR_aa_5k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.547564303565301,
-0.874437851307620,
0.929381964274064,
-0.931756218288255,
0.967791921156360,
-0.855303237881406
},
.pvCoeffs = (float*) (const float[])
{
0.00321398613474079,
0.0103775071609725,
0.0204793631675465,
0.0207411754628459,
0.0142582715360116,
0.00416771325172439,
0.000586794199906653
}
};
// IIR elliptic 48ksps, 6th order, FStop = 10000Hz
// -60dB stopband att, ARMA structure, coefficients in reversed order
#define IIR_aa_10k_numStages 6
const arm_iir_lattice_instance_f32 IIR_aa_10k =
{
.numStages = IIR_aa_10k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.301098203357079,
-0.579105658407328,
0.734150184329856,
-0.671115797851337,
0.939860662239371,
-0.361103585037447
},
.pvCoeffs = (float*) (const float[])
{
0.0174468544108513,
0.0830073641688922,
0.190703913563757,
0.218380125399821,
0.102432703504841,
-0.00776347779147436,
-0.0322591772433757
}
};
// IIR elliptic 48ksps, 6th order, FStop = 8000Hz
// -60dB stopband att, ARMA structure, coefficients in reversed order
#define IIR_aa_8k_numStages 6
const arm_iir_lattice_instance_f32 IIR_aa_8k =
{
.numStages = IIR_aa_8k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.382097471026454,
-0.707860806497603,
0.822488299956015,
-0.809655940560093,
0.943091324035883,
-0.602154271982275
},
.pvCoeffs = (float*) (const float[])
{
0.00902518911329759,
0.0401957378835905,
0.0934435150148285,
0.114552126526293,
0.0734506802740618,
0.0130302613689033,
-0.0115031852097110
}
};
// IIR elliptic 48ksps, 6th order, FStop = 8500Hz
// -60dB stopband att, ARMA structure, coefficients in reversed order
#define IIR_aa_8k5_numStages 6
const arm_iir_lattice_instance_f32 IIR_aa_8k5 =
{
.numStages = IIR_aa_8k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.359939618704540,
-0.676489478729843,
0.801069829925990,
-0.780419188957268,
0.941052598112166,
-0.546710306936035
},
.pvCoeffs = (float*) (const float[])
{
0.0106702880998750,
0.0487135595564791,
0.113867385924671,
0.139020661730700,
0.0847861503854092,
0.0109690817210141,
-0.0167288132890575
}
};
// IIR elliptic 48ksps, 6th order, FStop = 9000Hz
// -60dB stopband att, ARMA structure, coefficients in reversed order
#define IIR_aa_9k_numStages 6
const arm_iir_lattice_instance_f32 IIR_aa_9k =
{
.numStages = IIR_aa_9k_numStages,
.pkCoeffs = (float*) (const float[])
{
0.339100957220132,
-0.644493816414106,
0.779034723806422,
-0.747849414036102,
0.939827557980654,
-0.487848762987584
},
.pvCoeffs = (float*) (const float[])
{
0.0125932272310938,
0.0585929866522992,
0.136927898008432,
0.165069644706020,
0.0942562014345331,
0.00684027662013907,
-0.0223862358637905
}
};
// IIR elliptic 48ksps, 6th order, FStop = 9500Hz
// -60dB stopband att, ARMA structure, coefficients in reversed order
#define IIR_aa_9k5_numStages 6
const arm_iir_lattice_instance_f32 IIR_aa_9k5 =
{
.numStages = IIR_aa_9k5_numStages,
.pkCoeffs = (float*) (const float[])
{
0.319509178478567,
-0.611998056066153,
0.756628846034883,
-0.711556259037217,
0.939435113760607,
-0.425861390235328
},
.pvCoeffs = (float*) (const float[])
{
0.0148361127984778,
0.0699763422680422,
0.162584450093266,
0.191872694251187,
0.100583774752231,
0.000539571340153120,
-0.0278287915752199
}
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,774 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "filters.h"
/*
* 89 tap FIR lowpass filter for AM demodulation
* 10 kHz lowpass
* Fc = 9.70 kHz
*
* -6dB @ 10.0 kHz
* -20dB @ 10.45 kHz
* -40dB @ 10.8 kHz
* -60dB > 11.13 kHz
*
* 20141205 by KA7OEI using Iowa Hills Hilbert Filter Designer
*/
const float iq_rx_am_10k_coeffs[IQ_RX_NUM_TAPS] =
{
0.000117722483050164,
-0.000190826378141535,
-0.000316454605198105,
0.000084665259804020,
0.000534437033829599,
0.000223520439161135,
-0.000632342517282613,
-0.000725725616097602,
0.000428204065620224,
0.001280215798621918,
0.000218375537426833,
-0.001601721502248780,
-0.001292161293926300,
0.001330681506383581,
0.002528032564646596,
-0.000185878610189881,
-0.003399805802419520,
-0.001839090604793820,
0.003240267262336325,
0.004316174080536414,
-0.001493992133713610,
-0.006356339576212520,
-0.001960308149814000,
0.006786095910800701,
0.006537642652845357,
-0.004528723876747330,
-0.010862593712668900,
-0.000897173759372233,
0.012971399058043866,
0.008921931792469471,
-0.010804921591446800,
-0.017688783704440600,
0.002879095183285337,
0.024147999555009812,
0.011076579092093660,
-0.024429930878634200,
-0.029750803160671700,
0.014143913162437944,
0.050329661474116855,
0.012584602054512364,
-0.069061533914324600,
-0.071583230484314000,
0.082176152223611229,
0.306148763236497423,
0.413192420891035361,
0.306148763236497423,
0.082176152223611229,
-0.071583230484314000,
-0.069061533914324600,
0.012584602054512364,
0.050329661474116855,
0.014143913162437944,
-0.029750803160671700,
-0.024429930878634200,
0.011076579092093660,
0.024147999555009812,
0.002879095183285337,
-0.017688783704440600,
-0.010804921591446800,
0.008921931792469471,
0.012971399058043866,
-0.000897173759372233,
-0.010862593712668900,
-0.004528723876747330,
0.006537642652845357,
0.006786095910800701,
-0.001960308149814000,
-0.006356339576212520,
-0.001493992133713610,
0.004316174080536414,
0.003240267262336325,
-0.001839090604793820,
-0.003399805802419520,
-0.000185878610189881,
0.002528032564646596,
0.001330681506383581,
-0.001292161293926300,
-0.001601721502248780,
0.000218375537426833,
0.001280215798621918,
0.000428204065620224,
-0.000725725616097602,
-0.000632342517282613,
0.000223520439161135,
0.000534437033829599,
0.000084665259804020,
-0.000316454605198105,
-0.000190826378141535,
0.000117722483050164
};
/*
* 89 tap FIR lowpass filter for AM demodulation
* "2.3 kHz lowpass"
*
* NOTE: Because the actual bandpass is TWICE the lowpass, and the fact that one must off-
* tune the receiver slightly to avoid the "zero-Hz" hole in the center of the passband,
* this filter is somewhat narrower than "2.3 kHz"
*
* Fc = 1.78 kHz
*
* -6dB @ 1.99 kHz
* -20dB @ 2.55 kHz
* -40dB @ 2.90 kHz
* -60dB > 3.09 kHz
*
* 20141205 by KA7OEI using Iowa Hills Hilbert Filter Designer
*/
const float iq_rx_am_2k3_coeffs[IQ_RX_NUM_TAPS] =
{
-0.000196778133038791,
-0.000275755280074211,
-0.000353984612861587,
-0.000418690075048854,
-0.000454047164276299,
-0.000442413394049822,
-0.000366184434389176,
-0.000210191425343778,
0.000035537284709122,
0.000372915408768254,
0.000793177355713582,
0.001274879514193310,
0.001782913716894173,
0.002268882448091475,
0.002673096205319169,
0.002928317701073665,
0.002965203927037432,
0.002719199905849284,
0.002138436401413266,
0.001192000201349910,
-0.000122197108217858,
-0.001770806410417120,
-0.003680540305986990,
-0.005736552692955240,
-0.007784289231932950,
-0.009635139756152750,
-0.011075900709763000,
-0.011881696038148500,
-0.011831641788347900,
-0.010726209478865100,
-0.008404982146732770,
-0.004763336656982600,
0.000233449848468894,
0.006540069844680653,
0.014026193505423940,
0.022477415356206891,
0.031602482869331178,
0.041046636023995359,
0.050410360909975617,
0.059272371807803176,
0.067215232024002855,
0.073851744736956312,
0.078850120995801118,
0.081955978437540142,
0.083009440825973363,
0.081955978437540142,
0.078850120995801118,
0.073851744736956312,
0.067215232024002855,
0.059272371807803176,
0.050410360909975617,
0.041046636023995359,
0.031602482869331178,
0.022477415356206891,
0.014026193505423940,
0.006540069844680653,
0.000233449848468894,
-0.004763336656982600,
-0.008404982146732770,
-0.010726209478865100,
-0.011831641788347900,
-0.011881696038148500,
-0.011075900709763000,
-0.009635139756152750,
-0.007784289231932950,
-0.005736552692955240,
-0.003680540305986990,
-0.001770806410417120,
-0.000122197108217858,
0.001192000201349910,
0.002138436401413266,
0.002719199905849284,
0.002965203927037432,
0.002928317701073665,
0.002673096205319169,
0.002268882448091475,
0.001782913716894173,
0.001274879514193310,
0.000793177355713582,
0.000372915408768254,
0.000035537284709122,
-0.000210191425343778,
-0.000366184434389176,
-0.000442413394049822,
-0.000454047164276299,
-0.000418690075048854,
-0.000353984612861587,
-0.000275755280074211,
-0.000196778133038791
};
/*
* 89 tap FIR lowpass filter for AM demodulation
* 3.6 kHz lowpass
* Fc = 3.41 kHz
*
* -6dB @ 3.61 kHz
* -20dB @ 4.15 kHz
* -40dB @ 4.52 kHz
* -60dB > 4.73 kHz
*
* 20141205 by KA7OEI using Iowa Hills Hilbert Filter Designer
*/
const float iq_rx_am_3k6_coeffs[IQ_RX_NUM_TAPS] =
{
0.000194236006761998,
0.000282693396615949,
0.000320173787778202,
0.000266033714965954,
0.000094222201767759,
-0.000191128727083006,
-0.000544956187969324,
-0.000880855390916938,
-0.001084659388254350,
-0.001042012424123070,
-0.000675226060253367,
0.000019767665763547,
0.000948239863530163,
0.001914701347780859,
0.002651381830401708,
0.002876822184755611,
0.002374910694293840,
0.001076052561405293,
-0.000882983411525426,
-0.003143324896504320,
-0.005167612397018260,
-0.006344612664735190,
-0.006139510014581940,
-0.004258448206719960,
-0.000785356097214045,
0.003751918439268481,
0.008418658238655563,
0.012024924259495225,
0.013375473421118661,
0.011573528596812616,
0.006312844603313269,
-0.001915215334099090,
-0.011763413332218100,
-0.021156533713089800,
-0.027603877979735700,
-0.028651610038411300,
-0.022392825182070700,
-0.007931207001952560,
0.014305891795470664,
0.042485370371458303,
0.073554037962071023,
0.103673924978429130,
0.128835165244061745,
0.145533526176055278,
0.151381738212891748,
0.145533526176055278,
0.128835165244061745,
0.103673924978429130,
0.073554037962071023,
0.042485370371458303,
0.014305891795470664,
-0.007931207001952560,
-0.022392825182070700,
-0.028651610038411300,
-0.027603877979735700,
-0.021156533713089800,
-0.011763413332218100,
-0.001915215334099090,
0.006312844603313269,
0.011573528596812616,
0.013375473421118661,
0.012024924259495225,
0.008418658238655563,
0.003751918439268481,
-0.000785356097214045,
-0.004258448206719960,
-0.006139510014581940,
-0.006344612664735190,
-0.005167612397018260,
-0.003143324896504320,
-0.000882983411525426,
0.001076052561405293,
0.002374910694293840,
0.002876822184755611,
0.002651381830401708,
0.001914701347780859,
0.000948239863530163,
0.000019767665763547,
-0.000675226060253367,
-0.001042012424123070,
-0.001084659388254350,
-0.000880855390916938,
-0.000544956187969324,
-0.000191128727083006,
0.000094222201767759,
0.000266033714965954,
0.000320173787778202,
0.000282693396615949,
0.000194236006761998,
};
/* alternative lowpass filter for AM demodulation
*
* the problem with the 5k filter is that heterodynes that occur
* because of the 5k carrier spacing in broadcast stations
* therefore this alternative filter with a cutoff at 4k5
* 89 tap FIR lowpass filter
* 4k5 lowpass ParkMcClellan
*
* -6dB @ 4.5kHz
* -24dB @ 5kHz
* Fc = 4.32kHz (0.1800)
* Window off, transition width 0.06
*
*/
const float iq_rx_am_4k5_coeffs[IQ_RX_NUM_TAPS] =
{
12.62505461280822420E-6,
-0.001023781148748946,
-0.001379392726010002,
-0.001857298885889991,
-0.002007930982712860,
-0.001645764802591053,
-729.0049453258671970E-6,
580.3247418821165410E-6,
0.001921187006321661,
0.002821439596649512,
0.002854663846093201,
0.001821006157654806,
-121.3991924356805900E-6,
-0.002433909268373769,
-0.004320619640730387,
-0.004979702734199301,
-0.003909641145391516,
-0.001164372205549721,
0.002556303856712675,
0.006045165075114637,
0.007947771772716222,
0.007246305410655999,
0.003707127946145705,
-0.001883752703394752,
-0.007825126886278979,
-0.011964484776280015,
-0.012414164965104595,
-0.008297951368073063,
-261.8431451219605610E-6,
0.009476671671323595,
0.017635986356801487,
0.020847250650268035,
0.016840077425071362,
0.005464567575098569,
-0.010818978674222298,
-0.027219933295235161,
-0.037686443762518306,
-0.036521090442195647,
-0.020119645677827265,
0.011696759647048831,
0.055173533570635397,
0.103180886473356048,
0.146733643399102748,
0.177107579650033015,
0.187997676685657006,
0.177107579650033015,
0.146733643399102748,
0.103180886473356048,
0.055173533570635397,
0.011696759647048831,
-0.020119645677827265,
-0.036521090442195647,
-0.037686443762518306,
-0.027219933295235161,
-0.010818978674222298,
0.005464567575098569,
0.016840077425071362,
0.020847250650268035,
0.017635986356801487,
0.009476671671323595,
-261.8431451219605610E-6,
-0.008297951368073063,
-0.012414164965104595,
-0.011964484776280015,
-0.007825126886278979,
-0.001883752703394752,
0.003707127946145705,
0.007246305410655999,
0.007947771772716222,
0.006045165075114637,
0.002556303856712675,
-0.001164372205549721,
-0.003909641145391516,
-0.004979702734199301,
-0.004320619640730387,
-0.002433909268373769,
-121.3991924356805900E-6,
0.001821006157654806,
0.002854663846093201,
0.002821439596649512,
0.001921187006321661,
580.3247418821165410E-6,
-729.0049453258671970E-6,
-0.001645764802591053,
-0.002007930982712860,
-0.001857298885889991,
-0.001379392726010002,
-0.001023781148748946,
12.62505461280822420E-6
};
/*
* 89 tap FIR lowpass filter for AM demodulation
* 5 kHz lowpass
* Fc = 2.30 kHz (0.096)
* BW = 5.09 kHz (0.212)
* Phase = 0.0 Deg
*
* -6dB @ 5.0 kHz
* -20dB @ 5.6 kHz
* -40dB @ 6.0 kHz
* -60dB > 6.2 kHz
*
* 20150724 by KA7OEI using Iowa Hills Hilbert Filter Designer
*
*/
const float iq_rx_am_5k_coeffs[IQ_RX_NUM_TAPS] =
{
-0.000045392289047881,
-0.000004812442917794,
0.000068727674699516,
0.000141963927224663,
0.000157520248119967,
0.000060286967439699,
-0.000162970122246712,
-0.000451252675540791,
-0.000669397068852781,
-0.000655786056558042,
-0.000308946001031250,
0.000323862987296878,
0.001009151276280870,
0.001383767648753940,
0.001108841899531180,
0.000068694704026995,
-0.001482086048715270,
-0.002923873990383000,
-0.003478042196892240,
-0.002567012208450240,
-0.000183058152703148,
0.002927838581731980,
0.005419014927265250,
0.005850957020138160,
0.003392366775186750,
-0.001589789749145610,
-0.007378579320778290,
-0.011384168739919100,
-0.011175196307323000,
-0.005700279233064870,
0.003842132441622350,
0.013983598060774900,
0.020024993830604200,
0.017901468392382700,
0.006217999161870360,
-0.012402093620729800,
-0.031370988485890300,
-0.041832006141384800,
-0.035588597131400500,
-0.008295098525339310,
0.038292174335735300,
0.095971206103503100,
0.151982896902598000,
0.192635286648254000,
0.207485090950182000,
0.192635286648256000,
0.151982896902602000,
0.095971206103507900,
0.038292174335739900,
-0.008295098525336090,
-0.035588597131399000,
-0.041832006141385000,
-0.031370988485891600,
-0.012402093620731500,
0.006217999161868930,
0.017901468392382000,
0.020024993830604300,
0.013983598060775600,
0.003842132441623320,
-0.005700279233064020,
-0.011175196307322600,
-0.011384168739919100,
-0.007378579320778620,
-0.001589789749146100,
0.003392366775186310,
0.005850957020137900,
0.005419014927265200,
0.002927838581732100,
-0.000183058152702952,
-0.002567012208450050,
-0.003478042196892130,
-0.002923873990382970,
-0.001482086048715290,
0.000068694704026947,
0.001108841899531140,
0.001383767648753930,
0.001009151276280870,
0.000323862987296896,
-0.000308946001031235,
-0.000655786056558038,
-0.000669397068852788,
-0.000451252675540803,
-0.000162970122246723,
0.000060286967439694,
0.000157520248119968,
0.000141963927224668,
0.000068727674699521,
-0.000004812442917792,
-0.000045392289047882
};
/*
* 89 tap FIR lowpass filter for AM demodulation
* 6 kHz lowpass
* Fc = 2.64 kHz (0.110)
* BW = 638 kHz (0.266)
* Phase = 0.0 Deg
*
* -6dB @ 6.0 kHz
* -20dB @ 6.5 kHz
* -40dB @ 7.0 kHz
* -60dB > 7.2 kHz
*
* 20150724 by KA7OEI using Iowa Hills Hilbert Filter Designer
*
*/
const float iq_rx_am_6k_coeffs[IQ_RX_NUM_TAPS] =
{
-0.000004880093665016,
0.000056249601206470,
0.000106631842609010,
0.000081384254139970,
-0.000058736957755522,
-0.000272365414638255,
-0.000425548359876376,
-0.000357422252818966,
-0.000005244245277373,
0.000490974654661712,
0.000803539254316524,
0.000594128178328428,
-0.000221469661023948,
-0.001297764644188940,
-0.001948810489192100,
-0.001535669576006110,
0.000035159562865781,
0.002042237502232450,
0.003191717421535510,
0.002364285159575850,
-0.000519165742550591,
-0.004107368177389280,
-0.006132446052393760,
-0.004729819141807280,
0.000138536384265303,
0.006130339738902320,
0.009466230734482630,
0.007122248925836100,
-0.000902500392804507,
-0.010767353229646000,
-0.016344468781221300,
-0.012706451532127600,
0.000277710569700882,
0.016562752996003900,
0.026194213119574000,
0.020664397226820300,
-0.001236820922091710,
-0.030498087727862400,
-0.050224694289173800,
-0.042930930378686000,
0.000379604033916306,
0.073954541623616600,
0.157507699515675000,
0.223513877747226000,
0.248565464214855000,
0.223513877747230000,
0.157507699515682000,
0.073954541623623400,
0.000379604033921383,
-0.042930930378683800,
-0.050224694289174300,
-0.030498087727864600,
-0.001236820922093990,
0.020664397226819100,
0.026194213119574100,
0.016562752996005000,
0.000277710569702140,
-0.012706451532126900,
-0.016344468781221300,
-0.010767353229646500,
-0.000902500392805126,
0.007122248925835810,
0.009466230734482780,
0.006130339738902770,
0.000138536384265717,
-0.004729819141807150,
-0.006132446052393960,
-0.004107368177389690,
-0.000519165742550966,
0.002364285159575700,
0.003191717421535620,
0.002042237502232730,
0.000035159562866079,
-0.001535669576005930,
-0.001948810489192080,
-0.001297764644189040,
-0.000221469661024093,
0.000594128178328318,
0.000803539254316486,
0.000490974654661737,
-0.000005244245277321,
-0.000357422252818922,
-0.000425548359876357,
-0.000272365414638260,
-0.000058736957755538,
0.000081384254139956,
0.000106631842609003,
0.000056249601206470,
-0.000004880093665013
};
/*
* 89 tap FIR lowpass filter for AM demodulation
* 7.5 kHz lowpass
* Fc = 3.50 kHz (0.146)
* BW = 7.73 kHz (0.322)
* Phase = 0.0 Deg
*
* -6dB @ 7.5 kHz
* -20dB @ 8.1 kHz
* -40dB @ 8.5 kHz
* -60dB > 8.7 kHz
*
* 20150724 by KA7OEI using Iowa Hills Hilbert Filter Designer
*/
const float iq_rx_am_7k5_coeffs[IQ_RX_NUM_TAPS] =
{
-0.000054368878976464,
-0.000109470418324297,
-0.000080800014418553,
0.000057887247193655,
0.000196248384784124,
0.000143919934167119,
-0.000166720581170554,
-0.000516949697820653,
-0.000513457595923984,
0.000019591792777547,
0.000715438143813412,
0.000837678462105361,
-0.000026672117250676,
-0.001346407587573860,
-0.001854545301167850,
-0.000681925402542360,
0.001516983112219140,
0.002720057594104930,
0.001264577750721170,
-0.002218475374737490,
-0.004716082694953130,
-0.003290070348926000,
0.001768705442798720,
0.006257195418023830,
0.005315470630396060,
-0.001674768165904550,
-0.009231015015775820,
-0.009741273892250180,
-0.000735938093829474,
0.011162261151525300,
0.014549180415936700,
0.003582582016681460,
-0.014560284217669600,
-0.023377317298097500,
-0.011097551616034700,
0.016127337734989000,
0.035061508247232000,
0.023170836448327200,
-0.018977720268267700,
-0.060089752963952400,
-0.055598189384657500,
0.019293047057938800,
0.145271104740461000,
0.264027404678331000,
0.312598578987250000,
0.264027404678338000,
0.145271104740472000,
0.019293047057947600,
-0.055598189384654300,
-0.060089752963954300,
-0.018977720268271600,
0.023170836448324900,
0.035061508247232500,
0.016127337734991200,
-0.011097551616033100,
-0.023377317298097600,
-0.014560284217671000,
0.003582582016680210,
0.014549180415936700,
0.011162261151526300,
-0.000735938093828361,
-0.009741273892249770,
-0.009231015015776210,
-0.001674768165905220,
0.005315470630395730,
0.006257195418023980,
0.001768705442799040,
-0.003290070348925910,
-0.004716082694953380,
-0.002218475374737840,
0.001264577750721040,
0.002720057594105130,
0.001516983112219500,
-0.000681925402542081,
-0.001854545301167810,
-0.001346407587574020,
-0.000026672117250873,
0.000837678462105258,
0.000715438143813428,
0.000019591792777618,
-0.000513457595923930,
-0.000516949697820644,
-0.000166720581170573,
0.000143919934167101,
0.000196248384784120,
0.000057887247193660,
-0.000080800014418548,
-0.000109470418324297,
-0.000054368878976467
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,32 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __IQ_TX_FILTER_H
#define __IQ_TX_FILTER_H
#include "filters.h"
#define IQ_TX_BLOCK_SIZE IQ_BLOCK_SIZE
#define IQ_TX_NUM_TAPS 89
#define IQ_TX_NUM_TAPS_WIDE 201
#define IQ_TX_NUM_TAPS_MAX 201
extern const IQ_FilterDescriptor iq_tx_narrow;
extern const IQ_FilterDescriptor iq_tx_wide;
extern const IQ_FilterDescriptor iq_tx_essb_4k; // Just for eSSB
extern const IQ_FilterDescriptor iq_tx_essb_5k; // Just for eSSB
extern const IQ_FilterDescriptor iq_tx_essb_6k; // Just for eSSB
#endif
@@ -0,0 +1,80 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: Created October 2015, KA7OEI **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __SUBAUDIBLE_TONE_TABLE
#define __SUBAUDIBLE_TONE_TABLE
static const float fm_subaudible_tone_table[] =
{
0,
67.0,
69.3,
71.9,
74.4,
77.0,
79.7,
82.5,
85.4,
88.5,
91.5,
94.8,
97.4,
100.0,
103.5,
107.2,
110.9,
114.8,
118.8,
123.0,
127.3,
131.8,
136.5,
141.3,
146.2,
150.0,
151.4,
156.7,
159.8,
162.2,
165.5,
167.9,
171.3,
173.8,
177.3,
179.9,
183.5,
186.2,
189.9,
192.8,
196.6,
199.5,
203.5,
206.5,
210.7,
213.8,
218.1,
221.3,
225.7,
229.1,
233.6,
237.1,
241.8,
245.5,
250.3,
254.1,
0
};
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,435 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "ui_driver.h"
#include "freedv_uhsdr.h"
#include "ui_lcd_layouts.h"
#include "profiling.h"
#include "ui_lcd_hy28.h"
#include "radio_management.h"
#if defined(USE_FREEDV) || defined(USE_ALTERNATE_NR)
MultiModeBuffer_t __MCHF_SPECIALMEM mmb;
#endif
#ifdef USE_FREEDV
#include "freedv_api.h"
freedv_conf_t freedv_conf;
struct freedv *f_FREEDV;
RingBuffer_DefineExtMem(fdv_demod_rb,sizeof(mmb.fdv_demod_buff)/sizeof(fdv_demod_rb_item_t), mmb.fdv_demod_buff)
RingBuffer_DefineExtMem(fdv_iq_rb,sizeof(mmb.fdv_iq_buff)/sizeof(fdv_iq_rb_item_t), mmb.fdv_iq_buff)
#define FDV_AUDIO_MEM_SIZE ((FDV_BUFFER_SIZE*2)+IQ_BLOCK_SIZE)
__MCHF_SPECIALMEM fdv_audio_rb_item_t fdv_audio_rb_mem[FDV_AUDIO_MEM_SIZE];
RingBuffer_DefineExtMem(fdv_audio_rb, FDV_AUDIO_MEM_SIZE, fdv_audio_rb_mem)
typedef struct {
int32_t start;
int32_t offset;
int32_t count;
} flex_buffer;
static uint16_t freedv_display_x_offset;
/**
* Returns the internal UHSDR configuration value in the value range for display and use with the freedv_api
* @param freedv_conf_p
* @return SNR in the range of -100 to 99
*/
int32_t FreeDV_Get_Squelch_SNR(freedv_conf_t* freedv_conf_p)
{
return (int32_t)(freedv_conf_p->squelch_snr_thresh) + FDV_SQUELCH_OFFSET;
}
/**
* Sets the internal UHSDR configuration value in the permitted value range
* @param freedv_conf_p
* @param squelch_snr SNR in the range of -100 to 99 , -100 represents OFF
*/
void FreeDV_Set_Squelch_SNR(freedv_conf_t* freedv_conf_p, int8_t squelch_snr)
{
int32_t internal_snr = squelch_snr - FDV_SQUELCH_OFFSET;
if (internal_snr > FDV_SQUELCH_MAX)
{
internal_snr = FDV_SQUELCH_MAX;
}
else if (internal_snr < FDV_SQUELCH_OFF)
{
internal_snr = FDV_SQUELCH_OFF;
}
freedv_conf_p->squelch_snr_thresh = internal_snr;
}
int FreeDV_Is_Squelch_Enable(freedv_conf_t* freedv_conf_p)
{
return freedv_conf_p->squelch_snr_thresh != FDV_SQUELCH_OFF;
}
void FreeDV_Squelch_Update(freedv_conf_t* freedv_conf_p)
{
UNUSED(freedv_conf_p);
freedv_set_squelch_en(f_FREEDV, FreeDV_Is_Squelch_Enable(&freedv_conf));
freedv_set_snr_squelch_thresh(f_FREEDV, FreeDV_Get_Squelch_SNR(&freedv_conf));
}
static void FreeDv_DisplayBer(void)
{
int ber = 0;
char ber_string[12];
ber = 1000*freedv_get_total_bit_errors(f_FREEDV)/freedv_get_total_bits(f_FREEDV);
snprintf(ber_string,12,"0.%03d",ber); //calculate and display the bit error rate
UiLcdHy28_PrintText(ts.Layout->FREEDV_BER.x + freedv_display_x_offset,ts.Layout->FREEDV_BER.y, ber_string,Yellow,Black, ts.Layout->FREEDV_FONT);
}
static void FreeDv_DisplaySnr(void)
{
static float SNR = 1;
float SNR_est;
char SNR_string[12];
int sync;
freedv_get_modem_stats(f_FREEDV,&sync,&SNR_est);
SNR = 0.95*SNR + 0.05 * SNR_est; //some averaging to keep it more calm
int32_t SNR_Int = roundf(SNR);
if (SNR_Int > 99)
{
SNR_Int = 99;
}
else if (SNR_Int < -99)
{
SNR_Int = -99;
}
uint32_t clr_fg;
if (FreeDV_Is_Squelch_Enable(&freedv_conf))
{
clr_fg = SNR_Int >= FreeDV_Get_Squelch_SNR(&freedv_conf)? Green:Red;
}
else
{
clr_fg = Yellow;
}
snprintf(SNR_string,12,"%-2ld",SNR_Int); //Display the current SNR and round it up to the next int
UiLcdHy28_PrintText(ts.Layout->FREEDV_SNR.x + freedv_display_x_offset, ts.Layout->FREEDV_SNR.y ,SNR_string,clr_fg,Black, ts.Layout->FREEDV_FONT);
}
void FreeDv_DisplayClear()
{
// UiLcdHy28_PrintText(ts.Layout->FREEDV_SNR.x, ts.Layout->FREEDV_SNR.y," ",Yellow,Black,ts.Layout->FREEDV_FONT);
// UiLcdHy28_PrintText(ts.Layout->FREEDV_BER.x, ts.Layout->FREEDV_BER.y," ",Yellow,Black,ts.Layout->FREEDV_FONT);
UiLcdHy28_PrintText(ts.Layout->FREEDV_SNR.x, ts.Layout->FREEDV_SNR.y," ",Yellow,Black,ts.Layout->FREEDV_FONT); //SNR=00
UiLcdHy28_PrintText(ts.Layout->FREEDV_BER.x, ts.Layout->FREEDV_BER.y," ",Yellow,Black,ts.Layout->FREEDV_FONT); //BER=0.000 (max 9 chars)
UiDriver_TextMsgClear();
}
void FreeDv_DisplayPrepare()
{
UiDriver_TextMsgClear();
freedv_display_x_offset = UiLcdHy28_TextWidth("SNR=", ts.Layout->FREEDV_FONT);
UiLcdHy28_PrintText(ts.Layout->FREEDV_SNR.x, ts.Layout->FREEDV_SNR.y,"SNR=",Yellow,Black, ts.Layout->FREEDV_FONT);
UiLcdHy28_PrintText(ts.Layout->FREEDV_BER.x, ts.Layout->FREEDV_BER.y,"BER=",Yellow,Black, ts.Layout->FREEDV_FONT);
}
void FreeDv_DisplayUpdate()
{
FreeDv_DisplayBer();
FreeDv_DisplaySnr();
UiDriver_TextMsgDisplay();
}
void FreeDv_HandleFreeDv()
{
// Freedv DL2FW
// static FDV_Out_Buffer FDV_TX_out_im_buff;
static bool tx_was_here = false;
static bool rx_was_here = true;
// we are always called, so check if there is FreeDV active
if (ts.digital_mode == DigitalMode_FreeDV)
{
if ((ts.txrx_mode == TRX_MODE_TX)
&& RingBuffer_GetData(&fdv_audio_rb) >= freedv_get_n_speech_samples(f_FREEDV)
&& RingBuffer_GetRoom(&fdv_iq_rb) >= freedv_get_n_nom_modem_samples(f_FREEDV))
{
// ...and if we are transmitting and samples from dv_tx_processor are ready
if (tx_was_here == false)
{
RingBuffer_ClearGetTail(&fdv_audio_rb);
RingBuffer_ClearPutHead(&fdv_iq_rb);
tx_was_here = true;
rx_was_here = false;
}
COMP iq_buffer[freedv_get_n_nom_modem_samples(f_FREEDV)];
int16_t audio_buffer[freedv_get_n_speech_samples(f_FREEDV)];
RingBuffer_GetSamples(&fdv_audio_rb, &audio_buffer, freedv_get_n_speech_samples(f_FREEDV));
profileTimedEventStart(7);
freedv_comptx(f_FREEDV,
iq_buffer,
audio_buffer); // start the encoding process
profileTimedEventStop(7);
for (int idx = 0; idx < freedv_get_n_nom_modem_samples(f_FREEDV); idx++)
{
fdv_iq_rb_item_t sample;
sample.real = iq_buffer[idx].real;
sample.imag = iq_buffer[idx].imag;
RingBuffer_PutSamples(&fdv_iq_rb, &sample, 1);
}
}
else if ((ts.txrx_mode == TRX_MODE_RX))
{
if (rx_was_here == false)
{
RingBuffer_ClearGetTail(&fdv_demod_rb);
RingBuffer_ClearPutHead(&fdv_audio_rb);
freedv_set_total_bit_errors(f_FREEDV,0); //reset ber calculation after coming from TX
freedv_set_total_bits(f_FREEDV,0);
rx_was_here = true; // this is used to clear buffers when going into TX
tx_was_here = false;
}
// these buffers are large enough to hold the requested/provided amount of data for freedv_comprx
// these are larger than the FDV_BUFFER_SIZE since some more bytes may be asked for.
#ifdef USE_SIMPLE_FREEDV_FILTERS
#define input_rb fdv_iq_rb
#define input_rb_item_t fdv_iq_rb_item_t
#define FREEDV_RX_FUNC freedv_comprx
#define FREEDV_RX_INPUT_T COMP
#else
#define input_rb fdv_demod_rb
#define input_rb_item_t fdv_demod_rb_item_t
#define FREEDV_RX_FUNC freedv_rx
#define FREEDV_RX_INPUT_T int16_t
#endif
FREEDV_RX_INPUT_T input_buffer[freedv_get_n_max_modem_samples(f_FREEDV)];
int16_t audio_buffer[freedv_get_n_max_modem_samples(f_FREEDV)];
// while makes this highest prio
// if may give more responsiveness but can cause interrupted reception
while (RingBuffer_GetData(&input_rb) >= freedv_nin(f_FREEDV)
&& RingBuffer_GetRoom(&fdv_audio_rb) >= freedv_nin(f_FREEDV))
{
// MchfBoard_GreenLed(LED_STATE_OFF);
// if we arrive here the rx_buffer is full enough and will be consumed now.
#ifdef USE_SIMPLE_FREEDV_FILTERS
for (int idx = 0; idx < freedv_nin(f_FREEDV); idx++ )
{
input_rb_item_t sample;
RingBuffer_GetSamples(&input_rb, &sample, 1);
input_buffer[idx].real = sample.real;
input_buffer[idx].imag = sample.imag;
}
#else
RingBuffer_GetSamples(&input_rb, &input_buffer, freedv_nin(f_FREEDV));
#endif
int count = FREEDV_RX_FUNC(f_FREEDV, audio_buffer, input_buffer); // run the decoding process
if (freedv_get_sync(f_FREEDV) != 0)
{
RingBuffer_PutSamples(&fdv_audio_rb, &audio_buffer, count);
}
}
}
}
else
{
if (tx_was_here == true || rx_was_here == true)
{
tx_was_here = false;
rx_was_here = false;
RingBuffer_ClearGetTail(&fdv_iq_rb);
RingBuffer_ClearGetTail(&fdv_audio_rb);
}
}
}
// FreeDV txt test - will be out of here
typedef struct {
char tx_str[80];
char *ptx_str;
} my_callback_state_t;
static my_callback_state_t my_cb_state;
static char my_get_next_tx_char(void *callback_state) {
my_callback_state_t* pstate = (my_callback_state_t*)callback_state;
char c = *pstate->ptx_str++;
if (*pstate->ptx_str == 0) {
pstate->ptx_str = pstate->tx_str;
}
return c;
}
static void my_put_next_rx_char(void *callback_state, char ch)
{
UNUSED(callback_state);
UiDriver_TextMsgPutChar(ch);
}
freedv_mode_desc_t freedv_modes[] =
{
{ "1600", "FD1600", FREEDV_MODE_1600 },
#ifdef USE_FREEDV_700D
{ "700D", "FD700D", FREEDV_MODE_700D },
#endif
};
const uint8_t freedv_modes_num = sizeof(freedv_modes)/sizeof(freedv_modes[0]);
// FreeDV txt test - will be out of here
void FreeDV_Init()
{
// Freedv Test DL2FW
// move this to ui_configuration;
FreeDV_Set_Squelch_SNR(&freedv_conf,-2);
freedv_conf.mode = 0; // 0 = 1600, 1 = 700D
FreeDV_SetMode(freedv_conf.mode, true);
}
/**
*
* @return true if mode was activated, false if old mode is still active
*/
int32_t FreeDV_SetMode(uint8_t fdv_mode, int32_t firstTime)
{
bool retval = true;
// turn off rx audio processing to be able to disable and reenable FreeDV processing
ads.af_disabled++;
// normal init starts
if (fdv_mode >= freedv_modes_num)
{
fdv_mode = 0;
}
// we don't switch if TX is currently active
if (firstTime == false)
{
retval = (ts.txrx_mode == TRX_MODE_RX);
if (retval == true && f_FREEDV != NULL && freedv_conf.mode != fdv_mode)
{
freedv_close(f_FREEDV);
f_FREEDV = NULL;
}
}
// only if all went well AND we have not yet initialized the mode,
// we are going to run the mode, so if same mode is being set, nothing happens
// really
if (retval && f_FREEDV == NULL)
{
f_FREEDV = freedv_open(freedv_modes[fdv_mode].freedv_id);
retval = f_FREEDV != NULL;
if (retval)
{
sprintf(my_cb_state.tx_str, ts.special_functions_enabled == 1 ? FREEDV_TX_DF8OE_MESSAGE : FREEDV_TX_MESSAGE);
my_cb_state.ptx_str = my_cb_state.tx_str;
freedv_set_callback_txt(f_FREEDV, &my_put_next_rx_char, &my_get_next_tx_char, &my_cb_state);
if (FreeDV_Is_Squelch_Enable(&freedv_conf))
{
freedv_set_squelch_en(f_FREEDV,1);
freedv_set_snr_squelch_thresh(f_FREEDV, FreeDV_Get_Squelch_SNR(&freedv_conf));
}
else
{
freedv_set_squelch_en(f_FREEDV,0);
}
freedv_set_tx_bpf(f_FREEDV, 0);
}
}
if (retval)
{
freedv_conf.mode = fdv_mode;
}
// turn on rx audio processing, FreeDV is ready for work
ads.af_disabled--;
return retval;
}
int32_t FreeDV_Iq_Get_FrameLen()
{
return freedv_get_n_nom_modem_samples(f_FREEDV);
}
int32_t FreeDV_Audio_Get_FrameLen()
{
return freedv_get_n_speech_samples(f_FREEDV);
}
#ifdef DEBUG_FREEDV
void FreeDV_Test()
{
ts.digital_mode = DigitalMode_FreeDV;
ts.txrx_mode = TRX_MODE_RX;
while (1)
{
for (int idx = 0; idx < FREEDV_TEST_BUFFER_FRAME_SIZE*FREEDV_TEST_BUFFER_FRAME_COUNT; idx++)
{
int16_t sample[2];
sample[0] = test_buffer[idx].real;
sample[1] = test_buffer[idx].imag;
RingBuffer_PutSamples(&fdv_iq_rb,&sample,2);
FreeDv_HandleFreeDv();
RingBuffer_GetSamples(&fdv_audio_rb,sample,1);
}
}
}
#endif
#endif
@@ -0,0 +1,134 @@
#ifndef __freedv_mchf__
#define __freedv_mchf__
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "uhsdr_types.h"
#include "uhsdr_board.h"
#include "uhsdr_board_config.h"
#include "comp.h"
#if defined(USE_FREEDV_700D)
#define FDV_MAX_IQ_FRAME_LEN_MS 160
#define FREEDV_MODE_UHSDR FREEDV_MODE_700D
#elif defined(USE_FREEDV_1600)
#define FDV_MAX_IQ_FRAME_LEN_MS 40
#define FREEDV_MODE_UHSDR FREEDV_MODE_1600
#endif
// one need to set this to the highest frame length to be intended to use
// FreeDV 700D requires 1280 samples for the 160ms frame, the audio size may be larger depending
// on the algorithm
// TODO: Explicitly list the variables in FreeDV to look at.
#include "rb.h"
typedef struct
{
int16_t real;
int16_t imag;
} COMP_int16_t;
RingBuffer_Declare(fdv_iq_rb, COMP_int16_t)
RingBuffer_Declare(fdv_audio_rb, int16_t)
RingBuffer_Declare(fdv_demod_rb, int16_t)
#define FDV_BUFFER_SIZE (FDV_MAX_IQ_FRAME_LEN_MS*8) // (160ms*8samples per ms)
#define NR_BUFFER_NUM 4
#define NR_BUFFER_SIZE 256 // 4*256*8 -> 8192
typedef struct {
COMP samples[NR_BUFFER_SIZE];
} NR_Buffer;
typedef union
{
fdv_demod_rb_item_t fdv_demod_buff[(FDV_BUFFER_SIZE * 3) + IQ_BLOCK_SIZE];
fdv_iq_rb_item_t fdv_iq_buff[(FDV_BUFFER_SIZE * 2) + IQ_BLOCK_SIZE];
NR_Buffer nr_audio_buff[NR_BUFFER_NUM];
} MultiModeBuffer_t;
#ifdef USE_FREEDV
#ifdef DEBUG_FREEDV
#define FREEDV_TEST_BUFFER_FRAME_COUNT 50
#define FREEDV_TEST_BUFFER_FRAME_SIZE 320
extern const COMP test_buffer[FREEDV_TEST_BUFFER_FRAME_SIZE*FREEDV_TEST_BUFFER_FRAME_COUNT];
#endif
#define FREEDV_TX_MESSAGE " CQ CQ CQ UHSDR " TRX_NAME " SDR with integrated FreeDV codec calling!"
#define FREEDV_TX_DF8OE_MESSAGE " DF8OE JO42jr using UHSDR " TRX_NAME " SDR with integrated FreeDV codec"
typedef struct {
char* name;
char* label;
uint8_t freedv_id;
} freedv_mode_desc_t;
extern freedv_mode_desc_t freedv_modes[];
extern const uint8_t freedv_modes_num;
typedef struct {
#define FDV_SQUELCH_OFF 0
#define FDV_SQUELCH_OFFSET (-100)
#define FDV_SQUELCH_MIN (-99-FDV_SQUELCH_OFFSET) // equals -99 SNR
#define FDV_SQUELCH_MAX (99-FDV_SQUELCH_OFFSET) // equals 99 SNR
#define FDV_SQUELCH_DEFAULT (-2-FDV_SQUELCH_OFFSET) // equals -2 SNR
uint8_t squelch_snr_thresh;
uint8_t mode;
bool mute_if_squelched;
} freedv_conf_t;
extern freedv_conf_t freedv_conf;
void FreeDv_HandleFreeDv(void);
void FreeDV_Init(void);
int32_t FreeDV_SetMode(uint8_t fdv_mode, int32_t firstTime);
void FreeDV_Test(void);
int32_t FreeDV_Iq_Get_FrameLen(void);
int32_t FreeDV_Audio_Get_FrameLen(void);
void FreeDv_DisplayClear(void);
void FreeDv_DisplayPrepare(void);
void FreeDv_DisplayUpdate(void);
int32_t FreeDV_Get_Squelch_SNR(freedv_conf_t* freedv_conf_p);
void FreeDV_Set_Squelch_SNR(freedv_conf_t* freedv_conf_p, int8_t squelch_snr);
int FreeDV_Is_Squelch_Enable(freedv_conf_t* freedv_conf_p);
void FreeDV_Squelch_Update(freedv_conf_t* freedv_conf_p);
#endif
#if defined(USE_FREEDV) || defined(USE_ALTERNATE_NR)
extern MultiModeBuffer_t mmb;
// we allow for one more pointer to a buffer as we have buffers
// why? because our implementation will only fill up the fifo only to N-1 elements
#define FDV_BUFFER_IQ_FIFO_SIZE (FDV_BUFFER_IQ_NUM+1)
#define NR_BUFFER_FIFO_SIZE (NR_BUFFER_NUM+1)
#endif
#endif
+334
View File
@@ -0,0 +1,334 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR FIRMWARE **
** **
**---------------------------------------------------------------------------------**
** Licence: GNU GPLv3, see LICENSE.md **
************************************************************************************/
#include <assert.h>
#include "freq_shift.h"
#include "audio_driver.h" // only for IQ_BLOCK_SIZE and IQ_SAMPLE_RATE_F
#include <math.h>
#include <stdlib.h>
// The following refer to the software frequency conversion/translation done in receive and transmit to shift the signals away from the
// "DC" IF
typedef struct {
float32_t Osc_Cos;
float32_t Osc_Sin;
float32_t Osc_Vect_Q;
float32_t Osc_Vect_I;
} fs_nco_approx_t;
/**
* Prepare the configuration for use with specified shift frequency.
* @param confptr stores all configuration and operational data of the shift algorithm.
* @param nco_freq shift frequency in Hz (please note that shift direction, i.e. the positive/negative sign is not taken into account here!)
* @param sample_rate sample rate of the incoming data in Hz
* @param blockSize size of data to be processed
*/
static void FreqShift_Approx_Prepare(fs_nco_approx_t* confptr, float32_t nco_freq, float32_t sample_rate)
{
// Configure NCO for the frequency translate function
/*
* using double here turned out in experiments to yield better precision of the results
* and since we use it only when changing the frequency, there is not much harm in
*/
double rate = (2 * M_PI * nco_freq) / sample_rate;
confptr->Osc_Cos = cos(rate);
confptr->Osc_Sin = sin(rate);
confptr->Osc_Vect_I = 0;
confptr->Osc_Vect_Q = 1;
}
/**
* Frequency shift using a low overhead algorithm using (simple) complex arithmetic and gain control approximation algorithm.
* @param confptr stores all configuration and operational data of the shift algorithm. It must have been configured using FreqShift_Approx_Prepare before!
* @param I_buffer incoming i data
* @param Q_buffer incoming q data
* @param blockSize size of data to be processed
* @param dir dir == SHIFT_UP moves receive frequency to the right in the spectrum, SHIFT_DOWN opposite direction
*/
static void FreqShift_Approx(fs_nco_approx_t* confptr, float32_t* I_buffer, float32_t* Q_buffer, const size_t blockSize, freq_shift_dir_t dir)
{
// KA7OEI: Below is the "on-the-fly" version of the frequency translator, generating a "live" version of the oscillator (NCO), which can be any
// frequency
// The values for the NCO are configured in the function "FreqShift_Approx_Prepare".
/*
* URLs for algorithm information:
* https://dsp.stackexchange.com/questions/124/how-to-implement-a-digital-oscillator (hint to recalc gain only from time to time)
* https://dspguru.com/dsp/howtos/how-to-create-oscillators-in-software/ (see complex oscillator)
*/
/*
* *
* Shift Down (shift == 1) => i = Q, q = I
* [moves receive frequency to the right in the spectrum display]
*
* Shift Up (shift == 0) => q = I, q = Q
* [moves receive frequency to the right in the spectrum display]
*
*/
float32_t* i_buffer = dir == FREQ_SHIFT_UP? I_buffer : Q_buffer;
float32_t* q_buffer = dir == FREQ_SHIFT_UP? Q_buffer : I_buffer;
for(int i = 0; i < blockSize; i++) {
// generate local oscillator on-the-fly: This takes a lot of processor time!
float32_t Osc_Q = (confptr->Osc_Vect_Q * confptr->Osc_Cos) - (confptr->Osc_Vect_I * confptr->Osc_Sin); // Q channel of oscillator
float32_t Osc_I = (confptr->Osc_Vect_I * confptr->Osc_Cos) + (confptr->Osc_Vect_Q * confptr->Osc_Sin); // I channel of oscillator
// do actual frequency conversion
float32_t q_temp = q_buffer[i];
float32_t i_temp = i_buffer[i]; // save temporary copies of data
q_buffer[i] = (q_temp * Osc_Q) - (i_temp * Osc_I);
i_buffer[i] = (i_temp * Osc_Q) + (q_temp * Osc_I); // multiply I/Q data by sine/cosine data to do translation
confptr->Osc_Vect_Q = Osc_Q;
confptr->Osc_Vect_I = Osc_I;
}
float32_t Osc_Gain = (3 - ((confptr->Osc_Vect_Q * confptr->Osc_Vect_Q) + (confptr->Osc_Vect_I * confptr->Osc_Vect_I)))/2; // Amplitude control of oscillator
// rotate vectors while maintaining constant oscillator amplitude
confptr->Osc_Vect_Q = Osc_Gain * confptr->Osc_Vect_Q;
confptr->Osc_Vect_I = Osc_Gain * confptr->Osc_Vect_I;
}
/*
* The oscillator implementation below is straightforward and based on calculating sin and cos using the normal library function.
* And then simply do the appropriate complex math operations using the arm lib.
* It supports 2 modes: one is using continuous recalculation of the sin/cos coeffs which is suitable for all shift frequencies
* It also supports a special mode for rates which are a power of 2, where the coeffs are only calculated once, since the coeffs
* repeat after blocksize length anyway.
*
* Downside is the memory usage which depends on the max blockSize (we need 6 x IQ_BLOCK_SIZE x sizeof(float32_t) as buffers)
* and for continuous mode the high load due to permanent sin/cos calculations.
*/
typedef struct
{
float32_t Osc_Sin_buffer[IQ_BLOCK_SIZE];
float32_t Osc_Cos_buffer[IQ_BLOCK_SIZE];
// we need to use double here for continuous mode
// otherwise the summation error grows to quickly
// no harm for single calc mode, since we run it only once
double rad_calc;
double rad_increment;
bool single_osc_calc;
} fs_nco_sincos_t;
/**
* Calculate the sin/cos values for the shift calculation.
* @param confptr stores all configuration and operational data of the shift algorithm. It must have been configured using FreqShift_SinCos_Prepare before!
* @param blockSize size of data to be processed
*/
static void FreqShift_SinCos_Coeffs(fs_nco_sincos_t* confptr, size_t blockSize)
{
for(int i = 0; i < blockSize; i++) // Now, let's do it!
{
confptr->rad_calc += confptr->rad_increment;
if (confptr->rad_calc > 2*M_PI)
{
confptr->rad_calc -= 2*M_PI;
}
sincosf(confptr->rad_calc, &confptr->Osc_Sin_buffer[i], &confptr->Osc_Cos_buffer[i]);
}
}
/**
* Prepare the configuration for use with specified shift frequency.
* @param confptr stores all configuration and operational data of the shift algorithm.
* @param nco_freq shift frequency in Hz (please note that shift direction, i.e. the positive/negative sign is not taken into account here!)
* @param sample_rate sample rate of the incoming data in Hz
* @param single_osc_calc true == use precalculated values, requires frequency to be integer division of samplerate, false == arbitrary shift frequency
* @param blockSize size of data to be processed
*/
static void FreqShift_SinCos_Prepare(fs_nco_sincos_t* confptr, float32_t nco_freq, float32_t sample_rate, bool single_osc_calc, size_t blockSize)
{
double rate = (2 * M_PI * nco_freq) / sample_rate;
confptr->rad_increment = rate;
confptr->single_osc_calc = single_osc_calc;
if (single_osc_calc)
{
confptr->rad_calc = 0;
FreqShift_SinCos_Coeffs(confptr, blockSize);
}
}
/**
* Frequency shift using a trigonometric math. Single coeff calculation operation gives good performance, continuous mode not so much.
* @param confptr stores all configuration and operational data of the shift algorithm. It must have been configured using FreqShift_SinCos_Prepare before!
* @param I_buffer incoming i data
* @param Q_buffer incoming q data
* @param blockSize size of data to be processed
* @param dir dir == SHIFT_UP moves receive frequency to the right in the spectrum, SHIFT_DOWN opposite direction
*/
static void FreqShift_SinCos(fs_nco_sincos_t* confptr, float32_t* I_buffer, float32_t* Q_buffer, size_t blockSize, uint16_t dir)
{
/*
* cos(i) + sin(q) for i channel
* cos(q) - sin(i) for q channel
*
* Shift Down (shift == 1) => i = Q, q = I
* [moves receive frequency to the right in the spectrum display]
*
* Shift Up (shift == 0) => q = I, q = Q
* [moves receive frequency to the right in the spectrum display]
*
*/
float32_t* i_buffer = dir == FREQ_SHIFT_UP? I_buffer : Q_buffer;
float32_t* q_buffer = dir == FREQ_SHIFT_UP? Q_buffer : I_buffer;
float32_t q_cos_buffer[blockSize];
float32_t i_sin_buffer[blockSize];
float32_t q_sin_buffer[blockSize];
float32_t i_cos_buffer[blockSize];
if (confptr->single_osc_calc == false)
{
FreqShift_SinCos_Coeffs(confptr, blockSize);
}
// Do frequency conversion using optimized ARM math functions [KA7OEI]
arm_mult_f32(q_buffer, confptr->Osc_Cos_buffer, q_cos_buffer, blockSize); // multiply products for converted I channel
arm_mult_f32(i_buffer, confptr->Osc_Sin_buffer, i_sin_buffer, blockSize);
arm_mult_f32(q_buffer, confptr->Osc_Sin_buffer, q_sin_buffer, blockSize);
arm_mult_f32(i_buffer, confptr->Osc_Cos_buffer, i_cos_buffer, blockSize); // multiply products for converted Q channel
arm_add_f32(i_cos_buffer, q_sin_buffer, i_buffer, blockSize); // cos(I) + sin(Q) for I channel
arm_sub_f32(q_cos_buffer, i_sin_buffer, q_buffer, blockSize); // cos(Q) - sin(I) for Q channel
}
/**
* Frequency shift using a just complex add and sub. Shifts by a quarter of the sample frequency. Best performance, single shift frequency.
* @param confptr stores all configuration and operational data of the shift algorithm. It must have been configured using FreqShift_SinCos_Prepare before!
* @param I_buffer incoming i data
* @param Q_buffer incoming q data
* @param blockSize size of data to be processed
* @param dir dir == FREQ_SHIFT_UP moves receive frequency to the right in the spectrum, FREQ_SHIFT_DOWN opposite direction
*/
static void FreqShift_QuarterFs(float32_t* I_buffer, float32_t* Q_buffer, int16_t blockSize, int16_t dir)
{
/**********************************************************************************
* Frequency translation by Fs/4 without multiplication
* Lyons (2011): chapter 13.1.2 page 646
* this is supposed to be much more efficient than a standard quadrature oscillator
* with precalculated sin waves
* Thanks, Clint, for pointing my interest to this method!, DD4WH 2016_12_28
**********************************************************************************/
/*
*
* Shift Down (shift == 1) => i = Q, q = I *
* [moves receive frequency to the right in the spectrum display]
*
* Shift Up (shift == 0) => q = I, q = Q
* [moves receive frequency to the right in the spectrum display]
*
*/
float32_t* i_buffer = dir == FREQ_SHIFT_UP? I_buffer : Q_buffer;
float32_t* q_buffer = dir == FREQ_SHIFT_UP? Q_buffer : I_buffer;
for(int i = 0; i < blockSize; i += 4)
{ // xnew(0) = xreal(0) + jximag(0)
// leave as it is!
// xnew(1) = ximag(1) - jxreal(1)
float32_t hh1 = q_buffer[i + 1];
float32_t hh2 = - i_buffer[i + 1];
i_buffer[i + 1] = hh1;
q_buffer[i + 1] = hh2;
// xnew(2) = -xreal(2) - jximag(2)
hh1 = - i_buffer[i + 2];
hh2 = - q_buffer[i + 2];
i_buffer[i + 2] = hh1;
q_buffer[i + 2] = hh2;
// xnew(3) = -ximag(3) + jxreal(3)
hh1 = - q_buffer[i + 3];
hh2 = i_buffer[i + 3];
i_buffer[i + 3] = hh1;
q_buffer[i + 3] = hh2;
}
}
/**
* Frequency shift using the best available algorithm. Handles changes to the shift frequency on the fly. This is
* the front end for all the shift algorithms. Sample rate is fixed to IQ_SAMPLE_RATE, max buffer is IQ_BLOCK_SIZE
* It cannot be called twice at the same time (i.e. must not be used in code running in different interrupt levels
* which may interrupt each other or mixed in normal code and interrupt code)
*
* @param I_buffer incoming i data
* @param Q_buffer incoming q data
* @param blockSize size of data to be processed
* @param shift > 0 SHIFT_UP moves receive frequency to the right in the spectrum, SHIFT_DOWN opposite direction
*/
void FreqShift(float32_t* i_buffer, float32_t* q_buffer, size_t blockSize, int32_t shift)
{
// keeps the generated data for frequency conversion
static int32_t conversion_freq = 0;
static bool single_osc_calc = false;
static bool is_quarter_of_fs = false;
static fs_nco_sincos_t nco_sincos;
static fs_nco_approx_t nco_approx;
assert(blockSize <= IQ_BLOCK_SIZE);
int32_t new_conversion_freq = abs(shift);
if (conversion_freq != new_conversion_freq || single_osc_calc == false)
{
if (conversion_freq != new_conversion_freq)
{
float32_t rate = new_conversion_freq / IQ_SAMPLE_RATE_F;
single_osc_calc = (rate == 0.25 || rate == 0.125 || rate == 0.0625 || rate == 0.03125);
// if the sample frequency divider to get shift frequency is a power of two no larger
// than the number of samples in the osc buffer
// we can avoid continuous recalculation of sin/cos table because it repeats after blockSize entries
is_quarter_of_fs = rate == 0.25;
// if we have shift equals a quarter of sample frequency, we can use a highly optimized formula, see below
conversion_freq = new_conversion_freq;
FreqShift_Approx_Prepare(&nco_approx, new_conversion_freq, IQ_SAMPLE_RATE_F);
// this algorithm works always, but takes more time
single_osc_calc = false;
if (single_osc_calc == true)
{
// Pre-calculate quadrature sine wave(s) ONCE for the conversion
FreqShift_SinCos_Prepare(&nco_sincos, new_conversion_freq, IQ_SAMPLE_RATE_F, single_osc_calc, blockSize);
}
}
}
bool dir = shift > 0;
if (is_quarter_of_fs == true)
{
FreqShift_QuarterFs(i_buffer, q_buffer, blockSize, dir);
}
else if (single_osc_calc == true) // 'not so optimized' frequency translation using sin/cos (+6kHz or -6kHz)
{
// Below is the call to frequency translation code that uses a "pre-calculated" sine wave - which means that the translation must be done at a sub-
// multiple of the sample frequency. This pre-calculation eliminates the processor overhead required to generate a sine wave on the fly.
// This also makes extensive use of the optimized ARM vector instructions for the calculation of the final I/Q vectors
FreqShift_SinCos(&nco_sincos, i_buffer, q_buffer, blockSize, dir);
}
else
{
FreqShift_Approx(&nco_approx, i_buffer, q_buffer, blockSize, dir);
}
}
+24
View File
@@ -0,0 +1,24 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR FIRMWARE **
** **
**---------------------------------------------------------------------------------**
** Licence: GNU GPLv3, see LICENSE.md **
************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FREQ_SHIFT_H
#define __FREQ_SHIF_H
#include "uhsdr_types.h"
typedef enum
{
FREQ_SHIFT_UP = 0,
FREQ_SHIFT_DOWN = 1,
} freq_shift_dir_t;
void FreqShift(float32_t* i_buffer, float32_t* q_buffer, size_t blockSize, int32_t shift);
#endif
+885
View File
@@ -0,0 +1,885 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR FIRMWARE **
** **
**---------------------------------------------------------------------------------**
** Licence: GNU GPLv3, see LICENSE.md **
************************************************************************************/
// Common
#include "psk.h"
#include <math.h>
#include <stdlib.h>
#include "softdds.h"
#include "uhsdr_digi_buffer.h"
#include "ui_driver.h" // only necessary because of UiDriver_TextMsgPutChar
#include "radio_management.h" // only necessary because of RadioManagement_Request_TxOff
// RX,TX common constants
#define PSK_SAMPLE_RATE 12000 // TODO This should come from elsewhere, to be fixed
// RX constants
#define PSK_BND_FLT_LEN 5
#define PSK_BUF_LEN (PSK_SAMPLE_RATE / PSK_OFFSET)
// this must be an integer result without remainder and must be a multiple of 4
#define PSK_SHIFT_DIFF (1.0 * PSK_OFFSET / PSK_SAMPLE_RATE) // phase change between two samples of PSK_OFFSET Hz
// TX constants
#define SAMPLE_MAX 32766 // max amplitude of generated samples
typedef struct
{
uint16_t rate;
uint16_t tx_idx;
uint8_t tx_char;
uint16_t tx_bits;
int16_t tx_wave_sign_next;
int16_t tx_wave_sign_current;
uint16_t tx_bit_phase;
uint32_t tx_bit_len;
int16_t tx_zeros;
int16_t tx_ones;
bool tx_win;
float32_t rx_phase;
float32_t rx_samples_in[PSK_BND_FLT_LEN];
float32_t rx_samples[PSK_BND_FLT_LEN];
int16_t rx_bnd_idx;
float32_t rx_cos_prod[PSK_BUF_LEN];
float32_t rx_sin_prod[PSK_BUF_LEN];
float32_t rx_scmix[PSK_BUF_LEN];
int32_t rx_idx;
int8_t rx_last_bit;
float32_t rx_err; // could be of interest for tuning
float32_t rx_last_symbol;
int16_t rx_symbol_len; // how many buffers fit into one bit
int16_t rx_symbol_idx;
// float32_t rx_symbol_buf[PSK_MAX_SYMBOL_BUF];
uint32_t rx_word;
psk_modulator_t tx_mod_state;
float32_t rx_sum_sin;
float32_t rx_sum_cos;
} PskState_Internal_t;
// table courtesy of fldigi pskvaricode.cxx
static const uint16_t psk_varicode[] = {
0b1010101011, /* 0 - <NUL> */
0b1011011011, /* 1 - <SOH> */
0b1011101101, /* 2 - <STX> */
0b1101110111, /* 3 - <ETX> */
0b1011101011, /* 4 - <EOT> */
0b1101011111, /* 5 - <ENQ> */
0b1011101111, /* 6 - <ACK> */
0b1011111101, /* 7 - <BEL> */
0b1011111111, /* 8 - <BS> */
0b11101111, /* 9 - <TAB> */
0b11101, /* 10 - <LF> */
0b1101101111, /* 11 - <VT> */
0b1011011101, /* 12 - <FF> */
0b11111, /* 13 - <CR> */
0b1101110101, /* 14 - <SO> */
0b1110101011, /* 15 - <SI> */
0b1011110111, /* 16 - <DLE> */
0b1011110101, /* 17 - <DC1> */
0b1110101101, /* 18 - <DC2> */
0b1110101111, /* 19 - <DC3> */
0b1101011011, /* 20 - <DC4> */
0b1101101011, /* 21 - <NAK> */
0b1101101101, /* 22 - <SYN> */
0b1101010111, /* 23 - <ETB> */
0b1101111011, /* 24 - <CAN> */
0b1101111101, /* 25 - <EM> */
0b1110110111, /* 26 - <SUB> */
0b1101010101, /* 27 - <ESC> */
0b1101011101, /* 28 - <FS> */
0b1110111011, /* 29 - <GS> */
0b1011111011, /* 30 - <RS> */
0b1101111111, /* 31 - <US> */
0b1, /* 32 - <SPC> */
0b111111111, /* 33 - ! */
0b101011111, /* 34 - '"' */
0b111110101, /* 35 - # */
0b111011011, /* 36 - $ */
0b1011010101, /* 37 - % */
0b1010111011, /* 38 - & */
0b101111111, /* 39 - ' */
0b11111011, /* 40 - ( */
0b11110111, /* 41 - ) */
0b101101111, /* 42 - * */
0b111011111, /* 43 - + */
0b1110101, /* 44 - , */
0b110101, /* 45 - - */
0b1010111, /* 46 - . */
0b110101111, /* 47 - / */
0b10110111, /* 48 - 0 */
0b10111101, /* 49 - 1 */
0b11101101, /* 50 - 2 */
0b11111111, /* 51 - 3 */
0b101110111, /* 52 - 4 */
0b101011011, /* 53 - 5 */
0b101101011, /* 54 - 6 */
0b110101101, /* 55 - 7 */
0b110101011, /* 56 - 8 */
0b110110111, /* 57 - 9 */
0b11110101, /* 58 - : */
0b110111101, /* 59 - ; */
0b111101101, /* 60 - < */
0b1010101, /* 61 - = */
0b111010111, /* 62 - > */
0b1010101111, /* 63 - ? */
0b1010111101, /* 64 - @ */
0b1111101, /* 65 - A */
0b11101011, /* 66 - B */
0b10101101, /* 67 - C */
0b10110101, /* 68 - D */
0b1110111, /* 69 - E */
0b11011011, /* 70 - F */
0b11111101, /* 71 - G */
0b101010101, /* 72 - H */
0b1111111, /* 73 - I */
0b111111101, /* 74 - J */
0b101111101, /* 75 - K */
0b11010111, /* 76 - L */
0b10111011, /* 77 - M */
0b11011101, /* 78 - N */
0b10101011, /* 79 - O */
0b11010101, /* 80 - P */
0b111011101, /* 81 - Q */
0b10101111, /* 82 - R */
0b1101111, /* 83 - S */
0b1101101, /* 84 - T */
0b101010111, /* 85 - U */
0b110110101, /* 86 - V */
0b101011101, /* 87 - W */
0b101110101, /* 88 - X */
0b101111011, /* 89 - Y */
0b1010101101, /* 90 - Z */
0b111110111, /* 91 - [ */
0b111101111, /* 92 - \ */
0b111111011, /* 93 - ] */
0b1010111111, /* 94 - ^ */
0b101101101, /* 95 - _ */
0b1011011111, /* 96 - ` */
0b1011, /* 97 - a */
0b1011111, /* 98 - b */
0b101111, /* 99 - c */
0b101101, /* 100 - d */
0b11, /* 101 - e */
0b111101, /* 102 - f */
0b1011011, /* 103 - g */
0b101011, /* 104 - h */
0b1101, /* 105 - i */
0b111101011, /* 106 - j */
0b10111111, /* 107 - k */
0b11011, /* 108 - l */
0b111011, /* 109 - m */
0b1111, /* 110 - n */
0b111, /* 111 - o */
0b111111, /* 112 - p */
0b110111111, /* 113 - q */
0b10101, /* 114 - r */
0b10111, /* 115 - s */
0b101, /* 116 - t */
0b110111, /* 117 - u */
0b1111011, /* 118 - v */
0b1101011, /* 119 - w */
0b11011111, /* 120 - x */
0b1011101, /* 121 - y */
0b111010101, /* 122 - z */
0b1010110111, /* 123 - { */
0b110111011, /* 124 - | */
0b1010110101, /* 125 - } */
0b1011010111, /* 126 - ~ */
0b1110110101, /* 127 - <DEL> */
0b1110111101, /* 128 - */
0b1110111111, /* 129 - */
0b1111010101, /* 130 - */
0b1111010111, /* 131 - */
0b1111011011, /* 132 - */
0b1111011101, /* 133 - */
0b1111011111, /* 134 - */
0b1111101011, /* 135 - */
0b1111101101, /* 136 - */
0b1111101111, /* 137 - */
0b1111110101, /* 138 - */
0b1111110111, /* 139 - */
0b1111111011, /* 140 - */
0b1111111101, /* 141 - */
0b1111111111, /* 142 - */
0b10101010101, /* 143 - */
0b10101010111, /* 144 - */
0b10101011011, /* 145 - */
0b10101011101, /* 146 - */
0b10101011111, /* 147 - */
0b10101101011, /* 148 - */
0b10101101101, /* 149 - */
0b10101101111, /* 150 - */
0b10101110101, /* 151 - */
0b10101110111, /* 152 - */
0b10101111011, /* 153 - */
0b10101111101, /* 154 - */
0b10101111111, /* 155 - */
0b10110101011, /* 156 - */
0b10110101101, /* 157 - */
0b10110101111, /* 158 - */
0b10110110101, /* 159 - */
0b10110110111, /* 160 - */
0b10110111011, /* 161 - */
0b10110111101, /* 162 - */
0b10110111111, /* 163 - */
0b10111010101, /* 164 - */
0b10111010111, /* 165 - */
0b10111011011, /* 166 - */
0b10111011101, /* 167 - */
0b10111011111, /* 168 - */
0b10111101011, /* 169 - */
0b10111101101, /* 170 - */
0b10111101111, /* 171 - */
0b10111110101, /* 172 - */
0b10111110111, /* 173 - */
0b10111111011, /* 174 - */
0b10111111101, /* 175 - */
0b10111111111, /* 176 - */
0b11010101011, /* 177 - */
0b11010101101, /* 178 - */
0b11010101111, /* 179 - */
0b11010110101, /* 180 - */
0b11010110111, /* 181 - */
0b11010111011, /* 182 - */
0b11010111101, /* 183 - */
0b11010111111, /* 184 - */
0b11011010101, /* 185 - */
0b11011010111, /* 186 - */
0b11011011011, /* 187 - */
0b11011011101, /* 188 - */
0b11011011111, /* 189 - */
0b11011101011, /* 190 - */
0b11011101101, /* 191 - */
0b11011101111, /* 192 - */
0b11011110101, /* 193 - */
0b11011110111, /* 194 - */
0b11011111011, /* 195 - */
0b11011111101, /* 196 - */
0b11011111111, /* 197 - */
0b11101010101, /* 198 - */
0b11101010111, /* 199 - */
0b11101011011, /* 200 - */
0b11101011101, /* 201 - */
0b11101011111, /* 202 - */
0b11101101011, /* 203 - */
0b11101101101, /* 204 - */
0b11101101111, /* 205 - */
0b11101110101, /* 206 - */
0b11101110111, /* 207 - */
0b11101111011, /* 208 - */
0b11101111101, /* 209 - */
0b11101111111, /* 210 - */
0b11110101011, /* 211 - */
0b11110101101, /* 212 - */
0b11110101111, /* 213 - */
0b11110110101, /* 214 - */
0b11110110111, /* 215 - */
0b11110111011, /* 216 - */
0b11110111101, /* 217 - */
0b11110111111, /* 218 - */
0b11111010101, /* 219 - */
0b11111010111, /* 220 - */
0b11111011011, /* 221 - */
0b11111011101, /* 222 - */
0b11111011111, /* 223 - */
0b11111101011, /* 224 - */
0b11111101101, /* 225 - */
0b11111101111, /* 226 - */
0b11111110101, /* 227 - */
0b11111110111, /* 228 - */
0b11111111011, /* 229 - */
0b11111111101, /* 230 - */
0b11111111111, /* 231 - */
0b101010101011, /* 232 - */
0b101010101101, /* 233 - */
0b101010101111, /* 234 - */
0b101010110101, /* 235 - */
0b101010110111, /* 236 - */
0b101010111011, /* 237 - */
0b101010111101, /* 238 - */
0b101010111111, /* 239 - */
0b101011010101, /* 240 - */
0b101011010111, /* 241 - */
0b101011011011, /* 242 - */
0b101011011101, /* 243 - */
0b101011011111, /* 244 - */
0b101011101011, /* 245 - */
0b101011101101, /* 246 - */
0b101011101111, /* 247 - */
0b101011110101, /* 248 - */
0b101011110111, /* 249 - */
0b101011111011, /* 250 - */
0b101011111101, /* 251 - */
0b101011111111, /* 252 - */
0b101101010101, /* 253 - */
0b101101010111, /* 254 - */
0b101101011011, /* 255 - */
};
#define PSK_VARICODE_NUM (sizeof(psk_varicode)/sizeof(*psk_varicode))
static const float32_t PskBndPassB_31[] = {
6.6165543533213894e-05,
0.0,
-0.00013233108706642779,
0.0,
6.6165543533213894e-05
};
static const float32_t PskBndPassA_31[] = {
1.0,
-3.8414813063247664,
5.6662277107033248,
-3.7972899991488904,
0.9771256616899302
};
static const float32_t PskBndPassB_63[] = {
0.0002616526950658905,
0.0,
-0.000523305390131781,
0.0,
0.0002616526950658905
};
static const float32_t PskBndPassA_63[] = {
1.0,
-3.8195192250239174,
5.6013869366249818,
-3.7321386869273105,
0.95477455992103932
};
static const float32_t PskBndPassB_125[] = {
0.0010232176384709002,
0.0,
-0.0020464352769418003,
0.0,
0.0010232176384709002
};
static const float32_t PskBndPassA_125[] = {
1.0,
-3.7763786572915334,
5.4745855184361272,
-3.6055008493327723,
0.91159449659996006
};
static soft_dds_t psk_dds;
static soft_dds_t psk_bit_dds;
static soft_dds_t psk_rx_dds;
#if 0
static void PskBufAdd(int len, float32_t buf[], float32_t v)
{
for (int i = len - 1; i > 0; i--)
{
buf[i] = buf[i-1];
}
buf[0] = v;
}
#endif
static float32_t Psk_IirNext(const float32_t bpf_b[], const float32_t bpf_a[], float32_t x[], float32_t y[], int idx, int taps)
{
float32_t resp = 0;
int iidx;
for (int i = 0; i < taps; i++)
{
iidx = (idx - i + taps) % taps;
resp += bpf_b[i] * x[iidx];
if (i>0)
{
resp -= bpf_a[i] * y[iidx];
}
}
return resp / bpf_a[0];
}
const psk_speed_item_t psk_speeds[PSK_SPEED_NUM] =
{
{ .id =PSK_SPEED_31, .value = 31.25, .bpf_b = PskBndPassB_31, .bpf_a = PskBndPassA_31, .rate = 384, .label = " 31" },
{ .id =PSK_SPEED_63, .value = 62.5, .bpf_b = PskBndPassB_63, .bpf_a = PskBndPassA_63, .rate = 192, .label = " 63" },
{ .id =PSK_SPEED_125, .value = 125.0, .bpf_b = PskBndPassB_125, .bpf_a = PskBndPassA_125, .rate = 96, .label = "125" }
};
psk_ctrl_t psk_ctrl_config =
{
.speed_idx = PSK_SPEED_31
};
PskState_Internal_t psk_state;
static void Bpsk_ResetWin(void) {
// little trick, we just reset the acc
// which brings us back to the first sample
psk_bit_dds.acc = 0;
}
void Psk_Modulator_PrepareTx()
{
Psk_Modulator_SetState(PSK_MOD_PREAMBLE);
}
void Bpsk_Demodulator_Init()
{
psk_state.rx_phase = 0;
psk_state.rx_bnd_idx = 0;
for (int i = 0; i < PSK_BND_FLT_LEN; i++)
{
psk_state.rx_samples_in[i] = 0;
psk_state.rx_samples[i] = 0;
}
for (int i = 0; i < PSK_BUF_LEN; i++)
{
psk_state.rx_cos_prod[i] = 0;
psk_state.rx_sin_prod[i] = 0;
psk_state.rx_scmix[i] = 0;
}
psk_state.rx_idx = 0;
psk_state.rx_last_bit = 0;
psk_state.rx_last_symbol = 0;
psk_state.rx_symbol_len = psk_state.rate / PSK_BUF_LEN;
psk_state.rx_symbol_idx = 0;
// for (int i = 0; i < psk_state.rx_symbol_len; i ++)
// {
// psk_state.rx_symbol_buf[i];
// }
psk_state.rx_word = 0;
}
void Psk_Modem_Init(uint32_t output_sample_rate)
{
psk_state.tx_idx = 0;
softdds_setFreqDDS(&psk_dds, PSK_OFFSET, output_sample_rate, true);
softdds_setFreqDDS(&psk_rx_dds, PSK_OFFSET, PSK_SAMPLE_RATE, true);
// we use a sine wave with a frequency of half of the bit rate
// as envelope generator
softdds_setFreqDDS(&psk_bit_dds, (float32_t)psk_speeds[psk_ctrl_config.speed_idx].value / 2.0, output_sample_rate, false);
psk_state.tx_bit_len = lround(output_sample_rate / psk_speeds[psk_ctrl_config.speed_idx].value * 2); // 480000 / 31.25 * 2 = 3072
psk_state.rate = PSK_SAMPLE_RATE / psk_speeds[psk_ctrl_config.speed_idx].value;
Bpsk_Demodulator_Init();
}
static char Bpsk_DecodeVaricode(uint16_t code)
{
char result = '*';
for (int i = 0; i<PSK_VARICODE_NUM; i++) {
if (psk_varicode[i] == code)
{
result = i;
break;
}
}
return result;
}
static uint16_t Bpsk_FindCharReversed(uint8_t c)
{
uint16_t retval = 0;
uint16_t code = psk_varicode[c];
// bit reverse the code bit pattern, we need MSB of code to be LSB for shifting
while(code > 0)
{
retval |= code & 0x1; // mask and transfer LSB
retval <<= 1; // left shift
code >>= 1; // right shift, next bit gets active
}
return retval;
}
/**
*
* Basically samples the phase every bit length based on a sample which is one full sine wave
* of the carrier frequency. So it is not using multiple sampling points or anything.
* We throw away a lot of phase differences symbols
* using more of these should make the code more robust, shouldn't it?
*
* @param symbol_out averaged phase of the samples.
*/
static void BpskDecoder_NextSymbol(float32_t symbol_out)
{
int8_t bit;
static float32_t symbol_store = 0;
// if (psk_state.rx_symbol_len - psk_state.rx_symbol_idx < 6)
{
symbol_store = symbol_out;
}
psk_state.rx_symbol_idx += 1;
if (psk_state.rx_symbol_idx >= psk_state.rx_symbol_len)
{
psk_state.rx_symbol_idx = 0;
// TODO here should come additional part to check if timing of sampling should be moved
if (psk_state.rx_last_symbol * symbol_store < 0)
{
bit = 0;
}
else
{
bit = 1;
}
psk_state.rx_last_symbol = symbol_store;
symbol_store = 0;
// have we found 2 consecutive 0 bits? And previously at least one received bit == 1?
// indicates an end of character
if (psk_state.rx_last_bit == 0 && bit == 0 && psk_state.rx_word != 0)
{
// we lookup up the bits received (minus the last zero, which we shift out to the right)
// and put it into the buffer
UiDriver_TextMsgPutChar(Bpsk_DecodeVaricode(psk_state.rx_word >> 1));
// clean out the stored bit pattern
psk_state.rx_word = 0;
}
else
{
psk_state.rx_word = (psk_state.rx_word << 1) | bit;
}
psk_state.rx_last_bit = bit;
}
}
static float32_t BpskDecoder_Bandpass(float32_t sample)
{
// save original sample in bpf's input buffer
psk_state.rx_samples_in[psk_state.rx_bnd_idx] = sample;
// IIR bandpass for signal frequency range, implemented in a ring buffer
float32_t retval = Psk_IirNext(psk_speeds[psk_ctrl_config.speed_idx].bpf_b, psk_speeds[psk_ctrl_config.speed_idx].bpf_a, psk_state.rx_samples_in,
psk_state.rx_samples, psk_state.rx_bnd_idx, PSK_BND_FLT_LEN);
// save filtered sample in state buffer, used in next run;
psk_state.rx_samples[psk_state.rx_bnd_idx] = retval;
// increment our ring buffer index
psk_state.rx_bnd_idx++;
psk_state.rx_bnd_idx %= PSK_BND_FLT_LEN;
return retval;
}
/**
* Process an audio sample and decode signal.
*
* @param audio sample at PSK_SAMPLE_RATE
*/
void Psk_Demodulator_ProcessSample(float32_t sample)
{
float32_t fsample = BpskDecoder_Bandpass(sample);
// VCO generates a sine/cosine wave "carrier" with PSK_OFFSET Hz as frequency
float32_t vco_sin, vco_cos;
// we have to use a different DDS as for TX (different sample rates), IQ gives us sin and cos waves of PSK_OFFSET Hz
softdds_genIQSingleTone(&psk_rx_dds, &vco_sin , &vco_cos, 1);
// we now multiple these carriers with our signal
// this allows us to compare phase differences
float32_t sin_mix = vco_sin * fsample;
float32_t cos_mix = vco_cos * fsample;
// update sums by differences between old and new value in ringbuffer
psk_state.rx_sum_sin += sin_mix - psk_state.rx_sin_prod[psk_state.rx_idx];
psk_state.rx_sum_cos += cos_mix - psk_state.rx_cos_prod[psk_state.rx_idx];
// store new value
psk_state.rx_sin_prod[psk_state.rx_idx] = sin_mix;
psk_state.rx_cos_prod[psk_state.rx_idx] = cos_mix;
// we now calculate an average
float32_t symbol_out = psk_state.rx_sum_sin / PSK_BUF_LEN;
float32_t cos_out = psk_state.rx_sum_cos / PSK_BUF_LEN;
// GUESS: now try to estimate the frequency error of our VCO vs. the incoming signal
float32_t rx_scmix = symbol_out * cos_out;
psk_state.rx_err += rx_scmix - psk_state.rx_scmix[psk_state.rx_idx];
psk_state.rx_scmix[psk_state.rx_idx] = rx_scmix;
float32_t smax = 0;
for (int i = 0; i < PSK_BUF_LEN; i++)
{
if (fabsf(psk_state.rx_cos_prod[i]) > smax)
{
smax = fabsf(psk_state.rx_cos_prod[i]);
}
if (fabsf(psk_state.rx_sin_prod[i]) > smax)
{
smax = fabsf(psk_state.rx_sin_prod[i]);
}
}
// calculate the final correction value from rx_err
// avoid division by zero if smax is 0
float32_t rx_err_corr = psk_state.rx_err/ (PSK_BUF_LEN * ((smax != 0) ? (smax * smax * 4.0) : 1.0));
// if the error is too large, we limit it to +/- 0.1
if(fabsf(rx_err_corr) > 0.1)
{
rx_err_corr = (rx_err_corr > 0) ? 0.1 : -0.1;
}
rx_err_corr = 0;
// now advance our phase counter with our error correction
psk_state.rx_phase += PSK_SHIFT_DIFF + rx_err_corr * PSK_SHIFT_DIFF;
// we just passed one "full" offset frequency wave length?
// now see if we have enough symbols to decide if it is a 1 or 0
// the symbol is basically an averaged phase difference over the last PSK_BUF_LEN
// samples.
if (psk_state.rx_phase > 1)
{
psk_state.rx_phase -= 1;
BpskDecoder_NextSymbol(symbol_out);
}
if (psk_state.rx_phase < 0)
{
psk_state.rx_phase += 1;
}
// we prepare us for the next sample in our ring buffer
psk_state.rx_idx = (psk_state.rx_idx + 1) % PSK_BUF_LEN;
}
static bool bit_start(uint16_t tx_bit_phase)
{
return tx_bit_phase == psk_state.tx_bit_len / 4;
}
static bool bit_middle(uint16_t tx_bit_phase)
{
return tx_bit_phase == 0;
}
/**
* Generates a BPSK signal. Uses an oscillator for generating continous base signal of a
* given frequency (defined in PSK_OFFSET). This signal is phase controlled by a variable
* psk_state.tx_wave_sign_current. A second frequency generator provides an envelope shape based on a cosine
* of half of the bpsk rate. For 0 bits to transmit the symbol is using the enveloped signal and shifts
* in the middle.
*
*
* @return
*/
int16_t Psk_Modulator_GenSample()
{
// tx_bit_len / 4 -> start of a bit
// tx_bit_len / 0 -> middle of a bit
// tx_bit_len / 2 -> end of a bit
int32_t retval = 0; // by default we produce silence
// check if the modulator is supposed to be active...
if (Psk_Modulator_GetState() != PSK_MOD_OFF)
{
// try to find out what to transmit next
if (bit_start(psk_state.tx_bit_phase))
{
// check if we still have bits to transmit
if (psk_state.tx_bits == 0)
{
// no, all bits have been transmitted
if (psk_state.tx_zeros < 2 || (Psk_Modulator_GetState() == PSK_MOD_PREAMBLE))
{
// send spacing zeros before anything else happens
// normal characters don't have 2 zeros following each other
psk_state.tx_zeros++;
// are we sending a preamble and have transmitted enough zeroes?
// we do this for roughly a second, i.e. we simply use the rate as "timer"
if ((Psk_Modulator_GetState() == PSK_MOD_PREAMBLE) && psk_state.tx_zeros >= psk_speeds[psk_ctrl_config.speed_idx].value)
{
Psk_Modulator_SetState(PSK_MOD_ACTIVE);
}
}
else if (DigiModes_TxBufferHasData())
{
DigiModes_TxBufferRemove( &psk_state.tx_char, BPSK );
Psk_Modulator_SetState(PSK_MOD_ACTIVE);
if (psk_state.tx_char == 0x04) // EOT, stop tranmission
{
// we send from buffer, and nothing more is in the buffer
// request sending the trailing sequence
Psk_Modulator_SetState(PSK_MOD_POSTAMBLE);
}
else
{
// if all zeros have been sent, look for new
// input from input buffer
psk_state.tx_bits = Bpsk_FindCharReversed(psk_state.tx_char);
// reset counter for spacing zeros
psk_state.tx_zeros = 0;
// reset counter for trailing postamble (which conclude a transmission)
psk_state.tx_ones = 0;
}
}
if (Psk_Modulator_GetState() == PSK_MOD_POSTAMBLE)
{
// this is for generating trailing postamble if the
// input comes from a buffer or if we are asked to
// switch off,
// we do this for roughly a second, i.e. we simply use the rate as "timer"
if (psk_state.tx_ones < psk_speeds[psk_ctrl_config.speed_idx].value)
{
psk_state.tx_ones+=16;
psk_state.tx_bits = 0xffff; // we add 16 bits of postamble
// so we may send a few more postamble than request, but who cares...
}
else
{
Psk_Modulator_SetState(PSK_MOD_INACTIVE);
}
}
}
// we test the current bit. If it is a zero, and we have no more postamble to transmit
// we alternate the phase of our signal phase (180 degree shift)
if ((psk_state.tx_bits & 0x1) == 0 && psk_state.tx_ones == 0)
{
psk_state.tx_wave_sign_next *= -1;
}
// if it is a phase shift, which equals a zero to transmit or we transmit our last bit
if (psk_state.tx_wave_sign_next != psk_state.tx_wave_sign_current || Psk_Modulator_GetState() == PSK_MOD_INACTIVE)
{
// we have to shape the signal
psk_state.tx_win = true;
}
else
{
// it is a one and not the end, so we simply keep the full swing,
// i.e. a constant amplitude signal
psk_state.tx_win = false;
}
psk_state.tx_bits >>= 1; // remove "used" bit
}
// here we are in the middle of bit
// we move the next sign in, since it may indicate a phase shift
// in this case we are transmitting a zero
if (bit_middle(psk_state.tx_bit_phase))
{
psk_state.tx_wave_sign_current = psk_state.tx_wave_sign_next;
// if we are in the middle of a bit AND it is a zero bit
// we have to start our envelope from null
if (psk_state.tx_win)
{
Bpsk_ResetWin(); // we start the envelope from 0 to max
}
if (Psk_Modulator_GetState() == PSK_MOD_INACTIVE)
{
// now turn us off, we're done.
Psk_Modulator_SetState(PSK_MOD_OFF);
}
}
// if we are shaping the signal envelope
// we use the "slow" to generate our shape.
// we use abs so that we are getting only the gain
// not the phase from here
// otherwise we use the SAMPLE_MAX as coeff for constant amplitude
int32_t coeff = psk_state.tx_win ? abs(softdds_nextSample(&psk_bit_dds)) : SAMPLE_MAX;
// the bit length counter is incremented after each sample
// and wraps around after one bit length
psk_state.tx_bit_phase = (psk_state.tx_bit_phase + 1) % (psk_state.tx_bit_len / 2); // % 1576 == 1 bit length
retval = (coeff * psk_state.tx_wave_sign_current * softdds_nextSample(&psk_dds)) / SAMPLE_MAX;
}
return retval;
}
/**
* Returns the operational state of the PSK modulator
* @return current state
*/
psk_modulator_t Psk_Modulator_GetState()
{
return psk_state.tx_mod_state;
}
/**
* Change the state of the psk modulator to a new operational state
* If necessary, checks if state can be changed and executes code
* for the state transistion such as resetting variables to a known etc.
*
* @param newState
* @return the previous state
*/
psk_modulator_t Psk_Modulator_SetState(psk_modulator_t newState)
{
psk_modulator_t retval = psk_state.tx_mod_state;
switch(newState)
{
case PSK_MOD_PREAMBLE:
psk_state.tx_ones = 0;
psk_state.tx_win = true;
psk_state.tx_char = '\0';
psk_state.tx_bits = 0;
psk_state.tx_wave_sign_next = 1;
psk_state.tx_wave_sign_current = 1;
psk_state.tx_bit_phase = 0;
psk_state.tx_zeros = 0;
psk_state.tx_mod_state = newState;
break;
case PSK_MOD_OFF:
RadioManagement_Request_TxOff();
psk_state.tx_mod_state = newState;
break;
default:
psk_state.tx_mod_state = newState;
break;
}
return retval;
}
+65
View File
@@ -0,0 +1,65 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR FIRMWARE **
** **
**---------------------------------------------------------------------------------**
** Licence: GNU GPLv3, see LICENSE.md **
************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __PSK_H
#define __PSK_H
#include "uhsdr_types.h"
// externally defined for UiSpectrum markers
#define PSK_OFFSET 500
#define PSK_SNAP_RANGE 100 // defines the range within which the SNAP algorithm in spectrum.c searches for the PSK carrier
typedef enum {
PSK_SPEED_31,
PSK_SPEED_63,
PSK_SPEED_125,
PSK_SPEED_NUM
} psk_speed_t;
typedef struct
{
psk_speed_t id;
float32_t value;
const float32_t* bpf_b;
const float32_t* bpf_a;
uint16_t rate;
char* label;
} psk_speed_item_t;
extern const psk_speed_item_t psk_speeds[PSK_SPEED_NUM];
typedef struct
{
psk_speed_t speed_idx;
} psk_ctrl_t;
extern psk_ctrl_t psk_ctrl_config;
typedef enum {
PSK_MOD_OFF,
PSK_MOD_PREAMBLE,
PSK_MOD_ACTIVE,
PSK_MOD_POSTAMBLE,
PSK_MOD_INACTIVE
} psk_modulator_t;
psk_modulator_t Psk_Modulator_GetState(void);
psk_modulator_t Psk_Modulator_SetState(psk_modulator_t newState);
void Psk_Modem_Init(uint32_t output_sample_rate);
void Psk_Modulator_PrepareTx(void);
void Psk_Demodulator_ProcessSample(float32_t sample);
int16_t Psk_Modulator_GenSample(void);
#endif
+72
View File
@@ -0,0 +1,72 @@
#include "rb.h"
void RingBuffer_ClearGetTail(RingBuffer_data_t* buf)
{
do
{
buf->buffer_tail = buf->buffer_head;
} while (buf->buffer_tail != buf->buffer_head);
}
void RingBuffer_ClearPutHead(RingBuffer_data_t* buf)
{
do
{
buf->buffer_head = buf->buffer_tail;
} while (buf->buffer_tail != buf->buffer_head);
}
int32_t RingBuffer_GetRoom(RingBuffer_data_t* buf)
{
int32_t retval = ((buf->buffer_tail + buf->conf.size) - buf->buffer_head) % buf->conf.size;
return retval == 0 ? buf->conf.size : retval;
}
int32_t RingBuffer_GetData(RingBuffer_data_t* buf)
{
int32_t retval = ((buf->buffer_head + buf->conf.size) - buf->buffer_tail) % buf->conf.size;
return retval;
}
bool RingBuffer_PutSamples(RingBuffer_data_t* buf, void* samples, int32_t len)
{
bool retval = false;
if (len < RingBuffer_GetRoom(buf))
{
int32_t maxupper = buf->conf.size - buf->buffer_head;
int32_t copylower = len - maxupper;
int32_t copyupper = copylower > 0 ? maxupper : len;
memcpy((char*)buf->conf.buffer + (buf->buffer_head * buf->conf.sizeofItem),(char*)samples,copyupper * buf->conf.sizeofItem);
if (copylower > 0)
{
memcpy((char*)buf->conf.buffer,(char*)samples + (maxupper * buf->conf.sizeofItem),copylower * buf->conf.sizeofItem);
}
buf->buffer_head = (buf->buffer_head + len) % buf->conf.size;
retval = true;
}
return retval;
}
bool RingBuffer_GetSamples(RingBuffer_data_t* buf, void* samples, int32_t len)
{
bool retval = false;
if (len <= RingBuffer_GetData(buf))
{
int32_t maxupper = buf->conf.size - buf->buffer_tail;
int32_t copylower = len - maxupper;
int32_t copyupper = copylower > 0 ? maxupper : len;
memcpy((char*)samples,(char*)buf->conf.buffer + (buf->buffer_tail * buf->conf.sizeofItem),copyupper * buf->conf.sizeofItem);
if (copylower > 0)
{
memcpy(((char*)samples) + (maxupper * buf->conf.sizeofItem), (char*)buf->conf.buffer,copylower * buf->conf.sizeofItem);
}
buf->buffer_tail = (buf->buffer_tail + len) % buf->conf.size;
retval = true;
}
return retval;
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef __RING_BUFFER_H__
#define __RING_BUFFER_H__
#include "uhsdr_types.h"
typedef struct {
int32_t size;
int32_t sizeofItem;
void* buffer; //buffer for filtered PCM data from Recv.
} RingBuffer_conf_t;
typedef struct {
volatile uint16_t buffer_tail;
volatile uint16_t buffer_head;
const RingBuffer_conf_t conf;
} RingBuffer_data_t;
#define RingBuffer_Declare(name, type) typedef type name##_item_t; extern RingBuffer_data_t name;
#define RingBuffer_Define(name, buf_size) name##_item_t name##_buffer[(buf_size)]; RingBuffer_data_t name = { .buffer_tail = 0, .buffer_head = 0, .conf = { .size = (buf_size), .sizeofItem = sizeof(name##_item_t) , .buffer = name##_buffer } };
#define RingBuffer_DefineExtMem(name, buf_size, buf_addr) RingBuffer_data_t name = { .buffer_tail = 0, .buffer_head = 0, .conf = { .size = (buf_size), .sizeofItem = sizeof(name##_item_t) , .buffer = (buf_addr) } };
void RingBuffer_ClearGetTail(RingBuffer_data_t* buf);
void RingBuffer_ClearPutHead(RingBuffer_data_t* buf);
int32_t RingBuffer_GetRoom(RingBuffer_data_t* buf);
int32_t RingBuffer_GetData(RingBuffer_data_t* buf);
bool RingBuffer_PutSamples(RingBuffer_data_t* buf, void* samples, int32_t len);
bool RingBuffer_GetSamples(RingBuffer_data_t* buf, void* samples, int32_t len);
#endif
+931
View File
@@ -0,0 +1,931 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR FIRMWARE **
** **
**---------------------------------------------------------------------------------**
** Licence: GNU GPLv3, see LICENSE.md **
************************************************************************************/
// Common
#include <assert.h>
#include "uhsdr_board.h"
#include "profiling.h"
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include "softdds.h"
#include "rtty.h"
#include "ui_driver.h" // only necessary because of UiDriver_TextMsgPutChar
#include "radio_management.h" // only necessary because of RadioManagement_Request_TxOff
#include "uhsdr_digi_buffer.h"
// character tables borrowed from fldigi / rtty.cxx
static const char RTTYLetters[] = {
'\0', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
'\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
'O', 'B', 'G', ' ', 'M', 'X', 'V', ' '
};
static const char RTTYSymbols[32] = {
'\0', '3', '\n', '-', ' ', '\a', '8', '7',
'\r', '$', '4', '\'', ',', '!', ':', '(',
'5', '"', ')', '2', '#', '6', '0', '1',
'9', '?', '&', ' ', '.', '/', ';', ' '
};
// bits 0-4 -> baudot, bit 5 1 == LETTER, 0 == NUMBER/FIGURE
const uint8_t Ascii2Baudot[128] =
{
0,
0,
0,
0,
0,
0,
0,
0b001011, // BEL N
0,
0,
0b000010, // \n NL
0,
0,
0b001000, // \r NL
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0b100100, // NL
0b001101, // ! N
0b010001, // " N
0b010100, // # N
0b001001, // $ N
0, // %
0b011010, // & N
0b000101, // ' N
0b001111, // ( N
0b010010, // ) N
0, // *
0, // +
0b001100, // , N
0b000011, // - N
0b011100, // . N
0b011101, // / N
0b010110, // 0 N
0b010111, // 1 N
0b010011, // 2 N
0b000001, // 3 N
0b001010, // 4 N
0b010000, // 5 N
0b010101, // 6 N
0b000111, // 7 N
0b000110, // 8 N
0b011000, // 9 N
0b001110, // : N
0b011110, // ; N
0, // <
0, // =
0, // >
0b011001, // ? N
0, // @
0b100011, // A L
0b111001, // B L
0b101110, // C L
0b101001, // D L
0b100001, // E L
0b101101, // F L
0b111010, // G L
0b110100, // H L
0b100110, // I L
0b101011, // J L
0b101111, // K L
0b110010, // L L
0b111100, // M L
0b101100, // N L
0b111000, // O L
0b110110, // P L
0b110111, // Q L
0b101010, // R L
0b100101, // S L
0b110000, // T L
0b100111, // U L
0b111110, // V L
0b110011, // W L
0b111101, // X L
0b110101, // Y L
0b110001, // Z L
0,
0,
0,
0,
0,
0,
0b100011, // A L
0b111001, // B L
0b101110, // C L
0b101001, // D L
0b100001, // E L
0b101101, // F L
0b111010, // G L
0b110100, // H L
0b100110, // I L
0b101011, // J L
0b101111, // K L
0b110010, // L L
0b111100, // M L
0b101100, // N L
0b111000, // O L
0b110110, // P L
0b110111, // Q L
0b101010, // R L
0b100101, // S L
0b110000, // T L
0b100111, // U L
0b111110, // V L
0b110011, // W L
0b111101, // X L
0b110101, // Y L
0b110001, // Z L
0,
0,
0,
0,
0,
};
#define RTTY_SYMBOL_CODE (0b11011)
#define RTTY_LETTER_CODE (0b11111)
// RTTY Experiment based on code from the DSP Tutorial at http://dp.nonoo.hu/projects/ham-dsp-tutorial/18-rtty-decoder-using-iir-filters/
// Used with permission from Norbert Varga, HA2NON under GPLv3 license
/*
* Experimental Code
*/
#ifdef USE_RTTY_PROCESSOR
const rtty_speed_item_t rtty_speeds[RTTY_SPEED_NUM] =
{
{ .id =RTTY_SPEED_45, .value = 45.45, .label = "45" },
{ .id =RTTY_SPEED_50, .value = 50, .label = "50" },
};
const rtty_shift_item_t rtty_shifts[RTTY_SHIFT_NUM] =
{
{ RTTY_SHIFT_85, 85, " 85" },
{ RTTY_SHIFT_170, 170, "170" },
{ RTTY_SHIFT_200, 200, "200" },
{ RTTY_SHIFT_425, 425, "425" },
{ RTTY_SHIFT_450, 450, "450" },
{ RTTY_SHIFT_850, 850, "850" },
};
rtty_ctrl_t rtty_ctrl_config =
{
.shift_idx = RTTY_SHIFT_170,
.speed_idx = RTTY_SPEED_45,
.stopbits_idx = RTTY_STOP_1_5
};
typedef struct
{
float32_t gain;
float32_t coeffs[4];
uint16_t freq; // center freq
} rtty_bpf_config_t;
typedef struct
{
float32_t gain;
float32_t coeffs[2];
} rtty_lpf_config_t;
typedef struct
{
float32_t xv[5];
float32_t yv[5];
} rtty_bpf_data_t;
typedef struct
{
float32_t xv[3];
float32_t yv[3];
} rtty_lpf_data_t;
static float32_t RttyDecoder_bandPassFreq(float32_t sampleIn, const rtty_bpf_config_t* coeffs, rtty_bpf_data_t* data) {
data->xv[0] = data->xv[1]; data->xv[1] = data->xv[2]; data->xv[2] = data->xv[3]; data->xv[3] = data->xv[4];
data->xv[4] = sampleIn / coeffs->gain; // gain at centre
data->yv[0] = data->yv[1]; data->yv[1] = data->yv[2]; data->yv[2] = data->yv[3]; data->yv[3] = data->yv[4];
data->yv[4] = (data->xv[0] + data->xv[4]) - 2 * data->xv[2]
+ (coeffs->coeffs[0] * data->yv[0]) + (coeffs->coeffs[1] * data->yv[1])
+ (coeffs->coeffs[2] * data->yv[2]) + (coeffs->coeffs[3] * data->yv[3]);
return data->yv[4];
}
static float32_t RttyDecoder_lowPass(float32_t sampleIn, const rtty_lpf_config_t* coeffs, rtty_lpf_data_t* data) {
data->xv[0] = data->xv[1]; data->xv[1] = data->xv[2];
data->xv[2] = sampleIn / coeffs->gain; // gain at DC
data->yv[0] = data->yv[1]; data->yv[1] = data->yv[2];
data->yv[2] = (data->xv[0] + data->xv[2]) + 2 * data->xv[1]
+ (coeffs->coeffs[0] * data->yv[0]) + (coeffs->coeffs[1] * data->yv[1]);
return data->yv[2];
}
typedef enum {
RTTY_RUN_STATE_WAIT_START = 0,
RTTY_RUN_STATE_BIT,
} rtty_run_state_t;
typedef enum {
RTTY_MODE_LETTERS = 0,
RTTY_MODE_SYMBOLS
} rtty_charSetMode_t;
typedef struct {
rtty_bpf_data_t bpfSpaceData;
rtty_bpf_data_t bpfMarkData;
rtty_lpf_data_t lpfData;
rtty_bpf_config_t *bpfSpaceConfig;
rtty_bpf_config_t *bpfMarkConfig;
rtty_lpf_config_t *lpfConfig;
uint16_t oneBitSampleCount;
int32_t DPLLOldVal;
int32_t DPLLBitPhase;
uint8_t byteResult;
uint16_t byteResultp;
rtty_charSetMode_t charSetMode;
rtty_run_state_t state;
soft_dds_t tx_dds[2];
const rtty_mode_config_t* config_p;
} rtty_decoder_data_t;
rtty_decoder_data_t rttyDecoderData;
#if 0 // 48Khz filters, not needed
// this is for 48ksps sample rate
// for filter designing, see http://www-users.cs.york.ac.uk/~fisher/mkfilter/
// order 2 Butterworth, freqs: 865-965 Hz
rtty_bpf_config_t rtty_bp_48khz_915 =
{
.gain = 2.356080041e+04, // gain at centre
.coeffs = {-0.9816582826, 3.9166274264, -5.8882201843, 3.9530488323 },
.freq = 915
};
// order 2 Butterworth, freqs: 1035-1135 Hz
rtty_bpf_config_t rtty_bp_48khz_1085 =
{
.gain = 2.356080365e+04,
.coeffs = {-0.9816582826, 3.9051693660, -5.8653953990, 3.9414842213 },
.freq = 1085
};
#endif
// order 2 Butterworth, freq: 50 Hz
rtty_lpf_config_t rtty_lp_48khz_50 =
{
.gain = 9.381008646e+04,
.coeffs = {-0.9907866988, 1.9907440595 }
};
// this is for 12ksps sample rate
// for filter designing, see http://www-users.cs.york.ac.uk/~fisher/mkfilter/
// order 2 Butterworth, freqs: 865-965 Hz, centre: 915 Hz
static rtty_bpf_config_t rtty_bp_12khz_915 =
{
.gain = 1.513364755e+03,
.coeffs = { -0.9286270861, 3.3584472566, -4.9635817596, 3.4851652468 },
.freq = 915
};
// order 2 Butterworth, freqs: 1315-1415 Hz, centre 1365Hz
static rtty_bpf_config_t rtty_bp_12khz_1365 =
{
.gain = 1.513365019e+03,
.coeffs = { -0.9286270861, 2.8583904591, -4.1263569881, 2.9662407442 },
.freq = 1365
};
// order 2 Butterworth, freqs: 1035-1135 Hz, centre: 1085Hz
static rtty_bpf_config_t rtty_bp_12khz_1085 =
{
.gain = 1.513364927e+03,
.coeffs = { -0.9286270861, 3.1900687350, -4.6666321298, 3.3104336142 },
.freq = 1085
};
// order 2 Butterworth, freqs: 1065-1165 Hz, centre: 1115Hz
// for 200Hz shift
static rtty_bpf_config_t rtty_bp_12khz_1115 =
{
.gain = 1.513364944e+03,
.coeffs = { -0.9286270861, 3.1576917276, -4.6112830458, 3.2768349860 },
.freq = 1115
};
// for 85Hz shift --> 915 + 85Hz = space = 1000Hz
// 3dB bandwidth 50Hz
// order 2 Butterworth, freqs: 975-1025 Hz, centre: 1000Hz
static rtty_bpf_config_t rtty_bp_12khz_1000 =
{
.gain = 5.944465260e+03,
.coeffs = { -0.9636529842, 3.3693752166, -4.9084595657, 3.4323354886 },
.freq = 1000
};
// for 425Hz shift --> 915 + 425Hz = space = 1340Hz
// 3dB bandwidth 100Hz
// order 2 Butterworth, freqs: 1290 - 1390 Hz, centre: 1340Hz
static rtty_bpf_config_t rtty_bp_12khz_1340 =
{
.gain = 1.513365018e+03,
.coeffs = { -0.9286270862, 2.8906128091, -4.1762457780, 2.9996788796 },
.freq = 1340
};
// for 850Hz shift --> 915 + 850Hz = space = 1765Hz
// 3dB bandwidth 100Hz
// order 2 Butterworth, freqs: 1715 - 1815 Hz, centre: 1765Hz
static rtty_bpf_config_t rtty_bp_12khz_1765 =
{
.gain = 1.513365057e+03,
.coeffs = { -0.9286270862, 2.1190223173, -3.1352567157, 2.1989754113 },
.freq = 1765
};
static rtty_lpf_config_t rtty_lp_12khz_50 =
{
.gain = 5.944465310e+03,
.coeffs = { -0.9636529842, 1.9629800894 }
};
static rtty_mode_config_t rtty_mode_current_config;
void Rtty_Modem_Init(uint32_t output_sample_rate)
{
// TODO: pass config as parameter and make it changeable via menu
rtty_mode_current_config.samplerate = 12000;
rtty_mode_current_config.shift = rtty_shifts[rtty_ctrl_config.shift_idx].value;
rtty_mode_current_config.speed = rtty_speeds[rtty_ctrl_config.speed_idx].value;
rtty_mode_current_config.stopbits = rtty_ctrl_config.stopbits_idx;
rttyDecoderData.config_p = &rtty_mode_current_config;
// common config to all supported modes
rttyDecoderData.oneBitSampleCount = (uint16_t)roundf(rttyDecoderData.config_p->samplerate/rttyDecoderData.config_p->speed);
rttyDecoderData.charSetMode = RTTY_MODE_LETTERS;
rttyDecoderData.state = RTTY_RUN_STATE_WAIT_START;
rttyDecoderData.bpfMarkConfig = &rtty_bp_12khz_915; // this is mark, or '1'
rttyDecoderData.lpfConfig = &rtty_lp_12khz_50;
// now we handled the specifics
switch (rttyDecoderData.config_p->shift)
{
case 85:
rttyDecoderData.bpfSpaceConfig = &rtty_bp_12khz_1000;
break;
case 200:
rttyDecoderData.bpfSpaceConfig = &rtty_bp_12khz_1115;
break;
case 425:
rttyDecoderData.bpfSpaceConfig = &rtty_bp_12khz_1340;
break;
case 450:
rttyDecoderData.bpfSpaceConfig = &rtty_bp_12khz_1365; // this is space or '0'
break;
case 850:
rttyDecoderData.bpfSpaceConfig = &rtty_bp_12khz_1765; // this is space or '0'
break;
case 170:
default:
// all unsupported shifts are mapped to 170
rttyDecoderData.bpfSpaceConfig = &rtty_bp_12khz_1085; // this is space or '0'
}
// configure DDS for transmission
softdds_setFreqDDS(&rttyDecoderData.tx_dds[0], rttyDecoderData.bpfSpaceConfig->freq, output_sample_rate, 0);
softdds_setFreqDDS(&rttyDecoderData.tx_dds[1], rttyDecoderData.bpfMarkConfig->freq, output_sample_rate, 0);
}
float32_t decayavg(float32_t average, float32_t input, int weight)
{ // adapted from https://github.com/ukhas/dl-fldigi/blob/master/src/include/misc.h
float32_t retval;
if (weight <= 1)
{
retval = input;
}
else
{
retval = ( ( input - average ) / (float32_t)weight ) + average ;
}
return retval;
}
// this function returns the bit value of the current sample
static int RttyDecoder_demodulator(float32_t sample)
{
float32_t space_mag = RttyDecoder_bandPassFreq(sample, rttyDecoderData.bpfSpaceConfig, &rttyDecoderData.bpfSpaceData);
float32_t mark_mag = RttyDecoder_bandPassFreq(sample, rttyDecoderData.bpfMarkConfig, &rttyDecoderData.bpfMarkData);
float32_t v1 = 0.0;
// calculating the RMS of the two lines (squaring them)
space_mag *= space_mag;
mark_mag *= mark_mag;
if(rtty_ctrl_config.atc_disable == false)
{ // RTTY decoding with ATC = automatic threshold correction
// FIXME: space & mark seem to be swapped in the following code
// dirty fix
float32_t helper = space_mag;
space_mag = mark_mag;
mark_mag = helper;
static float32_t mark_env = 0.0;
static float32_t space_env = 0.0;
static float32_t mark_noise = 0.0;
static float32_t space_noise = 0.0;
// experiment to implement an ATC (Automatic threshold correction), DD4WH, 2017_08_24
// everything taken from FlDigi, licensed by GNU GPLv2 or later
// https://github.com/ukhas/dl-fldigi/blob/master/src/cw_rtty/rtty.cxx
// calculate envelope of the mark and space signals
// uses fast attack and slow decay
mark_env = decayavg (mark_env, mark_mag,
(mark_mag > mark_env) ? rttyDecoderData.oneBitSampleCount / 4 : rttyDecoderData.oneBitSampleCount * 16);
space_env = decayavg (space_env, space_mag,
(space_mag > space_env) ? rttyDecoderData.oneBitSampleCount / 4 : rttyDecoderData.oneBitSampleCount * 16);
// calculate the noise on the mark and space signals
mark_noise = decayavg (mark_noise, mark_mag,
(mark_mag < mark_noise) ? rttyDecoderData.oneBitSampleCount / 4 : rttyDecoderData.oneBitSampleCount * 48);
space_noise = decayavg (space_noise, space_mag,
(space_mag < space_noise) ? rttyDecoderData.oneBitSampleCount / 4 : rttyDecoderData.oneBitSampleCount * 48);
// the noise floor is the lower signal of space and mark noise
float32_t noise_floor = (space_noise < mark_noise) ? space_noise : mark_noise;
// Linear ATC, section 3 of www.w7ay.net/site/Technical/ATC
// v1 = space_mag - mark_mag - 0.5 * (space_env - mark_env);
// Compensating for the noise floor by using clipping
float32_t mclipped = 0.0, sclipped = 0.0;
mclipped = mark_mag > mark_env ? mark_env : mark_mag;
sclipped = space_mag > space_env ? space_env : space_mag;
if (mclipped < noise_floor)
{
mclipped = noise_floor;
}
if (sclipped < noise_floor)
{
sclipped = noise_floor;
}
// we could add options for mark-only or space-only decoding
// however, the current implementation with ATC already works quite well with mark-only/space-only
/* switch (progdefaults.rtty_cwi) {
case 1 : // mark only decode
space_env = sclipped = noise_floor;
break;
case 2: // space only decode
mark_env = mclipped = noise_floor;
default : ;
}
*/
// Optimal ATC (Section 6 of of www.w7ay.net/site/Technical/ATC)
v1 = (mclipped - noise_floor) * (mark_env - noise_floor) -
(sclipped - noise_floor) * (space_env - noise_floor) -
0.25 * ((mark_env - noise_floor) * (mark_env - noise_floor) -
(space_env - noise_floor) * (space_env - noise_floor));
v1 = RttyDecoder_lowPass(v1, rttyDecoderData.lpfConfig, &rttyDecoderData.lpfData);
}
else
{ // RTTY without ATC, which works very well too!
// inverting line 1
mark_mag *= -1;
// summing the two lines
v1 = mark_mag + space_mag;
// lowpass filtering the summed line
v1 = RttyDecoder_lowPass(v1, rttyDecoderData.lpfConfig, &rttyDecoderData.lpfData);
}
return (v1 > 0)?0:1;
}
// this function returns true once at the half of a bit with the bit's value
static bool RttyDecoder_getBitDPLL(float32_t sample, bool* val_p) {
static bool phaseChanged = false;
bool retval = false;
if (rttyDecoderData.DPLLBitPhase < rttyDecoderData.oneBitSampleCount)
{
*val_p = RttyDecoder_demodulator(sample);
if (!phaseChanged && *val_p != rttyDecoderData.DPLLOldVal) {
if (rttyDecoderData.DPLLBitPhase < rttyDecoderData.oneBitSampleCount/2)
{
// rttyDecoderData.DPLLBitPhase += rttyDecoderData.oneBitSampleCount/8; // early
rttyDecoderData.DPLLBitPhase += rttyDecoderData.oneBitSampleCount/32; // early
}
else
{
//rttyDecoderData.DPLLBitPhase -= rttyDecoderData.oneBitSampleCount/8; // late
rttyDecoderData.DPLLBitPhase -= rttyDecoderData.oneBitSampleCount/32; // late
}
phaseChanged = true;
}
rttyDecoderData.DPLLOldVal = *val_p;
rttyDecoderData.DPLLBitPhase++;
}
if (rttyDecoderData.DPLLBitPhase >= rttyDecoderData.oneBitSampleCount)
{
rttyDecoderData.DPLLBitPhase -= rttyDecoderData.oneBitSampleCount;
retval = true;
}
return retval;
}
// this function returns only true when the start bit is successfully received
static bool RttyDecoder_waitForStartBit(float32_t sample) {
bool retval = false;
int bitResult;
static int16_t wait_for_start_state = 0;
static int16_t wait_for_half = 0;
bitResult = RttyDecoder_demodulator(sample);
switch (wait_for_start_state)
{
case 0:
// waiting for a falling edge
if (bitResult != 0)
{
wait_for_start_state++;
}
break;
case 1:
if (bitResult != 1)
{
wait_for_start_state++;
}
break;
case 2:
wait_for_half = rttyDecoderData.oneBitSampleCount/2;
wait_for_start_state ++;
/* fall through */ // this is for the compiler, the following comment is for Eclipse
/* no break */
case 3:
wait_for_half--;
if (wait_for_half == 0)
{
retval = (bitResult == 0);
wait_for_start_state = 0;
}
break;
}
return retval;
}
//static const char RTTYLetters[] = "<E\nA SIU\nDRJNFCKTZLWHYPQOBG^MXV^";
//static const char RTTYSymbols[] = "<3\n- ,87\n$4#,.:(5+)2.60197.^./=^";
// character tables borrowed from fldigi / rtty.cxx
void Rtty_Demodulator_ProcessSample(float32_t sample)
{
switch(rttyDecoderData.state)
{
case RTTY_RUN_STATE_WAIT_START: // not synchronized, need to wait for start bit
if (RttyDecoder_waitForStartBit(sample))
{
rttyDecoderData.state = RTTY_RUN_STATE_BIT;
rttyDecoderData.byteResultp = 1;
rttyDecoderData.byteResult = 0;
}
break;
case RTTY_RUN_STATE_BIT:
// reading 7 more bits
if (rttyDecoderData.byteResultp < 8)
{
bool bitResult = false;
if (RttyDecoder_getBitDPLL(sample, &bitResult))
{
switch (rttyDecoderData.byteResultp)
{
case 6: // stop bit 1
case 7: // stop bit 2
if (bitResult == false)
{
// not in sync
rttyDecoderData.state = RTTY_RUN_STATE_WAIT_START;
}
if (rttyDecoderData.config_p->stopbits != RTTY_STOP_2 && rttyDecoderData.byteResultp == 6)
{
// we pretend to be at the 7th bit after receiving the first stop bit if we have less than 2 stop bits
// this omits check for 1.5 bit condition but we should be more or less safe here, may cause
// a little more unaligned receive but without that shortcut we simply cannot receive these configurations
// so it is worth it
rttyDecoderData.byteResultp = 7;
}
break;
default:
// System.out.print(bitResult);
rttyDecoderData.byteResult |= (bitResult?1:0) << (rttyDecoderData.byteResultp-1);
}
rttyDecoderData.byteResultp++;
}
}
if (rttyDecoderData.byteResultp == 8 && rttyDecoderData.state == RTTY_RUN_STATE_BIT)
{
char charResult;
switch (rttyDecoderData.byteResult) {
case RTTY_LETTER_CODE:
rttyDecoderData.charSetMode = RTTY_MODE_LETTERS;
// System.out.println(" ^L^");
break;
case RTTY_SYMBOL_CODE:
rttyDecoderData.charSetMode = RTTY_MODE_SYMBOLS;
// System.out.println(" ^F^");
break;
default:
switch (rttyDecoderData.charSetMode)
{
case RTTY_MODE_SYMBOLS:
charResult = RTTYSymbols[rttyDecoderData.byteResult];
break;
case RTTY_MODE_LETTERS:
default:
charResult = RTTYLetters[rttyDecoderData.byteResult];
break;
}
UiDriver_TextMsgPutChar(charResult);
break;
}
rttyDecoderData.state = RTTY_RUN_STATE_WAIT_START;
}
}
}
typedef enum
{
MSK_IDLE = 0,
MSK_WAIT_FOR_NEG,
MSK_WAIT_FOR_PLUS,
MSK_WAIT_FOR_PLUSGRAD, // wait for the values growing towards max
MSK_WAIT_FOR_MAX, // wait for the value after max, needs to be done one the growing part of the curve
} msk_state_t;
typedef struct
{
uint8_t char_bit_idx;
uint16_t char_bits;
uint32_t char_bit_samples;
uint16_t last_bit;
int16_t last_value;
uint8_t current_bit;
msk_state_t msk_state;
rtty_charSetMode_t char_mode;
} rtty_tx_encoder_state_t;
rtty_tx_encoder_state_t rtty_tx =
{
.last_bit = 1,
.last_value = INT16_MIN,
.char_mode = RTTY_MODE_LETTERS,
};
#define USE_RTTY_MSK
#define RTTY_CODE_MODE_MASK (0b100000)
#define RTTY_CODE_MODE_LETTER (RTTY_CODE_MODE_MASK)
#define RTTY_CODE_MODE_SYMBOL (0)
static void Rtty_BaudotAdd(uint8_t bits)
{
uint8_t bitCount = rttyDecoderData.config_p->stopbits == RTTY_STOP_1?7:8;
bits <<= 1; // add stop bit
if (bitCount == 7)
{
bits |= 0b01000000;
}
else
{
// for 1.5 and 2 we use 2 bits for now
bits |= 0b11000000;
}
rtty_tx.char_bits |= bits << rtty_tx.char_bit_idx;
// we add more bits towards the MSB if we already have bits
rtty_tx.char_bit_idx += bitCount;
// now remember how many bits we added
}
void Rtty_Modulator_Code2Bits(uint8_t baudot_info)
{
rtty_tx.char_bits = 0;
rtty_tx.char_bit_idx = 0;
if ((baudot_info & RTTY_CODE_MODE_MASK) == RTTY_CODE_MODE_LETTER)
{
if(rtty_tx.char_mode != RTTY_MODE_LETTERS)
{
rtty_tx.char_mode = RTTY_MODE_LETTERS;
Rtty_BaudotAdd(RTTY_LETTER_CODE);
}
}
else
{
if(rtty_tx.char_mode != RTTY_MODE_SYMBOLS)
{
rtty_tx.char_mode = RTTY_MODE_SYMBOLS;
Rtty_BaudotAdd(RTTY_SYMBOL_CODE);
}
}
Rtty_BaudotAdd(baudot_info & ~RTTY_CODE_MODE_MASK);
}
// MUST BE CALLED BEFORE WE START RTTY TX, i.e. in RX Mode !!!
void Rtty_Modulator_StartTX()
{
rtty_tx.last_bit = 1;
rtty_tx.last_value = INT16_MIN;
rtty_tx.char_mode = RTTY_MODE_LETTERS;
Rtty_Modulator_Code2Bits(RTTY_LETTER_CODE);
}
const char rtty_test_string[] = " --- UHSDR FIRMWARE RTTY TX TEST DE DB4PLE --- ";
int16_t Rtty_Modulator_GenSample()
{
if (rtty_tx.char_bit_samples == 0)
{
rtty_tx.char_bit_samples = rttyDecoderData.oneBitSampleCount * 4;
rtty_tx.char_bits >>= 1;
if (rtty_tx.char_bit_idx == 0)
{
// load the character and add the stop bits;
bool bitsFilled = false;
uint8_t current_ascii;
while ( bitsFilled == false && DigiModes_TxBufferRemove( &current_ascii, RTTY ) )
{
if (current_ascii == 0x04 ) //EOT
{
RadioManagement_Request_TxOff();
}
else
{
uint8_t current_baudot = Ascii2Baudot[current_ascii & 0x7f];
if (current_baudot > 0)
{ // we have valid baudot code
Rtty_Modulator_Code2Bits(current_baudot);
bitsFilled = true;
}
}
}
if (bitsFilled == false)
{
// IDLE
Rtty_Modulator_Code2Bits(RTTY_LETTER_CODE | RTTY_CODE_MODE_LETTER);
// we ensure that we will switch back to letter mode by making the switch symbol a letter
#if 0
for (uint8_t idx = 0; idx < sizeof(rtty_test_string); idx++)
{
DigiModes_TxBufferPutChar( rtty_test_string[idx] , CW );
}
#endif
}
}
rtty_tx.char_bit_idx--;
rtty_tx.current_bit = rtty_tx.char_bits&1;
#ifdef USE_RTTY_MSK
if (rtty_tx.last_bit != rtty_tx.current_bit)
{
if (rtty_tx.last_bit == 1)
{
rtty_tx.msk_state = MSK_WAIT_FOR_NEG;
// WAIT_FOR_NEG
// WAIT_FOR_ZERO or plus
rtty_tx.current_bit = rtty_tx.last_bit;
}
else
{
rtty_tx.msk_state = MSK_WAIT_FOR_PLUSGRAD;
rtty_tx.current_bit = rtty_tx.last_bit;
rtty_tx.last_value = INT16_MIN;
// WAIT_FOR_MAX
}
}
#else
rtty_tx.msk_state = MSK_IDLE;
#endif
}
rtty_tx.char_bit_samples--;
int16_t current_value = softdds_nextSample(&rttyDecoderData.tx_dds[rtty_tx.current_bit]);
switch(rtty_tx.msk_state)
{
case MSK_WAIT_FOR_NEG:
if (current_value < 0)
{
rtty_tx.msk_state = MSK_WAIT_FOR_PLUS;
}
break;
case MSK_WAIT_FOR_PLUS:
if (current_value >= 0)
{
rtty_tx.msk_state = MSK_IDLE;
rtty_tx.current_bit = rtty_tx.char_bits&1;
rttyDecoderData.tx_dds[rtty_tx.current_bit].acc = rttyDecoderData.tx_dds[rtty_tx.last_bit].acc;
rtty_tx.last_bit = rtty_tx.current_bit;
current_value = softdds_nextSample(&rttyDecoderData.tx_dds[rtty_tx.current_bit]);
}
break;
case MSK_WAIT_FOR_PLUSGRAD:
if (current_value > rtty_tx.last_value)
{
rtty_tx.msk_state = MSK_WAIT_FOR_MAX;
}
break;
case MSK_WAIT_FOR_MAX:
if (current_value >= rtty_tx.last_value)
{
rtty_tx.last_value = current_value;
}
else if (current_value < rtty_tx.last_value && rtty_tx.last_value > 0)
{
rtty_tx.msk_state = MSK_IDLE;
rtty_tx.current_bit = rtty_tx.char_bits&1;
rttyDecoderData.tx_dds[rtty_tx.current_bit].acc = rttyDecoderData.tx_dds[rtty_tx.last_bit].acc;
rtty_tx.last_bit = rtty_tx.current_bit;
current_value = softdds_nextSample(&rttyDecoderData.tx_dds[rtty_tx.current_bit]);
}
break;
default:
break;
}
rtty_tx.last_value = current_value;
return current_value;
}
#endif
+84
View File
@@ -0,0 +1,84 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR FIRMWARE **
** **
**---------------------------------------------------------------------------------**
** Licence: GNU GPLv3, see LICENSE.md **
************************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __RTTY_H
#define __RTTY_H
#include "uhsdr_types.h"
typedef enum {
RTTY_STOP_1 = 0,
RTTY_STOP_1_5,
RTTY_STOP_2
} rtty_stop_t;
typedef struct
{
float32_t speed;
rtty_stop_t stopbits;
uint16_t shift;
float32_t samplerate;
} rtty_mode_config_t;
typedef enum {
RTTY_SPEED_45,
RTTY_SPEED_50,
RTTY_SPEED_NUM
} rtty_speed_t;
typedef enum {
RTTY_SHIFT_85,
RTTY_SHIFT_170,
RTTY_SHIFT_200,
RTTY_SHIFT_425,
RTTY_SHIFT_450,
RTTY_SHIFT_850,
RTTY_SHIFT_NUM
} rtty_shift_t;
typedef struct
{
rtty_speed_t id;
float32_t value;
char* label;
} rtty_speed_item_t;
// TODO: Probably we should define just a few for the various value types and let
// the id be an uint32_t
typedef struct
{
rtty_shift_t id;
uint32_t value;
char* label;
} rtty_shift_item_t;
extern const rtty_speed_item_t rtty_speeds[RTTY_SPEED_NUM];
extern const rtty_shift_item_t rtty_shifts[RTTY_SHIFT_NUM];
extern float32_t decayavg(float32_t average, float32_t input, int weight);
// TODO: maybe this should be placed in the ui or radio management part
typedef struct
{
rtty_shift_t shift_idx;
rtty_speed_t speed_idx;
rtty_stop_t stopbits_idx;
bool atc_disable; // should the automatic level control be turned off?
} rtty_ctrl_t;
extern rtty_ctrl_t rtty_ctrl_config;
void Rtty_Modem_Init(uint32_t output_sample_rate);
void Rtty_Demodulator_ProcessSample(float32_t sample);
int16_t Rtty_Modulator_GenSample(void);
#endif
@@ -0,0 +1,89 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "uhsdr_types.h"
#include "dds_table.h"
// this table represents 2*PI, i.e. a full sine wave
const int16_t DDS_TABLE[DDS_TBL_SIZE] =
{
// 1024 points
0,
201, 402, 603, 804, 1005, 1206, 1406, 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011,
3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195,
6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319,
9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353,
12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268,
15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036,
18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631,
20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027,
23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201,
25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132,
27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802,
28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195,
30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, 30851, 30918, 30984, 31049, 31113, 31175, 31236, 31297,
31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097,
32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588,
32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766,
32766, 32766, 32764, 32761, 32757, 32751, 32744, 32736, 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628,
32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176,
32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413,
31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349,
30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992,
28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355,
27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456,
25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311,
23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942,
20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371,
18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623,
15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724,
12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703,
9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589,
6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411,
3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, 1607, 1406, 1206, 1005, 804, 603, 402, 201,
0,
-201, -402, -603, -804, -1005, -1206, -1406, -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011,
-3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195,
-6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319,
-9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353,
-12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268,
-15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036,
-18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631,
-20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027,
-23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201,
-25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132,
-27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802,
-28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195,
-30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297,
-31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097,
-32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588,
-32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766,
-32766, -32766, -32764, -32761, -32757, -32751, -32744, -32736, -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628,
-32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176,
-32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413,
-31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349,
-30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992,
-28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355,
-27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456,
-25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311,
-23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942,
-20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371,
-18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623,
-15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724,
-12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703,
-9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589,
-6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411,
-3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, -1607, -1406, -1206, -1005, -804, -603, -402, -201
};
@@ -0,0 +1,24 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __DDS_TABLE_H
#define __DDS_TABLE_H
#define DDS_TBL_BITS 10
#define DDS_TBL_SIZE (1 << DDS_TBL_BITS) // 10 = 1024
extern const int16_t DDS_TABLE[DDS_TBL_SIZE];
#endif
@@ -0,0 +1,157 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
// Credits - SDR cube!!!
// Common
#include "uhsdr_board.h"
#include "dds_table.h"
#include "softdds.h"
// Two Tone Dds
soft_dds_t dbldds[2];
uint32_t softdds_stepForSampleRate(float32_t freq, uint32_t samp_rate)
{
uint64_t freq64_shifted = freq * DDS_TBL_SIZE;
freq64_shifted <<= SOFTDDS_ACC_SHIFT;
uint64_t step = freq64_shifted / samp_rate;
return step;
}
/**
* Initialize softdds for given frequency and sample rate
*/
void softdds_setFreqDDS(soft_dds_t* softdds_p, float32_t freq, uint32_t samp_rate,uint8_t smooth)
{
// Reset accumulator, if need smooth tone
// transition, do not reset it (e.g. wspr)
if(smooth == false)
{
softdds_p->acc = 0;
}
// Calculate new step
softdds_p->step = softdds_stepForSampleRate(freq,samp_rate);
}
/**
* Initialize softdds for given frequency and sample rate
*/
void softdds_configRunIQ(float freq[2],uint32_t samp_rate,uint8_t smooth)
{
softdds_setFreqDDS(&dbldds[0],freq[0],samp_rate,smooth);
softdds_setFreqDDS(&dbldds[1],freq[1],samp_rate,smooth);
}
void softdds_genIQSingleTone(soft_dds_t* dds, float32_t *i_buff,float32_t *q_buff,uint16_t size)
{
for(uint16_t i = 0; i < size; i++)
{
// Calculate next sample
uint32_t k = softdds_nextSampleIndex(dds);
// Load I value (sin)
*i_buff = DDS_TABLE[k];
// -90 degrees shift (cos)
// Load Q value
*q_buff = DDS_TABLE[softdds_phase_shift90(k)];
// Next ptr
i_buff++;
q_buff++;
}
}
/*
* Generates the addition of two sinus frequencies as IQ data stream
* min/max value is +/-2^15-1
* Frequencies need to be configured using softdds_setfreq_dbl
*/
void softdds_genIQTwoTone(soft_dds_t* ddsA, soft_dds_t* ddsB, float *i_buff,float *q_buff,ushort size)
{
for(int i = 0; i < size; i++)
{
uint32_t k[2];
// Calculate next sample
k[0] = softdds_nextSampleIndex(ddsA);
k[1] = softdds_nextSampleIndex(ddsB);
// Load I value 0.5*(sin(a)+sin(b))
*i_buff = ((int32_t)DDS_TABLE[k[0]] + (int32_t)DDS_TABLE[k[1]])/2;
*q_buff = ((int32_t)DDS_TABLE[softdds_phase_shift90(k[0])] + (int32_t)DDS_TABLE[softdds_phase_shift90(k[1])])/2;
// Next ptr
i_buff++;
q_buff++;
}
}
/**
* Overlays an audio stream with a beep signal
* @param dds The previously initialized dds configuration
* @param buffer audio buffer of blockSize (mono/single channel) samples
* @param blockSize
* @param scaling scale the resulting sine wave with this factor
*/
void softdds_addSingleTone(soft_dds_t* dds_ptr, float32_t* buffer, const size_t blockSize, float32_t scaling)
{
for(int i=0; i < blockSize; i++) // transfer to DMA buffer and do conversion to INT
{
buffer[i] += (float32_t)softdds_nextSample(dds_ptr) * scaling; // load indexed sine wave value, adding it to audio, scaling the amplitude and putting it on "b" - speaker (ONLY)
}
}
/**
* Overlays an audio stream with a beep signal
* @param dds The previously initialized dds configuration
* @param buffer1 audio buffer of blockSize (mono/single channel) samples
* @param buffer2 audio buffer of blockSize (mono/single channel) samples
* @param blockSize
* @param scaling scale the resulting sine wave with this factor
*/
void softdds_addSingleToneToTwobuffers(soft_dds_t* dds_ptr, float32_t* buffer1, float32_t* buffer2, const size_t blockSize, float32_t scaling)
{
float32_t Tone;
for(int i=0; i < blockSize; i++) // transfer to DMA buffer and do conversion to INT
{
Tone=(float32_t) softdds_nextSample(dds_ptr) * scaling; // load indexed sine wave value, adding it to audio, scaling the amplitude and putting it on "b" - speaker (ONLY)
buffer1[i] += Tone;
buffer2[i] += Tone;
}
}
/*
* Generates the sinus frequencies as IQ data stream
* min/max value is +/-2^15-1
* Frequency needs to be configured using softdds_setfreq
*/
void softdds_runIQ(float32_t *i_buff,float32_t *q_buff,uint16_t size)
{
if (dbldds[1].step>0.0)
{
softdds_genIQTwoTone(&dbldds[0], &dbldds[1], i_buff, q_buff,size);
}
else
{
softdds_genIQSingleTone(&dbldds[0],i_buff,q_buff,size);
}
}
@@ -0,0 +1,81 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __SOFTDDS_H
#define __SOFTDDS_H
#include "dds_table.h"
// we use a 32 bit accumulator
#define SOFTDDS_ACC_SHIFT (32-DDS_TBL_BITS)
// Soft DDS public structure
typedef struct
{
// DDS accumulator
uint32_t acc;
// DDS step - not working if part of the structure
uint32_t step;
} soft_dds_t;
/**
* Execute a single step in the sinus generation
*/
inline uint32_t softdds_nextSampleIndex(soft_dds_t* dds)
{
uint32_t retval = (dds->acc >> SOFTDDS_ACC_SHIFT)%DDS_TBL_SIZE;
dds->acc += dds->step;
// now scale down precision and make sure that
// index wraps around properly
return retval;
}
/*
* Get the index which represents a -90 degree shift compared to
* k, i.e. get k = sin(a) => cos(a)
*/
inline uint32_t softdds_phase_shift90(uint32_t k)
{
// make sure that
// index wraps around properly
return (k+(3*DDS_TBL_SIZE/4))%DDS_TBL_SIZE;
}
/**
* Execute a single step in the sinus generation and return actual sample value
*/
inline int16_t softdds_nextSample(soft_dds_t* dds)
{
return DDS_TABLE[softdds_nextSampleIndex(dds)];
}
void softdds_setFreqDDS(soft_dds_t* dds, float32_t freq, uint32_t samp_rate, uint8_t smooth);
void softdds_genIQSingleTone(soft_dds_t* dds, float32_t *i_buff,float32_t *q_buff,uint16_t size);
void softdds_genIQTwoTone(soft_dds_t* ddsA, soft_dds_t* ddsB, float *i_buff,float *q_buff,ushort size);
void softdds_addSingleTone(soft_dds_t* dds_ptr, float32_t* buffer, const size_t blockSize, float32_t scaling);
void softdds_addSingleToneToTwobuffers(soft_dds_t* dds_ptr, float32_t* buffer1, float32_t* buffer2, const size_t blockSize, float32_t scaling);
void softdds_runIQ(float32_t *i_buff, float32_t *q_buff, uint16_t size);
void softdds_configRunIQ(float32_t freq[2],uint32_t samp_rate,uint8_t smooth);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** UHSDR **
** a powerful firmware for STM32 based SDR transceivers **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __TX_PROCESSOR_H
#define __TX_PROCESSOR_H
#include "uhsdr_board_config.h"
#include "uhsdr_types.h"
#include "audio_driver.h" // for types
void TxProcessor_Init(void);
void TxProcessor_Set(uint8_t dmod_mode);
void TxProcessor_PrepareRun(void);
void TxProcessor_Run(AudioSample_t * const srcCodec, IqSample_t * const dst, AudioSample_t * const audioDst, uint16_t blockSize, bool external_mute);
#endif
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#ifndef __CAT_DRIVER_H
#define __CAT_DRIVER_H
#include "uhsdr_types.h"
typedef enum
{
CAT_DISCONNECTED = 0,
CAT_CONNECTED
} CatInterfaceState;
typedef enum
{
UNKNOWN = 0,
FT817 = 1
} CatInterfaceProtocol;
// Exports
CatInterfaceState CatDriver_GetInterfaceState(void);
int CatDriver_InterfaceBufferAddData(uint8_t c);
void CatDriver_HandleProtocol(void);
bool CatDriver_CloneOutStart(void);
bool CatDriver_CloneInStart(void);
bool CatDriver_CWKeyPressed(void);
bool CatDriver_CatPttActive(void);
#endif
+76
View File
@@ -0,0 +1,76 @@
//
// This file is part of the µOS++ III distribution.
// Copyright (c) 2014 Liviu Ionescu.
//
// ----------------------------------------------------------------------------
#if defined(TRACE)
#include <stdio.h>
#include <stdarg.h>
#include "diag/Trace.h"
#include "string.h"
#ifndef OS_INTEGER_TRACE_PRINTF_TMP_ARRAY_SIZE
#define OS_INTEGER_TRACE_PRINTF_TMP_ARRAY_SIZE (128)
#endif
// ----------------------------------------------------------------------------
int
trace_printf(const char* format, ...)
{
int ret;
va_list ap;
va_start (ap, format);
// TODO: rewrite it to no longer use newlib, it is way too heavy
static char buf[OS_INTEGER_TRACE_PRINTF_TMP_ARRAY_SIZE];
// Print to the local buffer
ret = vsnprintf (buf, sizeof(buf), format, ap);
if (ret > 0)
{
// Transfer the buffer to the device
ret = trace_write (buf, (size_t)ret);
}
va_end (ap);
return ret;
}
int
trace_puts(const char *s)
{
trace_write(s, strlen(s));
return trace_write("\n", 1);
}
int
trace_putchar(int c)
{
trace_write((const char*)&c, 1);
return c;
}
void
trace_dump_args(int argc, char* argv[])
{
trace_printf("main(argc=%d, argv=[", argc);
for (int i = 0; i < argc; ++i)
{
if (i != 0)
{
trace_printf(", ");
}
trace_printf("\"%s\"", argv[i]);
}
trace_printf("]);\n");
}
// ----------------------------------------------------------------------------
#endif // TRACE
+146
View File
@@ -0,0 +1,146 @@
//
// This file is part of the µOS++ III distribution.
// Copyright (c) 2014 Liviu Ionescu.
//
#ifndef DIAG_TRACE_H_
#define DIAG_TRACE_H_
// ----------------------------------------------------------------------------
#include <unistd.h>
// ----------------------------------------------------------------------------
// The trace device is an independent output channel, intended for debug
// purposes.
//
// The API is simple, and mimics the standard output calls:
// - trace_printf()
// - trace_puts()
// - trace_putchar();
//
// The implementation is done in
// - trace_write()
//
// Trace support is enabled by adding the TRACE definition.
// By default the trace messages are forwarded to the ITM output,
// but can be rerouted via any device or completely suppressed by
// changing the definitions required in system/src/diag/trace_impl.c
// (currently OS_USE_TRACE_ITM, OS_USE_TRACE_SEMIHOSTING_DEBUG/_STDOUT).
//
// When TRACE is not defined, all functions are inlined to empty bodies.
// This has the advantage that the trace call do not need to be conditionally
// compiled with #ifdef TRACE/#endif
#if defined(TRACE)
#if defined(__cplusplus)
extern "C"
{
#endif
void
trace_initialize(void);
// Implementation dependent
ssize_t
trace_write(const char* buf, size_t nbyte);
// ----- Portable -----
int
trace_printf(const char* format, ...);
int
trace_puts(const char *s);
int
trace_putchar(int c);
void
trace_dump_args(int argc, char* argv[]);
#if defined(__cplusplus)
}
#endif
#else // !defined(TRACE)
#if defined(__cplusplus)
extern "C"
{
#endif
inline void
trace_initialize(void);
// Implementation dependent
inline ssize_t
trace_write(const char* buf, size_t nbyte);
inline int
trace_printf(const char* format, ...);
inline int
trace_puts(const char *s);
inline int
trace_putchar(int c);
inline void
trace_dump_args(int argc, char* argv[]);
#if defined(__cplusplus)
}
#endif
inline void
__attribute__((always_inline))
trace_initialize(void)
{
}
// Empty definitions when trace is not defined
inline ssize_t
__attribute__((always_inline))
trace_write(const char* buf __attribute__((unused)),
size_t nbyte __attribute__((unused)))
{
return 0;
}
inline int
__attribute__((always_inline))
trace_printf(const char* format __attribute__((unused)), ...)
{
return 0;
}
inline int
__attribute__((always_inline))
trace_puts(const char *s __attribute__((unused)))
{
return 0;
}
inline int
__attribute__((always_inline))
trace_putchar(int c)
{
return c;
}
inline void
__attribute__((always_inline))
trace_dump_args(int argc __attribute__((unused)),
char* argv[] __attribute__((unused)))
{
}
#endif // defined(TRACE)
// ----------------------------------------------------------------------------
#endif // DIAG_TRACE_H_
@@ -0,0 +1,120 @@
//
// This file is part of the µOS++ III distribution.
// Copyright (c) 2014 Liviu Ionescu.
//
#ifndef ARM_SEMIHOSTING_H_
#define ARM_SEMIHOSTING_H_
// ----------------------------------------------------------------------------
// Semihosting operations.
enum OperationNumber
{
// Regular operations
SEMIHOSTING_EnterSVC = 0x17,
SEMIHOSTING_ReportException = 0x18,
SEMIHOSTING_SYS_CLOSE = 0x02,
SEMIHOSTING_SYS_CLOCK = 0x10,
SEMIHOSTING_SYS_ELAPSED = 0x30,
SEMIHOSTING_SYS_ERRNO = 0x13,
SEMIHOSTING_SYS_FLEN = 0x0C,
SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
SEMIHOSTING_SYS_HEAPINFO = 0x16,
SEMIHOSTING_SYS_ISERROR = 0x08,
SEMIHOSTING_SYS_ISTTY = 0x09,
SEMIHOSTING_SYS_OPEN = 0x01,
SEMIHOSTING_SYS_READ = 0x06,
SEMIHOSTING_SYS_READC = 0x07,
SEMIHOSTING_SYS_REMOVE = 0x0E,
SEMIHOSTING_SYS_RENAME = 0x0F,
SEMIHOSTING_SYS_SEEK = 0x0A,
SEMIHOSTING_SYS_SYSTEM = 0x12,
SEMIHOSTING_SYS_TICKFREQ = 0x31,
SEMIHOSTING_SYS_TIME = 0x11,
SEMIHOSTING_SYS_TMPNAM = 0x0D,
SEMIHOSTING_SYS_WRITE = 0x05,
SEMIHOSTING_SYS_WRITEC = 0x03,
SEMIHOSTING_SYS_WRITE0 = 0x04,
// Codes returned by SEMIHOSTING_ReportException
ADP_Stopped_ApplicationExit = ((2 << 16) + 38),
ADP_Stopped_RunTimeError = ((2 << 16) + 35),
};
// ----------------------------------------------------------------------------
// SWI numbers and reason codes for RDI (Angel) monitors.
#define AngelSWI_ARM 0x123456
#ifdef __thumb__
#define AngelSWI 0xAB
#else
#define AngelSWI AngelSWI_ARM
#endif
// For thumb only architectures use the BKPT instruction instead of SWI.
#if defined(__ARM_ARCH_7M__) \
|| defined(__ARM_ARCH_7EM__) \
|| defined(__ARM_ARCH_6M__)
#define AngelSWIInsn "bkpt"
#define AngelSWIAsm bkpt
#else
#define AngelSWIInsn "swi"
#define AngelSWIAsm swi
#endif
#if defined(OS_DEBUG_SEMIHOSTING_FAULTS)
// Testing the local semihosting handler cannot use another BKPT, since this
// configuration cannot trigger HaedFault exceptions while the debugger is
// connected, so we use an illegal op code, that will trigger an
// UsageFault exception.
#define AngelSWITestFault "setend be"
#define AngelSWITestFaultOpCode (0xB658)
#endif
static inline int
__attribute__ ((always_inline))
call_host (int reason, void* arg)
{
int value;
asm volatile (
" mov r0, %[rsn] \n"
" mov r1, %[arg] \n"
#if defined(OS_DEBUG_SEMIHOSTING_FAULTS)
" " AngelSWITestFault " \n"
#else
" " AngelSWIInsn " %[swi] \n"
#endif
" mov %[val], r0"
: [val] "=r" (value) /* Outputs */
: [rsn] "r" (reason), [arg] "r" (arg), [swi] "i" (AngelSWI) /* Inputs */
: "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc"
// Clobbers r0 and r1, and lr if in supervisor mode
);
// Accordingly to page 13-77 of ARM DUI 0040D other registers
// can also be clobbered. Some memory positions may also be
// changed by a system call, so they should not be kept in
// registers. Note: we are assuming the manual is right and
// Angel is respecting the APCS.
return value;
}
// ----------------------------------------------------------------------------
// Function used in _exit() to return the status code as Angel exception.
static inline void
__attribute__ ((always_inline,noreturn))
report_exception (int reason)
{
call_host (SEMIHOSTING_ReportException, (void*) reason);
for (;;)
;
}
// ----------------------------------------------------------------------------
#endif // ARM_SEMIHOSTING_H_
+252
View File
@@ -0,0 +1,252 @@
//
// This file is part of the µOS++ III distribution.
// Copyright (c) 2014 Liviu Ionescu.
//
// ----------------------------------------------------------------------------
#if defined(TRACE)
#include "cmsis_device.h"
#include "diag/Trace.h"
// ----------------------------------------------------------------------------
// One of these definitions must be passed via the compiler command line
// Note: small Cortex-M0/M0+ might implement a simplified debug interface.
//#define OS_USE_TRACE_ITM
//#define OS_USE_TRACE_SEMIHOSTING_DEBUG
//#define OS_USE_TRACE_SEMIHOSTING_STDOUT
#if !(defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__))
#if defined(OS_USE_TRACE_ITM)
#undef OS_USE_TRACE_ITM
#warning "ITM unavailable"
#endif // defined(OS_USE_TRACE_ITM)
#endif // !(defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__))
#if defined(OS_DEBUG_SEMIHOSTING_FAULTS)
#if defined(OS_USE_TRACE_SEMIHOSTING_STDOUT) || defined(OS_USE_TRACE_SEMIHOSTING_DEBUG)
#error "Cannot debug semihosting using semihosting trace; use OS_USE_TRACE_ITM"
#endif
#endif
// ----------------------------------------------------------------------------
// Forward definitions.
#if defined(OS_USE_TRACE_ITM)
static ssize_t
_trace_write_itm (const char* buf, size_t nbyte);
#endif
#if defined(OS_USE_TRACE_SEMIHOSTING_STDOUT)
static ssize_t
_trace_write_semihosting_stdout(const char* buf, size_t nbyte);
#endif
#if defined(OS_USE_TRACE_SEMIHOSTING_DEBUG)
static ssize_t
_trace_write_semihosting_debug(const char* buf, size_t nbyte);
#endif
// ----------------------------------------------------------------------------
void
trace_initialize(void)
{
// For regular ITM / semihosting, no inits required.
}
// ----------------------------------------------------------------------------
// This function is called from _write() for fd==1 or fd==2 and from some
// of the trace_* functions.
ssize_t
trace_write (const char* buf __attribute__((unused)),
size_t nbyte __attribute__((unused)))
{
#if defined(OS_USE_TRACE_ITM)
return _trace_write_itm (buf, nbyte);
#elif defined(OS_USE_TRACE_SEMIHOSTING_STDOUT)
return _trace_write_semihosting_stdout(buf, nbyte);
#elif defined(OS_USE_TRACE_SEMIHOSTING_DEBUG)
return _trace_write_semihosting_debug(buf, nbyte);
#endif
return -1;
}
// ----------------------------------------------------------------------------
#if defined(OS_USE_TRACE_ITM)
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
// ITM is the ARM standard mechanism, running over SWD/SWO on Cortex-M3/M4
// devices, and is the recommended setting, if available.
//
// The JLink probe and the GDB server fully support SWD/SWO
// and the JLink Debugging plug-in enables it by default.
// The current OpenOCD does not include support to parse the SWO stream,
// so this configuration will not work on OpenOCD (will not crash, but
// nothing will be displayed in the output console).
#if !defined(OS_INTEGER_TRACE_ITM_STIMULUS_PORT)
#define OS_INTEGER_TRACE_ITM_STIMULUS_PORT (0)
#endif
static ssize_t
_trace_write_itm (const char* buf, size_t nbyte)
{
for (size_t i = 0; i < nbyte; i++)
{
// Check if ITM or the stimulus port are not enabled
if (((ITM->TCR & ITM_TCR_ITMENA_Msk) == 0)
|| ((ITM->TER & (1UL << OS_INTEGER_TRACE_ITM_STIMULUS_PORT)) == 0))
{
return (ssize_t)i; // return the number of sent characters (may be 0)
}
// Wait until STIMx is ready...
while (ITM->PORT[OS_INTEGER_TRACE_ITM_STIMULUS_PORT].u32 == 0)
;
// then send data, one byte at a time
ITM->PORT[OS_INTEGER_TRACE_ITM_STIMULUS_PORT].u8 = (uint8_t) (*buf++);
}
return (ssize_t)nbyte; // all characters successfully sent
}
#endif // defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
#endif // OS_USE_TRACE_ITM
// ----------------------------------------------------------------------------
#if defined(OS_USE_TRACE_SEMIHOSTING_DEBUG) || defined(OS_USE_TRACE_SEMIHOSTING_STDOUT)
#include "arm/semihosting.h"
// Semihosting is the other output channel that can be used for the trace
// messages. It comes in two flavours: STDOUT and DEBUG. The STDOUT channel
// is the equivalent of the stdout in POSIX and in most cases it is forwarded
// to the GDB server stdout stream. The debug channel is a separate
// channel. STDOUT is buffered, so nothing is displayed until a \n;
// DEBUG is not buffered, but can be slow.
//
// Choosing between semihosting stdout and debug depends on the capabilities
// of your GDB server, and also on specific needs. It is recommended to test
// DEBUG first, and if too slow, try STDOUT.
//
// The JLink GDB server fully support semihosting, and both configurations
// are available; to activate it, use "monitor semihosting enable" or check
// the corresponding button in the JLink Debugging plug-in.
// In OpenOCD, support for semihosting can be enabled using
// "monitor arm semihosting enable".
//
// Note: Applications built with semihosting output active normally cannot
// be executed without the debugger connected and active, since they use
// BKPT to communicate with the host. However, with a carefully written
// HardFault_Handler, the semihosting BKPT calls can be processed, making
// possible to run semihosting applications as standalone, without being
// terminated with hardware faults.
#endif // OS_USE_TRACE_SEMIHOSTING_DEBUG_*
// ----------------------------------------------------------------------------
#if defined(OS_USE_TRACE_SEMIHOSTING_STDOUT)
static ssize_t
_trace_write_semihosting_stdout (const char* buf, size_t nbyte)
{
static int handle;
void* block[3];
int ret;
if (handle == 0)
{
// On the first call get the file handle from the host
block[0] = ":tt"; // special filename to be used for stdin/out/err
block[1] = (void*) 4; // mode "w"
// length of ":tt", except null terminator
block[2] = (void*) (sizeof(":tt") - 1);
ret = call_host (SEMIHOSTING_SYS_OPEN, (void*) block);
if (ret == -1)
return -1;
handle = ret;
}
block[0] = (void*) handle;
block[1] = (void*) buf;
block[2] = (void*) nbyte;
// send character array to host file/device
ret = call_host (SEMIHOSTING_SYS_WRITE, (void*) block);
// this call returns the number of bytes NOT written (0 if all ok)
// -1 is not a legal value, but SEGGER seems to return it
if (ret == -1)
return -1;
// The compliant way of returning errors
if (ret == (int) nbyte)
return -1;
// Return the number of bytes written
return (ssize_t) (nbyte) - (ssize_t) ret;
}
#endif // OS_USE_TRACE_SEMIHOSTING_STDOUT
// ----------------------------------------------------------------------------
#if defined(OS_USE_TRACE_SEMIHOSTING_DEBUG)
#define OS_INTEGER_TRACE_TMP_ARRAY_SIZE (16)
static ssize_t
_trace_write_semihosting_debug (const char* buf, size_t nbyte)
{
// Since the single character debug channel is quite slow, try to
// optimise and send a null terminated string, if possible.
if (buf[nbyte] == '\0')
{
// send string
call_host (SEMIHOSTING_SYS_WRITE0, (void*) buf);
}
else
{
// If not, use a local buffer to speed things up
char tmp[OS_INTEGER_TRACE_TMP_ARRAY_SIZE];
size_t togo = nbyte;
while (togo > 0)
{
unsigned int n = ((togo < sizeof(tmp)) ? togo : sizeof(tmp));
unsigned int i = 0;
for (; i < n; ++i, ++buf)
{
tmp[i] = *buf;
}
tmp[i] = '\0';
call_host (SEMIHOSTING_SYS_WRITE0, (void*) tmp);
togo -= n;
}
}
// All bytes written
return (ssize_t) nbyte;
}
#endif // OS_USE_TRACE_SEMIHOSTING_DEBUG
#endif // TRACE
// ----------------------------------------------------------------------------
@@ -0,0 +1,279 @@
#
# Generated sources
#
set(D ${CMAKE_CURRENT_SOURCE_DIR}/codebook)
# lsp quantisers
set(CODEBOOKS
${D}/lsp1.txt
${D}/lsp2.txt
${D}/lsp3.txt
${D}/lsp4.txt
${D}/lsp5.txt
${D}/lsp6.txt
${D}/lsp7.txt
${D}/lsp8.txt
${D}/lsp9.txt
${D}/lsp10.txt
)
# lspd quantisers
set(CODEBOOKSD
${D}/dlsp1.txt
${D}/dlsp2.txt
${D}/dlsp3.txt
${D}/dlsp4.txt
${D}/dlsp5.txt
${D}/dlsp6.txt
${D}/dlsp7.txt
${D}/dlsp8.txt
${D}/dlsp9.txt
${D}/dlsp10.txt
)
set(CODEBOOKSJVM
${D}/lspjvm1.txt
${D}/lspjvm2.txt
${D}/lspjvm3.txt
)
set(CODEBOOKSMEL
${D}/mel1.txt
${D}/mel2.txt
${D}/mel3.txt
${D}/mel4.txt
${D}/mel5.txt
${D}/mel6.txt
)
set(CODEBOOKSLSPMELVQ
${D}/lspmelvq1.txt
${D}/lspmelvq2.txt
${D}/lspmelvq3.txt
)
set(CODEBOOKSGE ${D}/gecb.txt)
# when crosscompiling we need a native executable
if(CMAKE_CROSSCOMPILING)
include(ExternalProject)
ExternalProject_Add(codec2_native
SOURCE_DIR ${CMAKE_SOURCE_DIR}
BUILD_COMMAND $(MAKE) generate_codebook
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy src/generate_codebook ${CMAKE_CURRENT_BINARY_DIR}
)
add_executable(generate_codebook IMPORTED)
set_target_properties(generate_codebook PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/generate_codebook)
add_dependencies(generate_codebook codec2_native)
else(CMAKE_CROSSCOMPILING)
# Build code generator binaries. These do not get installed.
# generate_codebook
add_executable(generate_codebook generate_codebook.c)
target_link_libraries(generate_codebook ${CMAKE_REQUIRED_LIBRARIES})
# genlspdtcb
add_executable(genlspdtcb genlspdtcb.c)
target_link_libraries(genlspdtcb ${CMAKE_REQUIRED_LIBRARIES})
# Make native builds available for cross-compiling.
export(TARGETS generate_codebook genlspdtcb
FILE ${CMAKE_BINARY_DIR}/ImportExecutables.cmake)
endif(CMAKE_CROSSCOMPILING)
# codebook.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codebook.c
COMMAND generate_codebook lsp_cb ${CODEBOOKS} > ${CMAKE_CURRENT_BINARY_DIR}/codebook.c
DEPENDS generate_codebook ${CODEBOOKS}
)
# codebookd.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codebookd.c
COMMAND generate_codebook lsp_cbd ${CODEBOOKSD} > ${CMAKE_CURRENT_BINARY_DIR}/codebookd.c
DEPENDS generate_codebook ${CODEBOOKSD}
)
# codebookjvm.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codebookjvm.c
COMMAND generate_codebook lsp_cbjvm ${CODEBOOKSJVM} > ${CMAKE_CURRENT_BINARY_DIR}/codebookjvm.c
DEPENDS generate_codebook ${CODEBOOKSJVM}
)
# codebookmel.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codebookmel.c
COMMAND generate_codebook mel_cb ${CODEBOOKSMEL} > ${CMAKE_CURRENT_BINARY_DIR}/codebookmel.c
DEPENDS generate_codebook ${CODEBOOKSMEL}
)
# codebooklspmelvq.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codebooklspmelvq.c
COMMAND generate_codebook lspmelvq_cb ${CODEBOOKSLSPMELVQ} > ${CMAKE_CURRENT_BINARY_DIR}/codebooklspmelvq.c
DEPENDS generate_codebook ${CODEBOOKSLSPMELVQ}
)
# codebookge.c
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/codebookge.c
COMMAND generate_codebook ge_cb ${CODEBOOKSGE} > ${CMAKE_CURRENT_BINARY_DIR}/codebookge.c
DEPENDS generate_codebook ${CODEBOOKSGE}
)
#
# codec2 library sources
#
set(CODEC2_SRCS
dump.c
lpc.c
nlp.c
postfilter.c
sine.c
codec2.c
cohpsk.c
fifo.c
fdmdv.c
fm.c
kiss_fft.c
linreg.c
interp.c
lsp.c
phase.c
quantise.c
pack.c
codebook.c
codebookd.c
codebookjvm.c
codebookmel.c
codebooklspmelvq.c
codebookge.c
golay23.c
freedv_api.c
varicode.c
modem_stats.c
)
set(CODEC2_PUBLIC_HEADERS
golay23.h
codec2.h
codec2_fdmdv.h
codec2_cohpsk.h
codec2_fm.h
codec2_fifo.h
comp.h
comp_prim.h
modem_stats.h
kiss_fft.h
freedv_api.h
varicode.h
freedv_api_internal.h
)
#
# Setup the codec2 library
#
add_library(codec2 ${CODEC2_SRCS})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(codec2 ${CMAKE_REQUIRED_LIBRARIES})
set_target_properties(codec2 PROPERTIES
SOVERSION ${SOVERSION}
PUBLIC_HEADER "${CODEC2_PUBLIC_HEADERS}"
)
add_executable(c2demo c2demo.c)
target_link_libraries(c2demo ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(c2enc c2enc.c)
target_link_libraries(c2enc ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(c2dec c2dec.c)
target_link_libraries(c2dec ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(c2sim c2sim.c ampexp.c phaseexp.c)
target_link_libraries(c2sim ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(fdmdv_get_test_bits fdmdv_get_test_bits.c fdmdv.c kiss_fft.c)
target_link_libraries(fdmdv_get_test_bits ${CMAKE_REQUIRED_LIBRARIES})
add_executable(fdmdv_mod fdmdv_mod.c fdmdv.c kiss_fft.c)
target_link_libraries(fdmdv_mod ${CMAKE_REQUIRED_LIBRARIES})
add_executable(fdmdv_demod fdmdv_demod.c fdmdv.c kiss_fft.c octave.c modem_stats.c)
target_link_libraries(fdmdv_demod ${CMAKE_REQUIRED_LIBRARIES})
add_executable(fdmdv_put_test_bits fdmdv_put_test_bits.c fdmdv.c kiss_fft.c)
target_link_libraries(fdmdv_put_test_bits ${CMAKE_REQUIRED_LIBRARIES})
add_executable(fdmdv_channel fdmdv_channel.c)
target_link_libraries(fdmdv_channel ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(fdmdv_interleave fdmdv_interleave.c)
target_link_libraries(fdmdv_interleave ${CMAKE_REQUIRED_LIBRARIES})
add_executable(insert_errors insert_errors.c)
target_link_libraries(insert_errors ${CMAKE_REQUIRED_LIBRARIES})
add_executable(fec_enc fec_enc.c golay23.c)
target_link_libraries(fec_enc ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(fec_dec fec_dec.c golay23.c)
target_link_libraries(fec_dec ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(freedv_tx freedv_tx.c)
target_link_libraries(freedv_tx ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(freedv_rx freedv_rx.c)
target_link_libraries(freedv_rx ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(fm_demod fm_demod.c fm.c)
target_link_libraries(fm_demod ${CMAKE_REQUIRED_LIBRARIES})
add_executable(cohpsk_mod cohpsk_mod.c)
target_link_libraries(cohpsk_mod ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(cohpsk_demod cohpsk_demod.c octave.c)
target_link_libraries(cohpsk_demod ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(cohpsk_get_test_bits cohpsk_get_test_bits.c)
target_link_libraries(cohpsk_get_test_bits ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(cohpsk_put_test_bits cohpsk_put_test_bits.c octave.c)
target_link_libraries(cohpsk_put_test_bits ${CMAKE_REQUIRED_LIBRARIES} codec2)
add_executable(cohpsk_ch cohpsk_ch.c)
target_link_libraries(cohpsk_ch ${CMAKE_REQUIRED_LIBRARIES} codec2)
install(TARGETS codec2 EXPORT codec2-config
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/codec2
)
#install(EXPORT codec2-config
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/codec2
#)
install(TARGETS
c2demo
c2demo
c2enc
c2dec
c2sim
fdmdv_get_test_bits
fdmdv_mod fdmdv_demod
fm_demod
fdmdv_put_test_bits
fdmdv_interleave
insert_errors
fec_enc
fec_dec
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
@@ -0,0 +1,22 @@
/*
FILE....: HRA_112_112.c
Static arrays for LDPC codec HRA_112_112, generated by ldpc_gen_c_h_file.m.
*/
#include <stdint.h>
#include "HRA_112_112.h"
const uint16_t HRA_112_112_H_rows[] = {
22, 18, 15, 63, 16, 13, 1, 2, 29, 25, 28, 4, 36, 10, 38, 7, 60, 23, 11, 38, 28, 1, 12, 31, 57, 45, 57, 30, 23, 59, 67, 14, 16, 4, 14, 62, 15, 50, 7, 70, 64, 6, 42, 48, 9, 31, 19, 40, 49, 2, 25, 3, 41, 49, 36, 9, 29, 39, 31, 5, 17, 1, 29, 25, 11, 21, 18, 2, 8, 22, 39, 15, 8, 22, 13, 3, 19, 4, 21, 62, 34, 43, 6, 24, 17, 60, 8, 74, 6, 44, 60, 10, 33, 12, 26, 24, 45, 81, 69, 80, 41, 28, 23, 5, 10, 20, 52, 18, 13, 86, 3, 7, 59, 21, 65, 72, 34, 37, 26, 55, 47, 48, 34, 5, 44, 47, 68, 96, 82, 111, 61, 74, 30, 17, 55, 98, 81, 66, 89, 35, 74, 82, 91, 51, 55, 51, 30, 89, 61, 75, 40, 71, 73, 11, 56, 54, 19, 47, 94, 69, 64, 20, 64, 12, 54, 77, 42, 88, 36, 52, 90, 63, 70, 27, 32, 73, 91, 32, 56, 46, 9, 78, 51, 68, 88, 67, 20, 43, 40, 14, 66, 86, 39, 97, 38, 27, 50, 84, 54, 92, 61, 46, 67, 24, 58, 35, 58, 37, 98, 85, 73, 84, 48, 35, 57, 16, 26, 37, 65, 32, 72, 95, 107, 33, 77, 33, 85, 105, 106, 75, 56, 71, 79, 59, 52, 105, 79, 90, 93, 100, 88, 112, 86, 80, 65, 42, 106, 100, 93, 94, 99, 97, 93, 101, 111, 99, 83, 53, 85, 95, 108, 107, 41, 109, 84, 78, 104, 101, 69, 110, 98, 103, 80, 83, 77, 71, 76, 78, 87, 102, 104, 95, 96, 83, 87, 50, 110, 103, 112, 45, 58, 70, 94, 91, 89, 81, 101, 82, 63, 72, 100, 97, 76, 112, 53, 105, 49, 75, 109, 102, 66, 111, 68, 87, 92, 79, 96, 43, 90, 44, 110, 99, 102, 92, 103, 106, 62, 53, 27, 46, 108, 104, 107, 108, 109, 76
};
const uint16_t HRA_112_112_H_cols[] = {
7, 8, 52, 12, 12, 42, 16, 69, 45, 14, 19, 23, 6, 32, 3, 5, 22, 2, 45, 50, 2, 1, 18, 84, 10, 7, 62, 11, 9, 21, 24, 63, 2, 5, 28, 13, 6, 15, 58, 39, 39, 22, 76, 13, 26, 68, 9, 10, 49, 38, 32, 11, 34, 44, 8, 7, 25, 67, 1, 17, 19, 36, 4, 41, 3, 26, 31, 15, 45, 40, 8, 4, 41, 20, 6, 53, 1, 42, 9, 20, 25, 17, 33, 41, 3, 19, 55, 17, 27, 14, 31, 88, 15, 26, 36, 16, 28, 24, 27, 16, 30, 56, 48, 43, 4, 5, 38, 37, 40, 46, 18, 18, 22, 50, 76, 34, 60, 83, 39, 73, 56, 92, 42, 52, 75, 35, 37, 33, 61, 67, 47, 75, 66, 70, 29, 92, 51, 95, 84, 21, 57, 28, 46, 66, 93, 11, 94, 55, 96, 20, 71, 48, 53, 43, 82, 90, 66, 90, 14, 44, 54, 62, 34, 58, 81, 53, 23, 43, 27, 93, 10, 86, 37, 80, 60, 49, 21, 79, 74, 72, 48, 61, 40, 76, 64, 29, 38, 79, 51, 54, 13, 49, 72, 30, 50, 86, 35, 80, 61, 56, 36, 59, 65, 91, 25, 47, 58, 59, 78, 47, 32, 24, 44, 86, 64, 57, 12, 23, 109, 107, 85, 63, 31, 65, 62, 68, 111, 78, 104, 89, 112, 87, 69, 105, 65, 94, 109, 78, 72, 104, 85, 108, 77, 106, 79, 74, 103, 96, 64, 105, 105, 102, 63, 35, 59, 108, 112, 81, 102, 57, 106, 83, 81, 77, 101, 55, 94, 96, 97, 106, 46, 101, 83, 85, 71, 107, 104, 87, 33, 67, 103, 95, 30, 91, 89, 103, 75, 51, 107, 87, 91, 89, 99, 68, 52, 109, 99, 88, 84, 112, 54, 70, 92, 100, 98, 74, 60, 100, 98, 110, 90, 73, 71, 95, 70, 100, 29, 69, 110, 93, 82, 97, 98, 77, 73, 99, 101, 108, 82, 102, 111, 110, 111, 97, 88, 80
};
const float HRA_112_112_input[] = {
-3.7496794787890972, 14.372112019392226, -7.5640452729302359, 6.9426063455159657, 5.3103644888713299, -6.9203550501252273, 8.4296575778653775, 13.495087143587781, 18.111520666852243, -9.9125748623510912, 10.601298534930972, -10.468591112149715, -9.0757329437720475, -14.471433733514324, 5.2048820572852641, -11.353785810284556, -9.4511008284496416, -9.5255219979484025, -2.0499245561876696, -9.8739646459388748, 22.03442141444015, -9.9745566449839878, -8.4276711655946226, -4.9811962116476307, -13.018434575859896, -5.3358535334627293, -5.6704294937789648, 14.243964608060018, -11.417925510314507, 9.1332657371467878, -14.380214782394296, 14.090409878618974, 6.5602278279998272, 15.53025696352436, -9.1752771765906616, -11.384503450560766, 12.240329442222599, -12.640059450058276, -11.824715154614376, -13.487656131954735, 15.38073452845444, -13.816294924566529, 6.3461114450644454, -2.5192445130977559, -11.916088712873863, 5.4360722876642518, 0.038031547223147381, -12.367220238860654, -2.747864039796549, -14.920508782249289, 16.487336720060863, -13.290002442259247, 19.142698450560925, -0.39443060583296108, 11.723442316413736, -3.6131702833965047, -4.6196487103817017, -11.794290650694531, -14.342351103186955, 2.8079943208330334, -15.290175151123936, 9.0801740558512414, 10.184385069676226, 8.400722260237572, 9.3504690108712936, -14.223531676384166, 11.752768386971752, 11.36995822251677, -15.285021241405444, -13.070613695054403, -11.869191325617697, 4.3191750845563401, 2.0836933404582791, -16.363829786416495, -5.7778094839806595, 11.06389861779129, 13.285433846434705, 9.2552396418849021, -11.065999403824057, -10.167040394420443, -7.0107225044503565, 2.3886881673282474, 5.0014484787306932, -9.2464083853314278, -12.043309174487364, -11.638411967211738, -16.302815497922911, 13.347129717938067, -4.1390259986125226, 0.7947480277507295, 11.538620744796759, -7.4410706619926028, 14.572449028311253, 12.392747919231169, -3.3027890746379289, -9.8431096813736687, 11.582657487369399, -7.85736442083219, -7.3780721969188443, -7.4006260265172212, -8.3937994980934327, -6.6804071011469555, 19.656301355404196, 1.1084340389939762, 3.6028635453146465, -4.5409495140900562, 7.3831459854578982, -5.5905999874445662, -13.852328482738232, 8.9999210644983041, 8.4742375282492315, 16.989947243749878, 7.5590035165610168, -6.154674423116183, 4.1119120658251855, 12.351217703790844, 11.070972687846792, 11.182587746846833, -4.9345619923565645, 9.0054892370887334, -10.841725474869696, 13.902796293412067, -6.7575171884905396, -5.8196703210757335, 1.9284357540668857, 9.9905382141440455, -13.983067199220674, -4.9130522479706453, -8.2369300184767908, 6.8953565265629644, 2.9285103862640871, -2.6303471135655325, -8.3563361642086047, 9.5712349244763715, 4.9728623009661161, -11.045088919587242, -5.7781337596219604, -17.732999074602972, 8.1353860976076646, -11.066240843831284, -1.7079574457159534, -16.411685365171998, -9.0471090651358299, -10.959376227315447, 8.5840398495674126, 6.6373658260736024, 11.422094029020409, 14.85785089306844, 13.185747281780415, 4.2063935223916191, -6.9166135608899282, 10.843153262137262, 5.3913075109409441, -10.744469667642237, -12.491640291445655, 14.141118162062066, 16.425476099516025, 9.8833761863476042, 2.8719064151687883, 14.982021915112442, 1.3588165304065343, -11.657839635726177, 11.066314862965077, -3.0565490195476204, 1.7820159270701772, -13.535333311782074, 4.4026933190218367, -11.097334550496313, -11.322820869044248, 16.418516996530371, 5.8239202459876136, 15.054905601216154, -9.3058742038490152, 8.48902767802557, -8.3853534273227748, -7.9255089736435176, -9.6156735881618811, 11.502594413898008, -6.0542015398269911, 7.1229229147355149, 0.31483632310264387, -11.482093481730768, -9.3225703551629309, 5.8001228713062831, -9.3515917458791051, 7.9778737065172969, 9.7095180444854847, -14.060064536791135, 4.9797253221020545, -6.9210799657794224, 6.6736460552213845, -7.7636429824024606, 10.233132490278882, 8.401747393605044, -10.861100567451366, 13.631509744686715, -15.723791754613185, -8.7931294115815923, -9.9520037489001609, -10.312792052906007, -8.0681893911111917, -15.411052087079765, 10.938779471602952, -8.751795633239853, -9.1302029882284419, -2.3357314769649777, -7.9130658335895596, 2.7508172894969509, -9.1666780515772324, 12.793063537524359, -13.39091818112591, 7.2827402370664842, -10.400778532411657, -1.90854156128735, -4.1272702472088971, 12.696932922959466, -4.0180403457213805, 10.828999052972396, 14.720617452742685, -8.3763729074389719, 3.955093172344033, 0.90932711822659873, -5.6696817865337819, -5.8822086115513805
};
const char HRA_112_112_detected_data[] = {
1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1
};
@@ -0,0 +1,19 @@
/*
FILE....: HRA_112_112.h
Static arrays for LDPC codec HRA_112_112, generated by ldpc_gen_c_h_file.m.
*/
#define HRA_112_112_NUMBERPARITYBITS 112
#define HRA_112_112_MAX_ROW_WEIGHT 3
#define HRA_112_112_CODELENGTH 224
#define HRA_112_112_NUMBERROWSHCOLS 112
#define HRA_112_112_MAX_COL_WEIGHT 3
#define HRA_112_112_DEC_TYPE 0
#define HRA_112_112_MAX_ITER 100
extern const uint16_t HRA_112_112_H_rows[];
extern const uint16_t HRA_112_112_H_cols[];
extern const float HRA_112_112_input[];
extern const char HRA_112_112_detected_data[];

Some files were not shown because too many files have changed in this diff Show More