From 380bfded2cb5fecff14672bc3bf9657e35079dad Mon Sep 17 00:00:00 2001 From: ha7ilm Date: Sun, 8 May 2016 09:33:40 +0200 Subject: [PATCH] First concept of serial_line_decoder_f_u8. --- csdr.c | 101 ++++++++++++++++++++++++++++-- libcsdr.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++----- libcsdr.h | 60 +++++++++++++++++- 3 files changed, 321 insertions(+), 22 deletions(-) diff --git a/csdr.c b/csdr.c index d0a7652..6e5b470 100644 --- a/csdr.c +++ b/csdr.c @@ -113,8 +113,11 @@ char usage[]= " fft_exchange_sides_ff \n" " squelch_and_smeter_cc --fifo --outfifo \n" " fifo \n" -" bpsk31_varicode2ascii_sy_u8\n" +" bpsk31_line_decoder_sy_u8\n" " invert_sy_sy\n" +" rtty_line_decoder_sy_u8\n" +" rtty_baudot2ascii_u8_u8\n" +" serial_line_decoder_sy_u8\n" " \n" ; @@ -1836,7 +1839,7 @@ int main(int argc, char *argv[]) } } - if(!strcmp(argv[1],"bpsk31_varicode2ascii_sy_u8")) //not tested + if(!strcmp(argv[1],"bpsk31_line_decoder_sy_u8")) { unsigned long long status_shr = 0; unsigned char output; @@ -1844,14 +1847,14 @@ int main(int argc, char *argv[]) unsigned char i=0; for(;;) { - if((output=psk31_varicode_push(&status_shr, getchar()))) { putchar(output); fflush(stdout); } + if((output=psk31_varicode_decoder_push(&status_shr, getchar()))) { putchar(output); fflush(stdout); } if(i++) continue; //do the following at every 256th execution of the loop body: FEOF_CHECK; TRY_YIELD; } } - if(!strcmp(argv[1],"invert_sy_sy")) //not tested + if(!strcmp(argv[1],"invert_sy_sy")) { if(!sendbufsize(initialize_buffers())) return -2; unsigned char i=0; @@ -1864,11 +1867,97 @@ int main(int argc, char *argv[]) } } + if(!strcmp(argv[1],"rtty_line_decoder_sy_u8")) + { + static rtty_baudot_decoder_t status_baudot; //created on .bss -> initialized to 0 + unsigned char output; + if(!sendbufsize(initialize_buffers())) return -2; + unsigned char i=0; + for(;;) + { + if((output=rtty_baudot_decoder_push(&status_baudot, getchar()))) { putchar(output); fflush(stdout); } + if(i++) continue; //do the following at every 256th execution of the loop body: + FEOF_CHECK; + TRY_YIELD; + } + } + + if(!strcmp(argv[1],"rtty_baudot2ascii_u8_u8")) + { + unsigned char fig_mode = 0; + unsigned char output; + if(!sendbufsize(initialize_buffers())) return -2; + unsigned char i=0; + for(;;) + { + if((output=rtty_baudot_decoder_lookup(&fig_mode, getchar()))) { putchar(output); fflush(stdout); } + if(i++) continue; //do the following at every 256th execution of the loop body: + FEOF_CHECK; + TRY_YIELD; + } + } + + if(!strcmp(argv[1],"binary_slicer_f_u8")) + { + if(!sendbufsize(initialize_buffers())) return -2; + for(;;) + { + FEOF_CHECK; + if(!FREAD_R) break; + binary_slicer_f_u8(input_buffer, (unsigned char*)output_buffer, the_bufsize); + FWRITE_U8; + TRY_YIELD; + } + return 0; + } + + if(!strcmp(argv[1],"serial_line_decoder_f_u8")) + { + bigbufs=1; + + serial_line_t serial; + + if(argc<=2) return badsyntax("need required parameter (samples_per_bits)"); + sscanf(argv[2],"%f",&serial.samples_per_bits); + if(serial.samples_per_bits<1) return badsyntax("samples_per_bits should be at least 1."); + if(serial.samples_per_bits<5) fprintf(stderr, "serial_line_decoder_sy_u8: warning: this algorithm does not work well if samples_per_bits is too low. It should be at least 5.\n"); + serial.actual_samples_per_bits = serial.samples_per_bits; + + serial.databits=8; + if(argc>3) sscanf(argv[3],"%d",&serial.databits); + if(serial.databits>8 || serial.databits<1) return badsyntax("databits should be between 1 and 8."); + + serial.stopbits=1; + if(argc>4) sscanf(argv[4],"%f",&serial.stopbits); + if(serial.stopbits<1) return badsyntax("stopbits should be equal or above 1."); + + serial.samples_per_bits_max_deviation_rate=0.001; + serial.samples_per_bits_loop_gain=0.05; + serial.input_used=0; + + if(!sendbufsize(initialize_buffers())) return -2; + + for(;;) + { + FEOF_CHECK; + if(serial.input_used) + { + memmove(input_buffer, input_buffer+serial.input_used, the_bufsize-serial.input_used); + fread(input_buffer+(the_bufsize-serial.input_used), sizeof(unsigned char), serial.input_used, stdin); + } + else fread(input_buffer, sizeof(unsigned char), the_bufsize, stdin); //should happen only on the first run + serial_line_decoder_f_u8(&serial,input_buffer, (unsigned char*)output_buffer, the_bufsize); + if(serial.input_used==0) { fprintf(stderr, "serial_line_decoder_sy_u8: error: serial_line_decoder() stuck.\n"); return -3; } + fwrite(output_buffer, sizeof(unsigned char), serial.output_size, stdout); + TRY_YIELD; + } + } + if(!strcmp(argv[1],"none")) { return 0; } - return badsyntax("function name given in argument 1 does not exist. Possible causes:\n- You mistyped the commandline.\n- You need to update csdr to a newer version (if available)."); - + fprintf(stderr, "csdr: function name \"%s\" given in argument 1 does not exist. Possible causes:\n- You mistyped the commandline.\n- You need to update csdr to a newer version (if available).", argv[1]); + return -1; } diff --git a/libcsdr.c b/libcsdr.c index 5dd38c7..dc8351c 100644 --- a/libcsdr.c +++ b/libcsdr.c @@ -960,14 +960,6 @@ void logpower_cf(complexf* input, float* output, int size, float add_db) |___/ */ - -typedef struct psk31_varicode_item_s -{ - unsigned long long code; - int bitcount; - unsigned char ascii; -} psk31_varicode_item_t; - psk31_varicode_item_t psk31_varicode_items[] = { { .code = 0b1010101011, .bitcount=10, .ascii=0x00 }, //NUL, null @@ -980,10 +972,10 @@ psk31_varicode_item_t psk31_varicode_items[] = { .code = 0b1011111101, .bitcount=10, .ascii=0x07 }, //BEL, bell { .code = 0b1011111111, .bitcount=10, .ascii=0x08 }, //BS, backspace { .code = 0b11101111, .bitcount=8, .ascii=0x09 }, //TAB, horizontal tab - { .code = 0b11101, .bitcount=5, .ascii=0x0a }, //LF, NL line feed, new line + { .code = 0b11101, .bitcount=5, .ascii=0x0a }, //LF, NL line feed, new line { .code = 0b1101101111, .bitcount=10, .ascii=0x0b }, //VT, vertical tab { .code = 0b1011011101, .bitcount=10, .ascii=0x0c }, //FF, NP form feed, new page - { .code = 0b11111, .bitcount=5, .ascii=0x0d }, //CR, carriage return (overwrite) + { .code = 0b11111, .bitcount=5, .ascii=0x0d }, //CR, carriage return (overwrite) { .code = 0b1101110101, .bitcount=10, .ascii=0x0e }, //SO, shift out { .code = 0b1110101011, .bitcount=10, .ascii=0x0f }, //SI, shift in { .code = 0b1011110111, .bitcount=10, .ascii=0x10 }, //DLE, data link escape @@ -1100,8 +1092,6 @@ psk31_varicode_item_t psk31_varicode_items[] = { .code = 0b1110110101, .bitcount=10, .ascii=0x7f }, //DEL }; -const int n_psk31_varicode_items = sizeof(psk31_varicode_items) / sizeof(psk31_varicode_item_t); - unsigned long long psk31_varicode_masklen_helper[] = { 0b0000000000000000000000000000000000000000000000000000000000000000, @@ -1170,7 +1160,9 @@ unsigned long long psk31_varicode_masklen_helper[] = 0b0111111111111111111111111111111111111111111111111111111111111111 }; -char psk31_varicode_push(unsigned long long* status_shr, unsigned char symbol) +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) { *status_shr=((*status_shr)<<1)|(!!symbol); //shift new bit in shift register //fprintf(stderr,"*status_shr = %llx\n", *status_shr); @@ -1185,15 +1177,175 @@ char psk31_varicode_push(unsigned long long* status_shr, unsigned char symbol) return 0; } +rtty_baudot_item_t rtty_baudot_items[] = +{ + { .code = 0b00000, .ascii_letter=0, .ascii_figure=0 }, + { .code = 0b10000, .ascii_letter='E', .ascii_figure='3' }, + { .code = 0b01000, .ascii_letter='\n', .ascii_figure='\n' }, + { .code = 0b11000, .ascii_letter='A', .ascii_figure='-' }, + { .code = 0b00100, .ascii_letter=' ', .ascii_figure=' ' }, + { .code = 0b10100, .ascii_letter='S', .ascii_figure='\'' }, + { .code = 0b01100, .ascii_letter='I', .ascii_figure='8' }, + { .code = 0b11100, .ascii_letter='U', .ascii_figure='7' }, + { .code = 0b00010, .ascii_letter='\r', .ascii_figure='\r' }, + { .code = 0b10010, .ascii_letter='D', .ascii_figure='#' }, + { .code = 0b01010, .ascii_letter='R', .ascii_figure='4' }, + { .code = 0b11010, .ascii_letter='J', .ascii_figure='\a' }, + { .code = 0b00110, .ascii_letter='N', .ascii_figure=',' }, + { .code = 0b10110, .ascii_letter='F', .ascii_figure='@' }, + { .code = 0b01110, .ascii_letter='C', .ascii_figure=':' }, + { .code = 0b11110, .ascii_letter='K', .ascii_figure='(' }, + { .code = 0b00001, .ascii_letter='T', .ascii_figure='5' }, + { .code = 0b10001, .ascii_letter='Z', .ascii_figure='+' }, + { .code = 0b01001, .ascii_letter='L', .ascii_figure=')' }, + { .code = 0b11001, .ascii_letter='W', .ascii_figure='2' }, + { .code = 0b00101, .ascii_letter='H', .ascii_figure='$' }, + { .code = 0b10101, .ascii_letter='Y', .ascii_figure='6' }, + { .code = 0b01101, .ascii_letter='P', .ascii_figure='0' }, + { .code = 0b11101, .ascii_letter='Q', .ascii_figure='1' }, + { .code = 0b00011, .ascii_letter='O', .ascii_figure='9' }, + { .code = 0b10011, .ascii_letter='B', .ascii_figure='?' }, + { .code = 0b01011, .ascii_letter='G', .ascii_figure='*' }, + { .code = 0b00111, .ascii_letter='M', .ascii_figure='.' }, + { .code = 0b10111, .ascii_letter='X', .ascii_figure='/' }, + { .code = 0b01111, .ascii_letter='V', .ascii_figure='=' } +}; +const int n_rtty_baudot_items = sizeof(rtty_baudot_items) / sizeof(rtty_baudot_item_t); -/* +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;istate) + { + 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; +} +void serial_line_decoder_f_u8(serial_line_t* s, float* input, unsigned char* output, int input_size) +{ + s->output_size = 0; + s->input_used = 0; + int oi=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 0) { startbit_start=i; break; } + if(startbit_start == -1) { s->input_used += i; fprintf(stderr,"sld:nstartbit\n"); return; } + fprintf(stderr,"sld:startbit_found at %d\n", startbit_start); + //We estimate where the stop bit edge can be, and search for it. + int stopbit_end = -1; + float all_bits = 1 + s->databits + s->stopbits; + float stopbit_end_estimate = startbit_start + s->samples_per_bits * all_bits; + float stopbit_end_search_start = stopbit_end_estimate - s->actual_samples_per_bits * 0.4; + float stopbit_end_search_end = stopbit_end_estimate + s->actual_samples_per_bits * 0.4; + if(stopbit_end_search_end>=input_size) return; + fprintf(stderr,"sld:all_bits = %f\n", all_bits); + fprintf(stderr,"sld:actual_samples_per_bits = %f\n", s->actual_samples_per_bits); + fprintf(stderr,"sld:stopbit_end_search_start = %f\n", stopbit_end_search_start); + fprintf(stderr,"sld:stopbit_end_search_end = %f\n", stopbit_end_search_end); + //If it is too far and 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. + if(stopbit_end_search_end>=input_size) + for(i=stopbit_end_search_start+1;i 0) { stopbit_end=i; break; } + if(stopbit_end == -1) + { + s->input_used += i+1; + if(s->input_used >= input_size) + { + s->input_used = input_size; + fprintf(stderr,"sld:nstopbit input_used out %d\n", s->input_used); + return; + } + input += s->input_used; + input_size -= s->input_used; + fprintf(stderr,"sld:nstopbit remain = %d\n", input_size); continue; + } + fprintf(stderr,"sld:stopbit_end = %d\n", stopbit_end); + //If we have the position of the stop bit, we calculate the actual_samples_per_bits: + float calculated_samples_per_bits = (stopbit_end - startbit_start) / all_bits; + float error_samples_per_bits = s->actual_samples_per_bits - calculated_samples_per_bits; + s->actual_samples_per_bits = s->actual_samples_per_bits - s->samples_per_bits_loop_gain * error_samples_per_bits; + s->actual_samples_per_bits = MIN_M(s->actual_samples_per_bits, (1+s->samples_per_bits_max_deviation_rate) * s->samples_per_bits); + s->actual_samples_per_bits = MAX_M(s->actual_samples_per_bits, (1-s->samples_per_bits_max_deviation_rate) * s->samples_per_bits); + fprintf(stderr,"sld:calculated_samples_per_bits = %f\n", calculated_samples_per_bits); + fprintf(stderr,"sld:error_samples_per_bits = %f\n", error_samples_per_bits); + + fprintf(stderr, "actual_samples_per_bits = %f\n", s->actual_samples_per_bits); + + //Now we have an actual_samples_per_bits, we do the actual sampling + int di; //databit counter + unsigned shr = 0; + for(di=0; di < s->databits; di++) + { + int databit_start = startbit_start + di * s->actual_samples_per_bits; + int databit_end = startbit_start + (di+1) * s->actual_samples_per_bits; + float databit_acc = 0; + for(i=databit_start;i0); + } + + //optionally we could check if the stopbit is correct + + //we write the output sample + if(s->databits <= 8) output[oi++] = shr; + else if(s->databits <= 16) output_s[oi] = shr; + else output_u[oi++] = shr; + + int samples_used_up_now = MIN_M(stopbit_end + s->actual_samples_per_bits, input_size); + s->input_used += samples_used_up_now; + input += samples_used_up_now; + input_size -= samples_used_up_now; + } + s->output_size = oi; + fprintf(stderr, "so: %d\n", s->output_size); +} + +void binary_slicer_f_u8(float* input, unsigned char* output, int input_size) +{ + for(int i=0;i 0; +} /* _____ _ _ diff --git a/libcsdr.h b/libcsdr.h index 419c064..90adaab 100644 --- a/libcsdr.h +++ b/libcsdr.h @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #define MIN_M(x,y) (((x)>(y))?(y):(x)) +#define MAX_M(x,y) (((x)<(y))?(y):(x)) /* _____ _ @@ -179,4 +180,61 @@ void convert_i16_f(short* input, float* output, int input_size); int is_nan(float f); -char psk31_varicode_push(unsigned long long* status_shr, unsigned char symbol); +//digital demod + +typedef struct rtty_baudot_item_s +{ + unsigned long long code; + unsigned char ascii_letter; + unsigned char ascii_figure; +} rtty_baudot_item_t; + +typedef enum rtty_baudot_decoder_state_e +{ + RTTY_BAUDOT_WAITING_STOP_PULSE = 0, + RTTY_BAUDOT_WAITING_START_PULSE, + RTTY_BAUDOT_RECEIVING_DATA +} rtty_baudot_decoder_state_t; + +typedef struct rtty_baudot_decoder_s +{ + unsigned char fig_mode; + unsigned char character_received; + unsigned short shr; + unsigned char bit_cntr; + rtty_baudot_decoder_state_t state; +} rtty_baudot_decoder_t; + +#define RTTY_FIGURE_MODE_SELECT_CODE 0b11011 +#define RTTY_LETTER_MODE_SELECT_CODE 0b11111 + +char rtty_baudot_decoder_lookup(unsigned char* fig_mode, unsigned char c); +char rtty_baudot_decoder_push(rtty_baudot_decoder_t* s, unsigned char symbol); + +//PSK31 + +typedef struct psk31_varicode_item_s +{ + unsigned long long code; + int bitcount; + unsigned char ascii; +} psk31_varicode_item_t; + +char psk31_varicode_decoder_push(unsigned long long* status_shr, unsigned char symbol); + +//Serial + +typedef struct serial_line_s +{ + float samples_per_bits; + float actual_samples_per_bits; + int databits; //including parity + float stopbits; + int output_size; + int input_used; + float samples_per_bits_max_deviation_rate; + float samples_per_bits_loop_gain; +} serial_line_t; + +void serial_line_decoder_f_u8(serial_line_t* s, float* input, unsigned char* output, int input_size); +void binary_slicer_f_u8(float* input, unsigned char* output, int input_size);