First concept of serial_line_decoder_f_u8.

This commit is contained in:
ha7ilm 2016-05-08 09:33:40 +02:00
parent 7c36a0ffce
commit 380bfded2c
3 changed files with 321 additions and 22 deletions

101
csdr.c
View file

@ -113,8 +113,11 @@ char usage[]=
" fft_exchange_sides_ff <fft_size>\n"
" squelch_and_smeter_cc --fifo <squelch_fifo> --outfifo <smeter_fifo> <use_every_nth> <report_every_nth>\n"
" fifo <buffer_size> <number_of_buffers>\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;
}

182
libcsdr.c
View file

@ -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;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;
}
*/
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;
}
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<input_size;i++) if(input[i] < 0 && input[i-1] > 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<stopbit_end_search_end;i++) if(input[i-1] < 0 && input[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;i<databit_end;i++) databit_acc += input[i];
shr=(shr<<1)|!!(databit_acc>0);
}
//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<input_size;i++) output[i] = input[i] > 0;
}
/*
_____ _ _

View file

@ -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);