Added timing_recovery_cc
This commit is contained in:
parent
e524dc2af1
commit
676e9388fc
3 changed files with 176 additions and 1 deletions
32
csdr.c
32
csdr.c
|
@ -2349,6 +2349,38 @@ int main(int argc, char *argv[])
|
||||||
//fprintf(stderr, "| o");
|
//fprintf(stderr, "| o");
|
||||||
TRY_YIELD;
|
TRY_YIELD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!strcmp(argv[1],"timing_recovery_cc")) //<algorithm> <decimation> [--add_q]
|
||||||
|
{
|
||||||
|
if(argc<=2) return badsyntax("need required parameter (algorithm)");
|
||||||
|
timing_recovery_algorithm_t algorithm = timing_recovery_get_algorithm_from_string(argv[2]);
|
||||||
|
/* if(algorithm == TIMING_RECOVERY_ALGORITHM_DEFAULT) */
|
||||||
|
fprintf(stderr,"timing_recovery_cc: algorithm = %s\n",timing_recovery_get_string_from_algorithm(algorithm));
|
||||||
|
if(argc<=3) return badsyntax("need required parameter (decimation factor)");
|
||||||
|
int decimation;
|
||||||
|
sscanf(argv[3],"%d",&decimation);
|
||||||
|
if(decimation<=4 || decimation&3) badsyntax("decimation factor should be a positive integer divisible by 4");
|
||||||
|
|
||||||
|
int add_q = (argc>=5 && !strcmp(argv[4], "--add_q"));
|
||||||
|
|
||||||
|
if(!initialize_buffers()) return -2;
|
||||||
|
sendbufsize(the_bufsize/decimation);
|
||||||
|
|
||||||
|
timing_recovery_state_t state = timing_recovery_init(algorithm, decimation, add_q);
|
||||||
|
|
||||||
|
FREAD_C;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
FEOF_CHECK;
|
||||||
|
timing_recovery((complexf*)input_buffer, (complexf*)output_buffer, the_bufsize, &state);
|
||||||
|
//fprintf(stderr, "os %d\n",state->output_size);
|
||||||
|
fwrite(output_buffer, sizeof(complexf), state->output_size, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
TRY_YIELD;
|
||||||
|
memmove((complexf*)input_buffer,((complexf*)input_buffer)+state->input_processed,(the_bufsize-state->input_processed)*sizeof(complexf)); //memmove lets the source and destination overlap
|
||||||
|
fread(((complexf*)input_buffer)+(the_bufsize-state->input_processed), sizeof(complexf), state->input_processed, stdin);
|
||||||
|
//fprintf(stderr,"iskip=%d state->output_size=%d start=%x target=%x skipcount=%x \n",state->input_processed,state->output_size,input_buffer, ((complexf*)input_buffer)+(BIG_BUFSIZE-state->input_processed),(BIG_BUFSIZE-state->input_processed));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!strcmp(argv[1],"none"))
|
if(!strcmp(argv[1],"none"))
|
||||||
|
|
123
libcsdr.c
123
libcsdr.c
|
@ -1572,6 +1572,17 @@ 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;
|
for(int i=0;i<input_size;i++) output[i] = input[i] > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
_____ _ _______ _ _ _____
|
||||||
|
/ ____| (_) ___ |__ __(_) (_) | __ \
|
||||||
|
| | __ _ _ __ _ __ _ ___ _ __ ( _ ) | | _ _ __ ___ _ _ __ __ _ | |__) |___ ___ _____ _____ _ __ _ _
|
||||||
|
| | / _` | '__| '__| |/ _ \ '__| / _ \/\ | | | | '_ ` _ \| | '_ \ / _` | | _ // _ \/ __/ _ \ \ / / _ \ '__| | | |
|
||||||
|
| |___| (_| | | | | | | __/ | | (_> < | | | | | | | | | | | | | (_| | | | \ \ __/ (_| (_) \ V / __/ | | |_| |
|
||||||
|
\_____\__,_|_| |_| |_|\___|_| \___/\/ |_| |_|_| |_| |_|_|_| |_|\__, | |_| \_\___|\___\___/ \_/ \___|_| \__, |
|
||||||
|
__/ | __/ |
|
||||||
|
|___/ |___/
|
||||||
|
*/
|
||||||
|
|
||||||
void pll_cc_init_pi_controller(pll_t* p, float bandwidth, float ko, float kd, float damping_factor)
|
void pll_cc_init_pi_controller(pll_t* p, float bandwidth, float ko, float kd, float damping_factor)
|
||||||
{
|
{
|
||||||
//kd: detector gain
|
//kd: detector gain
|
||||||
|
@ -1620,7 +1631,7 @@ void pll_cc(pll_t* p, complexf* input, float* output_dphase, complexf* output_nc
|
||||||
p->dphase = new_dphase * p->alpha + p->iir_temp;
|
p->dphase = new_dphase * p->alpha + p->iir_temp;
|
||||||
p->iir_temp += new_dphase * p->beta;
|
p->iir_temp += new_dphase * p->beta;
|
||||||
|
|
||||||
while(p->dphase>PI) p->dphase-=2*PI; //ez nem fog kelleni
|
while(p->dphase>PI) p->dphase-=2*PI; //won't need this one
|
||||||
while(p->dphase<-PI) p->dphase+=2*PI;
|
while(p->dphase<-PI) p->dphase+=2*PI;
|
||||||
}
|
}
|
||||||
else if(p->pll_type == PLL_P_CONTROLLER)
|
else if(p->pll_type == PLL_P_CONTROLLER)
|
||||||
|
@ -1633,6 +1644,116 @@ void pll_cc(pll_t* p, complexf* input, float* output_dphase, complexf* output_nc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
return to_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timing_recovery(complexf* input, complexf* output, int input_size, timing_recovery_state_t* state)
|
||||||
|
{
|
||||||
|
//We always assume that the input starts at center of the first symbol cross before the first symbol.
|
||||||
|
//Last time we consumed that much from the input samples that it is there.
|
||||||
|
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;
|
||||||
|
float error;
|
||||||
|
int si;
|
||||||
|
if(state->algorithm == TIMING_RECOVERY_ALGORITHM_GARDNER)
|
||||||
|
{
|
||||||
|
for(si=0;;si++)
|
||||||
|
{
|
||||||
|
if(current_bitstart_index + num_samples_halfbit * 3 >= input_size) break;
|
||||||
|
output[si++] = input[current_bitstart_index + num_samples_halfbit];
|
||||||
|
error = (
|
||||||
|
iof(input, current_bitstart_index + num_samples_halfbit * 3) - iof(input, current_bitstart_index + num_samples_halfbit)
|
||||||
|
) * iof(input, current_bitstart_index + num_samples_halfbit * 2);
|
||||||
|
if(state->use_q)
|
||||||
|
{
|
||||||
|
error += (
|
||||||
|
qof(input, current_bitstart_index + num_samples_halfbit * 3) - qof(input, current_bitstart_index + num_samples_halfbit)
|
||||||
|
) * qof(input, current_bitstart_index + num_samples_halfbit * 2);
|
||||||
|
error /= 2;
|
||||||
|
}
|
||||||
|
current_bitstart_index += num_samples_halfbit * 2 + (error)?((error>0)?1:-1):0;
|
||||||
|
|
||||||
|
}
|
||||||
|
state->input_processed = current_bitstart_index;
|
||||||
|
state->output_size = si;
|
||||||
|
}
|
||||||
|
else if(state->algorithm == TIMING_RECOVERY_ALGORITHM_EARLYLATE)
|
||||||
|
{
|
||||||
|
for(si=0;;si++)
|
||||||
|
{
|
||||||
|
if(current_bitstart_index + num_samples_bit >= input_size) break;
|
||||||
|
output[si++] = input[current_bitstart_index + num_samples_halfbit];
|
||||||
|
error = (
|
||||||
|
iof(input, current_bitstart_index + num_samples_quarterbit * 3) - iof(input, current_bitstart_index + num_samples_quarterbit)
|
||||||
|
); //* iof(input, current_bitstart_index + num_samples_halfbit); //I don't think we need the end of the Nutaq formula
|
||||||
|
if(state->use_q)
|
||||||
|
{
|
||||||
|
error += qof(input, current_bitstart_index + num_samples_quarterbit * 3) - qof(input, current_bitstart_index + num_samples_quarterbit);
|
||||||
|
error /= 2;
|
||||||
|
}
|
||||||
|
//Correction method #1: this version can only move a single sample in any direction
|
||||||
|
//current_bitstart_index += num_samples_halfbit * 2 + (error)?((error<0)?1:-1):0;
|
||||||
|
//Correction method #2: this can move in proportional to the error
|
||||||
|
if(error>2) error=2;
|
||||||
|
if(error<-2) error=-2;
|
||||||
|
current_bitstart_index += num_samples_halfbit * 2 + num_samples_halfbit * (-error/2);
|
||||||
|
}
|
||||||
|
state->input_processed = current_bitstart_index;
|
||||||
|
state->output_i = si;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
in struct:
|
||||||
|
char* debug_string;
|
||||||
|
char* debug_string_end;
|
||||||
|
int debug_string_length;
|
||||||
|
int debug_mode;
|
||||||
|
int debug_first_n_symbols;
|
||||||
|
func:
|
||||||
|
void timing_recovery_write_debug_string
|
||||||
|
(
|
||||||
|
int input,
|
||||||
|
int input_length,
|
||||||
|
int current_bitstart_index,
|
||||||
|
int current_output_index,
|
||||||
|
int current_p1_index,
|
||||||
|
int current_p2_index,
|
||||||
|
timing_recovery_state_t* state
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if(state->debug_string_end==state->debug_string+debug_string_length) return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
_____ _ _
|
_____ _ _
|
||||||
| __ \ | | (_)
|
| __ \ | | (_)
|
||||||
|
|
22
libcsdr.h
22
libcsdr.h
|
@ -287,3 +287,25 @@ typedef struct pll_s
|
||||||
void pll_cc_init_pi_controller(pll_t* p, float bandwidth, float ko, float kd, float damping_factor);
|
void pll_cc_init_pi_controller(pll_t* p, float bandwidth, float ko, float kd, float damping_factor);
|
||||||
void pll_cc_init_p_controller(pll_t* p, float alpha);
|
void pll_cc_init_p_controller(pll_t* p, float alpha);
|
||||||
void pll_cc(pll_t* p, complexf* input, float* output_dphase, complexf* output_nco, int input_size);
|
void pll_cc(pll_t* p, complexf* input, float* output_dphase, complexf* output_nco, int input_size);
|
||||||
|
|
||||||
|
typedef enum timing_recovery_algorithm_e
|
||||||
|
{
|
||||||
|
TIMING_RECOVERY_ALGORITHM_GARDNER,
|
||||||
|
TIMING_RECOVERY_ALGORITHM_EARLYLATE
|
||||||
|
} timing_recovery_algorithm_t;
|
||||||
|
|
||||||
|
#define TIMING_RECOVERY_ALGORITHM_DEFAULT TIMING_RECOVERY_ALGORITHM_GARDNER
|
||||||
|
|
||||||
|
typedef struct timing_recovery_state_s
|
||||||
|
{
|
||||||
|
timing_recovery_algorithm_t algorithm;
|
||||||
|
int decimation_rate; // = input_rate / output_rate. We should get an input signal that is N times oversampled.
|
||||||
|
int output_size;
|
||||||
|
int input_processed;
|
||||||
|
int use_q; //use both I and Q for calculating the error
|
||||||
|
} timing_recovery_state_t;
|
||||||
|
|
||||||
|
timing_recovery_state_t timing_recovery_init(timing_recovery_algorithm_t algorithm, int decimation_rate, int use_q);
|
||||||
|
int timing_recovery(complexf* input, complexf* output, int input_length, timing_recovery_state_t* state);
|
||||||
|
timing_recovery_algorithm_t timing_recovery_get_algorithm_from_string(char* input);
|
||||||
|
char* timing_recovery_get_string_from_algorithm(timing_recovery_algorithm_t algorithm);
|
||||||
|
|
Loading…
Reference in a new issue