From 2a7f8214dffe6e145fd0a2eb6a433cd97fae2bb7 Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Sat, 18 Jun 2011 21:03:20 -0700 Subject: [PATCH] minimodem -T rtty (baudot RTTY transmit) --- src/baudot.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++-- src/baudot.h | 11 ++- src/minimodem.c | 48 ++++++++--- 3 files changed, 248 insertions(+), 20 deletions(-) diff --git a/src/baudot.c b/src/baudot.c index 6e623ca..b1bfaf0 100644 --- a/src/baudot.c +++ b/src/baudot.c @@ -9,10 +9,19 @@ #include +#include +#include + +//#define BAUDOT_DEBUG +#ifdef BAUDOT_DEBUG +# define debug_log(format, args...) fprintf(stderr, format, ## args) +#else +# define debug_log(format, args...) +#endif static char -baudot_table[32][3] = { +baudot_decode_table[32][3] = { // letter, U.S. figs, CCITT No.2 figs (Europe) { '*', '*', '*' }, // NUL { 'E', '3', '3' }, @@ -51,18 +60,137 @@ baudot_table[32][3] = { { '*', '*', '*' }, // LTRS }; +static char +baudot_encode_table[0x60][2] = { + // index: ascii char; values: bits, ltrs_or_figs_or_neither_or_both + + /* 0x00 */ + /* NUL */ { 0x00, 3 }, // NUL + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* BEL */ { 0x05, 2 }, // BELL (or CCITT2 apostrophe) + /* BS */ { 0, 0 }, // non-encodable (FIXME???) + /* xxx */ { 0, 0 }, // non-encodable + /* LF */ { 0x02, 3 }, // LF + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* 0xD */ { 0x08, 3 }, // CR + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + + /* 0x10 */ + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + /* xxx */ { 0, 0 }, // non-encodable + + /* 0x20 */ + /* */ { 0x04, 3 }, // SPACE + /* ! */ { 0x0d, 2 }, // + /* " */ { 0x11, 2 }, // + /* # */ { 0x14, 2 }, // '#' (or CCITT2 British pounds symbol) + /* $ */ { 0x09, 2 }, // '$' (or CCITT2 ENQ) + /* % */ { 0, 0 }, // non-encodable + /* & */ { 0x1a, 2 }, // + /* ' */ { 0x0b, 2 }, // apostrophe (or CCITT2 BELL) + /* ( */ { 0x0f, 2 }, // + /* ) */ { 0x12, 2 }, // + /* * */ { 0, 0 }, // non-encodable + /* + */ { 0x12, 2 }, // + /* , */ { 0x0c, 2 }, // + /* - */ { 0x03, 2 }, // + /* . */ { 0x1c, 2 }, // + /* / */ { 0x1d, 2 }, // + + /* 0x30 */ + /* 0 */ { 0x16, 2 }, // + /* 1 */ { 0x17, 2 }, // + /* 2 */ { 0x13, 2 }, // + /* 3 */ { 0x01, 2 }, // + /* 4 */ { 0x0a, 2 }, // + /* 5 */ { 0x10, 2 }, // + /* 6 */ { 0x15, 2 }, // + /* 7 */ { 0x07, 2 }, // + /* 8 */ { 0x06, 2 }, // + /* 9 */ { 0x18, 2 }, // + /* : */ { 0x0e, 2 }, // + /* ; */ { 0x1e, 2 }, // + /* < */ { 0, 0 }, // non-encodable + /* = */ { 0, 0 }, // non-encodable + /* > */ { 0, 0 }, // non-encodable + /* ? */ { 0x19, 2 }, // + + /* 0x40 */ + /* @ */ { 0, 0 }, // non-encodable + /* A */ { 0x03, 1 }, // + /* B */ { 0x19, 1 }, // + /* C */ { 0x0e, 1 }, // + /* D */ { 0x09, 1 }, // + /* E */ { 0x01, 1 }, // + /* F */ { 0x0d, 1 }, // + /* G */ { 0x1a, 1 }, // + /* H */ { 0x14, 1 }, // + /* I */ { 0x06, 1 }, // + /* J */ { 0x0b, 1 }, // + /* K */ { 0x0f, 1 }, // + /* L */ { 0x12, 1 }, // + /* M */ { 0x1c, 1 }, // + /* N */ { 0x0c, 1 }, // + /* O */ { 0x18, 1 }, // + + /* 0x50 */ + /* P */ { 0x16, 1 }, // + /* Q */ { 0x17, 1 }, // + /* R */ { 0x0a, 1 }, // + /* S */ { 0x05, 1 }, // + /* T */ { 0x10, 1 }, // + /* U */ { 0x07, 1 }, // + /* V */ { 0x1e, 1 }, // + /* W */ { 0x13, 1 }, // + /* X */ { 0x1d, 1 }, // + /* Y */ { 0x15, 1 }, // + /* Z */ { 0x11, 1 }, // + /* [ */ { 0, 0 }, // non-encodable + /* \\ */ { 0, 0 }, // non-encodable + /* ] */ { 0, 0 }, // non-encodable + /* ^ */ { 0, 0 }, // non-encodable + /* _ */ { 0, 0 }, // non-encodable + +}; + #define BAUDOT_LTRS 0x1F #define BAUDOT_FIGS 0x1B #define BAUDOT_SPACE 0x04 +/* + * 0 unknown state + * 1 LTRS state + * 2 FIGS state + */ static int baudot_charset = 0; // FIXME void baudot_reset() { - baudot_charset = 0; + baudot_charset = 1; } @@ -79,16 +207,83 @@ baudot_decode( char *char_outp, unsigned char databits ) int stuff_char = 1; if ( databits == BAUDOT_FIGS ) { - baudot_charset = 1; + baudot_charset = 2; stuff_char = 0; } else if ( databits == BAUDOT_LTRS ) { - baudot_charset = 0; + baudot_charset = 1; stuff_char = 0; - } else if ( databits == BAUDOT_SPACE ) { - baudot_charset = 0; + } else if ( databits == BAUDOT_SPACE ) { /* RX un-shift on space */ + baudot_charset = 1; } if ( stuff_char ) - *char_outp = baudot_table[databits][baudot_charset]; + *char_outp = baudot_decode_table[databits][baudot_charset-1]; return stuff_char; } + +static void +baudot_skip_warning( char char_out ) +{ + unsigned char byte = char_out; + fprintf(stderr, "W: baudot skipping non-encodable character '%c' 0x%02x\n", + char_out, byte); +} + +/* + * Returns the number of 5-bit data words stuffed into *databits_outp (1 or 2) + */ +int +baudot_encode( unsigned int *databits_outp, char char_out ) +{ + + char_out = toupper(char_out); + if( char_out >= 0x60 || char_out < 0 ) { + baudot_skip_warning(char_out); + return 0; + } + + unsigned char ind = char_out; + + int n = 0; + + unsigned char charset_mask = baudot_encode_table[ind][1]; + + debug_log("I: (baudot_charset==%u) input character '%c' 0x%02x charset_mask=%u\n", baudot_charset, char_out, char_out, charset_mask); + + if ( (baudot_charset & charset_mask ) == 0 ) { + if ( charset_mask == 0 ) { + baudot_skip_warning(char_out); + return 0; + } + + if ( baudot_charset == 0 ) + baudot_charset = 1; + + if ( charset_mask != 3 ) + baudot_charset = charset_mask; + + if ( baudot_charset == 1 ) + databits_outp[n++] = BAUDOT_LTRS; + else if ( baudot_charset == 2 ) + databits_outp[n++] = BAUDOT_FIGS; + else + assert(0); + + debug_log("I: emit charset select 0x%02X\n", databits_outp[n-1]); + } + + if ( !( baudot_charset == 1 || baudot_charset == 2 ) ) { + fprintf(stderr, "E: baudot input character failed '%c' 0x%02x\n", + char_out, char_out); + fprintf(stderr, "E: baudot_charset==%u\n", baudot_charset); + assert(0); + } + + databits_outp[n++] = baudot_encode_table[ind][0]; + + /* TX un-shift on space */ + if ( char_out == ' ' ) + baudot_charset = 1; + + return n; +} diff --git a/src/baudot.h b/src/baudot.h index cd3ebea..f361ef1 100644 --- a/src/baudot.h +++ b/src/baudot.h @@ -11,9 +11,16 @@ void baudot_reset(); - /* - * returns nonzero if *char_outp was stuffed with an output character + * Returns 1 if *char_outp was stuffed with an output character + * or 0 if no output character was stuffed (in other words, returns + * the count of characters decoded and stuffed). */ int baudot_decode( char *char_outp, unsigned char databits ); + +/* + * Returns the number of 5-bit datawords stuffed into *databits_outp (1 or 2) + */ +int +baudot_encode( unsigned int *databits_outp, char char_out ); diff --git a/src/minimodem.c b/src/minimodem.c index 5ef3f76..7c7dee1 100644 --- a/src/minimodem.c +++ b/src/minimodem.c @@ -21,8 +21,17 @@ /* - * ASCII 8-bit data framebits decoder (passthrough) + * ASCII 8-bit data framebits decoder/encoder (passthrough) */ + +/* returns the number of datawords stuffed into *databits_outp */ +int +framebits_encode_ascii8( unsigned int *databits_outp, char char_out ) +{ + *databits_outp = char_out; + return 1; +} + /* returns nbytes decoded */ static unsigned int framebits_decode_ascii8( char *dataout_p, unsigned int dataout_size, @@ -38,8 +47,11 @@ framebits_decode_ascii8( char *dataout_p, unsigned int dataout_size, /* - * Baudot 5-bit data framebits decoder + * Baudot 5-bit data framebits decoder/encoder */ + +#define framebits_encode_baudot baudot_encode + /* returns nbytes decoded */ static unsigned int framebits_decode_baudot( char *dataout_p, unsigned int dataout_size, @@ -63,7 +75,8 @@ static void fsk_transmit_stdin( float data_rate, float bfsk_mark_f, float bfsk_space_f, - int n_data_bits + int n_data_bits, + int (*framebits_encoder)( unsigned int *databits_outp, char char_out ) ) { size_t sample_rate = simpleaudio_get_rate(sa_out); @@ -73,14 +86,23 @@ static void fsk_transmit_stdin( simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec leader while ( (c = getchar()) != EOF ) { - simpleaudio_tone(sa_out, bfsk_space_f, bit_nsamples); // start - int i; - for ( i=0; i> i ) & 1; - float tone_freq = bit == 1 ? bfsk_mark_f : bfsk_space_f; - simpleaudio_tone(sa_out, tone_freq, bit_nsamples); + + // HACK - baudot + unsigned int nwords; + unsigned int bits[2]; + nwords = framebits_encoder(bits, c); + + unsigned int j; + for ( j=0; j> i ) & 1; + float tone_freq = bit == 1 ? bfsk_mark_f : bfsk_space_f; + simpleaudio_tone(sa_out, tone_freq, bit_nsamples); + } + simpleaudio_tone(sa_out, bfsk_mark_f, bit_nsamples); // stop } - simpleaudio_tone(sa_out, bfsk_mark_f, bit_nsamples); // stop } simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec tail @@ -113,6 +135,7 @@ main( int argc, char*argv[] ) float bfsk_data_rate; int bfsk_n_data_bits; + int (*bfsk_framebits_encode)( unsigned int *databits_outp, char char_out ); unsigned int (*bfsk_framebits_decode)( char *dataout_p, unsigned int dataout_size, unsigned int bits ); @@ -121,10 +144,12 @@ main( int argc, char*argv[] ) bfsk_data_rate = 45.45; bfsk_n_data_bits = 5; bfsk_framebits_decode = framebits_decode_baudot; + bfsk_framebits_encode = framebits_encode_baudot; } else { bfsk_data_rate = atof(argv[argi]); bfsk_n_data_bits = 8; bfsk_framebits_decode = framebits_decode_ascii8; + bfsk_framebits_encode = framebits_encode_ascii8; } argi++; @@ -171,7 +196,8 @@ main( int argc, char*argv[] ) fsk_transmit_stdin(sa_out, bfsk_data_rate, bfsk_mark_f, bfsk_space_f, - bfsk_n_data_bits + bfsk_n_data_bits, + bfsk_framebits_encode ); return 0; }