diff --git a/src/Makefile.am b/src/Makefile.am index da672d7..5704382 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,6 +46,7 @@ DATABITS_SRC = \ databits.h \ databits_ascii.c \ databits_binary.c \ + databits_callerid.c \ databits_baudot.c $(BAUDOT_SRC) minimodem_LDADD = $(DEPS_LIBS) diff --git a/src/Makefile.in b/src/Makefile.in index f3da11b..88ee040 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -67,7 +67,8 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" PROGRAMS = $(bin_PROGRAMS) am__objects_1 = baudot.$(OBJEXT) am__objects_2 = databits_ascii.$(OBJEXT) databits_binary.$(OBJEXT) \ - databits_baudot.$(OBJEXT) $(am__objects_1) + databits_callerid.$(OBJEXT) databits_baudot.$(OBJEXT) \ + $(am__objects_1) am__objects_3 = fsk.$(OBJEXT) am__objects_4 = simpleaudio.$(OBJEXT) simple-tone-generator.$(OBJEXT) \ simpleaudio-pulse.$(OBJEXT) simpleaudio-alsa.$(OBJEXT) \ @@ -228,6 +229,7 @@ DATABITS_SRC = \ databits.h \ databits_ascii.c \ databits_binary.c \ + databits_callerid.c \ databits_baudot.c $(BAUDOT_SRC) minimodem_LDADD = $(DEPS_LIBS) @@ -319,6 +321,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/databits_ascii.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/databits_baudot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/databits_binary.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/databits_callerid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minimodem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple-tone-generator.Po@am__quote@ diff --git a/src/databits.h b/src/databits.h index 0a71e6d..50a5da1 100644 --- a/src/databits.h +++ b/src/databits.h @@ -51,3 +51,8 @@ databits_decode_binary( char *dataout_p, unsigned int dataout_size, unsigned int bits, unsigned int n_databits ); +unsigned int +databits_decode_callerid( char *dataout_p, unsigned int dataout_size, + unsigned int bits, unsigned int n_databits ); + + diff --git a/src/databits_callerid.c b/src/databits_callerid.c new file mode 100644 index 0000000..cfcc353 --- /dev/null +++ b/src/databits_callerid.c @@ -0,0 +1,178 @@ +/* + * databits_callerid.c + * + * Copyright (C) 2012 Kamal Mostafa + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "databits.h" + +#include + +/* + * Caller-ID (USA SDMF/MDMF) databits decoder + * + * Reference: http://melabs.com/resources/callerid.htm + */ + +#define CID_MSG_MDMF 0x80 +#define CID_MSG_SDMF 0x04 + +#define CID_DATA_DATETIME 0x01 +#define CID_DATA_PHONE 0x02 +#define CID_DATA_PHONE_NA 0x04 +#define CID_DATA_NAME 0x07 +#define CID_DATA_NAME_NA 0x08 + +static const char *cid_datatype_names[] = { + "unknown0:", "Time:", "Phone:", "unknown3:", + "Phone:", "unknown5:", "unknown6:", "Name:", + "Name:" +}; + +static int cid_msgtype = 0; +static int cid_ndata = 0; +static unsigned char cid_buf[256]; + + +static unsigned int +decode_mdmf_callerid( char *dataout_p, unsigned int dataout_size ) +{ + unsigned int dataout_n = 0; + unsigned int cid_i = 0; + unsigned int cid_msglen = cid_buf[1]; + + unsigned char *m = cid_buf + 2; + while ( cid_i < cid_msglen ) { + + unsigned int cid_datatype = *m++; + if ( cid_datatype >= CID_DATA_NAME_NA ) { + // FIXME: bad datastream -- print something here + return databits_decode_callerid(0, 0, 0, 0); // reset + } + + unsigned int cid_datalen = *m++; + if ( m + 2 + cid_datalen >= cid_buf + sizeof(cid_buf) ) { + // FIXME: bad datastream -- print something here + return databits_decode_callerid(0, 0, 0, 0); // reset + } + + // dataout_n += sprintf(dataout_p+dataout_n, "CID: %d (%d)\n", cid_datatype, cid_datalen); + + dataout_n += sprintf(dataout_p+dataout_n, "%-6s ", + cid_datatype_names[cid_datatype]); + + if ( cid_datatype == CID_DATA_DATETIME ) + dataout_n += sprintf(dataout_p+dataout_n, "%.2s/%.2s %.2s:%.2s\n", + m+0, m+2, m+4, m+6); + else if ( cid_datatype == CID_DATA_PHONE && cid_datalen == 10 ) + dataout_n += sprintf(dataout_p+dataout_n, "%.3s-%.3s-%.4s\n", + m+0, m+3, m+6); + else + dataout_n += sprintf(dataout_p+dataout_n, "%.*s\n", + cid_datalen, m); + + m += cid_datalen; + cid_i += cid_datalen + 2; + } + + return dataout_n; +} + + +static unsigned int +decode_sdmf_callerid( char *dataout_p, unsigned int dataout_size ) +{ + unsigned int dataout_n = 0; + unsigned int cid_msglen = cid_buf[1]; + + unsigned char *m = cid_buf + 2; + + dataout_n += sprintf(dataout_p+dataout_n, "%-6s ", + cid_datatype_names[CID_DATA_DATETIME]); + dataout_n += sprintf(dataout_p+dataout_n, "%.2s/%.2s %.2s:%.2s\n", + m+0, m+2, m+4, m+6); + m += 8; + + dataout_n += sprintf(dataout_p+dataout_n, "%-6s ", + cid_datatype_names[CID_DATA_PHONE]); + unsigned int cid_datalen = cid_msglen - 8; + if ( cid_datalen == 10 ) + dataout_n += sprintf(dataout_p+dataout_n, "%.3s-%.3s-%.4s\n", + m+0, m+3, m+6); + else + dataout_n += sprintf(dataout_p+dataout_n, "%.*s\n", + cid_datalen, m); + + return dataout_n; +} + + +// FIXME: doesn't respect dataout_size at all! +/* returns nbytes decoded */ +unsigned int +databits_decode_callerid( char *dataout_p, unsigned int dataout_size, + unsigned int bits, unsigned int n_databits ) +{ + if ( ! dataout_p ) { // databits processor reset + cid_msgtype = 0; + cid_ndata = 0; + return 0; + } + + if ( cid_msgtype == 0 ) { + if ( bits == CID_MSG_MDMF ) + cid_msgtype = CID_MSG_MDMF; + else if ( bits == CID_MSG_SDMF ) + cid_msgtype = CID_MSG_SDMF; + cid_buf[cid_ndata++] = bits; + return 0; + } + + if ( cid_ndata >= sizeof(cid_buf) ) { + // FIXME? buffer overflow; do what here? + return databits_decode_callerid(0, 0, 0, 0); // reset + } + + cid_buf[cid_ndata++] = bits; + + // Collect input bytes until we've collected as many as the message + // length byte says there will be, plus two (the message type byte + // and the checksum byte) + unsigned int cid_msglen = cid_buf[1]; + if ( cid_ndata < cid_msglen + 2) + return 0; + + // Now we have a whole CID message in cid_buf[] -- decode it + + // FIXME: check the checksum + + unsigned int dataout_n = 0; + + dataout_n += sprintf(dataout_p+dataout_n, "CALLER-ID\n"); + + if ( cid_msgtype == CID_MSG_MDMF ) + dataout_n += decode_mdmf_callerid(dataout_p+dataout_n, + dataout_size-dataout_n); + else + dataout_n += decode_sdmf_callerid(dataout_p+dataout_n, + dataout_size-dataout_n); + + // All done; reset for the next one + databits_decode_callerid(0, 0, 0, 0); + + return dataout_n; +} + diff --git a/src/minimodem.1.in b/src/minimodem.1.in index 4d8d7e6..53bcc88 100644 --- a/src/minimodem.1.in +++ b/src/minimodem.1.in @@ -43,6 +43,32 @@ transmit mode: generate audio tones .TP .B \-r, \-\-rx, \-\-receive, \-\-read receive mode: decode audio tones +.SH {baudmode} +The required \fI{baudmode}\fR parameter may be any floating-point value to +specify a baud rate, or any of the special keywords listed below. +The \fI{baudmode}\fR also implies certain other parameter defaults +depending on the rate, including standard (or at least reasonable) +default mark and space tone frequencies. +.TP +.B {any floating point value N} +: Bell202-style at N bps \-\-ascii +.TP +.B 1200 +: Bell202 1200 bps \-\-ascii +.TP +.B 300 +: Bell103 300 bps \-\-ascii +.TP +.B rtty +: RTTY 45.45 bps \-\-baudot \-\-stopbits 1.5 +.TP +.B same +: SAME 520.83 bps \-\-startbits 0 \-\-stopbits 0 \-\-sync-byte 0xAB +.br + NOAA Specific Area Message Encoding (SAME) protocol +.TP +.B callerid +: Bell202 1200 bps Caller-ID (MDMF or SDMF) protocol .SH OPTIONS .TP .B \-a, \-\-auto-carrier @@ -146,29 +172,6 @@ Run and report internal performance tests (all other flags are ignored). .TP .B \-V, \-\-version print program version -.SH {baudmode} -The required \fI{baudmode}\fR parameter may be any floating-point value to -specify a baud rate, or either of the strings "rtty" or "same". -The \fI{baudmode}\fR also implies certain other parameter defaults -depending on the rate, including standard (or at least reasonable) -default mark and space tone frequencies. -.TP -.B {any floating point value N} -: Bell-syle N bps \-\-ascii -.TP -.B 1200 -: Bell202 1200 bps \-\-ascii -.TP -.B 300 -: Bell103 300 bps \-\-ascii -.TP -.B rtty -: RTTY 45.45 bps \-\-baudot \-\-stopbits 1.5 -.TP -.B same -: SAME 520.83 bps \-\-startbits 0 \-\-stopbits 0 \-\-sync-byte 0xAB -.br - NOAA Specific Area Message Encoding (SAME) protocol .SH EXAMPLES .TP .B minimodem --tx 100 diff --git a/src/minimodem.c b/src/minimodem.c index 9e5699c..0d0232d 100644 --- a/src/minimodem.c +++ b/src/minimodem.c @@ -319,6 +319,7 @@ usage() " 300 Bell103 300 bps --ascii\n" " rtty RTTY 45.45 bps --baudot --stopbits=1.5\n" " same NOAA SAME 520.83 bps --sync-byte=0xAB ...\n" + " callerid Bell202 CID 1200 bps\n" ); exit(1); } @@ -592,7 +593,12 @@ main( int argc, char*argv[] ) databits_encoder *bfsk_databits_encode; databits_decoder *bfsk_databits_decode; + bfsk_databits_decode = databits_decode_ascii8; + bfsk_databits_encode = databits_encode_ascii8; + if ( strncasecmp(modem_mode, "rtty",5)==0 ) { + bfsk_databits_decode = databits_decode_baudot; + bfsk_databits_encode = databits_encode_baudot; bfsk_data_rate = 45.45; if ( bfsk_n_data_bits == 0 ) bfsk_n_data_bits = 5; @@ -609,6 +615,14 @@ main( int argc, char*argv[] ) bfsk_mark_f = 2083.0 + 1/3.0; bfsk_space_f = 1562.5; band_width = bfsk_data_rate; + } else if ( strncasecmp(modem_mode, "caller",6)==0 ) { + if ( TX_mode ) { + fprintf(stderr, "E: callerid --tx mode is not supported.\n"); + return 1; + } + bfsk_databits_decode = databits_decode_callerid; + bfsk_data_rate = 1200; + bfsk_n_data_bits = 8; } else { bfsk_data_rate = atof(modem_mode); if ( bfsk_n_data_bits == 0 ) @@ -617,15 +631,6 @@ main( int argc, char*argv[] ) if ( bfsk_data_rate == 0.0 ) usage(); - if ( bfsk_n_data_bits == 8 ) { - bfsk_databits_decode = databits_decode_ascii8; - bfsk_databits_encode = databits_encode_ascii8; - } else if ( bfsk_n_data_bits == 5 ) { - bfsk_databits_decode = databits_decode_baudot; - bfsk_databits_encode = databits_encode_baudot; - } else { - assert( 0 && bfsk_n_data_bits ); - } if ( output_mode_binary ) bfsk_databits_decode = databits_decode_binary; diff --git a/tests/test-70-callerid-mdmf b/tests/test-70-callerid-mdmf new file mode 100755 index 0000000..e3da8b9 --- /dev/null +++ b/tests/test-70-callerid-mdmf @@ -0,0 +1,54 @@ +#!/bin/bash + +MINIMODEM="${MINIMODEM-./minimodem}" +[ -f "$MINIMODEM" ] || { + MINIMODEM="../src/minimodem" + [ -f "$MINIMODEM" ] || { + echo "E: cannot find minimodem in ./ or ../src/" 1>&2 + exit 1 + } +} + + +bytesfile="testdata-callerid-mdmf.bytes" +[ "$1" != "" ] && bytesfile="$1" + + +TMPF="/tmp/minimodem-test-$$" +trap "rm -f $TMPF.*" 0 + +set -e + +textfile="${bytesfile%%.bytes}.txt" + + +minimodem_tx_args="1200 --ascii" +minimodem_rx_args="callerid" + + +$MINIMODEM --tx --file $TMPF.wav $minimodem_tx_args < "$bytesfile" + +# cp $TMPF.wav /tmp/x.wav + +$MINIMODEM --rx --file $TMPF.wav $minimodem_rx_args \ + > $TMPF.out 2> $TMPF.err || { + cat $TMPF.err + exit 1 +} + +cmp "$textfile" $TMPF.out + +{ + read xlitcarrier + read xlitblankline + read xlithashes xlitnocarrier stats + stats="${stats% ###}" +} < $TMPF.err + + +result="OK " +exitcode=0 + +echo -e "$result $stats" + +exit $exitcode diff --git a/tests/test-71-callerid-sdmf b/tests/test-71-callerid-sdmf new file mode 100755 index 0000000..4798466 --- /dev/null +++ b/tests/test-71-callerid-sdmf @@ -0,0 +1,2 @@ +#!/bin/bash +exec ./test-70-callerid-mdmf testdata-callerid-sdmf.bytes diff --git a/tests/testdata-callerid-mdmf.bytes b/tests/testdata-callerid-mdmf.bytes new file mode 100644 index 0000000..d8265b0 --- /dev/null +++ b/tests/testdata-callerid-mdmf.bytes @@ -0,0 +1,2 @@ +€ 03240902JOHN DOE +8005551212} \ No newline at end of file diff --git a/tests/testdata-callerid-mdmf.txt b/tests/testdata-callerid-mdmf.txt new file mode 100644 index 0000000..e283254 --- /dev/null +++ b/tests/testdata-callerid-mdmf.txt @@ -0,0 +1,4 @@ +CALLER-ID +Time: 03/24 09:02 +Name: JOHN DOE +Phone: 800-555-1212 diff --git a/tests/testdata-callerid-sdmf.bytes b/tests/testdata-callerid-sdmf.bytes new file mode 100644 index 0000000..82a1433 --- /dev/null +++ b/tests/testdata-callerid-sdmf.bytes @@ -0,0 +1 @@ +093012246095551212Q \ No newline at end of file diff --git a/tests/testdata-callerid-sdmf.txt b/tests/testdata-callerid-sdmf.txt new file mode 100644 index 0000000..f5ba3ae --- /dev/null +++ b/tests/testdata-callerid-sdmf.txt @@ -0,0 +1,3 @@ +CALLER-ID +Time: 09/30 12:24 +Phone: 609-555-1212