minimodem -T rtty (baudot RTTY transmit)
This commit is contained in:
parent
286879cfb3
commit
2a7f8214df
209
src/baudot.c
209
src/baudot.c
|
@ -9,10 +9,19 @@
|
|||
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//#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;
|
||||
}
|
||||
|
|
11
src/baudot.h
11
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 );
|
||||
|
|
|
@ -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,15 +86,24 @@ static void fsk_transmit_stdin(
|
|||
simpleaudio_tone(sa_out, bfsk_mark_f, sample_rate/2); // 0.5 sec leader
|
||||
while ( (c = getchar()) != EOF )
|
||||
{
|
||||
|
||||
// HACK - baudot
|
||||
unsigned int nwords;
|
||||
unsigned int bits[2];
|
||||
nwords = framebits_encoder(bits, c);
|
||||
|
||||
unsigned int j;
|
||||
for ( j=0; j<nwords; j++ ) {
|
||||
simpleaudio_tone(sa_out, bfsk_space_f, bit_nsamples); // start
|
||||
int i;
|
||||
for ( i=0; i<n_data_bits; i++ ) { // data
|
||||
unsigned int bit = ( c >> i ) & 1;
|
||||
unsigned int bit = ( bits[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, sample_rate/2); // 0.5 sec tail
|
||||
|
||||
// 0.5 sec of zero samples to flush - FIXME lame
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue