Changes to the signaling tone detector to detect concurrent 2400Hz + 2600Hz

tones. This passes voice immunity and other key tests, but it bounces a bit
when transitions like 2400 -> 2400+2600 -> 2600 occur. Transitions between
tone off and tone on are clean.
This commit is contained in:
Steve Underwood 2010-06-06 22:24:20 +08:00
parent c807502773
commit bc13e944c6
3 changed files with 435 additions and 184 deletions

View File

@ -23,7 +23,7 @@
* License along with this program; if not, write to the Free Software * License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *
* $Id: sig_tone.c,v 1.39 2010/03/11 14:22:30 steveu Exp $ * $Id: sig_tone.c,v 1.40 2010/05/12 15:32:41 steveu Exp $
*/ */
/*! \file */ /*! \file */
@ -201,16 +201,25 @@ static const sig_tone_descriptor_t sig_tones[3] =
} }
}; };
static const int tone_present_bits[2] = static const int tone_present_bits[3] =
{ {
SIG_TONE_1_PRESENT, SIG_TONE_1_PRESENT,
SIG_TONE_2_PRESENT SIG_TONE_2_PRESENT,
SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT
}; };
static const int tone_change_bits[2] = static const int tone_change_bits[3] =
{ {
SIG_TONE_1_CHANGE, SIG_TONE_1_CHANGE,
SIG_TONE_2_CHANGE SIG_TONE_2_CHANGE,
SIG_TONE_1_CHANGE | SIG_TONE_2_CHANGE
};
static const int coeff_sets[3] =
{
0,
1,
0
}; };
SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len) SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len)
@ -273,7 +282,7 @@ SPAN_DECLARE(int) sig_tone_tx(sig_tone_tx_state_t *s, int16_t amp[], int len)
for (j = i; j < i + n; j++) for (j = i; j < i + n; j++)
{ {
tone = dds_mod(&(s->phase_acc[k]), s->phase_rate[k], s->tone_scaling[k][high_low], 0); tone = dds_mod(&(s->phase_acc[k]), s->phase_rate[k], s->tone_scaling[k][high_low], 0);
amp[j] = saturate(amp[j] + tone); amp[j] = saturated_add16(amp[j], tone);
} }
/*endfor*/ /*endfor*/
} }
@ -300,6 +309,11 @@ SPAN_DECLARE(void) sig_tone_tx_set_mode(sig_tone_tx_state_t *s, int mode, int du
if (new_tones && old_tones != new_tones) if (new_tones && old_tones != new_tones)
s->high_low_timer = s->desc->high_low_timeout; s->high_low_timer = s->desc->high_low_timeout;
/*endif*/ /*endif*/
/* If a tone is being turned on, let's start the phase from zero */
if ((mode & SIG_TONE_1_PRESENT) && !(s->current_tx_tone & SIG_TONE_1_PRESENT))
s->phase_acc[0] = 0;
if ((mode & SIG_TONE_2_PRESENT) && !(s->current_tx_tone & SIG_TONE_2_PRESENT))
s->phase_acc[1] = 0;
s->current_tx_tone = mode; s->current_tx_tone = mode;
s->current_tx_timeout = duration; s->current_tx_timeout = duration;
} }
@ -352,63 +366,78 @@ SPAN_DECLARE(int) sig_tone_tx_free(sig_tone_tx_state_t *s)
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
int nnn = 0;
SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len) SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
{ {
#if defined(SPANDSP_USE_FIXED_POINT) #if defined(SPANDSP_USE_FIXED_POINT)
int16_t x; int16_t x;
int32_t v; int32_t v;
int16_t notched_signal[2]; int16_t notched_signal[3];
int16_t bandpass_signal; int16_t bandpass_signal;
int16_t signal;
#else #else
float x; float x;
float v; float v;
float notched_signal[2]; float notched_signal[3];
float bandpass_signal; float bandpass_signal;
float signal;
#endif #endif
int i; int i;
int j; int j;
int32_t notch_power[2]; int k;
int l;
int m;
int32_t notch_power[3];
int32_t flat_power; int32_t flat_power;
int immediate;
l = s->desc->tones;
if (l == 2)
l = 3;
notch_power[1] =
notch_power[2] = INT32_MAX;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
if (s->signalling_state_duration < INT_MAX) if (s->signalling_state_duration < INT_MAX)
s->signalling_state_duration++; s->signalling_state_duration++;
/*endif*/ /*endif*/
for (j = 0; j < s->desc->tones; j++) signal = amp[i];
for (j = 0; j < l; j++)
{ {
k = coeff_sets[j];
/* The notch filter is two cascaded biquads. */ /* The notch filter is two cascaded biquads. */
#if defined(SPANDSP_USE_FIXED_POINT) #if defined(SPANDSP_USE_FIXED_POINT)
v = ((int32_t) amp[i]*s->desc->notch[j]->a1[0]) v = ((int32_t) signal*s->desc->notch[k]->a1[0])
+ ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[j]->b1[1]) + ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[k]->b1[1])
+ ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[j]->b1[2]); + ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[k]->b1[2]);
x = v >> 15; x = v >> 15;
v += ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[j]->a1[1]) v += ((int32_t) s->tone[j].notch_z1[0]*s->desc->notch[k]->a1[1])
+ ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[j]->a1[2]); + ((int32_t) s->tone[j].notch_z1[1]*s->desc->notch[k]->a1[2]);
s->tone[j].notch_z1[1] = s->tone[j].notch_z1[0]; s->tone[j].notch_z1[1] = s->tone[j].notch_z1[0];
s->tone[j].notch_z1[0] = x; s->tone[j].notch_z1[0] = x;
v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[j]->b2[1]) v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[k]->b2[1])
+ ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[j]->b2[2]); + ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[k]->b2[2]);
x = v >> 15; x = v >> 15;
v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[j]->a2[1]) v += ((int32_t) s->tone[j].notch_z2[0]*s->desc->notch[k]->a2[1])
+ ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[j]->a2[2]); + ((int32_t) s->tone[j].notch_z2[1]*s->desc->notch[k]->a2[2]);
s->tone[j].notch_z2[1] = s->tone[j].notch_z2[0]; s->tone[j].notch_z2[1] = s->tone[j].notch_z2[0];
s->tone[j].notch_z2[0] = x; s->tone[j].notch_z2[0] = x;
notched_signal[j] = v >> s->desc->notch[j]->postscale; notched_signal[j] = v >> s->desc->notch[k]->postscale;
#else #else
v = amp[i]*s->desc->notch[j]->a1[0] v = signal*s->desc->notch[k]->a1[0]
+ s->tone[j].notch_z1[0]*s->desc->notch[j]->b1[1] + s->tone[j].notch_z1[0]*s->desc->notch[k]->b1[1]
+ s->tone[j].notch_z1[1]*s->desc->notch[j]->b1[2]; + s->tone[j].notch_z1[1]*s->desc->notch[k]->b1[2];
x = v; x = v;
v += s->tone[j].notch_z1[0]*s->desc->notch[j]->a1[1] v += s->tone[j].notch_z1[0]*s->desc->notch[k]->a1[1]
+ s->tone[j].notch_z1[1]*s->desc->notch[j]->a1[2]; + s->tone[j].notch_z1[1]*s->desc->notch[k]->a1[2];
s->tone[j].notch_z1[1] = s->tone[j].notch_z1[0]; s->tone[j].notch_z1[1] = s->tone[j].notch_z1[0];
s->tone[j].notch_z1[0] = x; s->tone[j].notch_z1[0] = x;
v += s->tone[j].notch_z2[0]*s->desc->notch[j]->b2[1] v += s->tone[j].notch_z2[0]*s->desc->notch[k]->b2[1]
+ s->tone[j].notch_z2[1]*s->desc->notch[j]->b2[2]; + s->tone[j].notch_z2[1]*s->desc->notch[k]->b2[2];
x = v; x = v;
v += s->tone[j].notch_z2[0]*s->desc->notch[j]->a2[1] v += s->tone[j].notch_z2[0]*s->desc->notch[k]->a2[1]
+ s->tone[j].notch_z2[1]*s->desc->notch[j]->a2[2]; + s->tone[j].notch_z2[1]*s->desc->notch[k]->a2[2];
s->tone[j].notch_z2[1] = s->tone[j].notch_z2[0]; s->tone[j].notch_z2[1] = s->tone[j].notch_z2[0];
s->tone[j].notch_z2[0] = x; s->tone[j].notch_z2[0] = x;
notched_signal[j] = v; notched_signal[j] = v;
@ -417,9 +446,10 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
this isn't used in low tone detect mode, but we must keep the this isn't used in low tone detect mode, but we must keep the
power measurement rolling along. */ power measurement rolling along. */
notch_power[j] = power_meter_update(&s->tone[j].power, notched_signal[j]); notch_power[j] = power_meter_update(&s->tone[j].power, notched_signal[j]);
if (j == 1)
signal = notched_signal[j];
} }
if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT)))
if (s->tone[0].tone_present || s->tone[1].tone_present)
{ {
if (s->flat_mode_timeout && --s->flat_mode_timeout == 0) if (s->flat_mode_timeout && --s->flat_mode_timeout == 0)
s->flat_mode = TRUE; s->flat_mode = TRUE;
@ -432,8 +462,10 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
} }
/*endif*/ /*endif*/
immediate = -1;
if (s->flat_mode) if (s->flat_mode)
{ {
//printf("Flat mode %d %d\n", s->flat_mode_timeout, s->desc->sharp_flat_timeout);
/* Flat mode */ /* Flat mode */
bandpass_signal = amp[i]; bandpass_signal = amp[i];
if (s->desc->flat) if (s->desc->flat)
@ -464,10 +496,9 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
flat_power = power_meter_update(&s->flat_power, bandpass_signal); flat_power = power_meter_update(&s->flat_power, bandpass_signal);
/* For the flat receiver we use a simple power threshold! */ /* For the flat receiver we use a simple power threshold! */
if (s->tone[0].tone_present) if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT)))
{ {
s->tone[0].tone_present = (flat_power > s->flat_detection_threshold); if (flat_power < s->flat_detection_threshold)
if (!s->tone[0].tone_present)
{ {
s->signalling_state &= ~tone_present_bits[0]; s->signalling_state &= ~tone_present_bits[0];
s->signalling_state |= tone_change_bits[0]; s->signalling_state |= tone_change_bits[0];
@ -476,18 +507,15 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
} }
else else
{ {
s->tone[0].tone_present = (flat_power > s->flat_detection_threshold); if (flat_power > s->flat_detection_threshold)
if (s->tone[0].tone_present)
{
s->signalling_state |= (tone_present_bits[0] | tone_change_bits[0]); s->signalling_state |= (tone_present_bits[0] | tone_change_bits[0]);
}
/*endif*/ /*endif*/
} }
/*endif*/ /*endif*/
/* Notch insertion logic */ /* Notch insertion logic */
/* tone_present and tone_on are equivalent in flat mode */ /* tone_present and tone_on are equivalent in flat mode */
if (s->tone[0].tone_present) if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT)))
{ {
s->notch_insertion_timeout = s->desc->notch_lag_time; s->notch_insertion_timeout = s->desc->notch_lag_time;
} }
@ -504,64 +532,68 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
/* Sharp mode */ /* Sharp mode */
flat_power = power_meter_update(&s->flat_power, amp[i]); flat_power = power_meter_update(&s->flat_power, amp[i]);
for (j = 0; j < s->desc->tones; j++) /* Persistence checking and notch insertion logic */
if (flat_power >= s->sharp_detection_threshold)
{ {
/* Persistence checking and notch insertion logic */ /* Which is the better of the single tone responses? */
if (s->tone[j].tone_present) m = (notch_power[0] < notch_power[1]) ? 0 : 1;
/* Single tone has precedence. If the better one fails to detect, try
for a dual tone signal. */
if ((notch_power[m] >> 6)*s->detection_ratio < (flat_power >> 6))
immediate = m;
else if ((notch_power[2] >> 6)*s->detection_ratio < (flat_power >> 7))
immediate = 2;
}
//printf("Immediate = %d %d %d\n", immediate, s->signalling_state, s->tone_persistence_timeout);
if ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT)))
{
if (immediate != s->current_notch_filter)
{ {
if (flat_power < s->sharp_detection_threshold /* No tone is detected this sample */
|| if (--s->tone_persistence_timeout == 0)
(notch_power[j] >> 6)*s->detection_ratio > (flat_power >> 6))
{ {
/* Tone is not detected this sample */ /* Tone off is confirmed */
if (--s->tone[j].tone_persistence_timeout == 0) s->tone_persistence_timeout = s->desc->tone_on_check_time;
{ s->signalling_state |= ((s->signalling_state & (SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT)) << 1);
/* Tone off is confirmed */ s->signalling_state &= ~(SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT);
s->tone[j].tone_present = FALSE;
s->tone[j].tone_persistence_timeout = s->desc->tone_on_check_time;
s->signalling_state &= ~tone_present_bits[j];
s->signalling_state |= tone_change_bits[j];
}
/*endif*/
}
else
{
s->tone[j].tone_persistence_timeout = s->desc->tone_off_check_time;
} }
/*endif*/ /*endif*/
} }
else else
{ {
if (s->notch_insertion_timeout) s->tone_persistence_timeout = s->desc->tone_off_check_time;
s->notch_insertion_timeout--;
/*endif*/
if (flat_power > s->sharp_detection_threshold
&&
(notch_power[j] >> 6)*s->detection_ratio < (flat_power >> 6))
{
/* Tone is detected this sample */
if (--s->tone[j].tone_persistence_timeout == 0)
{
/* Tone on is confirmed */
s->tone[j].tone_present = TRUE;
s->tone[j].tone_persistence_timeout = s->desc->tone_off_check_time;
s->notch_insertion_timeout = s->desc->notch_lag_time;
s->signalling_state |= (tone_present_bits[j] | tone_change_bits[j]);
}
/*endif*/
}
else
{
s->tone[j].tone_persistence_timeout = s->desc->tone_on_check_time;
}
/*endif*/
} }
/*endif*/ /*endif*/
} }
/*endfor*/ else
{
if (s->notch_insertion_timeout)
s->notch_insertion_timeout--;
/*endif*/
if (immediate >= 0 && immediate == s->last_sample_tone_present)
{
/* Consistent tone detected this sample */
if (--s->tone_persistence_timeout == 0)
{
/* Tone on is confirmed */
s->tone_persistence_timeout = s->desc->tone_off_check_time;
s->notch_insertion_timeout = s->desc->notch_lag_time;
s->signalling_state |= (tone_present_bits[immediate] | tone_change_bits[immediate]);
s->current_notch_filter = immediate;
}
/*endif*/
}
else
{
s->tone_persistence_timeout = s->desc->tone_on_check_time;
}
/*endif*/
}
/*endif*/
//printf("XXX %d %d %d %d %d %d\n", nnn++, notch_power[0], notch_power[1], notch_power[2], flat_power, immediate*10000000);
} }
/*endif*/ /*endif*/
if (s->signalling_state & (SIG_TONE_1_CHANGE | SIG_TONE_2_CHANGE)) if ((s->signalling_state & (SIG_TONE_1_CHANGE | SIG_TONE_2_CHANGE)))
{ {
if (s->sig_update) if (s->sig_update)
s->sig_update(s->user_data, s->signalling_state, 0, s->signalling_state_duration); s->sig_update(s->user_data, s->signalling_state, 0, s->signalling_state_duration);
@ -574,7 +606,11 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
if ((s->current_rx_tone & SIG_TONE_RX_PASSTHROUGH)) if ((s->current_rx_tone & SIG_TONE_RX_PASSTHROUGH))
{ {
if ((s->current_rx_tone & SIG_TONE_RX_FILTER_TONE) || s->notch_insertion_timeout) if ((s->current_rx_tone & SIG_TONE_RX_FILTER_TONE) || s->notch_insertion_timeout)
amp[i] = saturate(notched_signal[0]); #if defined(SPANDSP_USE_FIXED_POINT)
amp[i] = saturate16(notched_signal[s->current_notch_filter]);
#else
amp[i] = fsaturatef(notched_signal[s->current_notch_filter]);
#endif
/*endif*/ /*endif*/
} }
else else
@ -583,6 +619,7 @@ SPAN_DECLARE(int) sig_tone_rx(sig_tone_rx_state_t *s, int16_t amp[], int len)
amp[i] = 0; amp[i] = 0;
} }
/*endif*/ /*endif*/
s->last_sample_tone_present = immediate;
} }
/*endfor*/ /*endfor*/
return len; return len;
@ -620,17 +657,19 @@ SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int
s->tone[j].notch_z1[i] = 0.0f; s->tone[j].notch_z1[i] = 0.0f;
s->tone[j].notch_z2[i] = 0.0f; s->tone[j].notch_z2[i] = 0.0f;
} }
s->flat_z[i] = 0.0f;
} }
for (i = 0; i < 2; i++)
s->flat_z[i] = 0.0f;
#endif #endif
s->last_sample_tone_present = -1;
s->sig_update = sig_update; s->sig_update = sig_update;
s->user_data = user_data; s->user_data = user_data;
s->desc = &sig_tones[tone_type - 1]; s->desc = &sig_tones[tone_type - 1];
power_meter_init(&s->tone[0].power, 5); for (i = 0; i < 3; i++)
power_meter_init(&s->tone[1].power, 5); power_meter_init(&s->tone[i].power, 5);
power_meter_init(&s->flat_power, 5); power_meter_init(&s->flat_power, 5);
s->flat_detection_threshold = power_meter_level_dbm0(s->desc->flat_detection_threshold); s->flat_detection_threshold = power_meter_level_dbm0(s->desc->flat_detection_threshold);

