diff --git a/Makefile.linux b/Makefile.linux index 8564f31..b9c5583 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -325,7 +325,7 @@ log2gpx : log2gpx.c textcolor.o misc.a # Test application to generate sound. -gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c misc.a +gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c dtmf.c textcolor.c dsp.c misc.a $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) # Unit test for AFSK demodulator @@ -588,7 +588,7 @@ install-rpi : dw-start.sh # Combine some unit tests into a single regression sanity check. -check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800 +check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800 # Can we encode and decode at popular data rates? @@ -632,8 +632,8 @@ check-modem4800 : gen_packets atest # Unit test for inner digipeater algorithm .PHONY : dtest -dtest : digipeater.c dedupe.c \ - pfilter.o ax25_pad.o fcs_calc.o tq.o textcolor.o \ +dtest : digipeater.c dedupe.c pfilter.c \ + ax25_pad.o fcs_calc.o tq.o textcolor.o \ decode_aprs.o dwgpsnmea.o dwgps.o dwgpsd.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a $(CC) $(CFLAGS) -DDIGITEST -o $@ $^ $(LDFLAGS) ./dtest @@ -717,6 +717,15 @@ xidtest : xid.c textcolor.o misc.a rm xidtest +# Unit Test for DTMF encode/decode. + +.PHONY: dtmftest +dtmftest : dtmf.c textcolor.o + $(CC) $(CFLAGS) -DDTMF_TEST -o $@ $^ $(LDFLAGS) + ./dtmftest + rm dtmftest + + # ----------------------------- Manual tests and experiments --------------------------- diff --git a/Makefile.macosx b/Makefile.macosx index 2b2c77f..0e95555 100644 --- a/Makefile.macosx +++ b/Makefile.macosx @@ -437,7 +437,7 @@ log2gpx : log2gpx.c # Test application to generate sound. -gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c textcolor.c dsp.c +gen_packets : gen_packets.c ax25_pad.c hdlc_send.c fcs_calc.c gen_tone.c morse.c dtmf.c textcolor.c dsp.c $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) -lm demod.o : tune.h diff --git a/decode_aprs.c b/decode_aprs.c index 8f9c013..b1643cf 100644 --- a/decode_aprs.c +++ b/decode_aprs.c @@ -111,7 +111,7 @@ static void aprs_station_capabilities (decode_aprs_t *A, char *, int); static void aprs_status_report (decode_aprs_t *A, char *, int); static void aprs_general_query (decode_aprs_t *A, char *, int, int quiet); static void aprs_directed_station_query (decode_aprs_t *A, char *addressee, char *query, int quiet); -static void aprs_telemetry (decode_aprs_t *A, char *, int, int quiet); +static void aprs_telemetry (decode_aprs_t *A, char *info, int info_len, int quiet); static void aprs_raw_touch_tone (decode_aprs_t *A, char *, int); static void aprs_morse_code (decode_aprs_t *A, char *, int); static void aprs_positionless_weather_report (decode_aprs_t *A, unsigned char *, int); @@ -3076,7 +3076,7 @@ double get_latitude_8 (char *p, int quiet) return (G_UNKNOWN); } - if (plat->minn[0] >= '0' || plat->minn[0] <= '5') + if (plat->minn[0] >= '0' && plat->minn[0] <= '5') result += ((plat->minn[0]) - '0') * (10. / 60.); else if (plat->minn[0] == ' ') ; @@ -3239,7 +3239,7 @@ double get_longitude_9 (char *p, int quiet) return (G_UNKNOWN); } - if (plon->minn[0] >= '0' || plon->minn[0] <= '5') + if (plon->minn[0] >= '0' && plon->minn[0] <= '5') result += ((plon->minn[0]) - '0') * (10. / 60.); else if (plon->minn[0] == ' ') ; @@ -3683,7 +3683,15 @@ static int data_extension_comment (decode_aprs_t *A, char *pdext) * *------------------------------------------------------------------*/ -#define MAX_TOCALLS 150 +// If I was more ambitious, this would dynamically allocate enough +// storage based on the file contents. Just stick in a constant for +// now. This takes an insignificant amount of space and +// I don't anticipate tocalls.txt growing that quickly. +// Version 1.4 - add message if too small instead of silently ignoring the rest. + +// Dec. 2016 tocalls.txt has 153 destination addresses. + +#define MAX_TOCALLS 200 static struct tocalls_s { unsigned char len; @@ -3780,7 +3788,7 @@ static void decode_tocall (decode_aprs_t *A, char *dest) if (strlen(tocalls[num_tocalls].prefix) > 2) { tocalls[num_tocalls].description = strdup(stuff+14); tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix); - // dw_printf("debug: %d '%s' -> '%s'\n", tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description); + // dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description); num_tocalls++; } @@ -3804,11 +3812,15 @@ static void decode_tocall (decode_aprs_t *A, char *dest) if (strlen(tocalls[num_tocalls].prefix) > 2) { tocalls[num_tocalls].description = strdup(stuff+14); tocalls[num_tocalls].len = strlen(tocalls[num_tocalls].prefix); - // dw_printf("debug: %d '%s' -> '%s'\n", tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description); + // dw_printf("debug %d: %d '%s' -> '%s'\n", num_tocalls, tocalls[num_tocalls].len, tocalls[num_tocalls].prefix, tocalls[num_tocalls].description); num_tocalls++; } } + if (num_tocalls == MAX_TOCALLS) { // oops. might have discarded some. + text_color_set(DW_COLOR_ERROR); + dw_printf("MAX_TOCALLS needs to be larger than %d to handle contents of 'tocalls.txt'.\n", MAX_TOCALLS); + } } fclose(fp); @@ -3832,9 +3844,12 @@ static void decode_tocall (decode_aprs_t *A, char *dest) dw_printf("System types in the destination field will not be decoded.\n"); } } - first_time = 0; + + //for (n=0; n '%s'\n", n, tocalls[n].len, tocalls[n].prefix, tocalls[n].description); + //} } diff --git a/direwolf.c b/direwolf.c index 5debb08..8a43b7d 100644 --- a/direwolf.c +++ b/direwolf.c @@ -107,6 +107,7 @@ #include "aprs_tt.h" #include "tt_user.h" #include "igate.h" +#include "pfilter.h" #include "symbols.h" #include "dwgps.h" #include "waypoint.h" @@ -164,6 +165,8 @@ static struct tt_config_s tt_config; //struct digi_config_s digi_config; //struct cdigi_config_s cdigi_config; +static const int audio_amplitude = 100; /* % of audio sample range. */ + /* This translates to +-32k for 16 bit samples. */ static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in hexadecimal. */ static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */ @@ -254,7 +257,7 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "D", __DATE__); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "E", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); #if defined(ENABLE_GPSD) || defined(USE_HAMLIB) @@ -693,7 +696,7 @@ int main (int argc, char *argv[]) /* * Initialize the touch tone decoder & APRStt gateway. */ - dtmf_init (&audio_config); + dtmf_init (&audio_config, audio_amplitude); aprs_tt_init (&tt_config); tt_user_init (&audio_config, &tt_config); @@ -702,8 +705,8 @@ int main (int argc, char *argv[]) * Note: This is not the same as a volume control you would see on the screen. * It is the range of the digital sound representation. */ - gen_tone_init (&audio_config, 100, 0); - morse_init (&audio_config, 100); + gen_tone_init (&audio_config, audio_amplitude, 0); + morse_init (&audio_config, audio_amplitude); assert (audio_config.adev[0].bits_per_sample == 8 || audio_config.adev[0].bits_per_sample == 16); assert (audio_config.adev[0].num_channels == 1 || audio_config.adev[0].num_channels == 2); @@ -746,6 +749,7 @@ int main (int argc, char *argv[]) digipeater_init (&audio_config, &digi_config); igate_init (&audio_config, &igate_config, &digi_config, d_i_opt); cdigipeater_init (&audio_config, &cdigi_config); + //FIXME//pfilter_init (&igate_config, 0); ax25_link_init (&misc_config); /* @@ -1015,16 +1019,21 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev } -/* Decode the contents of APRS frames and display in human-readable form. */ -/* Suppress decoding if "-q d" option used. */ - +/* + * Decode the contents of UI frames and display in human-readable form. + * Could be APRS or anything random for old fashioned packet beacons. + * + * Suppress printed decoding if "-q d" option used. + */ if (ax25_is_aprs(pp)) { decode_aprs_t A; - decode_aprs (&A, pp, 0); + // we still want to decode it for logging and other processing. + // Just be quiet about errors if "-qd" is set. + decode_aprs (&A, pp, q_d_opt); if ( ! q_d_opt ) { @@ -1046,9 +1055,10 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev // temp experiment. //log_rr_bits (&A, pp); - // Add to list of stations heard. + // Add to list of stations heard over the radio. mheard_save (chan, &A, pp, alevel, retries); + //FIXME//mheard_save_rf (chan, &A, pp, alevel, retries); // Convert to NMEA waypoint sentence if we have a location. diff --git a/dtmf.c b/dtmf.c index c4270f7..2f3709c 100644 --- a/dtmf.c +++ b/dtmf.c @@ -5,7 +5,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2013, 2014, 2015 John Langner, WB2OSZ +// Copyright (C) 2013, 2014, 2015, 2016 John Langner, WB2OSZ // // 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 @@ -40,9 +40,12 @@ #include #include #include +#include #include "dtmf.h" #include "hdlc_rec.h" // for dcd_change +#include "textcolor.h" +#include "gen_tone.h" @@ -78,8 +81,11 @@ static struct dd_s { /* Separate for each audio channel. */ } dd[MAX_CHANS]; +static int s_amplitude = 100; // range of 0 .. 100 +static void push_button (int chan, char button, int ms); + /*------------------------------------------------------------------ * @@ -99,17 +105,23 @@ static struct dd_s { /* Separate for each audio channel. */ * In version 1.2, we can have multiple soundcards * with potentially different sample rates. * + * amp - Signal amplitude, for transmit, on scale of 0 .. 100. + * + * 100 will produce maximum amplitude of +-32k samples. + * * Returns: None. * *----------------------------------------------------------------*/ -void dtmf_init (struct audio_s *p_audio_config) +void dtmf_init (struct audio_s *p_audio_config, int amp) { int j; /* Loop over all tones frequencies. */ int c; /* Loop over all audio channels. */ + s_amplitude = amp; + /* * Pick a suitable processing block size. * Larger = narrower bandwidth, slower response. @@ -118,15 +130,16 @@ void dtmf_init (struct audio_s *p_audio_config) for (c=0; csample_rate = p_audio_config->adev[a].samples_per_sec; + if (p_audio_config->achan[c].dtmf_decode != DTMF_DECODE_OFF) { #if DEBUG + text_color_set(DW_COLOR_DEBUG); dw_printf ("channel %d:\n", c); #endif - - D->sample_rate = p_audio_config->adev[a].samples_per_sec; D->block_size = (205 * D->sample_rate) / 8000; - #if DEBUG dw_printf (" freq k coef \n"); @@ -142,9 +155,9 @@ void dtmf_init (struct audio_s *p_audio_config) k = D->block_size * (float)(dtmf_tones[j]) / (float)(D->sample_rate); - D->coef[j] = 2 * cos(2 * M_PI * (float)k / (float)(D->block_size)); + D->coef[j] = 2.0f * cosf(2.0f * (float)M_PI * (float)k / (float)(D->block_size)); - assert (D->coef[j] > 0 && D->coef[j] < 2.0); + assert (D->coef[j] > 0.0f && D->coef[j] < 2.0f); #if DEBUG dw_printf ("%8d %5.1f %8.5f \n", dtmf_tones[j], k, D->coef[j]); #endif @@ -240,7 +253,7 @@ char dtmf_sample (int c, float input) */ -#define THRESHOLD 1.74 +#define THRESHOLD 1.74f if (output[0] > THRESHOLD * ( output[1] + output[2] + output[3])) row = 0; else if (output[1] > THRESHOLD * (output[0] + output[2] + output[3])) row = 1; @@ -273,7 +286,9 @@ char dtmf_sample (int c, float input) // Update Data Carrier Detect Indicator. +#ifndef DTMF_TEST dcd_change (c, MAX_SUBCHANS, 0, decoded != ' '); +#endif /* Reset timeout timer. */ if (decoded != ' ') { @@ -312,6 +327,182 @@ char dtmf_sample (int c, float input) } + +/*------------------------------------------------------------------- + * + * Name: dtmf_send + * + * Purpose: Generate DTMF tones from text string. + * + * Inputs: chan - Radio channel number. + * str - Character string to send. 0-9, A-D, *, # + * speed - Number of tones per second. Range 1 to 10. + * txdelay - Delay (ms) from PTT to start. + * txtail - Delay (ms) from end to PTT off. + * + * Returns: Total number of milliseconds to activate PTT. + * This includes delays before the first tone + * and after the last to avoid chopping off part of it. + * + * Description: xmit_thread calls this instead of the usual hdlc_send + * when we have a special packet that means send DTMF. + * + *--------------------------------------------------------------------*/ + +int dtmf_send (int chan, char *str, int speed, int txdelay, int txtail) +{ + char *p; + int len_ms; // Length of tone or gap between. + + len_ms = (int) ( ( 500.0f / (float)speed ) + 0.5f); + + push_button (chan, ' ', txdelay); + + for (p = str; *p != '\0'; p++) { + + push_button (chan, *p, len_ms); + push_button (chan, ' ', len_ms); + } + + push_button (chan, ' ', txtail); + +#ifndef DTMF_TEST + audio_flush(ACHAN2ADEV(chan)); +#endif + return (txdelay + + (int) (1000.0f * (float)strlen(str) / (float)speed + 0.5f) + + txtail); + +} /* end dtmf_send */ + + + +/*------------------------------------------------------------------ + * + * Name: push_button + * + * Purpose: Generate DTMF tone for a button push. + * + * Inputs: chan - Radio channel number. + * + * button - One of 0-9, A-D, *, #. Others result in silence. + * '?' is a special case used only for unit testing. + * + * ms - Duration in milliseconds. + * Use 50 ms for tone and 50 ms of silence for max rate of 10 per second. + * + * Outputs: Audio is sent to radio. + * + *----------------------------------------------------------------*/ + +static void push_button (int chan, char button, int ms) +{ + float phasea = 0; + float phaseb = 0; + float fa = 0; + float fb = 0; + int i; + float dtmf; // Audio. Sum of two sine waves. +#if DTMF_TEST + char x; + static char result[100]; + static int result_len = 0; +#endif + + switch (button) { + case '1': fa = dtmf_tones[0]; fb = dtmf_tones[4]; break; + case '2': fa = dtmf_tones[0]; fb = dtmf_tones[5]; break; + case '3': fa = dtmf_tones[0]; fb = dtmf_tones[6]; break; + case 'a': + case 'A': fa = dtmf_tones[0]; fb = dtmf_tones[7]; break; + + case '4': fa = dtmf_tones[1]; fb = dtmf_tones[4]; break; + case '5': fa = dtmf_tones[1]; fb = dtmf_tones[5]; break; + case '6': fa = dtmf_tones[1]; fb = dtmf_tones[6]; break; + case 'b': + case 'B': fa = dtmf_tones[1]; fb = dtmf_tones[7]; break; + + case '7': fa = dtmf_tones[2]; fb = dtmf_tones[4]; break; + case '8': fa = dtmf_tones[2]; fb = dtmf_tones[5]; break; + case '9': fa = dtmf_tones[2]; fb = dtmf_tones[6]; break; + case 'c': + case 'C': fa = dtmf_tones[2]; fb = dtmf_tones[7]; break; + + case '*': fa = dtmf_tones[3]; fb = dtmf_tones[4]; break; + case '0': fa = dtmf_tones[3]; fb = dtmf_tones[5]; break; + case '#': fa = dtmf_tones[3]; fb = dtmf_tones[6]; break; + case 'd': + case 'D': fa = dtmf_tones[3]; fb = dtmf_tones[7]; break; + +#if DTMF_TEST + + case '?': /* check result */ + + if (strcmp(result, "123A456B789C*0#D123$789$") == 0) { + text_color_set(DW_COLOR_REC); + dw_printf ("\nSuccess!\n"); + } + else if (strcmp(result, "123A456B789C*0#D123789") == 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\n * Time-out failed, otherwise OK *\n"); + dw_printf ("\"%s\"\n", result); + exit (EXIT_FAILURE); + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\n *** TEST FAILED ***\n"); + dw_printf ("\"%s\"\n", result); + exit (EXIT_FAILURE); + } + break; +#endif + } + + //dw_printf ("push_button (%d, '%c', %d), fa=%.0f, fb=%.0f. %d samples\n", chan, button, ms, fa, fb, (ms*dd[chan].sample_rate)/1000); + + for (i = 0; i < (ms*dd[chan].sample_rate)/1000; i++) { + + // This could be more efficient with a precomputed sine wave table + // but I'm not that worried about it. + // With a Raspberry Pi, model 2, default 1200 receiving takes about 14% of one CPU core. + // When transmitting tones, it briefly shoots up to about 33%. + + if (fa > 0 && fb > 0) { + dtmf = sinf(phasea) + sinf(phaseb); + phasea += 2.0f * (float)M_PI * fa / dd[chan].sample_rate; + phaseb += 2.0f * (float)M_PI * fb / dd[chan].sample_rate; + } + else { + dtmf = 0; + } + +#if DTMF_TEST + + /* Make sure it is insensitive to signal amplitude. */ + /* (Uncomment each of below when testing.) */ + + x = dtmf_sample (0, dtmf); + //x = dtmf_sample (0, dtmf * 1000); + //x = dtmf_sample (0, dtmf * 0.001); + + if (x != ' ' && x != '.') { + result[result_len] = x; + result_len++; + result[result_len] = '\0'; + } +#else + + // 'dtmf' can be in range of +-2.0 because it is sum of two sine waves. + // Amplitude of 100 would use full +-32k range. + + int sam = (int)(dtmf * 16383.0f * (float)s_amplitude / 100.0f); + gen_tone_put_sample (chan, ACHAN2ADEV(chan), sam); + +#endif + } +} + + /*------------------------------------------------------------------ * * Name: main @@ -319,142 +510,79 @@ char dtmf_sample (int c, float input) * Purpose: Unit test for functions above. * * Usage: rm a.exe ; gcc -DDTMF_TEST dtmf.c textcolor.c ; ./a.exe + * or + * make dtmftest * *----------------------------------------------------------------*/ - #if DTMF_TEST -push_button (char button, int ms) -{ - static float phasea = 0; - static float phaseb = 0; - float fa, fb; - int i; - float input; - char x; - static char result[100]; - static int result_len = 0; - int c = 0; // fake channel number. - - - switch (button) { - case '1': fa = dtmf_tones[0]; fb = dtmf_tones[4]; break; - case '2': fa = dtmf_tones[0]; fb = dtmf_tones[5]; break; - case '3': fa = dtmf_tones[0]; fb = dtmf_tones[6]; break; - case 'A': fa = dtmf_tones[0]; fb = dtmf_tones[7]; break; - case '4': fa = dtmf_tones[1]; fb = dtmf_tones[4]; break; - case '5': fa = dtmf_tones[1]; fb = dtmf_tones[5]; break; - case '6': fa = dtmf_tones[1]; fb = dtmf_tones[6]; break; - case 'B': fa = dtmf_tones[1]; fb = dtmf_tones[7]; break; - case '7': fa = dtmf_tones[2]; fb = dtmf_tones[4]; break; - case '8': fa = dtmf_tones[2]; fb = dtmf_tones[5]; break; - case '9': fa = dtmf_tones[2]; fb = dtmf_tones[6]; break; - case 'C': fa = dtmf_tones[2]; fb = dtmf_tones[7]; break; - case '*': fa = dtmf_tones[3]; fb = dtmf_tones[4]; break; - case '0': fa = dtmf_tones[3]; fb = dtmf_tones[5]; break; - case '#': fa = dtmf_tones[3]; fb = dtmf_tones[6]; break; - case 'D': fa = dtmf_tones[3]; fb = dtmf_tones[7]; break; - case '?': - -// TODO: why are timeouts failing. Do we care? - - if (strcmp(result, "123A456B789C*0#D123$789$") == 0) { - dw_printf ("\nSuccess!\n"); - } - else if (strcmp(result, "123A456B789C*0#D123789") == 0) { - dw_printf ("\n * Time-out failed, otherwise OK *\n"); - dw_printf ("\"%s\"\n", result); - } - else { - dw_printf ("\n *** TEST FAILED ***\n"); - dw_printf ("\"%s\"\n", result); - } - break; - - default: fa = 0; fb = 0; - } - - for (i = 0; i < (ms*dd[c].sample_rate)/1000; i++) { - - input = sin(phasea) + sin(phaseb); - phasea += 2 * M_PI * fa / dd[c].sample_rate; - phaseb += 2 * M_PI * fb / dd[c].sample_rate; - - /* Make sure it is insensitive to signal amplitude. */ - - x = dtmf_sample (0, input); - //x = dtmf_sample (0, input * 1000); - //x = dtmf_sample (0, input * 0.001); - - if (x != ' ' && x != '.') { - result[result_len] = x; - result_len++; - result[result_len] = '\0'; - } - } -} - static struct audio_s my_audio_config; -main () +int main () { + int c = 0; // radio channel. memset (&my_audio_config, 0, sizeof(my_audio_config)); - my_audio_config.adev[0].defined = 1; - my_audio_config.adev[0].samples_per_sec = 44100; - my_audio_config.achan[0].valid = 1; - my_audio_config.achan[0].dtmf_decode = DTMF_DECODE_ON; + my_audio_config.adev[ACHAN2ADEV(c)].defined = 1; + my_audio_config.adev[ACHAN2ADEV(c)].samples_per_sec = 44100; + my_audio_config.achan[c].valid = 1; + my_audio_config.achan[c].dtmf_decode = DTMF_DECODE_ON; - dtmf_init(&my_audio_config); + dtmf_init(&my_audio_config, 50); + text_color_set(DW_COLOR_INFO); dw_printf ("\nFirst, check all button tone pairs. \n\n"); /* Max auto dialing rate is 10 per second. */ - push_button ('1', 50); push_button (' ', 50); - push_button ('2', 50); push_button (' ', 50); - push_button ('3', 50); push_button (' ', 50); - push_button ('A', 50); push_button (' ', 50); + push_button (c, '1', 50); push_button (c, ' ', 50); + push_button (c, '2', 50); push_button (c, ' ', 50); + push_button (c, '3', 50); push_button (c, ' ', 50); + push_button (c, 'A', 50); push_button (c, ' ', 50); - push_button ('4', 50); push_button (' ', 50); - push_button ('5', 50); push_button (' ', 50); - push_button ('6', 50); push_button (' ', 50); - push_button ('B', 50); push_button (' ', 50); + push_button (c, '4', 50); push_button (c, ' ', 50); + push_button (c, '5', 50); push_button (c, ' ', 50); + push_button (c, '6', 50); push_button (c, ' ', 50); + push_button (c, 'B', 50); push_button (c, ' ', 50); - push_button ('7', 50); push_button (' ', 50); - push_button ('8', 50); push_button (' ', 50); - push_button ('9', 50); push_button (' ', 50); - push_button ('C', 50); push_button (' ', 50); + push_button (c, '7', 50); push_button (c, ' ', 50); + push_button (c, '8', 50); push_button (c, ' ', 50); + push_button (c, '9', 50); push_button (c, ' ', 50); + push_button (c, 'C', 50); push_button (c, ' ', 50); - push_button ('*', 50); push_button (' ', 50); - push_button ('0', 50); push_button (' ', 50); - push_button ('#', 50); push_button (' ', 50); - push_button ('D', 50); push_button (' ', 50); + push_button (c, '*', 50); push_button (c, ' ', 50); + push_button (c, '0', 50); push_button (c, ' ', 50); + push_button (c, '#', 50); push_button (c, ' ', 50); + push_button (c, 'D', 50); push_button (c, ' ', 50); + text_color_set(DW_COLOR_INFO); dw_printf ("\nShould reject very short pulses.\n\n"); - push_button ('1', 20); push_button (' ', 50); - push_button ('1', 20); push_button (' ', 50); - push_button ('1', 20); push_button (' ', 50); - push_button ('1', 20); push_button (' ', 50); - push_button ('1', 20); push_button (' ', 50); + push_button (c, '1', 20); push_button (c, ' ', 50); + push_button (c, '1', 20); push_button (c, ' ', 50); + push_button (c, '1', 20); push_button (c, ' ', 50); + push_button (c, '1', 20); push_button (c, ' ', 50); + push_button (c, '1', 20); push_button (c, ' ', 50); + text_color_set(DW_COLOR_INFO); dw_printf ("\nTest timeout after inactivity.\n\n"); /* For this test we use 1 second. */ /* In practice, it will probably more like 5. */ - push_button ('1', 250); push_button (' ', 500); - push_button ('2', 250); push_button (' ', 500); - push_button ('3', 250); push_button (' ', 1200); + push_button (c, '1', 250); push_button (c, ' ', 500); + push_button (c, '2', 250); push_button (c, ' ', 500); + push_button (c, '3', 250); push_button (c, ' ', 1200); - push_button ('7', 250); push_button (' ', 500); - push_button ('8', 250); push_button (' ', 500); - push_button ('9', 250); push_button (' ', 1200); + push_button (c, '7', 250); push_button (c, ' ', 500); + push_button (c, '8', 250); push_button (c, ' ', 500); + push_button (c, '9', 250); push_button (c, ' ', 1200); /* Check for expected results. */ - push_button ('?', 0); + push_button (c, '?', 0); + + exit (EXIT_SUCCESS); } /* end main */ diff --git a/dtmf.h b/dtmf.h index dea09df..c1b52b9 100644 --- a/dtmf.h +++ b/dtmf.h @@ -3,10 +3,12 @@ #include "audio.h" -void dtmf_init (struct audio_s *p_audio_config); +void dtmf_init (struct audio_s *p_audio_config, int amp); char dtmf_sample (int c, float input); +int dtmf_send (int chan, char *str, int speed, int txdelay, int txtail); + /* end dtmf.h */ diff --git a/gen_packets.c b/gen_packets.c index f6fbcc4..c239010 100644 --- a/gen_packets.c +++ b/gen_packets.c @@ -74,6 +74,7 @@ #include "gen_tone.h" #include "textcolor.h" #include "morse.h" +#include "dtmf.h" /* Own random number generator so we can get */ @@ -108,6 +109,8 @@ static void send_packet (char *str) if (g_morse_wpm > 0) { + // TODO: Why not use the destination field instead of command line option? + morse_send (0, str, g_morse_wpm, 100, 100); } else { @@ -404,6 +407,7 @@ int main(int argc, char **argv) case 'M': /* -M for morse code speed */ //TODO: document this. +// Why not base it on the destination field instead? g_morse_wpm = atoi(optarg); text_color_set(DW_COLOR_INFO); @@ -465,6 +469,7 @@ int main(int argc, char **argv) gen_tone_init (&modem, amplitude/2, 1); morse_init (&modem, amplitude/2); + dtmf_init (&modem, amplitude/2); assert (modem.adev[0].bits_per_sample == 8 || modem.adev[0].bits_per_sample == 16); @@ -956,3 +961,11 @@ static int audio_file_close (void) } /* end audio_close */ + +// To keep dtmf.c happy. + +#include "hdlc_rec.h" // for dcd_change + +void dcd_change (int chan, int subchan, int slice, int state) +{ +} \ No newline at end of file