2014-11-28 23:44:41 +08:00
|
|
|
/*
|
2016-03-20 23:41:37 +08:00
|
|
|
This software is part of libcsdr, a set of simple DSP routines for
|
2014-11-28 23:44:41 +08:00
|
|
|
Software Defined Radio.
|
|
|
|
|
|
|
|
Copyright (c) 2014, Andras Retzler <randras@sdr.hu>
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the copyright holder nor the
|
|
|
|
names of its contributors may be used to endorse or promote products
|
|
|
|
derived from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY
|
|
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
#define MIN_M(x,y) (((x)>(y))?(y):(x))
|
2016-05-08 15:33:40 +08:00
|
|
|
#define MAX_M(x,y) (((x)<(y))?(y):(x))
|
2014-11-28 23:44:41 +08:00
|
|
|
|
|
|
|
/*
|
2016-03-20 23:41:37 +08:00
|
|
|
_____ _
|
|
|
|
/ ____| | |
|
|
|
|
| | ___ _ __ ___ _ __ | | _____ __
|
|
|
|
| | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ /
|
|
|
|
| |___| (_) | | | | | | |_) | | __/> <
|
|
|
|
\_____\___/|_| |_| |_| .__/|_|\___/_/\_\
|
|
|
|
| |
|
|
|
|
|_|
|
2014-11-28 23:44:41 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct complexf_s { float i; float q; } complexf;
|
|
|
|
|
|
|
|
//apply to pointers:
|
|
|
|
#define iof(complexf_input_p,i) (*(((float*)complexf_input_p)+2*(i)))
|
|
|
|
#define qof(complexf_input_p,i) (*(((float*)complexf_input_p)+2*(i)+1))
|
2016-05-18 20:56:05 +08:00
|
|
|
#define absof(complexf_input_p,i) (sqrt((iof(complexf_input_p,i)*iof(complexf_input_p,i))+(qof(complexf_input_p,i)*qof(complexf_input_p,i))))
|
2014-11-28 23:44:41 +08:00
|
|
|
#define argof(complexf_input_p,i) (atan2(qof(complexf_input_p,i),iof(complexf_input_p,i)))
|
2016-05-18 20:56:05 +08:00
|
|
|
#define cmult(cfo, cfi1, cfi2) {iof(cfo,0)=iof(cfi1,0)*iof(cfi2,0)-qof(cfi1,0)*qof(cfi2,0);qof(cfo,0)=iof(cfi1,0)*qof(cfi2,0)+iof(cfi2,0)*qof(cfi1,0);}
|
2014-11-28 23:44:41 +08:00
|
|
|
//(ai+aq*j)*(bi+bq*j)=ai*bi-aq*bq+(aq*bi+ai*bq)*j
|
|
|
|
#define cmultadd(cfo, cfi1, cfi2) { iof(cfo,0)+=iof(cfi1,0)*iof(cfi2,0)-qof(cfi1,0)*qof(cfi2,0);qof(cfo,0)+=iof(cfi1,0)*qof(cfi2,0)+iof(cfi2,0)*qof(cfi1,0); }
|
|
|
|
#define csetnull(cf) { iof(cf,0)=0.0; qof(cf,0)=0.0; }
|
|
|
|
#define e_powj(cf,w) { iof(cf,0)=cos(w); qof(cf,0)=sin(w); }
|
|
|
|
#define ccopy(dst,src) { iof(dst,0)=iof(src,0); qof(dst,0)=qof(src,0); }
|
|
|
|
|
|
|
|
//apply to values
|
|
|
|
#define iofv(complexf_input) (*((float*)&complexf_input))
|
|
|
|
#define qofv(complexf_input) (*(((float*)&complexf_input)+1))
|
|
|
|
|
|
|
|
//they dropped M_PI in C99, so we define it:
|
|
|
|
#define PI ((float)3.14159265358979323846)
|
|
|
|
|
2015-11-30 03:05:28 +08:00
|
|
|
#define TIME_TAKEN(start,end) ((end.tv_sec-start.tv_sec)+(end.tv_nsec-start.tv_nsec)/1e9)
|
|
|
|
|
2014-11-28 23:44:41 +08:00
|
|
|
//window
|
2016-03-20 23:41:37 +08:00
|
|
|
typedef enum window_s
|
2014-11-28 23:44:41 +08:00
|
|
|
{
|
|
|
|
WINDOW_BOXCAR, WINDOW_BLACKMAN, WINDOW_HAMMING
|
|
|
|
} window_t;
|
|
|
|
|
|
|
|
#define WINDOW_DEFAULT WINDOW_HAMMING
|
|
|
|
|
|
|
|
//FFT
|
|
|
|
//Note: these reference to things in this file (e.g. complexf):
|
|
|
|
#include "fft_fftw.h"
|
|
|
|
#include "fft_rpi.h"
|
|
|
|
|
|
|
|
// =================================================================================
|
|
|
|
|
|
|
|
//filter design
|
|
|
|
void firdes_lowpass_f(float *output, int length, float cutoff_rate, window_t window);
|
|
|
|
void firdes_bandpass_c(complexf *output, int length, float lowcut, float highcut, window_t window);
|
|
|
|
float firdes_wkernel_blackman(float input);
|
|
|
|
float firdes_wkernel_hamming(float input);
|
|
|
|
float firdes_wkernel_boxcar(float input);
|
|
|
|
window_t firdes_get_window_from_string(char* input);
|
|
|
|
char* firdes_get_string_from_window(window_t window);
|
2014-12-14 06:46:41 +08:00
|
|
|
int firdes_filter_len(float transition_bw);
|
2014-11-28 23:44:41 +08:00
|
|
|
|
|
|
|
//demodulators
|
|
|
|
complexf fmdemod_quadri_cf(complexf* input, float* output, int input_size, float *temp, complexf last_sample);
|
|
|
|
complexf fmdemod_quadri_novect_cf(complexf* input, float* output, int input_size, complexf last_sample);
|
|
|
|
float fmdemod_atan_cf(complexf* input, float *output, int input_size, float last_phase);
|
|
|
|
void amdemod_cf(complexf* input, float *output, int input_size);
|
|
|
|
void amdemod_estimator_cf(complexf* input, float *output, int input_size, float alpha, float beta);
|
|
|
|
void limit_ff(float* input, float* output, int input_size, float max_amplitude);
|
|
|
|
|
|
|
|
//filters, decimators, resamplers, shift, etc.
|
2015-08-17 05:40:42 +08:00
|
|
|
float fir_one_pass_ff(float* input, float* taps, int taps_length);
|
2014-11-28 23:44:41 +08:00
|
|
|
int fir_decimate_cc(complexf *input, complexf *output, int input_size, int decimation, float *taps, int taps_length);
|
|
|
|
int deemphasis_nfm_ff (float* input, float* output, int input_size, int sample_rate);
|
|
|
|
float deemphasis_wfm_ff (float* input, float* output, int input_size, float tau, int sample_rate, float last_output);
|
|
|
|
float shift_math_cc(complexf *input, complexf* output, int input_size, float rate, float starting_phase);
|
|
|
|
|
|
|
|
typedef struct dcblock_preserve_s
|
|
|
|
{
|
|
|
|
float last_input;
|
|
|
|
float last_output;
|
|
|
|
} dcblock_preserve_t;
|
|
|
|
dcblock_preserve_t dcblock_ff(float* input, float* output, int input_size, float a, dcblock_preserve_t preserved);
|
|
|
|
float fastdcblock_ff(float* input, float* output, int input_size, float last_dc_level);
|
|
|
|
|
|
|
|
typedef struct fastagc_ff_s
|
|
|
|
{
|
2016-03-20 23:41:37 +08:00
|
|
|
float* buffer_1;
|
2014-11-28 23:44:41 +08:00
|
|
|
float* buffer_2;
|
|
|
|
float* buffer_input; //it is the actual input buffer to fill
|
|
|
|
float peak_1;
|
|
|
|
float peak_2;
|
|
|
|
int input_size;
|
|
|
|
float reference;
|
|
|
|
float last_gain;
|
|
|
|
} fastagc_ff_t;
|
|
|
|
|
|
|
|
void fastagc_ff(fastagc_ff_t* input, float* output);
|
|
|
|
|
|
|
|
typedef struct rational_resampler_ff_s
|
|
|
|
{
|
|
|
|
int input_processed;
|
|
|
|
int output_size;
|
|
|
|
int last_taps_delay;
|
|
|
|
} rational_resampler_ff_t;
|
|
|
|
|
|
|
|
rational_resampler_ff_t rational_resampler_ff(float *input, float *output, int input_size, int interpolation, int decimation, float *taps, int taps_length, int last_taps_delay);
|
|
|
|
void rational_resampler_get_lowpass_f(float* output, int output_size, int interpolation, int decimation, window_t window);
|
|
|
|
|
2016-03-26 17:44:28 +08:00
|
|
|
float *precalculate_window(int size, window_t window);
|
2014-11-28 23:44:41 +08:00
|
|
|
void apply_window_c(complexf* input, complexf* output, int size, window_t window);
|
2016-03-26 17:44:28 +08:00
|
|
|
void apply_precalculated_window_c(complexf* input, complexf* output, int size, float *windowt);
|
2014-11-28 23:44:41 +08:00
|
|
|
void apply_window_f(float* input, float* output, int size, window_t window);
|
|
|
|
void logpower_cf(complexf* input, float* output, int size, float add_db);
|
2016-03-26 00:00:20 +08:00
|
|
|
void accumulate_power_cf(complexf* input, float* output, int size);
|
|
|
|
void log_ff(float* input, float* output, int size, float add_db);
|
2014-11-28 23:44:41 +08:00
|
|
|
|
|
|
|
typedef struct fractional_decimator_ff_s
|
|
|
|
{
|
|
|
|
float remain;
|
|
|
|
int input_processed;
|
|
|
|
int output_size;
|
|
|
|
} fractional_decimator_ff_t;
|
|
|
|
fractional_decimator_ff_t fractional_decimator_ff(float* input, float* output, int input_size, float rate, float *taps, int taps_length, fractional_decimator_ff_t d);
|
|
|
|
|
2015-08-17 05:40:42 +08:00
|
|
|
typedef struct shift_table_data_s
|
|
|
|
{
|
|
|
|
float* table;
|
2016-03-20 23:41:37 +08:00
|
|
|
int table_size;
|
2015-08-17 05:40:42 +08:00
|
|
|
} shift_table_data_t;
|
|
|
|
void shift_table_deinit(shift_table_data_t table_data);
|
|
|
|
shift_table_data_t shift_table_init(int table_size);
|
|
|
|
float shift_table_cc(complexf* input, complexf* output, int input_size, float rate, shift_table_data_t table_data, float starting_phase);
|
|
|
|
|
2015-11-29 20:54:09 +08:00
|
|
|
typedef struct shift_addfast_data_s
|
|
|
|
{
|
|
|
|
float dsin[4];
|
|
|
|
float dcos[4];
|
2015-11-30 03:05:28 +08:00
|
|
|
float phase_increment;
|
2015-11-29 20:54:09 +08:00
|
|
|
} shift_addfast_data_t;
|
|
|
|
shift_addfast_data_t shift_addfast_init(float rate);
|
2015-11-30 06:46:06 +08:00
|
|
|
shift_addfast_data_t shift_addfast_init(float rate);
|
2015-11-29 20:54:09 +08:00
|
|
|
float shift_addfast_cc(complexf *input, complexf* output, int input_size, shift_addfast_data_t* d, float starting_phase);
|
|
|
|
|
2015-11-30 06:46:06 +08:00
|
|
|
typedef struct shift_unroll_data_s
|
|
|
|
{
|
|
|
|
float* dsin;
|
|
|
|
float* dcos;
|
|
|
|
float phase_increment;
|
|
|
|
int size;
|
|
|
|
} shift_unroll_data_t;
|
|
|
|
float shift_unroll_cc(complexf *input, complexf* output, int input_size, shift_unroll_data_t* d, float starting_phase);
|
|
|
|
shift_unroll_data_t shift_unroll_init(float rate, int size);
|
2015-08-17 05:40:42 +08:00
|
|
|
|
2014-11-28 23:44:41 +08:00
|
|
|
int log2n(int x);
|
|
|
|
int next_pow2(int x);
|
|
|
|
void apply_fir_fft_cc(FFT_PLAN_T* plan, FFT_PLAN_T* plan_inverse, complexf* taps_fft, complexf* last_overlap, int overlap_size);
|
|
|
|
void gain_ff(float* input, float* output, int input_size, float gain);
|
2016-03-20 23:41:37 +08:00
|
|
|
float get_power_f(float* input, int input_size, int decimation);
|
|
|
|
float get_power_c(complexf* input, int input_size, int decimation);
|
2014-11-28 23:44:41 +08:00
|
|
|
|
2015-10-31 23:22:33 +08:00
|
|
|
void add_dcoffset_cc(complexf* input, complexf* output, int input_size);
|
2015-10-31 23:42:17 +08:00
|
|
|
float fmmod_fc(float* input, complexf* output, int input_size, float last_phase);
|
2015-11-01 19:30:03 +08:00
|
|
|
void fixed_amplitude_cc(complexf* input, complexf* output, int input_size, float amp);
|
2015-10-31 23:22:33 +08:00
|
|
|
|
2014-11-28 23:44:41 +08:00
|
|
|
void convert_u8_f(unsigned char* input, float* output, int input_size);
|
|
|
|
void convert_f_u8(float* input, unsigned char* output, int input_size);
|
2015-11-23 03:53:10 +08:00
|
|
|
void convert_s8_f(signed char* input, float* output, int input_size);
|
|
|
|
void convert_f_s8(float* input, signed char* output, int input_size);
|
2016-02-14 18:19:36 +08:00
|
|
|
void convert_f_s16(float* input, short* output, int input_size);
|
|
|
|
void convert_s16_f(short* input, float* output, int input_size);
|
2014-11-28 23:44:41 +08:00
|
|
|
void convert_f_i16(float* input, short* output, int input_size);
|
|
|
|
void convert_i16_f(short* input, float* output, int input_size);
|
2016-06-21 06:14:03 +08:00
|
|
|
void convert_f_s24(float* input, unsigned char* output, int input_size, int bigendian);
|
|
|
|
void convert_s24_f(unsigned char* input, float* output, int input_size, int bigendian);
|
|
|
|
|
2014-11-28 23:44:41 +08:00
|
|
|
|
2015-08-17 05:40:42 +08:00
|
|
|
int is_nan(float f);
|
2016-05-05 05:46:23 +08:00
|
|
|
|
2016-05-08 15:33:40 +08:00
|
|
|
//digital demod
|
|
|
|
|
|
|
|
typedef struct rtty_baudot_item_s
|
|
|
|
{
|
|
|
|
unsigned long long code;
|
|
|
|
unsigned char ascii_letter;
|
|
|
|
unsigned char ascii_figure;
|
|
|
|
} rtty_baudot_item_t;
|
|
|
|
|
|
|
|
typedef enum rtty_baudot_decoder_state_e
|
|
|
|
{
|
|
|
|
RTTY_BAUDOT_WAITING_STOP_PULSE = 0,
|
|
|
|
RTTY_BAUDOT_WAITING_START_PULSE,
|
|
|
|
RTTY_BAUDOT_RECEIVING_DATA
|
|
|
|
} rtty_baudot_decoder_state_t;
|
|
|
|
|
|
|
|
typedef struct rtty_baudot_decoder_s
|
|
|
|
{
|
|
|
|
unsigned char fig_mode;
|
|
|
|
unsigned char character_received;
|
|
|
|
unsigned short shr;
|
|
|
|
unsigned char bit_cntr;
|
|
|
|
rtty_baudot_decoder_state_t state;
|
|
|
|
} rtty_baudot_decoder_t;
|
|
|
|
|
|
|
|
#define RTTY_FIGURE_MODE_SELECT_CODE 0b11011
|
|
|
|
#define RTTY_LETTER_MODE_SELECT_CODE 0b11111
|
|
|
|
|
|
|
|
char rtty_baudot_decoder_lookup(unsigned char* fig_mode, unsigned char c);
|
|
|
|
char rtty_baudot_decoder_push(rtty_baudot_decoder_t* s, unsigned char symbol);
|
|
|
|
|
|
|
|
//PSK31
|
|
|
|
|
|
|
|
typedef struct psk31_varicode_item_s
|
|
|
|
{
|
|
|
|
unsigned long long code;
|
|
|
|
int bitcount;
|
|
|
|
unsigned char ascii;
|
|
|
|
} psk31_varicode_item_t;
|
|
|
|
|
|
|
|
char psk31_varicode_decoder_push(unsigned long long* status_shr, unsigned char symbol);
|
|
|
|
|
|
|
|
//Serial
|
|
|
|
|
|
|
|
typedef struct serial_line_s
|
|
|
|
{
|
|
|
|
float samples_per_bits;
|
|
|
|
int databits; //including parity
|
|
|
|
float stopbits;
|
|
|
|
int output_size;
|
|
|
|
int input_used;
|
2016-05-08 18:38:09 +08:00
|
|
|
float bit_sampling_width_ratio;
|
2016-05-08 15:33:40 +08:00
|
|
|
} serial_line_t;
|
|
|
|
|
|
|
|
void serial_line_decoder_f_u8(serial_line_t* s, float* input, unsigned char* output, int input_size);
|
|
|
|
void binary_slicer_f_u8(float* input, unsigned char* output, int input_size);
|
2016-05-09 18:59:08 +08:00
|
|
|
|
|
|
|
|
|
|
|
typedef enum pll_type_e
|
|
|
|
{
|
2016-05-23 00:35:26 +08:00
|
|
|
PLL_P_CONTROLLER=1,
|
|
|
|
PLL_PI_CONTROLLER=2
|
2016-05-09 18:59:08 +08:00
|
|
|
} pll_type_t;
|
|
|
|
|
|
|
|
typedef struct pll_s
|
|
|
|
{
|
|
|
|
pll_type_t pll_type;
|
|
|
|
//common:
|
|
|
|
float output_phase;
|
|
|
|
float dphase;
|
|
|
|
float frequency;
|
|
|
|
float alpha;
|
2016-05-11 14:48:31 +08:00
|
|
|
float beta;
|
2016-05-18 20:56:05 +08:00
|
|
|
float iir_temp;
|
2016-05-09 18:59:08 +08:00
|
|
|
} pll_t;
|
|
|
|
|
2016-05-23 00:35:26 +08:00
|
|
|
void pll_cc_init_pi_controller(pll_t* p, float bandwidth, float ko, float kd, float damping_factor);
|
|
|
|
void pll_cc_init_p_controller(pll_t* p, float alpha);
|
2016-05-11 04:59:14 +08:00
|
|
|
void pll_cc(pll_t* p, complexf* input, float* output_dphase, complexf* output_nco, int input_size);
|
2017-03-02 19:17:31 +08:00
|
|
|
|
|
|
|
typedef enum timing_recovery_algorithm_e
|
|
|
|
{
|
|
|
|
TIMING_RECOVERY_ALGORITHM_GARDNER,
|
|
|
|
TIMING_RECOVERY_ALGORITHM_EARLYLATE
|
|
|
|
} timing_recovery_algorithm_t;
|
|
|
|
|
|
|
|
#define TIMING_RECOVERY_ALGORITHM_DEFAULT TIMING_RECOVERY_ALGORITHM_GARDNER
|
|
|
|
|
|
|
|
typedef struct timing_recovery_state_s
|
|
|
|
{
|
|
|
|
timing_recovery_algorithm_t algorithm;
|
|
|
|
int decimation_rate; // = input_rate / output_rate. We should get an input signal that is N times oversampled.
|
|
|
|
int output_size;
|
|
|
|
int input_processed;
|
|
|
|
int use_q; //use both I and Q for calculating the error
|
2017-03-03 23:21:45 +08:00
|
|
|
int debug_phase;
|
2017-03-04 01:23:11 +08:00
|
|
|
int debug_count;
|
2017-03-05 02:50:11 +08:00
|
|
|
int debug_force;
|
2017-03-06 00:21:38 +08:00
|
|
|
int debug_writefiles;
|
2017-03-06 02:56:15 +08:00
|
|
|
int last_correction_offset;
|
2017-03-07 06:00:34 +08:00
|
|
|
float earlylate_ratio;
|
2017-03-02 19:17:31 +08:00
|
|
|
} timing_recovery_state_t;
|
|
|
|
|
|
|
|
timing_recovery_state_t timing_recovery_init(timing_recovery_algorithm_t algorithm, int decimation_rate, int use_q);
|
2017-03-03 18:20:49 +08:00
|
|
|
void timing_recovery_cc(complexf* input, complexf* output, int input_length, timing_recovery_state_t* state);
|
2017-03-02 19:17:31 +08:00
|
|
|
timing_recovery_algorithm_t timing_recovery_get_algorithm_from_string(char* input);
|
|
|
|
char* timing_recovery_get_string_from_algorithm(timing_recovery_algorithm_t algorithm);
|
2017-03-03 23:21:45 +08:00
|
|
|
void timing_recovery_trigger_debug(timing_recovery_state_t* state, int debug_phase);
|
2017-03-06 02:56:15 +08:00
|
|
|
void octave_plot_point_on_cplxsig(complexf* signal, int signal_size, float error, int index, int correction_offset, int writefiles, int points_size, ...);
|