View File

@ -169,6 +169,8 @@ struct sig_tone_rx_state_s
int current_rx_tone; int current_rx_tone;
/*! \brief The timeout for switching from the high level to low level tone detector. */ /*! \brief The timeout for switching from the high level to low level tone detector. */
int high_low_timer; int high_low_timer;
/*! \brief ??? */
int current_notch_filter;
struct struct
{ {
@ -186,11 +188,7 @@ struct sig_tone_rx_state_s
/*! \brief The power output of the notch. */ /*! \brief The power output of the notch. */
power_meter_t power; power_meter_t power;
/*! \brief Persistence check for tone present */ } tone[3];
int tone_persistence_timeout;
/*! \brief TRUE if the tone is declared to be present */
int tone_present;
} tone[2];
#if defined(SPANDSP_USE_FIXED_POINT) #if defined(SPANDSP_USE_FIXED_POINT)
/*! \brief The z's for the weighting/bandpass filter. */ /*! \brief The z's for the weighting/bandpass filter. */
@ -202,6 +200,11 @@ struct sig_tone_rx_state_s
/*! \brief The output power of the flat (unfiltered or flat filtered) path. */ /*! \brief The output power of the flat (unfiltered or flat filtered) path. */
power_meter_t flat_power; power_meter_t flat_power;
/*! \brief Persistence check for tone present */
int tone_persistence_timeout;
/*! \brief The tone pattern on the last audio sample */
int last_sample_tone_present;
/*! \brief The minimum reading from the power meter for detection in flat mode */ /*! \brief The minimum reading from the power meter for detection in flat mode */
int32_t flat_detection_threshold; int32_t flat_detection_threshold;
/*! \brief The minimum reading from the power meter for detection in sharp mode */ /*! \brief The minimum reading from the power meter for detection in sharp mode */

