mirror of https://github.com/wb2osz/direwolf.git
PTT was being turned off too soon when sending Morse code.
Add capability to send DTMF tones. -qd option now suppresses complaints about telemetry packets. Handle more than 150 destinations in tocalls.txt.
This commit is contained in:
parent
7a88785fad
commit
200f669bbc
|
@ -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 ---------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<num_tocalls; n++) {
|
||||
// dw_printf("sorted %d: %d '%s' -> '%s'\n", n, tocalls[n].len, tocalls[n].prefix, tocalls[n].description);
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
|
28
direwolf.c
28
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.
|
||||
|
|
354
dtmf.c
354
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 <stdio.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#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; c<MAX_CHANS; c++) {
|
||||
struct dd_s *D = &(dd[c]);
|
||||
int a = ACHAN2ADEV(c);
|
||||
|
||||
D->sample_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 */
|
||||
|
||||
|
|
4
dtmf.h
4
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 */
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
Loading…
Reference in New Issue