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 .
*/
# include <stdio.h>
# include <time.h>
# include <math.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <limits.h>
# include "libcsdr.h"
# include "predefined.h"
# include <assert.h>
2017-03-03 23:21:45 +08:00
# include <stdarg.h>
2014-11-28 23:44:41 +08:00
/*
2016-03-20 23:41:37 +08:00
_ _ __ _ _
( _ ) | | / _ | | | ( _ )
__ ___ _ __ __ | | _____ __ | | _ _ _ _ __ ___ | | _ _ ___ _ __ ___
2014-11-28 23:44:41 +08:00
\ \ / \ / / | ' _ \ / _ ` | / _ \ \ / \ / / | _ | | | | ' _ \ / __ | __ | | / _ \ | ' _ \ / __ |
\ V V / | | | | | ( _ | | ( _ ) \ V V / | | | | _ | | | | | ( __ | | _ | | ( _ ) | | | \ __ \
\ _ / \ _ / | _ | _ | | _ | \ __ , _ | \ ___ / \ _ / \ _ / | _ | \ __ , _ | _ | | _ | \ ___ | \ __ | _ | \ ___ / | _ | | _ | ___ /
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
*/
# define MFIRDES_GWS(NAME) \
if ( ! strcmp ( # NAME , input ) ) return WINDOW_ # # NAME ;
window_t firdes_get_window_from_string ( char * input )
{
MFIRDES_GWS ( BOXCAR ) ;
MFIRDES_GWS ( BLACKMAN ) ;
MFIRDES_GWS ( HAMMING ) ;
return WINDOW_DEFAULT ;
}
# define MFIRDES_GSW(NAME) \
if ( window = = WINDOW_ # # NAME ) return # NAME ;
char * firdes_get_string_from_window ( window_t window )
{
MFIRDES_GSW ( BOXCAR ) ;
MFIRDES_GSW ( BLACKMAN ) ;
MFIRDES_GSW ( HAMMING ) ;
return " INVALID " ;
}
float firdes_wkernel_blackman ( float rate )
{
//Explanation at Chapter 16 of dspguide.com, page 2
//Blackman window has better stopband attentuation and passband ripple than Hamming, but it has slower rolloff.
rate = 0.5 + rate / 2 ;
return 0.42 - 0.5 * cos ( 2 * PI * rate ) + 0.08 * cos ( 4 * PI * rate ) ;
}
float firdes_wkernel_hamming ( float rate )
{
//Explanation at Chapter 16 of dspguide.com, page 2
//Hamming window has worse stopband attentuation and passband ripple than Blackman, but it has faster rolloff.
rate = 0.5 + rate / 2 ;
return 0.54 - 0.46 * cos ( 2 * PI * rate ) ;
}
float firdes_wkernel_boxcar ( float rate )
{ //"Dummy" window kernel, do not use; an unwindowed FIR filter may have bad frequency response
return 1.0 ;
}
float ( * firdes_get_window_kernel ( window_t window ) ) ( float )
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
if ( window = = WINDOW_HAMMING ) return firdes_wkernel_hamming ;
else if ( window = = WINDOW_BLACKMAN ) return firdes_wkernel_blackman ;
else if ( window = = WINDOW_BOXCAR ) return firdes_wkernel_boxcar ;
else return firdes_get_window_kernel ( WINDOW_DEFAULT ) ;
}
/*
2016-03-20 23:41:37 +08:00
______ _____ _____ __ _ _ _ _ _
| ____ | _ _ | __ \ / _ ( _ ) | | | | ( _ )
| | __ | | | | __ ) | | | _ _ | | | _ ___ _ __ __ | | ___ ___ _ __ _ _ __
| __ | | | | _ / | _ | | | __ / _ \ ' __ | / _ ` | / _ \ / __ | | / _ ` | ' _ \
2014-11-28 23:44:41 +08:00
| | _ | | _ | | \ \ | | | | | | | __ / | | ( _ | | __ / \ __ \ | ( _ | | | | |
| _ | | _____ | _ | \ _ \ | _ | | _ | _ | \ __ \ ___ | _ | \ __ , _ | \ ___ | | ___ / _ | \ __ , | _ | | _ |
2016-03-20 23:41:37 +08:00
__ / |
| ___ /
2014-11-28 23:44:41 +08:00
*/
void firdes_lowpass_f ( float * output , int length , float cutoff_rate , window_t window )
{ //Generates symmetric windowed sinc FIR filter real taps
// length should be odd
// cutoff_rate is (cutoff frequency/sampling frequency)
//Explanation at Chapter 16 of dspguide.com
int middle = length / 2 ;
float temp ;
float ( * window_function ) ( float ) = firdes_get_window_kernel ( window ) ;
output [ middle ] = 2 * PI * cutoff_rate * window_function ( 0 ) ;
for ( int i = 1 ; i < = middle ; i + + ) //@@firdes_lowpass_f: calculate taps
{
output [ middle - i ] = output [ middle + i ] = ( sin ( 2 * PI * cutoff_rate * i ) / i ) * window_function ( ( float ) i / middle ) ;
//printf("%g %d %d %d %d | %g\n",output[middle-i],i,middle,middle+i,middle-i,sin(2*PI*cutoff_rate*i));
}
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
//Normalize filter kernel
float sum = 0 ;
for ( int i = 0 ; i < length ; i + + ) //@firdes_lowpass_f: normalize pass 1
{
sum + = output [ i ] ;
}
for ( int i = 0 ; i < length ; i + + ) //@firdes_lowpass_f: normalize pass 2
{
output [ i ] / = sum ;
}
}
void firdes_bandpass_c ( complexf * output , int length , float lowcut , float highcut , window_t window )
{
//To generate a complex filter:
// 1. we generate a real lowpass filter with a bandwidth of highcut-lowcut
// 2. we shift the filter taps spectrally by multiplying with e^(j*w), so we get complex taps
//(tnx HA5FT)
float * realtaps = ( float * ) malloc ( sizeof ( float ) * length ) ;
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
firdes_lowpass_f ( realtaps , length , ( highcut - lowcut ) / 2 , window ) ;
float filter_center = ( highcut + lowcut ) / 2 ;
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
float phase = 0 , sinval , cosval ;
for ( int i = 0 ; i < length ; i + + ) //@@firdes_bandpass_c
{
cosval = cos ( phase ) ;
2016-03-20 23:41:37 +08:00
sinval = sin ( phase ) ;
2014-11-28 23:44:41 +08:00
phase + = 2 * PI * filter_center ;
while ( phase > 2 * PI ) phase - = 2 * PI ; //@@firdes_bandpass_c
2016-03-20 23:41:37 +08:00
while ( phase < 0 ) phase + = 2 * PI ;
2014-11-28 23:44:41 +08:00
iof ( output , i ) = cosval * realtaps [ i ] ;
qof ( output , i ) = sinval * realtaps [ i ] ;
//output[i] := realtaps[i] * e^j*w
}
}
2014-12-14 06:46:41 +08:00
int firdes_filter_len ( float transition_bw )
2014-11-28 23:44:41 +08:00
{
2014-12-14 06:46:41 +08:00
int result = 4.0 / transition_bw ;
2014-11-28 23:44:41 +08:00
if ( result % 2 = = 0 ) result + + ; //number of symmetric FIR filter taps should be odd
return result ;
}
/*
2016-03-20 23:41:37 +08:00
_____ _____ _____ __ _ _
| __ \ / ____ | __ \ / _ | | | ( _ )
| | | | ( ___ | | __ ) | | | _ _ _ _ __ ___ | | _ _ ___ _ __ ___
2014-11-28 23:44:41 +08:00
| | | | \ ___ \ | ___ / | _ | | | | ' _ \ / __ | __ | | / _ \ | ' _ \ / __ |
| | __ | | ____ ) | | | | | | _ | | | | | ( __ | | _ | | ( _ ) | | | \ __ \
| _____ / | _____ / | _ | | _ | \ __ , _ | _ | | _ | \ ___ | \ __ | _ | \ ___ / | _ | | _ | ___ /
2016-03-20 23:41:37 +08:00
*/
2014-11-28 23:44:41 +08:00
float shift_math_cc ( complexf * input , complexf * output , int input_size , float rate , float starting_phase )
{
rate * = 2 ;
//Shifts the complex spectrum. Basically a complex mixer. This version uses cmath.
float phase = starting_phase ;
float phase_increment = rate * PI ;
float cosval , sinval ;
for ( int i = 0 ; i < input_size ; i + + ) //@shift_math_cc
{
cosval = cos ( phase ) ;
sinval = sin ( phase ) ;
//we multiply two complex numbers.
//how? enter this to maxima (software) for explanation:
// (a+b*%i)*(c+d*%i), rectform;
iof ( output , i ) = cosval * iof ( input , i ) - sinval * qof ( input , i ) ;
qof ( output , i ) = sinval * iof ( input , i ) + cosval * qof ( input , i ) ;
phase + = phase_increment ;
while ( phase > 2 * PI ) phase - = 2 * PI ; //@shift_math_cc: normalize phase
while ( phase < 0 ) phase + = 2 * PI ;
}
return phase ;
}
2015-08-17 05:40:42 +08:00
shift_table_data_t shift_table_init ( int table_size )
{
//RTODO
shift_table_data_t output ;
output . table = ( float * ) malloc ( sizeof ( float ) * table_size ) ;
output . table_size = table_size ;
for ( int i = 0 ; i < table_size ; i + + )
{
output . table [ i ] = sin ( ( ( float ) i / table_size ) * ( PI / 2 ) ) ;
}
return output ;
}
void shift_table_deinit ( shift_table_data_t table_data )
{
free ( table_data . table ) ;
}
float shift_table_cc ( complexf * input , complexf * output , int input_size , float rate , shift_table_data_t table_data , float starting_phase )
{
//RTODO
rate * = 2 ;
//Shifts the complex spectrum. Basically a complex mixer. This version uses a pre-built sine table.
float phase = starting_phase ;
float phase_increment = rate * PI ;
float cosval , sinval ;
for ( int i = 0 ; i < input_size ; i + + ) //@shift_math_cc
{
int sin_index , cos_index , temp_index , sin_sign , cos_sign ;
//float vphase=fmodf(phase,PI/2); //between 0 and 90deg
int quadrant = phase / ( PI / 2 ) ; //between 0 and 3
float vphase = phase - quadrant * ( PI / 2 ) ;
2016-03-20 23:41:37 +08:00
sin_index = ( vphase / ( PI / 2 ) ) * table_data . table_size ;
2015-08-17 05:40:42 +08:00
cos_index = table_data . table_size - 1 - sin_index ;
if ( quadrant & 1 ) //in quadrant 1 and 3
{
temp_index = sin_index ;
sin_index = cos_index ;
cos_index = temp_index ;
}
sin_sign = ( quadrant > 1 ) ? - 1 : 1 ; //in quadrant 2 and 3
cos_sign = ( quadrant & & quadrant < 3 ) ? - 1 : 1 ; //in quadrant 1 and 2
sinval = sin_sign * table_data . table [ sin_index ] ;
cosval = cos_sign * table_data . table [ cos_index ] ;
//we multiply two complex numbers.
//how? enter this to maxima (software) for explanation:
// (a+b*%i)*(c+d*%i), rectform;
iof ( output , i ) = cosval * iof ( input , i ) - sinval * qof ( input , i ) ;
qof ( output , i ) = sinval * iof ( input , i ) + cosval * qof ( input , i ) ;
phase + = phase_increment ;
while ( phase > 2 * PI ) phase - = 2 * PI ; //@shift_math_cc: normalize phase
while ( phase < 0 ) phase + = 2 * PI ;
}
return phase ;
}
2015-11-29 20:54:09 +08:00
2015-11-30 06:46:06 +08:00
shift_unroll_data_t shift_unroll_init ( float rate , int size )
{
shift_unroll_data_t output ;
output . phase_increment = 2 * rate * PI ;
output . size = size ;
output . dsin = ( float * ) malloc ( sizeof ( float ) * size ) ;
output . dcos = ( float * ) malloc ( sizeof ( float ) * size ) ;
float myphase = 0 ;
for ( int i = 0 ; i < size ; i + + )
{
myphase + = output . phase_increment ;
while ( myphase > PI ) myphase - = 2 * PI ;
2017-03-03 23:21:45 +08:00
while ( myphase < - PI ) myphase + = 2 * PI ;
2015-11-30 06:46:06 +08:00
output . dsin [ i ] = sin ( myphase ) ;
output . dcos [ i ] = cos ( myphase ) ;
}
2017-03-03 23:21:45 +08:00
return output ;
2015-11-30 06:46:06 +08:00
}
float shift_unroll_cc ( complexf * input , complexf * output , int input_size , shift_unroll_data_t * d , float starting_phase )
{
//input_size should be multiple of 4
//fprintf(stderr, "shift_addfast_cc: input_size = %d\n", input_size);
float cos_start = cos ( starting_phase ) ;
float sin_start = sin ( starting_phase ) ;
register float cos_val , sin_val ;
for ( int i = 0 ; i < input_size ; i + + ) //@shift_unroll_cc
{
cos_val = cos_start * d - > dcos [ i ] - sin_start * d - > dsin [ i ] ;
sin_val = sin_start * d - > dcos [ i ] + cos_start * d - > dsin [ i ] ;
iof ( output , i ) = cos_val * iof ( input , i ) - sin_val * qof ( input , i ) ;
qof ( output , i ) = sin_val * iof ( input , i ) + cos_val * qof ( input , i ) ;
}
starting_phase + = input_size * d - > phase_increment ;
while ( starting_phase > PI ) starting_phase - = 2 * PI ;
while ( starting_phase < - PI ) starting_phase + = 2 * PI ;
return starting_phase ;
}
2015-11-29 20:54:09 +08:00
shift_addfast_data_t shift_addfast_init ( float rate )
{
shift_addfast_data_t output ;
2015-11-30 03:05:28 +08:00
output . phase_increment = 2 * rate * PI ;
2015-11-29 20:54:09 +08:00
for ( int i = 0 ; i < 4 ; i + + )
{
2015-11-30 03:05:28 +08:00
output . dsin [ i ] = sin ( output . phase_increment * ( i + 1 ) ) ;
output . dcos [ i ] = cos ( output . phase_increment * ( i + 1 ) ) ;
2015-11-29 20:54:09 +08:00
}
return output ;
}
2015-09-30 21:52:43 +08:00
# ifdef NEON_OPTS
2015-11-30 03:05:28 +08:00
# pragma message "Manual NEON optimizations are ON: we have a faster shift_addfast_cc now."
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 )
{
//input_size should be multiple of 4
2015-11-30 03:05:28 +08:00
float cos_start [ 4 ] , sin_start [ 4 ] ;
float cos_vals [ 4 ] , sin_vals [ 4 ] ;
2017-03-03 23:21:45 +08:00
for ( int i = 0 ; i < 4 ; i + + )
2015-11-30 03:05:28 +08:00
{
cos_start [ i ] = cos ( starting_phase ) ;
sin_start [ i ] = sin ( starting_phase ) ;
}
float * pdcos = d - > dcos ;
float * pdsin = d - > dsin ;
register float * pinput = ( float * ) input ;
2015-11-30 04:47:00 +08:00
register float * pinput_end = ( float * ) ( input + input_size ) ;
2015-11-30 03:05:28 +08:00
register float * poutput = ( float * ) output ;
2015-11-30 04:47:00 +08:00
//Register map:
2015-11-30 03:05:28 +08:00
# define RDCOS "q0" //dcos, dsin
# define RDSIN "q1"
# define RCOSST "q2" //cos_start, sin_start
# define RSINST "q3"
# define RCOSV "q4" //cos_vals, sin_vals
# define RSINV "q5"
# define ROUTI "q6" //output_i, output_q
2017-03-03 23:21:45 +08:00
# define ROUTQ "q7"
2015-11-30 03:05:28 +08:00
# define RINPI "q8" //input_i, input_q
# define RINPQ "q9"
# define R3(x,y,z) x ", " y ", " z "\n\t"
2015-11-30 03:13:24 +08:00
asm volatile ( //(the range of q is q0-q15)
2015-11-30 03:05:28 +08:00
" vld1.32 { " RDCOS " }, [%[pdcos]] \n \t "
" vld1.32 { " RDSIN " }, [%[pdsin]] \n \t "
" vld1.32 { " RCOSST " }, [%[cos_start]] \n \t "
" vld1.32 { " RSINST " }, [%[sin_start]] \n \t "
2017-03-03 23:21:45 +08:00
" for_addfast: vld2.32 { " RINPI " - " RINPQ " }, [%[pinput]]! \n \t " //load q0 and q1 directly from the memory address stored in pinput, with interleaving (so that we get the I samples in RINPI and the Q samples in RINPQ), also increment the memory address in pinput (hence the "!" mark)
2015-11-30 03:05:28 +08:00
//C version:
//cos_vals[j] = cos_start * d->dcos[j] - sin_start * d->dsin[j];
//sin_vals[j] = sin_start * d->dcos[j] + cos_start * d->dsin[j];
" vmul.f32 " R3 ( RCOSV , RCOSST , RDCOS ) //cos_vals[i] = cos_start * d->dcos[i]
" vmls.f32 " R3 ( RCOSV , RSINST , RDSIN ) //cos_vals[i] -= sin_start * d->dsin[i]
2015-11-30 03:13:24 +08:00
" vmul.f32 " R3 ( RSINV , RSINST , RDCOS ) //sin_vals[i] = sin_start * d->dcos[i]
2015-11-30 04:47:00 +08:00
" vmla.f32 " R3 ( RSINV , RCOSST , RDSIN ) //sin_vals[i] += cos_start * d->dsin[i]
2015-11-30 03:05:28 +08:00
//C version:
//iof(output,4*i+j)=cos_vals[j]*iof(input,4*i+j)-sin_vals[j]*qof(input,4*i+j);
2017-03-03 23:21:45 +08:00
//qof(output,4*i+j)=sin_vals[j]*iof(input,4*i+j)+cos_vals[j]*qof(input,4*i+j);
2015-11-30 06:46:06 +08:00
" vmul.f32 " R3 ( ROUTI , RCOSV , RINPI ) //output_i = cos_vals * input_i
" vmls.f32 " R3 ( ROUTI , RSINV , RINPQ ) //output_i -= sin_vals * input_q
" vmul.f32 " R3 ( ROUTQ , RSINV , RINPI ) //output_q = sin_vals * input_i
" vmla.f32 " R3 ( ROUTQ , RCOSV , RINPQ ) //output_i += cos_vals * input_q
" vst2.32 { " ROUTI " - " ROUTQ " }, [%[poutput]]! \n \t " //store the outputs in memory
//" add %[poutput],%[poutput],#32\n\t"
" vdup.32 " RCOSST " , d9[1] \n \t " // cos_start[0-3] = cos_vals[3]
" vdup.32 " RSINST " , d11[1] \n \t " // sin_start[0-3] = sin_vals[3]
" cmp %[pinput], %[pinput_end] \n \t " //if(pinput != pinput_end)
" bcc for_addfast \n \t " // then goto for_addfast
2015-11-30 03:05:28 +08:00
:
[ pinput ] " +r " ( pinput ) , [ poutput ] " +r " ( poutput ) //output operand list -> C variables that we will change from ASM
:
[ pinput_end ] " r " ( pinput_end ) , [ pdcos ] " r " ( pdcos ) , [ pdsin ] " r " ( pdsin ) , [ sin_start ] " r " ( sin_start ) , [ cos_start ] " r " ( cos_start ) //input operand list
2017-03-03 23:21:45 +08:00
:
2015-11-30 03:05:28 +08:00
" memory " , " q0 " , " q1 " , " q2 " , " q4 " , " q5 " , " q6 " , " q7 " , " q8 " , " q9 " , " cc " //clobber list
) ;
2015-11-30 06:46:06 +08:00
starting_phase + = input_size * d - > phase_increment ;
while ( starting_phase > PI ) starting_phase - = 2 * PI ;
while ( starting_phase < - PI ) starting_phase + = 2 * PI ;
return starting_phase ;
2015-11-30 03:05:28 +08:00
}
# else
2015-11-30 07:20:22 +08:00
# if 1
# define SADF_L1(j) cos_vals_ ## j = cos_start * dcos_ ## j - sin_start * dsin_ ## j; \
sin_vals_ # # j = sin_start * dcos_ # # j + cos_start * dsin_ # # j ;
# define SADF_L2(j) iof(output,4*i+j)=(cos_vals_ ## j)*iof(input,4*i+j)-(sin_vals_ ## j)*qof(input,4*i+j); \
qof ( output , 4 * i + j ) = ( sin_vals_ # # j ) * iof ( input , 4 * i + j ) + ( cos_vals_ # # j ) * qof ( input , 4 * i + j ) ;
float shift_addfast_cc ( complexf * input , complexf * output , int input_size , shift_addfast_data_t * d , float starting_phase )
{
//input_size should be multiple of 4
//fprintf(stderr, "shift_addfast_cc: input_size = %d\n", input_size);
float cos_start = cos ( starting_phase ) ;
float sin_start = sin ( starting_phase ) ;
float register cos_vals_0 , cos_vals_1 , cos_vals_2 , cos_vals_3 ,
2017-03-03 23:21:45 +08:00
sin_vals_0 , sin_vals_1 , sin_vals_2 , sin_vals_3 ,
2015-11-30 07:20:22 +08:00
dsin_0 = d - > dsin [ 0 ] , dsin_1 = d - > dsin [ 1 ] , dsin_2 = d - > dsin [ 2 ] , dsin_3 = d - > dsin [ 3 ] ,
dcos_0 = d - > dcos [ 0 ] , dcos_1 = d - > dcos [ 1 ] , dcos_2 = d - > dcos [ 2 ] , dcos_3 = d - > dcos [ 3 ] ;
for ( int i = 0 ; i < input_size / 4 ; i + + ) //@shift_addfast_cc
{
SADF_L1 ( 0 )
SADF_L1 ( 1 )
SADF_L1 ( 2 )
SADF_L1 ( 3 )
SADF_L2 ( 0 )
SADF_L2 ( 1 )
SADF_L2 ( 2 )
SADF_L2 ( 3 )
cos_start = cos_vals_3 ;
sin_start = sin_vals_3 ;
}
starting_phase + = input_size * d - > phase_increment ;
while ( starting_phase > PI ) starting_phase - = 2 * PI ;
while ( starting_phase < - PI ) starting_phase + = 2 * PI ;
return starting_phase ;
}
# else
2015-11-30 03:05:28 +08:00
float shift_addfast_cc ( complexf * input , complexf * output , int input_size , shift_addfast_data_t * d , float starting_phase )
{
//input_size should be multiple of 4
//fprintf(stderr, "shift_addfast_cc: input_size = %d\n", input_size);
2015-11-29 20:54:09 +08:00
float cos_start = cos ( starting_phase ) ;
float sin_start = sin ( starting_phase ) ;
float cos_vals [ 4 ] , sin_vals [ 4 ] ;
for ( int i = 0 ; i < input_size / 4 ; i + + ) //@shift_addfast_cc
{
2015-11-30 03:05:28 +08:00
for ( int j = 0 ; j < 4 ; j + + ) //@shift_addfast_cc
2015-11-29 20:54:09 +08:00
{
2015-11-30 03:05:28 +08:00
cos_vals [ j ] = cos_start * d - > dcos [ j ] - sin_start * d - > dsin [ j ] ;
sin_vals [ j ] = sin_start * d - > dcos [ j ] + cos_start * d - > dsin [ j ] ;
2015-11-29 20:54:09 +08:00
}
2015-11-30 03:05:28 +08:00
for ( int j = 0 ; j < 4 ; j + + ) //@shift_addfast_cc
2015-11-29 20:54:09 +08:00
{
iof ( output , 4 * i + j ) = cos_vals [ j ] * iof ( input , 4 * i + j ) - sin_vals [ j ] * qof ( input , 4 * i + j ) ;
qof ( output , 4 * i + j ) = sin_vals [ j ] * iof ( input , 4 * i + j ) + cos_vals [ j ] * qof ( input , 4 * i + j ) ;
}
cos_start = cos_vals [ 3 ] ;
sin_start = sin_vals [ 3 ] ;
}
2015-11-30 06:46:06 +08:00
starting_phase + = input_size * d - > phase_increment ;
while ( starting_phase > PI ) starting_phase - = 2 * PI ;
while ( starting_phase < - PI ) starting_phase + = 2 * PI ;
return starting_phase ;
2015-11-29 20:54:09 +08:00
}
2015-11-30 07:20:22 +08:00
# endif
2015-11-29 20:54:09 +08:00
2015-11-30 03:05:28 +08:00
# endif
2015-09-30 21:52:43 +08:00
# ifdef NEON_OPTS
2015-11-30 03:05:28 +08:00
# pragma message "Manual NEON optimizations are ON: we have a faster fir_decimate_cc now."
2015-09-30 21:52:43 +08:00
//max help: http://community.arm.com/groups/android-community/blog/2015/03/27/arm-neon-programming-quick-reference
int fir_decimate_cc ( complexf * input , complexf * output , int input_size , int decimation , float * taps , int taps_length )
{
//Theory: http://www.dspguru.com/dsp/faqs/multirate/decimation
//It uses real taps. It returns the number of output samples actually written.
//It needs overlapping input based on its returned value:
//number of processed input samples = returned value * decimation factor
//The output buffer should be at least input_length / 3.
// i: input index | ti: tap index | oi: output index
int oi = 0 ;
for ( int i = 0 ; i < input_size ; i + = decimation ) //@fir_decimate_cc: outer loop
{
if ( i + taps_length > input_size ) break ;
2016-10-06 17:12:34 +08:00
register float * pinput = ( float * ) & ( input [ i ] ) ;
2015-09-30 21:52:43 +08:00
register float * ptaps = taps ;
register float * ptaps_end = taps + taps_length ;
float quad_acciq [ 8 ] ;
2016-03-20 23:41:37 +08:00
2015-09-30 21:52:43 +08:00
/*
q0 , q1 : input signal I sample and Q sample
2016-03-20 23:41:37 +08:00
q2 : taps
2015-09-30 21:52:43 +08:00
q4 , q5 : accumulator for I branch and Q branch ( will be the output )
*/
asm volatile (
2016-10-06 17:12:34 +08:00
" veor q4, q4 \n \t "
" veor q5, q5 \n \t "
2015-09-30 21:52:43 +08:00
" for_fdccasm: vld2.32 {q0-q1}, [%[pinput]]! \n \t " //load q0 and q1 directly from the memory address stored in pinput, with interleaving (so that we get the I samples in q0 and the Q samples in q1), also increment the memory address in pinput (hence the "!" mark) //http://community.arm.com/groups/processors/blog/2010/03/17/coding-for-neon--part-1-load-and-stores
2016-03-20 23:41:37 +08:00
" vld1.32 {q2}, [%[ptaps]]! \n \t "
2015-09-30 21:52:43 +08:00
" vmla.f32 q4, q0, q2 \n \t " //quad_acc_i += quad_input_i * quad_taps_1 //http://stackoverflow.com/questions/3240440/how-to-use-the-multiply-and-accumulate-intrinsics-in-arm-cortex-a8 //http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489e/CIHEJBIE.html
" vmla.f32 q5, q1, q2 \n \t " //quad_acc_q += quad_input_q * quad_taps_1
2015-11-30 06:46:06 +08:00
" cmp %[ptaps], %[ptaps_end] \n \t " //if(ptaps != ptaps_end)
2015-09-30 21:52:43 +08:00
" bcc for_fdccasm \n \t " // then goto for_fdcasm
" vst1.32 {q4}, [%[quad_acci]] \n \t " //if the loop is finished, store the two accumulators in memory
" vst1.32 {q5}, [%[quad_accq]] \n \t "
:
[ pinput ] " +r " ( pinput ) , [ ptaps ] " +r " ( ptaps ) //output operand list
:
[ ptaps_end ] " r " ( ptaps_end ) , [ quad_acci ] " r " ( quad_acciq ) , [ quad_accq ] " r " ( quad_acciq + 4 ) //input operand list
2016-03-20 23:41:37 +08:00
:
2015-09-30 21:52:43 +08:00
" memory " , " q0 " , " q1 " , " q2 " , " q4 " , " q5 " , " cc " //clobber list
) ;
//original for loops for reference:
2016-03-20 23:41:37 +08:00
//for(int ti=0; ti<taps_length; ti++) acci += (iof(input,i+ti)) * taps[ti]; //@fir_decimate_cc: i loop
2015-09-30 21:52:43 +08:00
//for(int ti=0; ti<taps_length; ti++) accq += (qof(input,i+ti)) * taps[ti]; //@fir_decimate_cc: q loop
2016-03-20 23:41:37 +08:00
2015-09-30 21:52:43 +08:00
//for(int n=0;n<8;n++) fprintf(stderr, "\n>> [%d] %g \n", n, quad_acciq[n]);
iof ( output , oi ) = quad_acciq [ 0 ] + quad_acciq [ 1 ] + quad_acciq [ 2 ] + quad_acciq [ 3 ] ; //we're still not ready, as we have to add up the contents of a quad accumulator register to get a single accumulated value
qof ( output , oi ) = quad_acciq [ 4 ] + quad_acciq [ 5 ] + quad_acciq [ 6 ] + quad_acciq [ 7 ] ;
oi + + ;
}
return oi ;
}
# else
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 )
{
//Theory: http://www.dspguru.com/dsp/faqs/multirate/decimation
//It uses real taps. It returns the number of output samples actually written.
//It needs overlapping input based on its returned value:
//number of processed input samples = returned value * decimation factor
//The output buffer should be at least input_length / 3.
// i: input index | ti: tap index | oi: output index
int oi = 0 ;
for ( int i = 0 ; i < input_size ; i + = decimation ) //@fir_decimate_cc: outer loop
{
if ( i + taps_length > input_size ) break ;
float acci = 0 ;
for ( int ti = 0 ; ti < taps_length ; ti + + ) acci + = ( iof ( input , i + ti ) ) * taps [ ti ] ; //@fir_decimate_cc: i loop
float accq = 0 ;
for ( int ti = 0 ; ti < taps_length ; ti + + ) accq + = ( qof ( input , i + ti ) ) * taps [ ti ] ; //@fir_decimate_cc: q loop
iof ( output , oi ) = acci ;
qof ( output , oi ) = accq ;
oi + + ;
}
return oi ;
}
2015-09-30 21:52:43 +08:00
# endif
/*
int fir_decimate_cc ( complexf * input , complexf * output , int input_size , int decimation , float * taps , int taps_length )
{
//Theory: http://www.dspguru.com/dsp/faqs/multirate/decimation
//It uses real taps. It returns the number of output samples actually written.
//It needs overlapping input based on its returned value:
//number of processed input samples = returned value * decimation factor
//The output buffer should be at least input_length / 3.
// i: input index | ti: tap index | oi: output index
int oi = 0 ;
for ( int i = 0 ; i < input_size ; i + = decimation ) //@fir_decimate_cc: outer loop
{
if ( i + taps_length > input_size ) break ;
float acci = 0 ;
int taps_halflength = taps_length / 2 ;
for ( int ti = 0 ; ti < taps_halflength ; ti + + ) acci + = ( iof ( input , i + ti ) + iof ( input , i + taps_length - ti ) ) * taps [ ti ] ; //@fir_decimate_cc: i loop
float accq = 0 ;
for ( int ti = 0 ; ti < taps_halflength ; ti + + ) accq + = ( qof ( input , i + ti ) + qof ( input , i + taps_length - ti ) ) * taps [ ti ] ; //@fir_decimate_cc: q loop
iof ( output , oi ) = acci + iof ( input , i + taps_halflength ) * taps [ taps_halflength ] ;
qof ( output , oi ) = accq + qof ( input , i + taps_halflength ) * taps [ taps_halflength ] ;
oi + + ;
}
return oi ;
}
*/
2017-02-19 21:02:10 +08:00
int fir_interpolate_cc ( complexf * input , complexf * output , int input_size , int interpolation , float * taps , int taps_length )
{
2017-02-19 22:41:33 +08:00
//i: input index
//oi: output index
//ti: tap index
//ti: secondary index (inside filter function)
//ip: interpolation phase (0 <= ip < interpolation)
2017-02-19 21:02:10 +08:00
int oi = 0 ;
2017-02-19 22:41:33 +08:00
for ( int i = 0 ; i < input_size ; i + + ) //@fir_interpolate_cc: outer loop
2017-02-19 21:02:10 +08:00
{
2017-02-19 22:41:33 +08:00
if ( i * interpolation + ( interpolation - 1 ) + taps_length > input_size * interpolation ) break ;
for ( int ip = 0 ; ip < interpolation ; ip + + )
{
float acci = 0 ;
float accq = 0 ;
2017-02-19 23:06:08 +08:00
//int tistart = (interpolation-ip)%interpolation;
int tistart = ( interpolation - ip ) ; //why does this work? why don't we need the % part?
2017-02-19 23:22:49 +08:00
for ( int ti = tistart , si = 0 ; ti < taps_length ; ( ti + = interpolation ) , ( si + + ) ) acci + = ( iof ( input , i + si ) ) * taps [ ti ] ; //@fir_interpolate_cc: i loop
for ( int ti = tistart , si = 0 ; ti < taps_length ; ( ti + = interpolation ) , ( si + + ) ) accq + = ( qof ( input , i + si ) ) * taps [ ti ] ; //@fir_interpolate_cc: q loop
2017-02-19 22:41:33 +08:00
iof ( output , oi ) = acci ;
qof ( output , oi ) = accq ;
oi + + ;
}
2017-02-19 21:02:10 +08:00
}
return oi ;
}
2014-11-28 23:44:41 +08:00
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 )
{
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
//Theory: http://www.dspguru.com/dsp/faqs/multirate/resampling
//oi: output index, i: tap index
int output_size = input_size * interpolation / decimation ;
int oi ;
int startingi , delayi ;
//fprintf(stderr,"rational_resampler_ff | interpolation = %d | decimation = %d\ntaps_length = %d | input_size = %d | output_size = %d | last_taps_delay = %d\n",interpolation,decimation,taps_length,input_size,output_size,last_taps_delay);
for ( oi = 0 ; oi < output_size ; oi + + ) //@rational_resampler_ff (outer loop)
{
float acc = 0 ;
startingi = ( oi * decimation + interpolation - 1 - last_taps_delay ) / interpolation ; //index of first input item to apply FIR on
//delayi=startingi*interpolation-oi*decimation; //delay on FIR taps
delayi = ( last_taps_delay + startingi * interpolation - oi * decimation ) % interpolation ; //delay on FIR taps
if ( startingi + taps_length / interpolation + 1 > input_size ) break ; //we can't compute the FIR filter to some input samples at the end
//fprintf(stderr,"outer loop | oi = %d | startingi = %d | taps delay = %d\n",oi,startingi,delayi);
for ( int i = 0 ; i < ( taps_length - delayi ) / interpolation ; i + + ) //@rational_resampler_ff (inner loop)
{
//fprintf(stderr,"inner loop | input index = %d | tap index = %d | acc = %g\n",startingi+ii,i,acc);
acc + = input [ startingi + i ] * taps [ delayi + i * interpolation ] ;
}
output [ oi ] = acc * interpolation ;
}
rational_resampler_ff_t d ;
d . input_processed = startingi ;
d . output_size = oi ;
d . last_taps_delay = delayi ;
return d ;
}
/*
2016-03-20 23:41:37 +08:00
The greatest challenge in resampling is figuring out which tap should be applied to which sample .
2014-11-28 23:44:41 +08:00
Typical test patterns for rational_resampler_ff :
interpolation = 3 , decimation = 1
values of [ oi , startingi , taps delay ] in the outer loop should be :
0 0 0
1 1 2
2 1 1
3 1 0
4 2 2
5 2 1
interpolation = 3 , decimation = 2
values of [ oi , startingi , taps delay ] in the outer loop should be :
0 0 0
1 1 1
2 2 2
3 2 0
4 3 1
5 4 2
*/
void rational_resampler_get_lowpass_f ( float * output , int output_size , int interpolation , int decimation , window_t window )
{
//See 4.1.6 at: http://www.dspguru.com/dsp/faqs/multirate/resampling
float cutoff_for_interpolation = 1.0 / interpolation ;
float cutoff_for_decimation = 1.0 / decimation ;
float cutoff = ( cutoff_for_interpolation < cutoff_for_decimation ) ? cutoff_for_interpolation : cutoff_for_decimation ; //get the lower
firdes_lowpass_f ( output , output_size , cutoff / 2 , window ) ;
}
float inline fir_one_pass_ff ( float * input , float * taps , int taps_length )
{
float acc = 0 ;
for ( int i = 0 ; i < taps_length ; i + + ) acc + = taps [ i ] * input [ i ] ; //@fir_one_pass_ff
return acc ;
}
2017-02-18 22:49:04 +08:00
old_fractional_decimator_ff_t old_fractional_decimator_ff ( float * input , float * output , int input_size , float rate , float * taps , int taps_length , old_fractional_decimator_ff_t d )
2014-11-28 23:44:41 +08:00
{
if ( rate < = 1.0 ) return d ; //sanity check, can't decimate <=1.0
2016-03-20 23:41:37 +08:00
//This routine can handle floating point decimation rates.
//It linearly interpolates between two samples that are taken into consideration from the filtered input.
2014-11-28 23:44:41 +08:00
int oi = 0 ;
int index_high ;
float where = d . remain ;
float result_high , result_low ;
2016-03-20 23:41:37 +08:00
if ( where = = 0.0 ) //in the first iteration index_high may be zero (so using the item index_high-1 would lead to invalid memory access).
2014-11-28 23:44:41 +08:00
{
output [ oi + + ] = fir_one_pass_ff ( input , taps , taps_length ) ;
where + = rate ;
}
int previous_index_high = - 1 ;
//we optimize to calculate ceilf(where) only once every iteration, so we do it here:
for ( ; ( index_high = ceilf ( where ) ) + taps_length < input_size ; where + = rate ) //@fractional_decimator_ff
{
if ( previous_index_high = = index_high - 1 ) result_low = result_high ; //if we step less than 2.0 then we do already have the result for the FIR filter for that index
2016-03-20 23:41:37 +08:00
else result_low = fir_one_pass_ff ( input + index_high - 1 , taps , taps_length ) ;
2014-11-28 23:44:41 +08:00
result_high = fir_one_pass_ff ( input + index_high , taps , taps_length ) ;
float register rate_between_samples = where - index_high + 1 ;
output [ oi + + ] = result_low * ( 1 - rate_between_samples ) + result_high * rate_between_samples ;
previous_index_high = index_high ;
}
d . input_processed = index_high - 1 ;
d . remain = where - d . input_processed ;
d . output_size = oi ;
return d ;
}
2017-02-18 22:49:04 +08:00
fractional_decimator_ff_t fractional_decimator_ff_init ( float rate , int num_poly_points , float * taps , int taps_length )
{
fractional_decimator_ff_t d ;
d . num_poly_points = num_poly_points & ~ 1 ; //num_poly_points needs to be even!
d . poly_precalc_denomiator = ( float * ) malloc ( d . num_poly_points * sizeof ( float ) ) ;
//x0..x3
//-1,0,1,2
//-(4/2)+1
//x0..x5
//-2,-1,0,1,2,3
d . xifirst = - ( num_poly_points / 2 ) + 1 , d . xilast = num_poly_points / 2 ;
int id = 0 ; //index in poly_precalc_denomiator
for ( int xi = d . xifirst ; xi < = d . xilast ; xi + + )
{
d . poly_precalc_denomiator [ id ] = 1 ;
for ( int xj = d . xifirst ; xj < = d . xilast ; xj + + )
{
if ( xi ! = xj ) d . poly_precalc_denomiator [ id ] * = ( xi - xj ) ; //poly_precalc_denomiator could be integer as well. But that would later add a necessary conversion.
}
id + + ;
}
d . where = - d . xifirst ;
d . coeffs_buf = ( float * ) malloc ( d . num_poly_points * sizeof ( float ) ) ;
d . filtered_buf = ( float * ) malloc ( d . num_poly_points * sizeof ( float ) ) ;
//d.last_inputs_circbuf = (float)malloc(d.num_poly_points*sizeof(float));
//d.last_inputs_startsat = 0;
//d.last_inputs_samplewhere = -1;
//for(int i=0;i<num_poly_points; i++) d.last_inputs_circbuf[i] = 0;
d . rate = rate ;
d . taps = taps ;
d . taps_length = taps_length ;
2017-04-03 05:23:47 +08:00
d . input_processed = 0 ;
2017-02-18 22:49:04 +08:00
return d ;
}
# define DEBUG_ASSERT 1
void fractional_decimator_ff ( float * input , float * output , int input_size , fractional_decimator_ff_t * d )
{
//This routine can handle floating point decimation rates.
//It applies polynomial interpolation to samples that are taken into consideration from a pre-filtered input.
//The pre-filter can be switched off by applying taps=NULL.
//fprintf(stderr, "drate=%f\n", d->rate);
if ( DEBUG_ASSERT ) assert ( d - > rate > 1.0 ) ;
2017-02-18 23:01:14 +08:00
if ( DEBUG_ASSERT ) assert ( d - > where > = - d - > xifirst ) ;
2017-02-18 22:49:04 +08:00
int oi = 0 ; //output index
int index_high ;
# define FD_INDEX_LOW (index_high-1)
//we optimize to calculate ceilf(where) only once every iteration, so we do it here:
2017-02-18 23:36:36 +08:00
for ( ; ( index_high = ceilf ( d - > where ) ) + d - > num_poly_points + d - > taps_length < input_size ; d - > where + = d - > rate ) //@fractional_decimator_ff
2017-02-18 22:49:04 +08:00
{
2017-02-18 23:37:49 +08:00
//d->num_poly_points above is theoretically more than we could have here, but this makes the spectrum look good
2017-02-18 22:49:04 +08:00
int sxifirst = FD_INDEX_LOW + d - > xifirst ;
int sxilast = FD_INDEX_LOW + d - > xilast ;
2017-02-28 07:27:41 +08:00
if ( d - > taps )
for ( int wi = 0 ; wi < d - > num_poly_points ; wi + + ) d - > filtered_buf [ wi ] = fir_one_pass_ff ( input + FD_INDEX_LOW + wi , d - > taps , d - > taps_length ) ;
else
for ( int wi = 0 ; wi < d - > num_poly_points ; wi + + ) d - > filtered_buf [ wi ] = * ( input + FD_INDEX_LOW + wi ) ;
2017-02-18 22:49:04 +08:00
int id = 0 ;
float xwhere = d - > where - FD_INDEX_LOW ;
for ( int xi = d - > xifirst ; xi < = d - > xilast ; xi + + )
{
d - > coeffs_buf [ id ] = 1 ;
for ( int xj = d - > xifirst ; xj < = d - > xilast ; xj + + )
{
if ( xi ! = xj ) d - > coeffs_buf [ id ] * = ( xwhere - xj ) ;
}
id + + ;
}
float acc = 0 ;
for ( int i = 0 ; i < d - > num_poly_points ; i + + )
{
acc + = ( d - > coeffs_buf [ i ] / d - > poly_precalc_denomiator [ i ] ) * d - > filtered_buf [ i ] ; //(xnom/xden)*yn
}
output [ oi + + ] = acc ;
}
2017-02-18 23:01:14 +08:00
d - > input_processed = FD_INDEX_LOW + d - > xifirst ;
2017-02-18 23:24:19 +08:00
d - > where - = d - > input_processed ;
2017-02-18 23:01:14 +08:00
d - > output_size = oi ;
2017-02-18 22:49:04 +08:00
}
/*
2017-03-01 00:03:21 +08:00
* Some notes to myself on the circular buffer I wanted to implement here :
2017-02-18 22:49:04 +08:00
int last_input_samplewhere_shouldbe = ( index_high - 1 ) + xifirst ;
int last_input_offset = last_input_samplewhere_shouldbe - d - > last_input_samplewhere ;
if ( last_input_offset < num_poly_points )
{
//if we can move the last_input circular buffer, we move, and add the new samples at the end
d - > last_inputs_startsat + = last_input_offset ;
d - > last_inputs_startsat % = num_poly_points ;
int num_copied_samples = 0 ;
for ( int i = 0 ; i < last_input_offset ; i + + )
{
d - > last_inputs_circbuf [ i ] =
}
d - > last_input_samplewhere = d - > las
}
2017-03-01 00:03:21 +08:00
However , I think I should just rather do a continuous big buffer .
2017-02-18 22:49:04 +08:00
*/
2014-11-28 23:44:41 +08:00
void apply_fir_fft_cc ( FFT_PLAN_T * plan , FFT_PLAN_T * plan_inverse , complexf * taps_fft , complexf * last_overlap , int overlap_size )
{
2015-08-17 05:40:42 +08:00
//use the overlap & add method for filtering
2014-11-28 23:44:41 +08:00
//calculate FFT on input buffer
fft_execute ( plan ) ;
//multiply the filter and the input
complexf * in = plan - > output ;
complexf * out = plan_inverse - > input ;
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
for ( int i = 0 ; i < plan - > size ; i + + ) //@apply_fir_fft_cc: multiplication
{
iof ( out , i ) = iof ( in , i ) * iof ( taps_fft , i ) - qof ( in , i ) * qof ( taps_fft , i ) ;
qof ( out , i ) = iof ( in , i ) * qof ( taps_fft , i ) + qof ( in , i ) * iof ( taps_fft , i ) ;
}
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
//calculate inverse FFT on multiplied buffer
fft_execute ( plan_inverse ) ;
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
//add the overlap of the previous segment
complexf * result = plan_inverse - > output ;
for ( int i = 0 ; i < plan - > size ; i + + ) //@apply_fir_fft_cc: normalize by fft_size
{
iof ( result , i ) / = plan - > size ;
qof ( result , i ) / = plan - > size ;
}
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
for ( int i = 0 ; i < overlap_size ; i + + ) //@apply_fir_fft_cc: add overlap
{
iof ( result , i ) = iof ( result , i ) + iof ( last_overlap , i ) ;
qof ( result , i ) = qof ( result , i ) + qof ( last_overlap , i ) ;
}
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
}
/*
2016-03-20 23:41:37 +08:00
__ __ _ _ _ _
/ \ | \ / | | | | | | | | |
/ \ | \ / | __ | | ___ _ __ ___ ___ __ | | _ _ | | __ _ | | _ ___ _ __ ___
2014-11-28 23:44:41 +08:00
/ / \ \ | | \ / | | / _ ` | / _ \ ' _ ` _ \ / _ \ / _ ` | | | | | / _ ` | __ / _ \ | ' __ / __ |
/ ____ \ | | | | | ( _ | | __ / | | | | | ( _ ) | ( _ | | | _ | | | ( _ | | | | ( _ ) | | \ __ \
/ _ / \ _ \ _ | | _ | \ __ , _ | \ ___ | _ | | _ | | _ | \ ___ / \ __ , _ | \ __ , _ | _ | \ __ , _ | \ __ \ ___ / | _ | | ___ /
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
*/
void amdemod_cf ( complexf * input , float * output , int input_size )
{
//@amdemod: i*i+q*q
for ( int i = 0 ; i < input_size ; i + + )
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
output [ i ] = iof ( input , i ) * iof ( input , i ) + qof ( input , i ) * qof ( input , i ) ;
}
//@amdemod: sqrt
for ( int i = 0 ; i < input_size ; i + + )
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
output [ i ] = sqrt ( output [ i ] ) ;
}
}
void amdemod_estimator_cf ( complexf * input , float * output , int input_size , float alpha , float beta )
{
//concept is explained here:
//http://www.dspguru.com/dsp/tricks/magnitude-estimator
//default: optimize for min RMS error
2016-03-20 23:41:37 +08:00
if ( alpha = = 0 )
2014-11-28 23:44:41 +08:00
{
alpha = 0.947543636291 ;
beta = 0.392485425092 ;
}
//@amdemod_estimator
for ( int i = 0 ; i < input_size ; i + + )
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
float abs_i = iof ( input , i ) ;
if ( abs_i < 0 ) abs_i = - abs_i ;
float abs_q = qof ( input , i ) ;
if ( abs_q < 0 ) abs_q = - abs_q ;
float max_iq = abs_i ;
if ( abs_q > max_iq ) max_iq = abs_q ;
float min_iq = abs_i ;
if ( abs_q < min_iq ) min_iq = abs_q ;
output [ i ] = alpha * max_iq + beta * min_iq ;
}
}
dcblock_preserve_t dcblock_ff ( float * input , float * output , int input_size , float a , dcblock_preserve_t preserved )
{
//after AM demodulation, a DC blocking filter should be used to remove the DC component from the signal.
//Concept: http://peabody.sapp.org/class/dmp2/lab/dcblock/
//output size equals to input_size;
//preserve can be initialized to zero on first run.
if ( a = = 0 ) a = 0.999 ; //default value, simulate in octave: freqz([1 -1],[1 -0.99])
output [ 0 ] = input [ 0 ] - preserved . last_input + a * preserved . last_output ;
for ( int i = 1 ; i < input_size ; i + + ) //@dcblock_f
{
output [ i ] = input [ i ] - input [ i - 1 ] + a * output [ i - 1 ] ;
}
preserved . last_input = input [ input_size - 1 ] ;
preserved . last_output = output [ input_size - 1 ] ;
return preserved ;
}
float fastdcblock_ff ( float * input , float * output , int input_size , float last_dc_level )
{
//this DC block filter does moving average block-by-block.
//this is the most computationally efficient
//input and output buffer is allowed to be the same
//http://www.digitalsignallabs.com/dcblock.pdf
float avg = 0.0 ;
for ( int i = 0 ; i < input_size ; i + + ) //@fastdcblock_ff: calculate block average
{
avg + = input [ i ] ;
}
avg / = input_size ;
2016-03-20 23:41:37 +08:00
float avgdiff = avg - last_dc_level ;
2014-11-28 23:44:41 +08:00
//DC removal level will change lineraly from last_dc_level to avg.
for ( int i = 0 ; i < input_size ; i + + ) //@fastdcblock_ff: remove DC component
{
float dc_removal_level = last_dc_level + avgdiff * ( ( float ) i / input_size ) ;
output [ i ] = input [ i ] - dc_removal_level ;
}
return avg ;
}
2015-09-30 21:52:43 +08:00
//#define FASTAGC_MAX_GAIN (65e3)
# define FASTAGC_MAX_GAIN 50
2014-11-28 23:44:41 +08:00
void fastagc_ff ( fastagc_ff_t * input , float * output )
{
//Gain is processed on blocks of samples.
//You have to supply three blocks of samples before the first block comes out.
//AGC reaction speed equals input_size*samp_rate*2
2016-03-20 23:41:37 +08:00
//The algorithm calculates target gain at the end of the first block out of the peak value of all the three blocks.
2014-11-28 23:44:41 +08:00
//This way the gain change can easily react if there is any peak in the third block.
//Pros: can be easily speeded up with loop vectorization, easy to implement
//Cons: needs 3 buffers, dos not behave similarly to real AGC circuits
//Get the peak value of new input buffer
float peak_input = 0 ;
for ( int i = 0 ; i < input - > input_size ; i + + ) //@fastagc_ff: peak search
{
float val = fabs ( input - > buffer_input [ i ] ) ;
if ( val > peak_input ) peak_input = val ;
}
//Determine the maximal peak out of the three blocks
float target_peak = peak_input ;
if ( target_peak < input - > peak_2 ) target_peak = input - > peak_2 ;
if ( target_peak < input - > peak_1 ) target_peak = input - > peak_1 ;
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
//we change the gain linearly on the apply_block from the last_gain to target_gain.
float target_gain = input - > reference / target_peak ;
if ( target_gain > FASTAGC_MAX_GAIN ) target_gain = FASTAGC_MAX_GAIN ;
2015-09-30 21:52:43 +08:00
//fprintf(stderr, "target_gain: %g\n",target_gain);
2014-11-28 23:44:41 +08:00
for ( int i = 0 ; i < input - > input_size ; i + + ) //@fastagc_ff: apply gain
{
float rate = ( float ) i / input - > input_size ;
float gain = input - > last_gain * ( 1.0 - rate ) + target_gain * rate ;
output [ i ] = input - > buffer_1 [ i ] * gain ;
}
//Shift the three buffers
float * temp_pointer = input - > buffer_1 ;
input - > buffer_1 = input - > buffer_2 ;
input - > peak_1 = input - > peak_2 ;
input - > buffer_2 = input - > buffer_input ;
input - > peak_2 = peak_input ;
input - > buffer_input = temp_pointer ;
input - > last_gain = target_gain ;
//fprintf(stderr,"target_gain=%g\n", target_gain);
}
/*
2016-03-20 23:41:37 +08:00
______ __ __ _ _ _ _
| ____ | \ / | | | | | | | | |
| | __ | \ / | __ | | ___ _ __ ___ ___ __ | | _ _ | | __ _ | | _ ___ _ __ ___
2014-11-28 23:44:41 +08:00
| __ | | | \ / | | / _ ` | / _ \ ' _ ` _ \ / _ \ / _ ` | | | | | / _ ` | __ / _ \ | ' __ / __ |
| | | | | | | ( _ | | __ / | | | | | ( _ ) | ( _ | | | _ | | | ( _ | | | | ( _ ) | | \ __ \
| _ | | _ | | _ | \ __ , _ | \ ___ | _ | | _ | | _ | \ ___ / \ __ , _ | \ __ , _ | _ | \ __ , _ | \ __ \ ___ / | _ | | ___ /
*/
float fmdemod_atan_cf ( complexf * input , float * output , int input_size , float last_phase )
{
//GCC most likely won't vectorize nor atan, nor atan2.
//For more comments, look at: https://github.com/simonyiszk/minidemod/blob/master/minidemod-wfm-atan.c
float phase , dphase ;
for ( int i = 0 ; i < input_size ; i + + ) //@fmdemod_atan_novect
{
phase = argof ( input , i ) ;
dphase = phase - last_phase ;
if ( dphase < - PI ) dphase + = 2 * PI ;
if ( dphase > PI ) dphase - = 2 * PI ;
output [ i ] = dphase / PI ;
last_phase = phase ;
}
return last_phase ;
}
# define fmdemod_quadri_K 0.340447550238101026565118445432744920253753662109375
//this constant ensures proper scaling for qa_fmemod testcases for SNR calculation and more.
complexf fmdemod_quadri_novect_cf ( complexf * input , float * output , int input_size , complexf last_sample )
{
output [ 0 ] = fmdemod_quadri_K * ( iof ( input , 0 ) * ( qof ( input , 0 ) - last_sample . q ) - qof ( input , 0 ) * ( iof ( input , 0 ) - last_sample . i ) ) / ( iof ( input , 0 ) * iof ( input , 0 ) + qof ( input , 0 ) * qof ( input , 0 ) ) ;
for ( int i = 1 ; i < input_size ; i + + ) //@fmdemod_quadri_novect_cf
{
float qnow = qof ( input , i ) ;
float qlast = qof ( input , i - 1 ) ;
float inow = iof ( input , i ) ;
float ilast = iof ( input , i - 1 ) ;
output [ i ] = fmdemod_quadri_K * ( inow * ( qnow - qlast ) - qnow * ( inow - ilast ) ) / ( inow * inow + qnow * qnow ) ;
//TODO: expression can be simplified as: (qnow*ilast-inow*qlast)/(inow*inow+qnow*qnow)
}
return input [ input_size - 1 ] ;
}
complexf fmdemod_quadri_cf ( complexf * input , float * output , int input_size , float * temp , complexf last_sample )
{
float * temp_dq = temp ;
float * temp_di = temp + input_size ;
temp_dq [ 0 ] = qof ( input , 0 ) - last_sample . q ;
for ( int i = 1 ; i < input_size ; i + + ) //@fmdemod_quadri_cf: dq
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
temp_dq [ i ] = qof ( input , i ) - qof ( input , i - 1 ) ;
}
temp_di [ 0 ] = iof ( input , 0 ) - last_sample . i ;
for ( int i = 1 ; i < input_size ; i + + ) //@fmdemod_quadri_cf: di
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
temp_di [ i ] = iof ( input , i ) - iof ( input , i - 1 ) ;
}
for ( int i = 0 ; i < input_size ; i + + ) //@fmdemod_quadri_cf: output numerator
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
output [ i ] = ( iof ( input , i ) * temp_dq [ i ] - qof ( input , i ) * temp_di [ i ] ) ;
}
for ( int i = 0 ; i < input_size ; i + + ) //@fmdemod_quadri_cf: output denomiator
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
temp [ i ] = iof ( input , i ) * iof ( input , i ) + qof ( input , i ) * qof ( input , i ) ;
2016-03-20 23:41:37 +08:00
}
2014-11-28 23:44:41 +08:00
for ( int i = 0 ; i < input_size ; i + + ) //@fmdemod_quadri_cf: output division
2016-03-20 23:41:37 +08:00
{
2016-03-21 06:30:21 +08:00
output [ i ] = ( temp [ i ] ) ? fmdemod_quadri_K * output [ i ] / temp [ i ] : 0 ;
2014-11-28 23:44:41 +08:00
}
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
return input [ input_size - 1 ] ;
}
2016-03-20 23:41:37 +08:00
inline int is_nan ( float f )
2015-08-17 05:40:42 +08:00
{
//http://stackoverflow.com/questions/570669/checking-if-a-double-or-float-is-nan-in-c
unsigned u = * ( unsigned * ) & f ;
return ( u & 0x7F800000 ) = = 0x7F800000 & & ( u & 0x7FFFFF ) ; // Both NaN and qNan.
}
2014-11-28 23:44:41 +08:00
float deemphasis_wfm_ff ( float * input , float * output , int input_size , float tau , int sample_rate , float last_output )
{
2016-03-20 23:41:37 +08:00
/*
2014-11-28 23:44:41 +08:00
typical time constant ( tau ) values :
WFM transmission in USA : 75 us - > tau = 75e-6
WFM transmission in EU : 50 us - > tau = 50e-6
More info at : http : //www.cliftonlaboratories.com/fm_receivers_and_de-emphasis.htm
Simulate in octave : tau = 75e-6 ; dt = 1 / 48000 ; alpha = dt / ( tau + dt ) ; freqz ( [ alpha ] , [ 1 - ( 1 - alpha ) ] )
*/
float dt = 1.0 / sample_rate ;
float alpha = dt / ( tau + dt ) ;
2015-08-17 05:40:42 +08:00
if ( is_nan ( last_output ) ) last_output = 0.0 ; //if last_output is NaN
2016-03-20 23:41:37 +08:00
output [ 0 ] = alpha * input [ 0 ] + ( 1 - alpha ) * last_output ;
2014-11-28 23:44:41 +08:00
for ( int i = 1 ; i < input_size ; i + + ) //@deemphasis_wfm_ff
output [ i ] = alpha * input [ i ] + ( 1 - alpha ) * output [ i - 1 ] ; //this is the simplest IIR LPF
2017-03-03 23:21:45 +08:00
return output [ input_size - 1 ] ;
2014-11-28 23:44:41 +08:00
}
# define DNFMFF_ADD_ARRAY(x) if(sample_rate==x) { taps=deemphasis_nfm_predefined_fir_##x; taps_length=sizeof(deemphasis_nfm_predefined_fir_##x) / sizeof(float); }
int deemphasis_nfm_ff ( float * input , float * output , int input_size , int sample_rate )
{
/*
Warning ! This only works on predefined samplerates , as it uses fixed FIR coefficients defined in predefined . h
However , there are the octave commands to generate the taps for your custom ( fixed ) sample rate .
2016-03-20 23:41:37 +08:00
What it does :
2014-11-28 23:44:41 +08:00
- reject below 400 Hz
- passband between 400 HZ - 4 kHz , but with 20 dB / decade rolloff ( for deemphasis )
- reject everything above 4 kHz
*/
float * taps ;
int taps_length = 0 ;
2014-12-12 17:22:57 +08:00
2014-11-28 23:44:41 +08:00
DNFMFF_ADD_ARRAY ( 48000 )
2014-12-12 17:22:57 +08:00
DNFMFF_ADD_ARRAY ( 44100 )
2014-11-28 23:44:41 +08:00
DNFMFF_ADD_ARRAY ( 8000 )
2015-08-17 05:40:42 +08:00
DNFMFF_ADD_ARRAY ( 11025 )
2014-12-12 17:22:57 +08:00
2014-11-28 23:44:41 +08:00
if ( ! taps_length ) return 0 ; //sample rate n
int i ;
for ( i = 0 ; i < input_size - taps_length ; i + + ) //@deemphasis_nfm_ff: outer loop
{
float acc = 0 ;
for ( int ti = 0 ; ti < taps_length ; ti + + ) acc + = taps [ ti ] * input [ i + ti ] ; //@deemphasis_nfm_ff: inner loop
output [ i ] = acc ;
}
return i ; //number of samples processed (and output samples)
}
void limit_ff ( float * input , float * output , int input_size , float max_amplitude )
{
for ( int i = 0 ; i < input_size ; i + + ) //@limit_ff
2016-03-20 23:41:37 +08:00
{
2014-11-28 23:44:41 +08:00
output [ i ] = ( max_amplitude < input [ i ] ) ? max_amplitude : input [ i ] ;
output [ i ] = ( - max_amplitude > output [ i ] ) ? - max_amplitude : output [ i ] ;
2016-03-20 23:41:37 +08:00
}
2014-11-28 23:44:41 +08:00
}
void gain_ff ( float * input , float * output , int input_size , float gain )
{
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = gain * input [ i ] ; //@gain_ff
}
2016-03-20 23:41:37 +08:00
float get_power_f ( float * input , int input_size , int decimation )
{
2017-03-03 23:21:45 +08:00
float acc = 0 ;
for ( int i = 0 ; i < input_size ; i + = decimation )
{
acc + = ( input [ i ] * input [ i ] ) / input_size ;
}
return acc ;
2016-03-20 23:41:37 +08:00
}
float get_power_c ( complexf * input , int input_size , int decimation )
{
2017-03-03 23:21:45 +08:00
float acc = 0 ;
for ( int i = 0 ; i < input_size ; i + = decimation )
{
acc + = ( iof ( input , i ) * iof ( input , i ) + qof ( input , i ) * qof ( input , i ) ) / input_size ;
}
return acc ;
2016-03-20 23:41:37 +08:00
}
2015-10-31 23:22:33 +08:00
/*
2017-03-03 23:21:45 +08:00
__ __ _ _ _
| \ / | | | | | | |
| \ / | ___ __ | | _ _ | | __ _ | | _ ___ _ __ ___
| | \ / | | / _ \ / _ ` | | | | | / _ ` | __ / _ \ | ' __ / __ |
| | | | ( _ ) | ( _ | | | _ | | | ( _ | | | | ( _ ) | | \ __ \
| _ | | _ | \ ___ / \ __ , _ | \ __ , _ | _ | \ __ , _ | \ __ \ ___ / | _ | | ___ /
2016-03-20 23:41:37 +08:00
2015-10-31 23:22:33 +08:00
*/
void add_dcoffset_cc ( complexf * input , complexf * output , int input_size )
{
2016-03-20 23:41:37 +08:00
for ( int i = 0 ; i < input_size ; i + + ) iof ( output , i ) = 0.5 + iof ( input , i ) / 2 ;
for ( int i = 0 ; i < input_size ; i + + ) qof ( output , i ) = qof ( input , i ) / 2 ;
2015-10-31 23:22:33 +08:00
}
2014-11-28 23:44:41 +08:00
2015-10-31 23:42:17 +08:00
float fmmod_fc ( float * input , complexf * output , int input_size , float last_phase )
{
float phase = last_phase ;
for ( int i = 0 ; i < input_size ; i + + )
{
phase + = input [ i ] * PI ;
while ( phase > PI ) phase - = 2 * PI ;
while ( phase < = - PI ) phase + = 2 * PI ;
iof ( output , i ) = cos ( phase ) ;
qof ( output , i ) = sin ( phase ) ;
}
return phase ;
}
2015-11-01 19:30:03 +08:00
void fixed_amplitude_cc ( complexf * input , complexf * output , int input_size , float new_amplitude )
{
for ( int i = 0 ; i < input_size ; i + + )
{
//float phase=atan2(iof(input,i),qof(input,i));
//iof(output,i)=cos(phase)*amp;
2016-03-20 23:41:37 +08:00
//qof(output,i)=sin(phase)*amp;
2015-11-01 19:30:03 +08:00
//A faster solution:
float amplitude_now = sqrt ( iof ( input , i ) * iof ( input , i ) + qof ( input , i ) * qof ( input , i ) ) ;
float gain = ( amplitude_now > 0 ) ? new_amplitude / amplitude_now : 0 ;
iof ( output , i ) = iof ( input , i ) * gain ;
qof ( output , i ) = qof ( input , i ) * gain ;
2016-03-20 23:41:37 +08:00
}
2015-11-01 19:30:03 +08:00
}
2014-11-28 23:44:41 +08:00
/*
2016-03-20 23:41:37 +08:00
______ _ ______ _ _______ __
| ____ | | | | ____ | ( _ ) | __ __ | / _ |
| | __ __ _ ___ | | _ | | __ ___ _ _ _ __ _ ___ _ __ | | _ __ __ _ _ __ ___ | | _ ___ _ __ _ __ ___
| __ / _ ` / __ | __ | | __ / _ \ | | | | ' __ | | / _ \ ' __ | | | ' __ / _ ` | ' _ \ / __ | _ / _ \ | ' __ | ' _ ` _ \
2014-11-28 23:44:41 +08:00
| | | ( _ | \ __ \ | _ | | | ( _ ) | | _ | | | | | __ / | | | | | ( _ | | | | \ __ \ | | ( _ ) | | | | | | | |
2016-03-20 23:41:37 +08:00
| _ | \ __ , _ | ___ / \ __ | | _ | \ ___ / \ __ , _ | _ | | _ | \ ___ | _ | | _ | _ | \ __ , _ | _ | | _ | ___ / _ | \ ___ / | _ | | _ | | _ | | _ |
2014-11-28 23:44:41 +08:00
*/
int log2n ( int x )
{
int result = - 1 ;
2016-03-20 23:41:37 +08:00
for ( int i = 0 ; i < 31 ; i + + )
2014-11-28 23:44:41 +08:00
{
if ( ( x > > i ) & 1 ) //@@log2n
{
if ( result = = - 1 ) result = i ;
else return - 1 ;
}
}
return result ;
}
int next_pow2 ( int x )
{
int pow2 ;
//portability? (31 is the problem)
2016-03-20 23:41:37 +08:00
for ( int i = 0 ; i < 31 ; i + + )
2014-11-28 23:44:41 +08:00
{
if ( x < ( pow2 = 1 < < i ) ) return pow2 ; //@@next_pow2
}
return - 1 ;
}
void apply_window_c ( complexf * input , complexf * output , int size , window_t window )
{
float ( * window_function ) ( float ) = firdes_get_window_kernel ( window ) ;
for ( int i = 0 ; i < size ; i + + ) //@apply_window_c
{
float rate = ( float ) i / ( size - 1 ) ;
iof ( output , i ) = iof ( input , i ) * window_function ( 2.0 * rate + 1.0 ) ;
qof ( output , i ) = qof ( input , i ) * window_function ( 2.0 * rate + 1.0 ) ;
}
}
2016-03-26 17:44:28 +08:00
float * precalculate_window ( int size , window_t window )
{
float ( * window_function ) ( float ) = firdes_get_window_kernel ( window ) ;
float * windowt ;
windowt = malloc ( sizeof ( float ) * size ) ;
for ( int i = 0 ; i < size ; i + + ) //@precalculate_window
{
float rate = ( float ) i / ( size - 1 ) ;
windowt [ i ] = window_function ( 2.0 * rate + 1.0 ) ;
}
return windowt ;
}
void apply_precalculated_window_c ( complexf * input , complexf * output , int size , float * windowt )
{
for ( int i = 0 ; i < size ; i + + ) //@apply_precalculated_window_c
{
iof ( output , i ) = iof ( input , i ) * windowt [ i ] ;
qof ( output , i ) = qof ( input , i ) * windowt [ i ] ;
}
}
2014-11-28 23:44:41 +08:00
void apply_window_f ( float * input , float * output , int size , window_t window )
{
float ( * window_function ) ( float ) = firdes_get_window_kernel ( window ) ;
for ( int i = 0 ; i < size ; i + + ) //@apply_window_f
{
float rate = ( float ) i / ( size - 1 ) ;
output [ i ] = input [ i ] * window_function ( 2.0 * rate + 1.0 ) ;
}
}
void logpower_cf ( complexf * input , float * output , int size , float add_db )
{
for ( int i = 0 ; i < size ; i + + ) output [ i ] = iof ( input , i ) * iof ( input , i ) + qof ( input , i ) * qof ( input , i ) ; //@logpower_cf: pass 1
2016-03-20 23:41:37 +08:00
2014-11-28 23:44:41 +08:00
for ( int i = 0 ; i < size ; i + + ) output [ i ] = log10 ( output [ i ] ) ; //@logpower_cf: pass 2
for ( int i = 0 ; i < size ; i + + ) output [ i ] = 10 * output [ i ] + add_db ; //@logpower_cf: pass 3
}
2016-03-26 00:00:20 +08:00
void accumulate_power_cf ( complexf * input , float * output , int size )
{
for ( int i = 0 ; i < size ; i + + ) output [ i ] + = iof ( input , i ) * iof ( input , i ) + qof ( input , i ) * qof ( input , i ) ; //@logpower_cf: pass 1
}
void log_ff ( float * input , float * output , int size , float add_db ) {
2016-10-23 03:16:47 +08:00
for ( int i = 0 ; i < size ; i + + ) output [ i ] = log10 ( input [ i ] ) ; //@logpower_cf: pass 2
2016-03-26 00:00:20 +08:00
for ( int i = 0 ; i < size ; i + + ) output [ i ] = 10 * output [ i ] + add_db ; //@logpower_cf: pass 3
}
2016-05-05 05:46:23 +08:00
/*
_____ _ _ _ _ _ _
| __ \ ( _ ) ( _ ) | | | | | | |
| | | | _ __ _ _ | | _ __ _ | | __ | | ___ _ __ ___ ___ __ | |
| | | | | / _ ` | | __ / _ ` | | / _ ` | / _ \ ' _ ` _ \ / _ \ / _ ` |
| | __ | | | ( _ | | | | | ( _ | | | | ( _ | | __ / | | | | | ( _ ) | ( _ | |
| _____ / | _ | \ __ , | _ | \ __ \ __ , _ | _ | \ __ , _ | \ ___ | _ | | _ | | _ | \ ___ / \ __ , _ |
__ / |
| ___ /
*/
psk31_varicode_item_t psk31_varicode_items [ ] =
{
{ . code = 0 b1010101011 , . bitcount = 10 , . ascii = 0x00 } , //NUL, null
{ . code = 0 b1011011011 , . bitcount = 10 , . ascii = 0x01 } , //SOH, start of heading
{ . code = 0 b1011101101 , . bitcount = 10 , . ascii = 0x02 } , //STX, start of text
{ . code = 0 b1101110111 , . bitcount = 10 , . ascii = 0x03 } , //ETX, end of text
{ . code = 0 b1011101011 , . bitcount = 10 , . ascii = 0x04 } , //EOT, end of transmission
{ . code = 0 b1101011111 , . bitcount = 10 , . ascii = 0x05 } , //ENQ, enquiry
{ . code = 0 b1011101111 , . bitcount = 10 , . ascii = 0x06 } , //ACK, acknowledge
{ . code = 0 b1011111101 , . bitcount = 10 , . ascii = 0x07 } , //BEL, bell
{ . code = 0 b1011111111 , . bitcount = 10 , . ascii = 0x08 } , //BS, backspace
{ . code = 0 b11101111 , . bitcount = 8 , . ascii = 0x09 } , //TAB, horizontal tab
2016-05-08 15:33:40 +08:00
{ . code = 0 b11101 , . bitcount = 5 , . ascii = 0x0a } , //LF, NL line feed, new line
2016-05-05 05:46:23 +08:00
{ . code = 0 b1101101111 , . bitcount = 10 , . ascii = 0x0b } , //VT, vertical tab
{ . code = 0 b1011011101 , . bitcount = 10 , . ascii = 0x0c } , //FF, NP form feed, new page
2016-05-08 15:33:40 +08:00
{ . code = 0 b11111 , . bitcount = 5 , . ascii = 0x0d } , //CR, carriage return (overwrite)
2016-05-05 05:46:23 +08:00
{ . code = 0 b1101110101 , . bitcount = 10 , . ascii = 0x0e } , //SO, shift out
{ . code = 0 b1110101011 , . bitcount = 10 , . ascii = 0x0f } , //SI, shift in
{ . code = 0 b1011110111 , . bitcount = 10 , . ascii = 0x10 } , //DLE, data link escape
{ . code = 0 b1011110101 , . bitcount = 10 , . ascii = 0x11 } , //DC1, device control 1
{ . code = 0 b1110101101 , . bitcount = 10 , . ascii = 0x12 } , //DC2, device control 2
{ . code = 0 b1110101111 , . bitcount = 10 , . ascii = 0x13 } , //DC3, device control 3
{ . code = 0 b1101011011 , . bitcount = 10 , . ascii = 0x14 } , //DC4, device control 4
{ . code = 0 b1101101011 , . bitcount = 10 , . ascii = 0x15 } , //NAK, negative acknowledge
{ . code = 0 b1101101101 , . bitcount = 10 , . ascii = 0x16 } , //SYN, synchronous idle
{ . code = 0 b1101010111 , . bitcount = 10 , . ascii = 0x17 } , //ETB, end of trans. block
{ . code = 0 b1101111011 , . bitcount = 10 , . ascii = 0x18 } , //CAN, cancel
{ . code = 0 b1101111101 , . bitcount = 10 , . ascii = 0x19 } , //EM, end of medium
{ . code = 0 b1110110111 , . bitcount = 10 , . ascii = 0x1a } , //SUB, substitute
{ . code = 0 b1101010101 , . bitcount = 10 , . ascii = 0x1b } , //ESC, escape
{ . code = 0 b1101011101 , . bitcount = 10 , . ascii = 0x1c } , //FS, file separator
{ . code = 0 b1110111011 , . bitcount = 10 , . ascii = 0x1d } , //GS, group separator
{ . code = 0 b1011111011 , . bitcount = 10 , . ascii = 0x1e } , //RS, record separator
{ . code = 0 b1101111111 , . bitcount = 10 , . ascii = 0x1f } , //US, unit separator
{ . code = 0 b1 , . bitcount = 1 , . ascii = 0x20 } , //szóköz
{ . code = 0 b111111111 , . bitcount = 9 , . ascii = 0x21 } , //!
{ . code = 0 b101011111 , . bitcount = 9 , . ascii = 0x22 } , //"
{ . code = 0 b111110101 , . bitcount = 9 , . ascii = 0x23 } , //#
{ . code = 0 b111011011 , . bitcount = 9 , . ascii = 0x24 } , //$
{ . code = 0 b1011010101 , . bitcount = 10 , . ascii = 0x25 } , //%
{ . code = 0 b1010111011 , . bitcount = 10 , . ascii = 0x26 } , //&
{ . code = 0 b101111111 , . bitcount = 9 , . ascii = 0x27 } , //'
{ . code = 0 b11111011 , . bitcount = 8 , . ascii = 0x28 } , //(
{ . code = 0 b11110111 , . bitcount = 8 , . ascii = 0x29 } , //)
{ . code = 0 b101101111 , . bitcount = 9 , . ascii = 0x2a } , //*
{ . code = 0 b111011111 , . bitcount = 9 , . ascii = 0x2b } , //+
{ . code = 0 b1110101 , . bitcount = 7 , . ascii = 0x2c } , //,
{ . code = 0 b110101 , . bitcount = 6 , . ascii = 0x2d } , //-
{ . code = 0 b1010111 , . bitcount = 7 , . ascii = 0x2e } , //.
{ . code = 0 b110101111 , . bitcount = 9 , . ascii = 0x2f } , ///
{ . code = 0 b10110111 , . bitcount = 8 , . ascii = 0x30 } , //0
{ . code = 0 b10111101 , . bitcount = 8 , . ascii = 0x31 } , //1
{ . code = 0 b11101101 , . bitcount = 8 , . ascii = 0x32 } , //2
{ . code = 0 b11111111 , . bitcount = 8 , . ascii = 0x33 } , //3
{ . code = 0 b101110111 , . bitcount = 9 , . ascii = 0x34 } , //4
{ . code = 0 b101011011 , . bitcount = 9 , . ascii = 0x35 } , //5
{ . code = 0 b101101011 , . bitcount = 9 , . ascii = 0x36 } , //6
{ . code = 0 b110101101 , . bitcount = 9 , . ascii = 0x37 } , //7
{ . code = 0 b110101011 , . bitcount = 9 , . ascii = 0x38 } , //8
{ . code = 0 b110110111 , . bitcount = 9 , . ascii = 0x39 } , //9
{ . code = 0 b11110101 , . bitcount = 8 , . ascii = 0x3a } , //:
{ . code = 0 b110111101 , . bitcount = 9 , . ascii = 0x3b } , //;
{ . code = 0 b111101101 , . bitcount = 9 , . ascii = 0x3c } , //<
{ . code = 0 b1010101 , . bitcount = 7 , . ascii = 0x3d } , //=
{ . code = 0 b111010111 , . bitcount = 9 , . ascii = 0x3e } , //>
{ . code = 0 b1010101111 , . bitcount = 10 , . ascii = 0x3f } , //?
{ . code = 0 b1010111101 , . bitcount = 10 , . ascii = 0x40 } , //@
{ . code = 0 b1111101 , . bitcount = 7 , . ascii = 0x41 } , //A
{ . code = 0 b11101011 , . bitcount = 8 , . ascii = 0x42 } , //B
{ . code = 0 b10101101 , . bitcount = 8 , . ascii = 0x43 } , //C
{ . code = 0 b10110101 , . bitcount = 8 , . ascii = 0x44 } , //D
{ . code = 0 b1110111 , . bitcount = 7 , . ascii = 0x45 } , //E
{ . code = 0 b11011011 , . bitcount = 8 , . ascii = 0x46 } , //F
{ . code = 0 b11111101 , . bitcount = 8 , . ascii = 0x47 } , //G
{ . code = 0 b101010101 , . bitcount = 9 , . ascii = 0x48 } , //H
{ . code = 0 b1111111 , . bitcount = 7 , . ascii = 0x49 } , //I
{ . code = 0 b111111101 , . bitcount = 9 , . ascii = 0x4a } , //J
{ . code = 0 b101111101 , . bitcount = 9 , . ascii = 0x4b } , //K
{ . code = 0 b11010111 , . bitcount = 8 , . ascii = 0x4c } , //L
{ . code = 0 b10111011 , . bitcount = 8 , . ascii = 0x4d } , //M
{ . code = 0 b11011101 , . bitcount = 8 , . ascii = 0x4e } , //N
{ . code = 0 b10101011 , . bitcount = 8 , . ascii = 0x4f } , //O
{ . code = 0 b11010101 , . bitcount = 8 , . ascii = 0x50 } , //P
{ . code = 0 b111011101 , . bitcount = 9 , . ascii = 0x51 } , //Q
{ . code = 0 b10101111 , . bitcount = 8 , . ascii = 0x52 } , //R
{ . code = 0 b1101111 , . bitcount = 7 , . ascii = 0x53 } , //S
{ . code = 0 b1101101 , . bitcount = 7 , . ascii = 0x54 } , //T
{ . code = 0 b101010111 , . bitcount = 9 , . ascii = 0x55 } , //U
{ . code = 0 b110110101 , . bitcount = 9 , . ascii = 0x56 } , //V
{ . code = 0 b101011101 , . bitcount = 9 , . ascii = 0x57 } , //W
{ . code = 0 b101110101 , . bitcount = 9 , . ascii = 0x58 } , //X
{ . code = 0 b101111011 , . bitcount = 9 , . ascii = 0x59 } , //Y
{ . code = 0 b1010101101 , . bitcount = 10 , . ascii = 0x5a } , //Z
{ . code = 0 b111110111 , . bitcount = 9 , . ascii = 0x5b } , //[
{ . code = 0 b111101111 , . bitcount = 9 , . ascii = 0x5c } , / / \
{ . code = 0 b111111011 , . bitcount = 9 , . ascii = 0x5d } , //]
{ . code = 0 b1010111111 , . bitcount = 10 , . ascii = 0x5e } , //^
{ . code = 0 b101101101 , . bitcount = 9 , . ascii = 0x5f } , //_
{ . code = 0 b1011011111 , . bitcount = 10 , . ascii = 0x60 } , //`
{ . code = 0 b1011 , . bitcount = 4 , . ascii = 0x61 } , //a
{ . code = 0 b1011111 , . bitcount = 7 , . ascii = 0x62 } , //b
{ . code = 0 b101111 , . bitcount = 6 , . ascii = 0x63 } , //c
{ . code = 0 b101101 , . bitcount = 6 , . ascii = 0x64 } , //d
{ . code = 0 b11 , . bitcount = 2 , . ascii = 0x65 } , //e
{ . code = 0 b111101 , . bitcount = 6 , . ascii = 0x66 } , //f
{ . code = 0 b1011011 , . bitcount = 7 , . ascii = 0x67 } , //g
{ . code = 0 b101011 , . bitcount = 6 , . ascii = 0x68 } , //h
{ . code = 0 b1101 , . bitcount = 4 , . ascii = 0x69 } , //i
{ . code = 0 b111101011 , . bitcount = 9 , . ascii = 0x6a } , //j
{ . code = 0 b10111111 , . bitcount = 8 , . ascii = 0x6b } , //k
{ . code = 0 b11011 , . bitcount = 5 , . ascii = 0x6c } , //l
{ . code = 0 b111011 , . bitcount = 6 , . ascii = 0x6d } , //m
{ . code = 0 b1111 , . bitcount = 4 , . ascii = 0x6e } , //n
{ . code = 0 b111 , . bitcount = 3 , . ascii = 0x6f } , //o
{ . code = 0 b111111 , . bitcount = 6 , . ascii = 0x70 } , //p
{ . code = 0 b110111111 , . bitcount = 9 , . ascii = 0x71 } , //q
{ . code = 0 b10101 , . bitcount = 5 , . ascii = 0x72 } , //r
{ . code = 0 b10111 , . bitcount = 5 , . ascii = 0x73 } , //s
{ . code = 0 b101 , . bitcount = 3 , . ascii = 0x74 } , //t
{ . code = 0 b110111 , . bitcount = 6 , . ascii = 0x75 } , //u
{ . code = 0 b1111011 , . bitcount = 7 , . ascii = 0x76 } , //v
{ . code = 0 b1101011 , . bitcount = 7 , . ascii = 0x77 } , //w
{ . code = 0 b11011111 , . bitcount = 8 , . ascii = 0x78 } , //x
{ . code = 0 b1011101 , . bitcount = 7 , . ascii = 0x79 } , //y
{ . code = 0 b111010101 , . bitcount = 9 , . ascii = 0x7a } , //z
{ . code = 0 b1010110111 , . bitcount = 10 , . ascii = 0x7b } , //{
{ . code = 0 b110111011 , . bitcount = 9 , . ascii = 0x7c } , //|
{ . code = 0 b1010110101 , . bitcount = 10 , . ascii = 0x7d } , //}
{ . code = 0 b1011010111 , . bitcount = 10 , . ascii = 0x7e } , //~
{ . code = 0 b1110110101 , . bitcount = 10 , . ascii = 0x7f } , //DEL
} ;
unsigned long long psk31_varicode_masklen_helper [ ] =
{
0 b0000000000000000000000000000000000000000000000000000000000000000 ,
0 b0000000000000000000000000000000000000000000000000000000000000001 ,
0 b0000000000000000000000000000000000000000000000000000000000000011 ,
0 b0000000000000000000000000000000000000000000000000000000000000111 ,
0 b0000000000000000000000000000000000000000000000000000000000001111 ,
0 b0000000000000000000000000000000000000000000000000000000000011111 ,
0 b0000000000000000000000000000000000000000000000000000000000111111 ,
0 b0000000000000000000000000000000000000000000000000000000001111111 ,
0 b0000000000000000000000000000000000000000000000000000000011111111 ,
0 b0000000000000000000000000000000000000000000000000000000111111111 ,
0 b0000000000000000000000000000000000000000000000000000001111111111 ,
0 b0000000000000000000000000000000000000000000000000000011111111111 ,
0 b0000000000000000000000000000000000000000000000000000111111111111 ,
0 b0000000000000000000000000000000000000000000000000001111111111111 ,
0 b0000000000000000000000000000000000000000000000000011111111111111 ,
0 b0000000000000000000000000000000000000000000000000111111111111111 ,
0 b0000000000000000000000000000000000000000000000001111111111111111 ,
0 b0000000000000000000000000000000000000000000000011111111111111111 ,
0 b0000000000000000000000000000000000000000000000111111111111111111 ,
0 b0000000000000000000000000000000000000000000001111111111111111111 ,
0 b0000000000000000000000000000000000000000000011111111111111111111 ,
0 b0000000000000000000000000000000000000000000111111111111111111111 ,
0 b0000000000000000000000000000000000000000001111111111111111111111 ,
0 b0000000000000000000000000000000000000000011111111111111111111111 ,
0 b0000000000000000000000000000000000000000111111111111111111111111 ,
0 b0000000000000000000000000000000000000001111111111111111111111111 ,
0 b0000000000000000000000000000000000000011111111111111111111111111 ,
0 b0000000000000000000000000000000000000111111111111111111111111111 ,
0 b0000000000000000000000000000000000001111111111111111111111111111 ,
0 b0000000000000000000000000000000000011111111111111111111111111111 ,
0 b0000000000000000000000000000000000111111111111111111111111111111 ,
0 b0000000000000000000000000000000001111111111111111111111111111111 ,
0 b0000000000000000000000000000000011111111111111111111111111111111 ,
0 b0000000000000000000000000000000111111111111111111111111111111111 ,
0 b0000000000000000000000000000001111111111111111111111111111111111 ,
0 b0000000000000000000000000000011111111111111111111111111111111111 ,
0 b0000000000000000000000000000111111111111111111111111111111111111 ,
0 b0000000000000000000000000001111111111111111111111111111111111111 ,
0 b0000000000000000000000000011111111111111111111111111111111111111 ,
0 b0000000000000000000000000111111111111111111111111111111111111111 ,
0 b0000000000000000000000001111111111111111111111111111111111111111 ,
0 b0000000000000000000000011111111111111111111111111111111111111111 ,
0 b0000000000000000000000111111111111111111111111111111111111111111 ,
0 b0000000000000000000001111111111111111111111111111111111111111111 ,
0 b0000000000000000000011111111111111111111111111111111111111111111 ,
0 b0000000000000000000111111111111111111111111111111111111111111111 ,
0 b0000000000000000001111111111111111111111111111111111111111111111 ,
0 b0000000000000000011111111111111111111111111111111111111111111111 ,
0 b0000000000000000111111111111111111111111111111111111111111111111 ,
0 b0000000000000001111111111111111111111111111111111111111111111111 ,
0 b0000000000000011111111111111111111111111111111111111111111111111 ,
0 b0000000000000111111111111111111111111111111111111111111111111111 ,
0 b0000000000001111111111111111111111111111111111111111111111111111 ,
0 b0000000000011111111111111111111111111111111111111111111111111111 ,
0 b0000000000111111111111111111111111111111111111111111111111111111 ,
0 b0000000001111111111111111111111111111111111111111111111111111111 ,
0 b0000000011111111111111111111111111111111111111111111111111111111 ,
0 b0000000111111111111111111111111111111111111111111111111111111111 ,
0 b0000001111111111111111111111111111111111111111111111111111111111 ,
0 b0000011111111111111111111111111111111111111111111111111111111111 ,
0 b0000111111111111111111111111111111111111111111111111111111111111 ,
0 b0001111111111111111111111111111111111111111111111111111111111111 ,
0 b0011111111111111111111111111111111111111111111111111111111111111 ,
0 b0111111111111111111111111111111111111111111111111111111111111111
} ;
2016-05-08 15:33:40 +08:00
const int n_psk31_varicode_items = sizeof ( psk31_varicode_items ) / sizeof ( psk31_varicode_item_t ) ;
char psk31_varicode_decoder_push ( unsigned long long * status_shr , unsigned char symbol )
2016-05-05 05:46:23 +08:00
{
* status_shr = ( ( * status_shr ) < < 1 ) | ( ! ! symbol ) ; //shift new bit in shift register
//fprintf(stderr,"*status_shr = %llx\n", *status_shr);
if ( ( * status_shr ) & 0xFFF = = 0 ) return 0 ;
for ( int i = 0 ; i < n_psk31_varicode_items ; i + + )
{
//fprintf(stderr,"| i = %d | %llx ?= %llx | bitsall = %d\n", i, psk31_varicode_items[i].code<<2, (*status_shr)&psk31_varicode_masklen_helper[(psk31_varicode_items[i].bitcount+4)&63], (psk31_varicode_items[i].bitcount+4)&63);
if ( ( psk31_varicode_items [ i ] . code < < 2 ) = = ( ( * status_shr ) & psk31_varicode_masklen_helper [ ( psk31_varicode_items [ i ] . bitcount + 4 ) & 63 ] ) )
{ /*fprintf(stderr,">>>>>>>>> %d %x %c\n", i, psk31_varicode_items[i].ascii, psk31_varicode_items[i].ascii);*/ return psk31_varicode_items [ i ] . ascii ; }
}
return 0 ;
}
2017-03-26 06:47:23 +08:00
void psk31_varicode_encoder_u8_u8 ( unsigned char * input , unsigned char * output , int input_size , int output_max_size , int * input_processed , int * output_size )
{
2017-03-27 19:44:01 +08:00
( * output_size ) = 0 ;
for ( ( * input_processed ) = 0 ; ( * input_processed ) < input_size ; ( * input_processed ) + + )
2017-03-26 06:47:23 +08:00
{
2017-03-27 19:44:01 +08:00
//fprintf(stderr, "ii = %d, input_size = %d, output_max_size = %d\n", *input_processed, input_size, output_max_size);
2017-03-26 06:47:23 +08:00
for ( int ci = 0 ; ci < n_psk31_varicode_items ; ci + + ) //ci: character index
{
2017-03-26 22:55:28 +08:00
psk31_varicode_item_t current_varicode = psk31_varicode_items [ ci ] ;
2017-03-26 06:47:23 +08:00
if ( input [ * input_processed ] = = current_varicode . ascii )
{
2017-03-27 19:44:01 +08:00
//fprintf(stderr, "ci = %d\n", ci);
if ( output_max_size < current_varicode . bitcount + 2 ) return ;
for ( int bi = 0 ; bi < current_varicode . bitcount + 2 ; bi + + ) //bi: bit index
2017-03-26 06:47:23 +08:00
{
2017-03-27 19:44:01 +08:00
//fprintf(stderr, "bi = %d\n", bi);
output [ * output_size ] = ( bi < current_varicode . bitcount ) ? ( psk31_varicode_items [ ci ] . code > > ( current_varicode . bitcount - bi - 1 ) ) & 1 : 0 ;
( * output_size ) + + ;
2017-03-26 06:47:23 +08:00
output_max_size - - ;
}
break ;
}
}
}
}
2016-05-08 15:33:40 +08:00
rtty_baudot_item_t rtty_baudot_items [ ] =
{
{ . code = 0 b00000 , . ascii_letter = 0 , . ascii_figure = 0 } ,
{ . code = 0 b10000 , . ascii_letter = ' E ' , . ascii_figure = ' 3 ' } ,
{ . code = 0 b01000 , . ascii_letter = ' \n ' , . ascii_figure = ' \n ' } ,
{ . code = 0 b11000 , . ascii_letter = ' A ' , . ascii_figure = ' - ' } ,
{ . code = 0 b00100 , . ascii_letter = ' ' , . ascii_figure = ' ' } ,
{ . code = 0 b10100 , . ascii_letter = ' S ' , . ascii_figure = ' \' ' } ,
{ . code = 0 b01100 , . ascii_letter = ' I ' , . ascii_figure = ' 8 ' } ,
{ . code = 0 b11100 , . ascii_letter = ' U ' , . ascii_figure = ' 7 ' } ,
{ . code = 0 b00010 , . ascii_letter = ' \r ' , . ascii_figure = ' \r ' } ,
{ . code = 0 b10010 , . ascii_letter = ' D ' , . ascii_figure = ' # ' } ,
{ . code = 0 b01010 , . ascii_letter = ' R ' , . ascii_figure = ' 4 ' } ,
{ . code = 0 b11010 , . ascii_letter = ' J ' , . ascii_figure = ' \a ' } ,
{ . code = 0 b00110 , . ascii_letter = ' N ' , . ascii_figure = ' , ' } ,
{ . code = 0 b10110 , . ascii_letter = ' F ' , . ascii_figure = ' @ ' } ,
{ . code = 0 b01110 , . ascii_letter = ' C ' , . ascii_figure = ' : ' } ,
{ . code = 0 b11110 , . ascii_letter = ' K ' , . ascii_figure = ' ( ' } ,
{ . code = 0 b00001 , . ascii_letter = ' T ' , . ascii_figure = ' 5 ' } ,
{ . code = 0 b10001 , . ascii_letter = ' Z ' , . ascii_figure = ' + ' } ,
{ . code = 0 b01001 , . ascii_letter = ' L ' , . ascii_figure = ' ) ' } ,
{ . code = 0 b11001 , . ascii_letter = ' W ' , . ascii_figure = ' 2 ' } ,
{ . code = 0 b00101 , . ascii_letter = ' H ' , . ascii_figure = ' $ ' } ,
{ . code = 0 b10101 , . ascii_letter = ' Y ' , . ascii_figure = ' 6 ' } ,
{ . code = 0 b01101 , . ascii_letter = ' P ' , . ascii_figure = ' 0 ' } ,
{ . code = 0 b11101 , . ascii_letter = ' Q ' , . ascii_figure = ' 1 ' } ,
{ . code = 0 b00011 , . ascii_letter = ' O ' , . ascii_figure = ' 9 ' } ,
{ . code = 0 b10011 , . ascii_letter = ' B ' , . ascii_figure = ' ? ' } ,
{ . code = 0 b01011 , . ascii_letter = ' G ' , . ascii_figure = ' * ' } ,
{ . code = 0 b00111 , . ascii_letter = ' M ' , . ascii_figure = ' . ' } ,
{ . code = 0 b10111 , . ascii_letter = ' X ' , . ascii_figure = ' / ' } ,
{ . code = 0 b01111 , . ascii_letter = ' V ' , . ascii_figure = ' = ' }
} ;
2016-05-05 05:46:23 +08:00
2016-05-08 15:33:40 +08:00
const int n_rtty_baudot_items = sizeof ( rtty_baudot_items ) / sizeof ( rtty_baudot_item_t ) ;
2016-05-05 05:46:23 +08:00
2016-05-08 15:33:40 +08:00
char rtty_baudot_decoder_lookup ( unsigned char * fig_mode , unsigned char c )
{
if ( c = = RTTY_FIGURE_MODE_SELECT_CODE ) { * fig_mode = 1 ; return 0 ; }
if ( c = = RTTY_LETTER_MODE_SELECT_CODE ) { * fig_mode = 0 ; return 0 ; }
for ( int i = 0 ; i < n_rtty_baudot_items ; i + + )
if ( rtty_baudot_items [ i ] . code = = c )
return ( * fig_mode ) ? rtty_baudot_items [ i ] . ascii_figure : rtty_baudot_items [ i ] . ascii_letter ;
return 0 ;
}
2016-05-05 05:46:23 +08:00
2016-05-08 15:33:40 +08:00
char rtty_baudot_decoder_push ( rtty_baudot_decoder_t * s , unsigned char symbol )
{
//For RTTY waveforms, check this: http://www.ham.hu/radiosatvitel/szoveg/RTTY/kepek/rtty.gif
//RTTY is much like an UART data transfer with 1 start bit, 5 data bits and 1 stop bit.
//The start pulse and stop pulse are used for synchronization.
symbol = ! ! symbol ; //We want symbol to be 0 or 1.
switch ( s - > state )
{
case RTTY_BAUDOT_WAITING_STOP_PULSE :
if ( symbol = = 1 ) { s - > state = RTTY_BAUDOT_WAITING_START_PULSE ; if ( s - > character_received ) return rtty_baudot_decoder_lookup ( & s - > fig_mode , s - > shr & 31 ) ; }
//If the character data is followed by a stop pulse, then we go on to wait for the next character.
else s - > character_received = 0 ;
//The character should be followed by a stop pulse. If the stop pulse is missing, that is certainly an error.
//In that case, we remove forget the character we just received.
break ;
case RTTY_BAUDOT_WAITING_START_PULSE :
s - > character_received = 0 ;
if ( symbol = = 0 ) { s - > state = RTTY_BAUDOT_RECEIVING_DATA ; s - > shr = s - > bit_cntr = 0 ; }
//Any number of high bits can come after each other, until interrupted with a low bit (start pulse) to indicate
//the beginning of a new character. If we get this start pulse, we go on to wait for the characters. We also
//clear the variables used for counting (bit_cntr) and storing (shr) the data bits.
break ;
case RTTY_BAUDOT_RECEIVING_DATA :
s - > shr = ( s - > shr < < 1 ) | ( ! ! symbol ) ;
//We store 5 bits into our shift register
if ( s - > bit_cntr + + = = 4 ) { s - > state = RTTY_BAUDOT_WAITING_STOP_PULSE ; s - > character_received = 1 ; }
//If this is the 5th bit stored, then we wait for the stop pulse.
break ;
default : break ;
}
return 0 ;
}
2016-05-05 05:46:23 +08:00
2016-05-08 18:38:09 +08:00
# define DEBUG_SERIAL_LINE_DECODER 0
//What has not been checked:
// behaviour on 1.5 stop bits
// check all exit conditions
2016-05-08 15:33:40 +08:00
void serial_line_decoder_f_u8 ( serial_line_t * s , float * input , unsigned char * output , int input_size )
{
2016-05-08 18:38:09 +08:00
static int abs_samples_helper = 0 ;
abs_samples_helper + = s - > input_used ;
int iabs_samples_helper = abs_samples_helper ;
2016-05-08 15:33:40 +08:00
s - > output_size = 0 ;
s - > input_used = 0 ;
short * output_s = ( short * ) output ;
unsigned * output_u = ( unsigned * ) output ;
for ( ; ; )
{
//we find the start bit (first negative edge on the line)
int startbit_start = - 1 ;
int i ;
for ( i = 1 ; i < input_size ; i + + ) if ( input [ i ] < 0 & & input [ i - 1 ] > 0 ) { startbit_start = i ; break ; }
2016-05-08 18:38:09 +08:00
if ( startbit_start = = - 1 ) { s - > input_used + = i ; DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:startbit_not_found (+%d) \n " , s - > input_used ) ; return ; }
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:startbit_found at %d (%d) \n " , startbit_start , iabs_samples_helper + startbit_start ) ;
2016-05-08 15:33:40 +08:00
2016-05-08 18:38:09 +08:00
//If the stop bit would be too far so that we reached the end of the buffer, then we return failed.
//The caller can rearrange the buffer so that the whole character fits into it.
float all_bits = 1 + s - > databits + s - > stopbits ;
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:all_bits = %f \n " , all_bits ) ;
if ( startbit_start + s - > samples_per_bits * all_bits > = input_size ) { s - > input_used + = MAX_M ( 0 , startbit_start - 2 ) ; DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:return_stopbit_too_far (+%d) \n " , s - > input_used ) ; return ; }
2016-05-08 15:33:40 +08:00
2016-05-08 18:38:09 +08:00
//We do the actual sampling.
2016-05-08 15:33:40 +08:00
int di ; //databit counter
unsigned shr = 0 ;
for ( di = 0 ; di < s - > databits ; di + + )
{
2016-05-08 18:38:09 +08:00
int databit_start = startbit_start + ( 1 + di + ( 0.5 * ( 1 - s - > bit_sampling_width_ratio ) ) ) * s - > samples_per_bits ;
int databit_end = startbit_start + ( 1 + di + ( 0.5 * ( 1 + s - > bit_sampling_width_ratio ) ) ) * s - > samples_per_bits ;
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:databit_start = %d (%d) \n " , databit_start , iabs_samples_helper + databit_start ) ;
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:databit_end = %d (%d) \n " , databit_end , iabs_samples_helper + databit_end ) ;
2016-05-08 15:33:40 +08:00
float databit_acc = 0 ;
2016-05-08 18:38:09 +08:00
for ( i = databit_start ; i < databit_end ; i + + ) { databit_acc + = input [ i ] ; /*DEBUG_SERIAL_LINE_DECODER && fprintf(stderr, "%f (%f) ", input[i], databit_acc);*/ }
//DEBUG_SERIAL_LINE_DECODER && fprintf(stderr,"\n");
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:databit_decision = %d \n " , ! ! ( databit_acc > 0 ) ) ;
2016-05-08 15:33:40 +08:00
shr = ( shr < < 1 ) | ! ! ( databit_acc > 0 ) ;
}
2016-05-08 18:38:09 +08:00
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:shr = 0x%x, %d \n " , shr , shr ) ;
//We check if the stopbit is correct.
int stopbit_start = startbit_start + ( 1 + s - > databits ) * s - > samples_per_bits + ( s - > stopbits * 0.5 * ( 1 - s - > bit_sampling_width_ratio ) ) * s - > samples_per_bits ;
int stopbit_end = startbit_start + ( 1 + s - > databits ) * s - > samples_per_bits + ( s - > stopbits * 0.5 * ( 1 + s - > bit_sampling_width_ratio ) ) * s - > samples_per_bits ;
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:stopbit_start = %d (%d) \n " , stopbit_start , iabs_samples_helper + stopbit_start ) ;
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:stopbit_end = %d (%d) \n " , stopbit_end , iabs_samples_helper + stopbit_end ) ;
float stopbit_acc = 0 ;
for ( i = stopbit_start ; i < stopbit_end ; i + + ) { stopbit_acc + = input [ i ] ; DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " %f (%f) " , input [ i ] , stopbit_acc ) ; }
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " \n " ) ;
if ( stopbit_acc < 0 ) { s - > input_used + = MIN_M ( startbit_start + 1 , input_size ) ; DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:return_stopbit_faulty (+%d) \n " , s - > input_used ) ; return ; }
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:stopbit_found \n " ) ;
2016-05-05 05:46:23 +08:00
2016-05-08 15:33:40 +08:00
//we write the output sample
2016-05-08 18:38:09 +08:00
if ( s - > databits < = 8 ) output [ s - > output_size ] = shr ;
else if ( s - > databits < = 16 ) output_s [ s - > output_size ] = shr ;
else output_u [ s - > output_size ] = shr ;
s - > output_size + + ;
2016-05-05 05:46:23 +08:00
2016-05-08 18:38:09 +08:00
int samples_used_up_now = MIN_M ( startbit_start + all_bits * s - > samples_per_bits , input_size ) ;
2016-05-08 15:33:40 +08:00
s - > input_used + = samples_used_up_now ;
input + = samples_used_up_now ;
input_size - = samples_used_up_now ;
2016-05-08 18:38:09 +08:00
iabs_samples_helper + = samples_used_up_now ;
if ( ! input_size ) { DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld:return_no_more_input (+%d) \n " , s - > input_used ) ; return ; }
2016-05-08 15:33:40 +08:00
}
2016-05-08 18:38:09 +08:00
DEBUG_SERIAL_LINE_DECODER & & fprintf ( stderr , " sld: >> output_size = %d (+%d) \n " , s - > output_size , s - > input_used ) ;
2016-05-08 15:33:40 +08:00
}
void binary_slicer_f_u8 ( float * input , unsigned char * output , int input_size )
{
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = input [ i ] > 0 ;
}
2016-05-05 05:46:23 +08:00
2017-03-26 22:55:28 +08:00
void psk_modulator_u8_c ( unsigned char * input , complexf * output , int input_size , int n_psk )
2017-03-26 06:47:23 +08:00
{
//outputs one complex sample per input symbol
2017-03-27 19:44:01 +08:00
float phase_increment = ( 2 * M_PI ) / n_psk ;
2017-03-26 06:47:23 +08:00
for ( int i = 0 ; i < input_size ; i + + )
{
2017-03-27 19:44:01 +08:00
float out_phase = phase_increment * input [ i ] ;
2017-03-26 06:47:23 +08:00
iof ( output , i ) = cos ( out_phase ) ;
qof ( output , i ) = sin ( out_phase ) ;
}
}
void duplicate_samples_ntimes_u8_u8 ( unsigned char * input , unsigned char * output , int input_size_bytes , int sample_size_bytes , int ntimes )
{
int l = 0 ;
for ( int i = 0 ; i < input_size_bytes ; i + = sample_size_bytes )
for ( int k = 0 ; k < ntimes ; k + + )
for ( int j = 0 ; j < sample_size_bytes ; j + + )
output [ l + + ] = input [ i + j ] ;
}
complexf psk31_interpolate_sine_cc ( complexf * input , complexf * output , int input_size , int interpolation , complexf last_input )
{
2017-03-27 19:44:01 +08:00
int oi = 0 ; //output index
2017-03-26 06:47:23 +08:00
for ( int i = 0 ; i < input_size ; i + + )
2017-03-27 19:44:01 +08:00
{
2017-03-26 06:47:23 +08:00
for ( int j = 0 ; j < interpolation ; j + + )
{
float rate = ( 1 + sin ( - ( M_PI / 2 ) + M_PI * ( ( j + 1 ) / ( float ) interpolation ) ) ) / 2 ;
2017-03-27 19:44:01 +08:00
iof ( output , oi ) = iof ( input , i ) * rate + iof ( & last_input , 0 ) * ( 1 - rate ) ;
qof ( output , oi ) = qof ( input , i ) * rate + qof ( & last_input , 0 ) * ( 1 - rate ) ;
oi + + ;
2017-03-26 06:47:23 +08:00
}
2017-03-27 19:44:01 +08:00
last_input = input [ i ] ;
}
2017-03-26 06:47:23 +08:00
return last_input ;
}
void pack_bits_8to1_u8_u8 ( unsigned char * input , unsigned char * output , int input_size )
{ //output size should be input_size × 8
for ( int i = 0 ; i < input_size ; i + + )
for ( int bi = 0 ; bi < 8 ; bi + + ) //bi: bit index
* ( output + + ) = ( input [ i ] > > bi ) & 1 ;
}
2017-03-27 19:44:01 +08:00
unsigned char differential_codec ( unsigned char * input , unsigned char * output , int input_size , int encode , unsigned char state )
{
if ( ! encode )
for ( int i = 0 ; i < input_size ; i + + )
{
output [ i ] = input [ i ] = = state ;
state = input [ i ] ;
}
else
for ( int i = 0 ; i < input_size ; i + + )
{
if ( ! input [ i ] ) state = ! state ;
output [ i ] = state ;
}
2017-04-07 22:05:38 +08:00
return state ;
2017-03-27 19:44:01 +08:00
}
2017-03-26 06:47:23 +08:00
2017-03-02 19:17:31 +08:00
/*
2017-03-03 23:21:45 +08:00
_____ _ _______ _ _ _____
/ ____ | ( _ ) ___ | __ __ ( _ ) ( _ ) | __ \
| | __ _ _ __ _ __ _ ___ _ __ ( _ ) | | _ _ __ ___ _ _ __ __ _ | | __ ) | ___ ___ _____ _____ _ __ _ _
2017-03-02 19:17:31 +08:00
| | / _ ` | ' __ | ' __ | | / _ \ ' __ | / _ \ / \ | | | | ' _ ` _ \ | | ' _ \ / _ ` | | _ // _ \/ __/ _ \ \ / / _ \ '__| | | |
| | ___ | ( _ | | | | | | | __ / | | ( _ > < | | | | | | | | | | | | | ( _ | | | | \ \ __ / ( _ | ( _ ) \ V / __ / | | | _ | |
\ _____ \ __ , _ | _ | | _ | | _ | \ ___ | _ | \ ___ / \ / | _ | | _ | _ | | _ | | _ | _ | _ | | _ | \ __ , | | _ | \ _ \ ___ | \ ___ \ ___ / \ _ / \ ___ | _ | \ __ , |
__ / | __ / |
2017-03-03 23:21:45 +08:00
| ___ / | ___ /
2017-03-02 19:17:31 +08:00
*/
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 )
2016-05-09 18:59:08 +08:00
{
2016-05-23 00:27:20 +08:00
//kd: detector gain
//ko: VCO gain
2016-05-18 20:56:05 +08:00
float bandwidth_omega = 2 * M_PI * bandwidth ;
p - > alpha = ( damping_factor * 2 * bandwidth_omega ) / ( ko * kd ) ;
2016-05-11 14:48:31 +08:00
float sampling_rate = 1 ; //the bandwidth is normalized to the sampling rate
p - > beta = ( bandwidth_omega * bandwidth_omega ) / ( sampling_rate * ko * kd ) ;
2016-05-18 20:56:05 +08:00
p - > iir_temp = p - > dphase = p - > output_phase = 0 ;
2016-05-09 18:59:08 +08:00
}
2016-05-23 00:35:26 +08:00
void pll_cc_init_p_controller ( pll_t * p , float alpha )
2016-05-09 18:59:08 +08:00
{
p - > alpha = alpha ;
p - > dphase = p - > output_phase = 0 ;
}
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 )
2016-05-09 18:59:08 +08:00
{
for ( int i = 0 ; i < input_size ; i + + )
{
p - > output_phase + = p - > dphase ;
while ( p - > output_phase > PI ) p - > output_phase - = 2 * PI ;
while ( p - > output_phase < - PI ) p - > output_phase + = 2 * PI ;
2016-05-11 14:59:54 +08:00
complexf current_nco ;
2016-05-18 20:56:05 +08:00
iof ( & current_nco , 0 ) = sin ( p - > output_phase ) ;
qof ( & current_nco , 0 ) = cos ( p - > output_phase ) ;
2016-05-11 14:59:54 +08:00
if ( output_nco ) output_nco [ i ] = current_nco ; //we don't output anything if it is a NULL pointer
2016-05-11 03:46:33 +08:00
2016-05-11 14:59:54 +08:00
//accurate phase detector: calculating error from phase offset
2016-05-23 00:27:20 +08:00
float input_phase = atan2 ( iof ( input , i ) , qof ( input , i ) ) ;
float new_dphase = input_phase - p - > output_phase ;
while ( new_dphase > PI ) new_dphase - = 2 * PI ;
while ( new_dphase < - PI ) new_dphase + = 2 * PI ;
//modeling analog phase detector, which would be abs(input[i] * current_nco) if we had a real output signal, but what if we have complex signals?
//qof(¤t_nco,0)=-qof(¤t_nco,0); //calculate conjugate
//complexf multiply_result;
//cmult(&multiply_result, &input[i], ¤t_nco);
//output_nco[i] = multiply_result;
//float new_dphase = absof(&multiply_result,0);
2016-05-11 14:48:31 +08:00
2016-05-23 00:35:26 +08:00
if ( p - > pll_type = = PLL_PI_CONTROLLER )
2016-05-09 18:59:08 +08:00
{
2016-05-18 20:56:05 +08:00
p - > dphase = new_dphase * p - > alpha + p - > iir_temp ;
p - > iir_temp + = new_dphase * p - > beta ;
2016-05-11 04:59:14 +08:00
2017-03-02 19:17:31 +08:00
while ( p - > dphase > PI ) p - > dphase - = 2 * PI ; //won't need this one
2016-05-11 03:46:33 +08:00
while ( p - > dphase < - PI ) p - > dphase + = 2 * PI ;
2016-05-09 18:59:08 +08:00
}
2016-05-23 00:35:26 +08:00
else if ( p - > pll_type = = PLL_P_CONTROLLER )
2016-05-09 18:59:08 +08:00
{
2016-05-11 04:59:14 +08:00
p - > dphase = new_dphase * p - > alpha ;
2016-05-09 18:59:08 +08:00
}
else return ;
2016-05-11 04:59:14 +08:00
if ( output_dphase ) output_dphase [ i ] = - p - > dphase ;
2016-05-23 00:27:20 +08:00
//if(output_dphase) output_dphase[i] = new_dphase/10;
2016-05-09 18:59:08 +08:00
}
}
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 , . . . )
2017-03-03 23:21:45 +08:00
{
2017-03-06 00:21:38 +08:00
static int figure_output_counter = 0 ;
2017-03-05 02:50:11 +08:00
int * points_z = ( int * ) malloc ( sizeof ( int ) * points_size ) ;
int * points_color = ( int * ) malloc ( sizeof ( int ) * points_size ) ;
2017-03-03 23:21:45 +08:00
va_list vl ;
va_start ( vl , points_size ) ;
for ( int i = 0 ; i < points_size ; i + + )
{
2017-03-05 02:50:11 +08:00
points_z [ i ] = va_arg ( vl , int ) ;
points_color [ i ] = va_arg ( vl , int ) ;
2017-03-03 23:21:45 +08:00
}
2017-03-06 00:21:38 +08:00
if ( writefiles & & ! figure_output_counter ) fprintf ( stderr , " cf=figure(); \n " ) ;
2017-03-05 02:50:11 +08:00
fprintf ( stderr , " N = %d; \n isig = [ " , signal_size ) ;
for ( int i = 0 ; i < signal_size ; i + + ) fprintf ( stderr , " %f " , iof ( signal , i ) ) ;
fprintf ( stderr , " ]; \n qsig = [ " ) ;
for ( int i = 0 ; i < signal_size ; i + + ) fprintf ( stderr , " %f " , qof ( signal , i ) ) ;
fprintf ( stderr , " ]; \n zsig = [0:N-1]; \n subplot(2,2,[2 4]); \n plot3(isig,zsig,qsig, \" b- \" , " ) ;
for ( int i = 0 ; i < points_size ; i + + )
fprintf ( stderr , " [%f],[%d],[%f], \" %c. \" %c " ,
iof ( signal , points_z [ i ] ) , points_z [ i ] , qof ( signal , points_z [ i ] ) ,
( char ) points_color [ i ] & 0xff , ( i < points_size - 1 ) ? ' , ' : ' '
) ;
2017-03-03 23:21:45 +08:00
va_end ( vl ) ;
2017-03-06 02:56:15 +08:00
fprintf ( stderr , " ); \n title( \" index = %d, error = %f, cxoffs = %d \" ); \n subplot(2,2,1); \n plot(zsig, isig, \" b- \" , " , index , error , correction_offset ) ;
2017-03-05 02:50:11 +08:00
for ( int i = 0 ; i < points_size ; i + + )
fprintf ( stderr , " [%d],[%f], \" %c. \" %c " ,
points_z [ i ] , iof ( signal , points_z [ i ] ) ,
( char ) points_color [ i ] & 0xff , ( i < points_size - 1 ) ? ' , ' : ' '
) ;
fprintf ( stderr , " ); \n subplot(2,2,3); \n plot(zsig, qsig, \" b- \" , " ) ;
for ( int i = 0 ; i < points_size ; i + + )
fprintf ( stderr , " [%d],[%f], \" %c. \" %c " ,
points_z [ i ] , qof ( signal , points_z [ i ] ) ,
( char ) points_color [ i ] & 0xff , ( i < points_size - 1 ) ? ' , ' : ' '
) ;
2017-03-05 02:17:11 +08:00
fprintf ( stderr , " ); \n " ) ;
2017-03-06 00:21:38 +08:00
if ( writefiles ) fprintf ( stderr , " print(cf, \" figs/%05d.png \" , \" -S1024,1024 \" ); \n " , figure_output_counter + + ) ;
2017-03-04 01:45:03 +08:00
fflush ( stderr ) ;
2017-03-05 23:30:12 +08:00
free ( points_z ) ;
free ( points_color ) ;
2017-03-03 23:21:45 +08:00
}
2017-03-02 19:17:31 +08:00
timing_recovery_state_t timing_recovery_init ( timing_recovery_algorithm_t algorithm , int decimation_rate , int use_q )
{
timing_recovery_state_t to_return ;
to_return . algorithm = algorithm ;
to_return . decimation_rate = decimation_rate ;
to_return . use_q = use_q ;
2017-03-03 23:21:45 +08:00
to_return . debug_phase = - 1 ;
2017-03-04 01:23:11 +08:00
to_return . debug_count = 3 ;
2017-03-05 02:50:11 +08:00
to_return . debug_force = 0 ;
2017-03-06 00:21:38 +08:00
to_return . debug_writefiles = 0 ;
2017-03-06 02:56:15 +08:00
to_return . last_correction_offset = 0 ;
2017-03-07 06:00:34 +08:00
to_return . earlylate_ratio = 0.25 ; //0..0.5
2017-03-02 19:17:31 +08:00
return to_return ;
}
2017-03-03 23:21:45 +08:00
void timing_recovery_trigger_debug ( timing_recovery_state_t * state , int debug_phase )
{
state - > debug_phase = debug_phase ;
}
2017-03-06 02:16:34 +08:00
# define MTIMINGR_HDEBUG 0
2017-03-03 23:21:45 +08:00
2017-04-28 20:20:14 +08:00
void timing_recovery_cc ( complexf * input , complexf * output , int input_size , float * timing_error , timing_recovery_state_t * state )
2017-03-02 19:17:31 +08:00
{
2017-03-03 23:21:45 +08:00
//We always assume that the input starts at center of the first symbol cross before the first symbol.
2017-03-02 19:17:31 +08:00
//Last time we consumed that much from the input samples that it is there.
2017-03-06 02:56:15 +08:00
int correction_offset = state - > last_correction_offset ;
2017-03-02 19:17:31 +08:00
int current_bitstart_index = 0 ;
int num_samples_bit = state - > decimation_rate ;
int num_samples_halfbit = state - > decimation_rate / 2 ;
int num_samples_quarterbit = state - > decimation_rate / 4 ;
2017-03-07 06:00:34 +08:00
int num_samples_earlylate_wing = num_samples_bit * state - > earlylate_ratio ;
2017-03-04 01:23:11 +08:00
int debug_i = state - > debug_count ;
2017-03-02 19:17:31 +08:00
float error ;
2017-03-07 06:00:34 +08:00
int el_point_left_index , el_point_right_index , el_point_mid_index ;
2017-04-01 18:54:21 +08:00
int si = 0 ;
2017-03-05 23:30:12 +08:00
if ( state - > debug_force ) fprintf ( stderr , " disp( \" begin timing_recovery_cc \" ); \n " ) ;
2017-03-06 02:16:34 +08:00
if ( MTIMINGR_HDEBUG ) fprintf ( stderr , " timing_recovery_cc started, nsb = %d, nshb = %d, nsqb = %d \n " , num_samples_bit , num_samples_halfbit , num_samples_quarterbit ) ;
2017-03-02 19:17:31 +08:00
{
2017-04-01 18:54:21 +08:00
for ( ; ; )
2017-03-02 19:17:31 +08:00
{
2017-03-06 02:56:15 +08:00
//the MathWorks style algorithm has correction_offset.
//correction_offset = 0;
2017-03-06 02:16:34 +08:00
if ( current_bitstart_index + num_samples_halfbit * 3 > = input_size ) break ;
2017-03-06 02:56:15 +08:00
if ( MTIMINGR_HDEBUG ) fprintf ( stderr , " current_bitstart_index = %d, input_size = %d, correction_offset(prev) = %d \n " ,
current_bitstart_index , input_size , correction_offset ) ;
if ( correction_offset < = - num_samples_quarterbit * 0.9 | | correction_offset > = 0.9 * num_samples_quarterbit )
{
if ( MTIMINGR_HDEBUG ) fprintf ( stderr , " correction_offset = %d, reset to 0! \n " , correction_offset ) ;
correction_offset = 0 ;
}
2017-03-07 06:00:34 +08:00
//should check if the sign of the correction_offset (or disabling it) has an effect on the EVM.
//it is also a possibility to disable multiplying with the magnitude
if ( state - > algorithm = = TIMING_RECOVERY_ALGORITHM_EARLYLATE )
{
//bitstart index should be at symbol edge, maximum effect point is at current_bitstart_index + num_samples_halfbit
el_point_right_index = current_bitstart_index + num_samples_earlylate_wing * 3 ;
el_point_left_index = current_bitstart_index + num_samples_earlylate_wing * 1 - correction_offset ;
el_point_mid_index = current_bitstart_index + num_samples_halfbit ;
2017-04-01 18:54:21 +08:00
output [ si + + ] = input [ el_point_mid_index ] ;
2017-03-07 06:00:34 +08:00
}
else if ( state - > algorithm = = TIMING_RECOVERY_ALGORITHM_GARDNER )
{
//maximum effect point is at current_bitstart_index
el_point_right_index = current_bitstart_index + num_samples_halfbit * 3 ;
el_point_left_index = current_bitstart_index + num_samples_halfbit * 1 ;
el_point_mid_index = current_bitstart_index + num_samples_halfbit * 2 ;
2017-04-01 18:54:21 +08:00
output [ si + + ] = input [ el_point_left_index ] ;
2017-03-07 06:00:34 +08:00
}
else break ;
2017-04-01 22:08:49 +08:00
error = ( iof ( input , el_point_right_index ) - iof ( input , el_point_left_index ) ) * iof ( input , el_point_mid_index ) ;
2017-03-03 23:21:45 +08:00
if ( state - > use_q )
2017-03-02 19:17:31 +08:00
{
2017-03-07 06:00:34 +08:00
error + = ( qof ( input , el_point_right_index ) - qof ( input , el_point_left_index ) ) * qof ( input , el_point_mid_index ) ;
2017-03-02 19:17:31 +08:00
error / = 2 ;
}
2017-03-07 06:00:34 +08:00
//Original correction method: this version can only move a single sample in any direction
2017-03-06 02:56:15 +08:00
//current_bitstart_index += num_samples_halfbit * 2 + (error)?((error<0)?1:-1):0;
2017-04-28 20:20:14 +08:00
if ( timing_error ) timing_error [ si - 1 ] = error ; //it is not written if NULL
2017-03-06 02:56:15 +08:00
if ( error > 2 ) error = 2 ;
if ( error < - 2 ) error = - 2 ;
2017-03-05 23:30:12 +08:00
if ( state - > debug_force | | ( state - > debug_phase > = si & & debug_i ) )
2017-03-03 23:21:45 +08:00
{
2017-03-05 23:30:12 +08:00
debug_i - - ;
if ( ! debug_i ) state - > debug_phase = - 1 ;
octave_plot_point_on_cplxsig ( input + current_bitstart_index , state - > decimation_rate * 2 ,
error ,
current_bitstart_index ,
2017-03-06 02:56:15 +08:00
correction_offset ,
2017-03-06 00:21:38 +08:00
state - > debug_writefiles ,
2017-03-07 05:31:32 +08:00
3 ,
2017-03-07 06:02:06 +08:00
el_point_left_index - current_bitstart_index , ' r ' ,
el_point_right_index - current_bitstart_index , ' r ' ,
el_point_mid_index - current_bitstart_index , ' r ' ,
2017-03-03 23:21:45 +08:00
0 ) ;
}
2017-03-07 06:00:34 +08:00
int error_sign = ( state - > algorithm = = TIMING_RECOVERY_ALGORITHM_GARDNER ) ? - 1 : 1 ;
correction_offset = num_samples_halfbit * error_sign * ( error / 2 ) ;
2017-03-06 02:56:15 +08:00
current_bitstart_index + = num_samples_bit + correction_offset ;
2017-03-05 23:30:12 +08:00
if ( si > = input_size )
{
if ( MTIMINGR_HDEBUG ) fprintf ( stderr , " oops_out_of_si! \n " ) ;
break ;
}
2017-03-02 19:17:31 +08:00
}
}
2017-03-06 02:56:15 +08:00
state - > input_processed = current_bitstart_index ;
state - > output_size = si ;
state - > last_correction_offset = correction_offset ;
2017-03-02 19:17:31 +08:00
}
# define MTIMINGR_GAS(NAME) \
if ( ! strcmp ( # NAME , input ) ) return TIMING_RECOVERY_ALGORITHM_ # # NAME ;
timing_recovery_algorithm_t timing_recovery_get_algorithm_from_string ( char * input )
{
MTIMINGR_GAS ( GARDNER ) ;
MTIMINGR_GAS ( EARLYLATE ) ;
return TIMING_RECOVERY_ALGORITHM_DEFAULT ;
}
# define MTIMINGR_GSA(NAME) \
if ( algorithm = = TIMING_RECOVERY_ALGORITHM_ # # NAME ) return # NAME ;
char * timing_recovery_get_string_from_algorithm ( timing_recovery_algorithm_t algorithm )
{
MTIMINGR_GSA ( GARDNER ) ;
MTIMINGR_GSA ( EARLYLATE ) ;
return " INVALID " ;
}
2017-04-01 03:23:07 +08:00
bpsk_costas_loop_state_t init_bpsk_costas_loop_cc ( float samples_per_bits )
2017-03-14 16:12:09 +08:00
{
2017-04-01 03:23:07 +08:00
bpsk_costas_loop_state_t state ;
state . vco_phase = 0 ;
2017-04-02 18:51:53 +08:00
state . last_vco_phase_addition = 0 ;
2017-03-29 19:09:19 +08:00
float virtual_sampling_rate = 10000 ;
float virtual_data_rate = virtual_sampling_rate / samples_per_bits ;
2017-04-01 03:23:07 +08:00
fprintf ( stderr , " virtual_sampling_rate = %g, virtual_data_rate = %g \n " , virtual_sampling_rate , virtual_data_rate ) ;
2017-04-02 18:51:53 +08:00
float rc_filter_cutoff = virtual_data_rate * 2 ; //this is so far the best
2017-03-29 19:09:19 +08:00
float rc_filter_rc = 1 / ( 2 * M_PI * rc_filter_cutoff ) ; //as of Equation 24 in Feigin
float virtual_sampling_dt = 1.0 / virtual_sampling_rate ;
2017-04-01 03:23:07 +08:00
fprintf ( stderr , " rc_filter_cutoff = %g, rc_filter_rc = %g, virtual_sampling_dt = %g \n " ,
rc_filter_cutoff , rc_filter_rc , virtual_sampling_dt ) ;
state . rc_filter_alpha = virtual_sampling_dt / ( rc_filter_rc + virtual_sampling_dt ) ; //https://en.wikipedia.org/wiki/Low-pass_filter
2017-03-29 19:09:19 +08:00
float rc_filter_omega_cutoff = 2 * M_PI * rc_filter_cutoff ;
2017-04-01 22:08:49 +08:00
state . vco_phase_addition_multiplier = 8 * rc_filter_omega_cutoff / ( virtual_sampling_rate ) ; //as of Equation 25 in Feigin, assuming input signal amplitude of 1 (to 1V) and (state.vco_phase_addition_multiplier*<vco_input>), a value in radians, will be added to the vco_phase directly.
2017-04-01 03:23:07 +08:00
fprintf ( stderr , " rc_filter_alpha = %g, rc_filter_omega_cutoff = %g, vco_phase_addition_multiplier = %g \n " ,
state . rc_filter_alpha , rc_filter_omega_cutoff , state . vco_phase_addition_multiplier ) ;
return state ;
2017-03-14 16:12:09 +08:00
}
void bpsk_costas_loop_cc ( complexf * input , complexf * output , int input_size , bpsk_costas_loop_state_t * state )
{
2017-04-01 03:23:07 +08:00
int debug = 0 ;
if ( debug ) fprintf ( stderr , " costas: \n " ) ;
2017-03-14 16:12:09 +08:00
for ( int i = 0 ; i < input_size ; i + + )
{
2017-03-29 19:09:19 +08:00
float input_phase = atan2 ( input [ i ] . q , input [ i ] . i ) ;
float input_and_vco_mixed_phase = input_phase - state - > vco_phase ;
2017-04-01 03:23:07 +08:00
if ( debug ) fprintf ( stderr , " %g | %g \n " , input_and_vco_mixed_phase , input_phase ) , debug - - ;
2017-03-29 19:09:19 +08:00
complexf input_and_vco_mixed_sample ;
e_powj ( & input_and_vco_mixed_sample , input_and_vco_mixed_phase ) ;
2017-04-02 03:30:58 +08:00
complexf vco_sample ;
e_powj ( & vco_sample , - state - > vco_phase ) ;
//cmult(&input_and_vco_mixed_sample, &input[i], &vco_sample);//if this is enabled, the real input sample is used, not the amplitude normalized
2017-03-29 19:09:19 +08:00
float loop_output_i =
input_and_vco_mixed_sample . i * state - > rc_filter_alpha + state - > last_lpfi_output * ( 1 - state - > rc_filter_alpha ) ;
float loop_output_q =
input_and_vco_mixed_sample . q * state - > rc_filter_alpha + state - > last_lpfq_output * ( 1 - state - > rc_filter_alpha ) ;
2017-04-02 03:30:58 +08:00
//loop_output_i = input_and_vco_mixed_sample.i;
//loop_output_q = input_and_vco_mixed_sample.q;
2017-03-29 19:09:19 +08:00
state - > last_lpfi_output = loop_output_i ;
state - > last_lpfq_output = loop_output_q ;
float vco_phase_addition = loop_output_i * loop_output_q * state - > vco_phase_addition_multiplier ;
2017-04-02 18:51:53 +08:00
//vco_phase_addition = vco_phase_addition * state->rc_filter_alpha + state->last_vco_phase_addition * (1-state->rc_filter_alpha);
//state->last_vco_phase_addition = vco_phase_addition;
2017-03-29 19:09:19 +08:00
state - > vco_phase + = vco_phase_addition ;
while ( state - > vco_phase > PI ) state - > vco_phase - = 2 * PI ;
while ( state - > vco_phase < - PI ) state - > vco_phase + = 2 * PI ;
2017-04-02 03:30:58 +08:00
cmult ( & output [ i ] , & input [ i ] , & vco_sample ) ;
2017-03-14 16:12:09 +08:00
}
}
2017-04-07 02:38:44 +08:00
void simple_agc_cc ( complexf * input , complexf * output , int input_size , float rate , float reference , float max_gain , float * current_gain )
{
float rate_1minus = 1 - rate ;
int debugn = 0 ;
for ( int i = 0 ; i < input_size ; i + + )
{
float amplitude = sqrt ( input [ i ] . i * input [ i ] . i + input [ i ] . q * input [ i ] . q ) ;
float ideal_gain = ( reference / amplitude ) ;
if ( ideal_gain > max_gain ) ideal_gain = max_gain ;
if ( ideal_gain < = 0 ) ideal_gain = 0 ;
//*current_gain += (ideal_gain-(*current_gain))*rate;
2017-04-07 04:03:09 +08:00
* current_gain = ( ideal_gain - ( * current_gain ) ) * rate + ( * current_gain ) ; //*rate_1minus;
//if(debugn<100) fprintf(stderr, "cgain: %g\n", *current_gain), debugn++;
2017-04-07 02:38:44 +08:00
output [ i ] . i = ( * current_gain ) * input [ i ] . i ;
output [ i ] . q = ( * current_gain ) * input [ i ] . q ;
}
}
2017-04-10 01:00:07 +08:00
void firdes_add_resonator_c ( complexf * output , int length , float rate , window_t window , int add , int normalize )
2017-04-08 18:39:52 +08:00
{
2017-04-10 01:00:07 +08:00
//add=0: malloc output previously
//add=1: calloc output previously
2017-04-08 22:36:10 +08:00
complexf * taps = ( complexf * ) malloc ( sizeof ( complexf ) * length ) ;
2017-04-08 18:39:52 +08:00
int middle = length / 2 ;
2017-04-09 20:53:22 +08:00
float phase = 0 , phase_addition = - rate * M_PI * 2 ;
2017-04-08 18:39:52 +08:00
float ( * window_function ) ( float ) = firdes_get_window_kernel ( window ) ;
2017-04-09 20:53:22 +08:00
for ( int i = 0 ; i < length ; i + + ) //@@firdes_add_resonator_c: calculate taps
2017-04-08 18:39:52 +08:00
{
2017-04-08 22:36:10 +08:00
e_powj ( & taps [ i ] , phase ) ;
2017-04-08 18:39:52 +08:00
float window_multiplier = window_function ( fabs ( ( float ) ( middle - i ) / middle ) ) ;
2017-04-08 22:36:10 +08:00
taps [ i ] . i * = window_multiplier ;
taps [ i ] . q * = window_multiplier ;
2017-04-08 18:39:52 +08:00
phase + = phase_addition ;
while ( phase > 2 * M_PI ) phase - = 2 * M_PI ;
2017-04-09 20:53:22 +08:00
while ( phase < 0 ) phase + = 2 * M_PI ;
2017-04-08 18:39:52 +08:00
}
//Normalize filter kernel
2017-04-10 01:00:07 +08:00
if ( add )
for ( int i = 0 ; i < length ; i + + )
{
output [ i ] . i + = taps [ i ] . i ;
output [ i ] . q + = taps [ i ] . q ;
}
else for ( int i = 0 ; i < length ; i + + ) output [ i ] = taps [ i ] ;
if ( normalize )
2017-04-08 22:36:10 +08:00
{
2017-04-10 01:00:07 +08:00
float sum = 0 ;
for ( int i = 0 ; i < length ; i + + ) //@firdes_add_resonator_c: normalize pass 1
{
sum + = sqrt ( output [ i ] . i * output [ i ] . i + output [ i ] . q * output [ i ] . q ) ;
}
for ( int i = 0 ; i < length ; i + + ) //@firdes_add_resonator_c: normalize pass 2
{
output [ i ] . i / = sum ;
output [ i ] . q / = sum ;
}
2017-04-08 18:39:52 +08:00
}
}
2017-04-09 20:53:22 +08:00
int apply_fir_cc ( complexf * input , complexf * output , int input_size , complexf * taps , int taps_length )
{
int i ;
for ( i = 0 ; i < input_size - taps_length + 1 ; i + + )
{
csetnull ( & output [ i ] ) ;
for ( int ti = 0 ; ti < taps_length ; ti + + )
{
cmultadd ( & output [ i ] , & input [ i + ti ] , & taps [ ti ] ) ;
}
}
return i ;
}
2017-04-07 02:38:44 +08:00
2017-03-02 19:17:31 +08:00
2014-11-28 23:44:41 +08:00
/*
2016-03-20 23:41:37 +08:00
_____ _ _
| __ \ | | ( _ )
| | | | __ _ | | _ __ _ ___ ___ _ ____ _____ _ __ ___ _ ___ _ __
| | | | / _ ` | __ / _ ` | / __ / _ \ | ' _ \ \ / / _ \ ' __ / __ | | / _ \ | ' _ \
2014-11-28 23:44:41 +08:00
| | __ | | ( _ | | | | ( _ | | | ( _ | ( _ ) | | | \ V / __ / | \ __ \ | ( _ ) | | | |
| _____ / \ __ , _ | \ __ \ __ , _ | \ ___ \ ___ / | _ | | _ | \ _ / \ ___ | _ | | ___ / _ | \ ___ / | _ | | _ |
2016-03-20 23:41:37 +08:00
*/
2014-11-28 23:44:41 +08:00
void convert_u8_f ( unsigned char * input , float * output , int input_size )
{
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = ( ( float ) input [ i ] ) / ( UCHAR_MAX / 2.0 ) - 1.0 ; //@convert_u8_f
}
2015-11-23 03:53:10 +08:00
void convert_s8_f ( signed char * input , float * output , int input_size )
{
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = ( ( float ) input [ i ] ) / SCHAR_MAX ; //@convert_s8_f
}
2016-02-14 18:19:36 +08:00
void convert_s16_f ( short * input , float * output , int input_size )
2014-11-28 23:44:41 +08:00
{
2016-02-14 18:19:36 +08:00
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = ( float ) input [ i ] / SHRT_MAX ; //@convert_s16_f
2014-11-28 23:44:41 +08:00
}
void convert_f_u8 ( float * input , unsigned char * output , int input_size )
{
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = input [ i ] * UCHAR_MAX * 0.5 + 128 ; //@convert_f_u8
2016-03-20 23:41:37 +08:00
//128 above is the correct value to add. In any other case a DC component
2014-11-28 23:44:41 +08:00
//of at least -60 dB is shown on the FFT plot after convert_f_u8 -> convert_u8_f
}
2015-11-23 03:53:10 +08:00
void convert_f_s8 ( float * input , signed char * output , int input_size )
{
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = input [ i ] * SCHAR_MAX ; //@convert_f_s8
}
2016-02-14 18:19:36 +08:00
void convert_f_s16 ( float * input , short * output , int input_size )
2014-11-28 23:44:41 +08:00
{
2016-03-20 23:41:37 +08:00
/*for(int i=0;i<input_size;i++)
2014-11-28 23:44:41 +08:00
{
if ( input [ i ] > 1.0 ) input [ i ] = 1.0 ;
if ( input [ i ] < - 1.0 ) input [ i ] = - 1.0 ;
} */
2016-02-14 18:19:36 +08:00
for ( int i = 0 ; i < input_size ; i + + ) output [ i ] = input [ i ] * SHRT_MAX ; //@convert_f_s16
2014-11-28 23:44:41 +08:00
}
2016-02-14 18:19:36 +08:00
void convert_i16_f ( short * input , float * output , int input_size ) { convert_s16_f ( input , output , input_size ) ; }
void convert_f_i16 ( float * input , short * output , int input_size ) { convert_f_s16 ( input , output , input_size ) ; }
2016-06-21 06:14:03 +08:00
void convert_f_s24 ( float * input , unsigned char * output , int input_size , int bigendian )
{
int k = 0 ;
if ( bigendian ) for ( int i = 0 ; i < input_size ; i + + )
{
2016-06-21 06:23:43 +08:00
int temp = input [ i ] * ( INT_MAX > > 8 ) ;
2016-06-21 06:14:03 +08:00
unsigned char * ptemp = ( unsigned char * ) & temp ;
2016-06-21 06:23:43 +08:00
output [ k + + ] = * ptemp ;
2016-06-23 16:50:08 +08:00
output [ k + + ] = * ( ptemp + 1 ) ;
output [ k + + ] = * ( ptemp + 2 ) ;
2016-06-21 06:14:03 +08:00
}
else for ( int i = 0 ; i < input_size ; i + + )
{
2016-06-21 06:23:43 +08:00
int temp = input [ i ] * ( INT_MAX > > 8 ) ;
2016-06-21 06:14:03 +08:00
unsigned char * ptemp = ( unsigned char * ) & temp ;
2016-06-21 06:23:43 +08:00
output [ k + + ] = * ( ptemp + 2 ) ;
2016-06-23 16:50:08 +08:00
output [ k + + ] = * ( ptemp + 1 ) ;
output [ k + + ] = * ptemp ;
2016-06-21 06:14:03 +08:00
}
}
void convert_s24_f ( unsigned char * input , float * output , int input_size , int bigendian )
{
int k = 0 ;
if ( bigendian ) for ( int i = 0 ; i < input_size * 3 ; i + = 3 )
{
2016-06-23 16:50:08 +08:00
int temp = ( input [ i + 2 ] < < 24 ) | ( input [ i + 1 ] < < 16 ) | ( input [ i ] < < 8 ) ;
output [ k + + ] = temp / ( float ) ( INT_MAX - 256 ) ;
2016-06-21 06:14:03 +08:00
}
else for ( int i = 0 ; i < input_size * 3 ; i + = 3 )
{
2016-06-23 16:50:08 +08:00
int temp = ( input [ i + 2 ] < < 8 ) | ( input [ i + 1 ] < < 16 ) | ( input [ i ] < < 24 ) ;
output [ k + + ] = temp / ( float ) ( INT_MAX - 256 ) ;
2016-06-21 06:14:03 +08:00
}
2014-11-28 23:44:41 +08:00
}
2017-04-28 21:34:14 +08:00
FILE * init_get_awgn_samples_f ( )
{
return fopen ( " /dev/urandom " , " r " ) ;
}
void get_awgn_samples_f ( float * output , int output_size , FILE * status )
{
int * pioutput = ( int * ) output ;
fread ( ( unsigned char * ) output , sizeof ( float ) , output_size , status ) ;
for ( int i = 0 ; i < output_size ; i + + )
output [ i ] = ( ( float ) pioutput [ i ] ) / INT_MAX ;
}
int deinit_get_agwn_samples_f ( FILE * status )
{
return fclose ( status ) ;
}
2014-11-28 23:44:41 +08:00
int trivial_vectorize ( )
{
//this function is trivial to vectorize and should pass on both NEON and SSE
int a [ 1024 ] , b [ 1024 ] , c [ 1024 ] ;
for ( int i = 0 ; i < 1024 ; i + + ) //@trivial_vectorize: should pass :-)
{
c [ i ] = a [ i ] * b [ i ] ;
}
return c [ 0 ] ;
}