View File

@ -70,6 +70,13 @@ const char *bellcore_files[] =
"" ""
}; };
typedef struct
{
double freq;
double min_level;
double max_level;
} template_t;
static int number_of_tones = 1; static int number_of_tones = 1;
static int sampleno = 0; static int sampleno = 0;
@ -81,47 +88,132 @@ static int dial_pulses = 0;
static int rx_handler_callbacks = 0; static int rx_handler_callbacks = 0;
static int tx_handler_callbacks = 0; static int tx_handler_callbacks = 0;
static int use_gui = FALSE;
static void plot_frequency_response(void)
{
FILE *gnucmd;
if ((gnucmd = popen("gnuplot", "w")) == NULL)
{
exit(2);
}
fprintf(gnucmd, "set autoscale\n");
fprintf(gnucmd, "unset log\n");
fprintf(gnucmd, "unset label\n");
fprintf(gnucmd, "set xtic auto\n");
fprintf(gnucmd, "set ytic auto\n");
fprintf(gnucmd, "set title 'Notch filter frequency response'\n");
fprintf(gnucmd, "set xlabel 'Frequency (Hz)'\n");
fprintf(gnucmd, "set ylabel 'Gain (dB)'\n");
fprintf(gnucmd, "plot 'sig_tone_notch' using 1:3 title 'min' with lines,"
"'sig_tone_notch' using 1:6 title 'actual' with lines,"
"'sig_tone_notch' using 1:9 title 'max' with lines\n");
fflush(gnucmd);
getchar();
if (pclose(gnucmd) == -1)
{
exit(2);
}
}
/*- End of function --------------------------------------------------------*/
static void tx_handler(void *user_data, int what, int level, int duration) static void tx_handler(void *user_data, int what, int level, int duration)
{ {
sig_tone_tx_state_t *s; sig_tone_tx_state_t *s;
int tone;
int time;
static const int pattern_1_tone[][2] =
{
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{600, SIG_TONE_1_PRESENT},
{0, 0}
};
static const int pattern_2_tones[][2] =
{
#if 0
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
{33, SIG_TONE_1_PRESENT},
{67, 0},
#endif
{100, SIG_TONE_1_PRESENT},
{100, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT},
{100, SIG_TONE_2_PRESENT},
#if 0
{100, 0},
{100, SIG_TONE_2_PRESENT},
{100, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT},
{100, SIG_TONE_1_PRESENT},
#endif
{0, 0}
};
s = (sig_tone_tx_state_t *) user_data; s = (sig_tone_tx_state_t *) user_data;
tx_handler_callbacks++; tx_handler_callbacks++;
//printf("What - %d, duration - %d\n", what, duration); //printf("What - %d, duration - %d\n", what, duration);
if ((what & SIG_TONE_TX_UPDATE_REQUEST)) if ((what & SIG_TONE_TX_UPDATE_REQUEST))
{ {
printf("Tx: update request\n");
/* The sig tone transmit side wants to know what to do next */ /* The sig tone transmit side wants to know what to do next */
switch (tx_section) printf("Tx: update request\n");
if (number_of_tones == 1)
{ {
case 0: time = pattern_1_tone[tx_section][0];
printf("33ms break - %d samples\n", ms_to_samples(33)); tone = pattern_1_tone[tx_section][1];
tx_section++; }
sig_tone_tx_set_mode(s, SIG_TONE_1_PRESENT, ms_to_samples(33)); else
break; {
case 1: time = pattern_2_tones[tx_section][0];
printf("67ms make - %d samples\n", ms_to_samples(67)); tone = pattern_2_tones[tx_section][1];
if (++dial_pulses == 9) }
tx_section++; if (time)
else {
tx_section--; printf("Tx: [%04x] %s %s for %d samples (%dms)\n",
/*endif*/ tone,
sig_tone_tx_set_mode(s, 0, ms_to_samples(67)); (tone & SIG_TONE_1_PRESENT) ? "on " : "off",
break; (tone & SIG_TONE_2_PRESENT) ? "on " : "off",
case 2: ms_to_samples(time),
tx_section++; time);
printf("600ms on - %d samples\n", ms_to_samples(600)); sig_tone_tx_set_mode(s, tone, ms_to_samples(time));
if (number_of_tones == 2) tx_section++;
sig_tone_tx_set_mode(s, SIG_TONE_2_PRESENT, ms_to_samples(600)); }
else else
sig_tone_tx_set_mode(s, SIG_TONE_1_PRESENT, ms_to_samples(600)); {
break; printf("End of sequence\n");
case 3:
printf("End of sequence\n");
sig_tone_tx_set_mode(s, SIG_TONE_1_PRESENT | SIG_TONE_TX_PASSTHROUGH, 0);
break;
} }
/*endswitch*/
} }
/*endif*/ /*endif*/
} }
@ -130,30 +222,46 @@ static void tx_handler(void *user_data, int what, int level, int duration)
static void rx_handler(void *user_data, int what, int level, int duration) static void rx_handler(void *user_data, int what, int level, int duration)
{ {
float ms; float ms;
int x;
rx_handler_callbacks++; rx_handler_callbacks++;
ms = 1000.0f*(float) duration/(float) SAMPLE_RATE; ms = 1000.0f*(float) duration/(float) SAMPLE_RATE;
printf("What - %d, duration - %d\n", what, duration); printf("Rx: [%04x]", what);
x = what & SIG_TONE_1_PRESENT;
if ((what & SIG_TONE_1_CHANGE)) if ((what & SIG_TONE_1_CHANGE))
{ {
tone_1_present = what & SIG_TONE_1_PRESENT; printf(" %s", (x) ? "on " : "off");
printf("Rx: tone 1 is %s after %d samples (%fms)\n", (tone_1_present) ? "on" : "off", duration, ms); if (x == tone_1_present)
exit(2);
tone_1_present = x;
}
else
{
printf(" ---");
if (x != tone_1_present)
exit(2);
} }
/*endif*/ /*endif*/
x = what & SIG_TONE_2_PRESENT;
if ((what & SIG_TONE_2_CHANGE)) if ((what & SIG_TONE_2_CHANGE))
{ {
tone_2_present = what & SIG_TONE_2_PRESENT; printf(" %s", (x) ? "on " : "off");
printf("Rx: tone 2 is %s after %d samples (%fms)\n", (tone_2_present) ? "on" : "off", duration, ms); if (x == tone_2_present)
exit(2);
tone_2_present = x;
}
else
{
if (x != tone_2_present)
exit(2);
printf(" ---");
} }
/*endif*/ /*endif*/
printf(" after %d samples (%.3fms)\n", duration, ms);
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
static void map_frequency_response(sig_tone_rx_state_t *s, static void map_frequency_response(sig_tone_rx_state_t *s, template_t template[])
double f1,
double f2,
double f3,
double f4)
{ {
int16_t buf[SAMPLES_PER_CHUNK]; int16_t buf[SAMPLES_PER_CHUNK];
int i; int i;
@ -163,12 +271,16 @@ static void map_frequency_response(sig_tone_rx_state_t *s,
swept_tone_state_t *swept; swept_tone_state_t *swept;
double freq; double freq;
double gain; double gain;
int template_entry;
FILE *file;
/* Things like noise don't highlight the frequency response of the high Q notch /* Things like noise don't highlight the frequency response of the high Q notch
very well. We use a slowly swept frequency to check it. */ very well. We use a slowly swept frequency to check it. */
printf("Frequency response test\n"); printf("Frequency response test\n");
sig_tone_rx_set_mode(s, SIG_TONE_RX_PASSTHROUGH | SIG_TONE_RX_FILTER_TONE, 0); sig_tone_rx_set_mode(s, SIG_TONE_RX_PASSTHROUGH | SIG_TONE_RX_FILTER_TONE, 0);
swept = swept_tone_init(NULL, 200.0f, 3900.0f, -10.0f, 120*SAMPLE_RATE, 0); swept = swept_tone_init(NULL, 200.0f, 3900.0f, -10.0f, 120*SAMPLE_RATE, 0);
template_entry = 0;
file = fopen("sig_tone_notch", "wb");
for (;;) for (;;)
{ {
if ((len = swept_tone(swept, buf, SAMPLES_PER_CHUNK)) <= 0) if ((len = swept_tone(swept, buf, SAMPLES_PER_CHUNK)) <= 0)
@ -188,22 +300,43 @@ static void map_frequency_response(sig_tone_rx_state_t *s,
gain = 10.0*log10(sumout/sumin); gain = 10.0*log10(sumout/sumin);
else else
gain = 0.0; gain = 0.0;
printf("%7.1f Hz %f dBm0\n", freq, gain); printf("%7.1f Hz %.3f dBm0 < %.3f dBm0 < %.3f dBm0\n",
if (gain > 0.0 freq,
|| template[template_entry].min_level,
(freq < f1 && gain < -1.0) gain,
|| template[template_entry].max_level);
(freq > f2 && freq < f3 && gain > -30.0) if (file)
||
(freq > f4 && gain < -1.0))
{ {
fprintf(file,
"%7.1f Hz %.3f dBm0 < %.3f dBm0 < %.3f dBm0\n",
freq,
template[template_entry].min_level,
gain,
template[template_entry].max_level);
}
/*endif*/
if (gain < template[template_entry].min_level || gain > template[template_entry].max_level)
{
printf("Expected: %.3f dBm0 to %.3f dBm0\n",
template[template_entry].min_level,
template[template_entry].max_level);
printf(" Failed\n"); printf(" Failed\n");
exit(2); exit(2);
} }
/*endif*/ /*endif*/
if (freq > template[template_entry].freq)
template_entry++;
} }
/*endfor*/ /*endfor*/
swept_tone_free(swept); swept_tone_free(swept);
if (file)
{
fclose(file);
if (use_gui)
plot_frequency_response();
/*endif*/
}
/*endif*/
printf(" Passed\n"); printf(" Passed\n");
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
@ -256,16 +389,17 @@ static void speech_immunity_tests(sig_tone_rx_state_t *s)
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
static void level_and_ratio_tests(sig_tone_rx_state_t *s, double pitch) static void level_and_ratio_tests(sig_tone_rx_state_t *s, double pitch[2])
{ {
awgn_state_t noise_source; awgn_state_t noise_source;
int32_t phase_rate; int32_t phase_rate[2];
uint32_t phase; uint32_t phase[2];
int16_t gain; int16_t gain;
int16_t amp[SAMPLE_RATE]; int16_t amp[SAMPLE_RATE];
int i; int i;
int j; int j;
int k; int k;
int l;
float noise_level; float noise_level;
float tone_level; float tone_level;
power_meter_t noise_meter; power_meter_t noise_meter;
@ -273,9 +407,12 @@ static void level_and_ratio_tests(sig_tone_rx_state_t *s, double pitch)
int16_t noise; int16_t noise;
int16_t tone; int16_t tone;
printf("Acceptable level and ratio test\n"); printf("Acceptable level and ratio test - %.2f Hz + %.2f Hz\n", pitch[0], pitch[1]);
phase = 0; for (l = 0; l < 2; l++)
phase_rate = dds_phase_rate(pitch); {
phase[l] = 0;
phase_rate[l] = (pitch[l] != 0.0) ? dds_phase_rate(pitch[l]) : 0;
}
for (k = -25; k > -60; k--) for (k = -25; k > -60; k--)
{ {
noise_level = k; noise_level = k;
@ -293,7 +430,9 @@ static void level_and_ratio_tests(sig_tone_rx_state_t *s, double pitch)
for (i = 0; i < SAMPLES_PER_CHUNK; i++) for (i = 0; i < SAMPLES_PER_CHUNK; i++)
{ {
noise = awgn(&noise_source); noise = awgn(&noise_source);
tone = dds_mod(&phase, phase_rate, gain, 0); tone = dds_mod(&phase[0], phase_rate[0], gain, 0);
if (phase_rate[1])
tone += dds_mod(&phase[1], phase_rate[1], gain, 0);
power_meter_update(&noise_meter, noise); power_meter_update(&noise_meter, noise);
power_meter_update(&tone_meter, tone); power_meter_update(&tone_meter, tone);
amp[i] = noise + tone; amp[i] = noise + tone;
@ -302,8 +441,10 @@ static void level_and_ratio_tests(sig_tone_rx_state_t *s, double pitch)
sig_tone_rx(s, amp, SAMPLES_PER_CHUNK); sig_tone_rx(s, amp, SAMPLES_PER_CHUNK);
if (rx_handler_callbacks) if (rx_handler_callbacks)
{ {
printf("Hit at tone = %fdBm0, noise = %fdBm0\n", tone_level, noise_level); printf("Hit at tone = %.2fdBm0, noise = %.2fdBm0\n", tone_level, noise_level);
printf("Noise = %fdBm0, tone = %fdBm0\n", power_meter_current_dbm0(&noise_meter), power_meter_current_dbm0(&tone_meter)); printf("Measured tone = %.2fdBm0, noise = %.2fdBm0\n", power_meter_current_dbm0(&tone_meter), power_meter_current_dbm0(&noise_meter));
if (rx_handler_callbacks != 1)
printf("Callbacks = %d\n", rx_handler_callbacks);
} }
/*endif*/ /*endif*/
tone_level += 1.0f; tone_level += 1.0f;
@ -327,6 +468,7 @@ static void sequence_tests(sig_tone_tx_state_t *tx_state, sig_tone_rx_state_t *r
int tx_samples; int tx_samples;
printf("Signalling sequence test\n"); printf("Signalling sequence test\n");
tx_section = 0;
if ((outhandle = sf_open_telephony_write(OUT_FILE_NAME, 2)) == NULL) if ((outhandle = sf_open_telephony_write(OUT_FILE_NAME, 2)) == NULL)
{ {
fprintf(stderr, " Cannot create audio file '%s'\n", OUT_FILE_NAME); fprintf(stderr, " Cannot create audio file '%s'\n", OUT_FILE_NAME);
@ -335,12 +477,14 @@ static void sequence_tests(sig_tone_tx_state_t *tx_state, sig_tone_rx_state_t *r
/*endif*/ /*endif*/
awgn_init_dbm0(&noise_source, 1234567, -20.0f); awgn_init_dbm0(&noise_source, 1234567, -20.0f);
for (sampleno = 0; sampleno < 60000; sampleno += SAMPLES_PER_CHUNK) sig_tone_tx_set_mode(tx_state, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT | SIG_TONE_TX_PASSTHROUGH, 0);
sig_tone_rx_set_mode(rx_state, SIG_TONE_RX_PASSTHROUGH, 0);
for (sampleno = 0; sampleno < 4000; sampleno += SAMPLES_PER_CHUNK)
{ {
if (sampleno == 8000) if (sampleno == 800)
{ {
/* 100ms seize */ /* 100ms seize */
printf("100ms seize - %d samples\n", ms_to_samples(100)); printf("Tx: [0000] off off for %d samples (%dms)\n", ms_to_samples(100), 100);
dial_pulses = 0; dial_pulses = 0;
sig_tone_tx_set_mode(tx_state, 0, ms_to_samples(100)); sig_tone_tx_set_mode(tx_state, 0, ms_to_samples(100));
} }
@ -381,24 +525,40 @@ int main(int argc, char *argv[])
sig_tone_tx_state_t tx_state; sig_tone_tx_state_t tx_state;
sig_tone_rx_state_t rx_state; sig_tone_rx_state_t rx_state;
codec_munge_state_t *munge; codec_munge_state_t *munge;
double f1; double fc[2];
double f2; int i;
double fc; template_t template[10];
double f3; int opt;
double f4;
use_gui = FALSE;
while ((opt = getopt(argc, argv, "g")) != -1)
{
switch (opt)
{
case 'g':
use_gui = TRUE;
break;
default:
//usage();
exit(2);
break;
}
}
for (type = 1; type <= 3; type++) for (type = 1; type <= 3; type++)
{ {
sampleno = 0; sampleno = 0;
tone_1_present = 0; tone_1_present = 0;
tone_2_present = 0; tone_2_present = 0;
tx_section = 0;
munge = NULL; munge = NULL;
f1 = for (i = 0; i < 10; i++)
f2 = {
fc = template[i].freq = 0.0;
f3 = template[i].min_level = 0.0;
f4 = 0.0; template[i].max_level = 0.0;
}
fc[0] =
fc[1] = 0.0;
switch (type) switch (type)
{ {
case 1: case 1:
@ -407,11 +567,33 @@ int main(int argc, char *argv[])
sig_tone_tx_init(&tx_state, SIG_TONE_2280HZ, tx_handler, &tx_state); sig_tone_tx_init(&tx_state, SIG_TONE_2280HZ, tx_handler, &tx_state);
sig_tone_rx_init(&rx_state, SIG_TONE_2280HZ, rx_handler, &rx_state); sig_tone_rx_init(&rx_state, SIG_TONE_2280HZ, rx_handler, &rx_state);
number_of_tones = 1; number_of_tones = 1;
f1 = 2280.0 - 200.0; fc[0] = 2280.0;
f2 = 2280.0 - 20.0;
fc = 2280.0; /* From BTNR 181 2.3.3.1 */
f3 = 2280.0 + 20.0; template[0].freq = 1150.0;
f4 = 2280.0 + 200.0; template[0].min_level = -0.2;
template[0].max_level = 0.0;
template[1].freq = 1880.0;
template[1].min_level = -0.5;
template[1].max_level = 0.0;
template[2].freq = 2080.0;
template[2].min_level = -5.0;
template[2].max_level = 0.0;
template[3].freq = 2280.0 - 20.0;
template[3].min_level = -99.0;
template[3].max_level = 0.0;
template[4].freq = 2280.0 + 20.0;
template[4].min_level = -99.0;
template[4].max_level = -30.0;
template[5].freq = 2480.0;
template[5].min_level = -99.0;
template[5].max_level = 0.0;
template[6].freq = 2680.0;
template[6].min_level = -5.0;
template[6].max_level = 0.0;
template[7].freq = 4000.0;
template[7].min_level = -0.5;
template[7].max_level = 0.0;
break; break;
case 2: case 2:
printf("2600Hz tests.\n"); printf("2600Hz tests.\n");
@ -419,11 +601,23 @@ int main(int argc, char *argv[])
sig_tone_tx_init(&tx_state, SIG_TONE_2600HZ, tx_handler, &tx_state); sig_tone_tx_init(&tx_state, SIG_TONE_2600HZ, tx_handler, &tx_state);
sig_tone_rx_init(&rx_state, SIG_TONE_2600HZ, rx_handler, &rx_state); sig_tone_rx_init(&rx_state, SIG_TONE_2600HZ, rx_handler, &rx_state);
number_of_tones = 1; number_of_tones = 1;
f1 = 2600.0 - 200.0; fc[0] = 2600.0;
f2 = 2600.0 - 20.0;
fc = 2600.0; template[0].freq = 2600.0 - 200.0;
f3 = 2600.0 + 20.0; template[0].min_level = -1.0;
f4 = 2600.0 + 200.0; template[0].max_level = 0.0;
template[1].freq = 2600.0 - 20.0;
template[1].min_level = -99.0;
template[1].max_level = 0.0;
template[2].freq = 2600.0 + 20.0;
template[2].min_level = -99.0;
template[2].max_level = -30.0;
template[3].freq = 2600.0 + 200.0;
template[3].min_level = -99.0;
template[3].max_level = 0.0;
template[4].freq = 4000.0;
template[4].min_level = -1.0;
template[4].max_level = 0.0;
break; break;
case 3: case 3:
printf("2400Hz/2600Hz tests.\n"); printf("2400Hz/2600Hz tests.\n");
@ -431,21 +625,36 @@ int main(int argc, char *argv[])
sig_tone_tx_init(&tx_state, SIG_TONE_2400HZ_2600HZ, tx_handler, &tx_state); sig_tone_tx_init(&tx_state, SIG_TONE_2400HZ_2600HZ, tx_handler, &tx_state);
sig_tone_rx_init(&rx_state, SIG_TONE_2400HZ_2600HZ, rx_handler, &rx_state); sig_tone_rx_init(&rx_state, SIG_TONE_2400HZ_2600HZ, rx_handler, &rx_state);
number_of_tones = 2; number_of_tones = 2;
f1 = 2400.0 - 200.0; fc[0] = 2400.0;
f2 = 2400.0 - 20.0; fc[1] = 2600.0;
fc = 2400.0;
f3 = 2400.0 + 20.0; template[0].freq = 2400.0 - 200.0;
f4 = 2400.0 + 200.0; template[0].min_level = -1.0;
template[0].max_level = 0.0;
template[1].freq = 2400.0 - 20.0;
template[1].min_level = -99.0;
template[1].max_level = 0.0;
template[2].freq = 2400.0 + 20.0;
template[2].min_level = -99.0;
template[2].max_level = -30.0;
template[3].freq = 2600.0 - 20.0;
template[3].min_level = -99.0;
template[3].max_level = 0.0;
template[4].freq = 2600.0 + 20.0;
template[4].min_level = -99.0;
template[4].max_level = -30.0;
template[5].freq = 2600.0 + 200.0;
template[5].min_level = -99.0;
template[5].max_level = 0.0;
template[6].freq = 4000.0;
template[6].min_level = -1.0;
template[6].max_level = 0.0;
break; break;
} }
/*endswitch*/ /*endswitch*/
/* Set to the default on hook condition */ map_frequency_response(&rx_state, template);
map_frequency_response(&rx_state, f1, f2, f3, f4);
speech_immunity_tests(&rx_state); speech_immunity_tests(&rx_state);
level_and_ratio_tests(&rx_state, fc); level_and_ratio_tests(&rx_state, fc);
sig_tone_tx_set_mode(&tx_state, SIG_TONE_1_PRESENT | SIG_TONE_2_PRESENT | SIG_TONE_TX_PASSTHROUGH, 0);
sig_tone_rx_set_mode(&rx_state, SIG_TONE_RX_PASSTHROUGH, 0);
sequence_tests(&tx_state, &rx_state, munge); sequence_tests(&tx_state, &rx_state, munge);
} }
/*endfor*/ /*endfor*/