Note this is only relevant to VoIP using A-law, u-law or similar.
Low bit rate codecs scramble DTMF too much for it to be recognised,
and often slip in units larger than a sample. */
- if (hit != s->in_digit)
+ if (hit != s->in_digit && s->last_hit != s->in_digit)
{
- if (s->last_hit != s->in_digit)
+ /* We have two successive indications that something has changed. */
+ /* To declare digit on, the hits must agree. Otherwise we declare tone off. */
+ hit = (hit && hit == s->last_hit) ? hit : 0;
+ if (s->realtime_callback)
{
- /* We have two successive indications that something has changed. */
- /* To declare digit on, the hits must agree. Otherwise we declare tone off. */
- hit = (hit && hit == s->last_hit) ? hit : 0;
- if (s->realtime_callback)
+ /* Avoid reporting multiple no digit conditions on flaky hits */
+ if (s->in_digit || hit)
{
- /* Avoid reporting multiple no digit conditions on flaky hits */
- if (s->in_digit || hit)
- {
- i = (s->in_digit && !hit) ? -99 : lfastrintf(log10f(s->energy)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER);
- s->realtime_callback(s->realtime_callback_data, hit, i, s->duration);
- s->duration = 0;
- }
+ i = (s->in_digit && !hit) ? -99 : lfastrintf(log10f(s->energy)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER);
+ s->realtime_callback(s->realtime_callback_data, hit, i, s->duration);
+ s->duration = 0;
}
- else
+ }
+ else
+ {
+ if (hit)
{
- if (hit)
+ if (s->current_digits < MAX_DTMF_DIGITS)
{
- if (s->current_digits < MAX_DTMF_DIGITS)
+ s->digits[s->current_digits++] = (char) hit;
+ s->digits[s->current_digits] = '\0';
+ if (s->digits_callback)
{
- s->digits[s->current_digits++] = (char) hit;
- s->digits[s->current_digits] = '\0';
- if (s->digits_callback)
- {
- s->digits_callback(s->digits_callback_data, s->digits, s->current_digits);
- s->current_digits = 0;
- }
- }
- else
- {
- s->lost_digits++;
+ s->digits_callback(s->digits_callback_data, s->digits, s->current_digits);
+ s->current_digits = 0;
}
}
+ else
+ {
+ s->lost_digits++;
+ }
}
- s->in_digit = hit;
}
+ s->in_digit = hit;
}
s->last_hit = hit;
#if defined(SPANDSP_USE_FIXED_POINT)
#endif
s->current_sample = 0;
/* Don't update the hit detection. Pretend it never happened. */
- /* TODO: Surely we can be a cleverer than this. */
+ /* TODO: Surely we can be cleverer than this. */
return 0;
}
/*- End of function --------------------------------------------------------*/
s->current_digits -= max;
}
buf[max] = '\0';
- return max;
+ return max;
}
/*- End of function --------------------------------------------------------*/
if (s == NULL)
{
if ((s = (dtmf_rx_state_t *) malloc(sizeof (*s))) == NULL)
- return NULL;
+ return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
if (s == NULL)
{
if ((s = (dtmf_tx_state_t *) malloc(sizeof (*s))) == NULL)
- return NULL;
+ return NULL;
}
memset(s, 0, sizeof(*s));
if (!dtmf_tx_inited)
s->rx_frames++;
s->rx_bytes += s->len - s->crc_bytes;
s->len -= s->crc_bytes;
- s->frame_handler(s->frame_user_data, s->buffer, s->len, TRUE);
+ if (s->frame_handler)
+ s->frame_handler(s->frame_user_data, s->buffer, s->len, TRUE);
}
else
{
if (s->report_bad_frames)
{
s->len -= s->crc_bytes;
- s->frame_handler(s->frame_user_data, s->buffer, s->len, FALSE);
+ if (s->frame_handler)
+ s->frame_handler(s->frame_user_data, s->buffer, s->len, FALSE);
}
}
}
s->len -= s->crc_bytes;
else
s->len = 0;
- s->frame_handler(s->frame_user_data, s->buffer, s->len, FALSE);
+ if (s->frame_handler)
+ s->frame_handler(s->frame_user_data, s->buffer, s->len, FALSE);
}
s->rx_length_errors++;
}
rx_tag = "";
tx_tag = "";
}
+ else if (strcmp(modem, "V.34_2400") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 10;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1600.0;
+ baud_rate = 2400.0;
+ rx_tag = "_2400_low_carrier";
+ tx_tag = "_2400";
+ }
+ else if (strcmp(modem, "V.34_2400_high") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 10;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1800.0;
+ baud_rate = 2400.0;
+ rx_tag = "_2400_high_carrier";
+ tx_tag = "_2400";
+ }
+ else if (strcmp(modem, "V.34_2743") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 35;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1646.0;
+ baud_rate = 2400.0*8.0/7.0;
+ rx_tag = "_2743_low_carrier";
+ tx_tag = "_2743";
+ }
+ else if (strcmp(modem, "V.34_2743_high") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 35;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1829.0;
+ baud_rate = 2400.0*8.0/7.0;
+ rx_tag = "_2743_high_carrier";
+ tx_tag = "_2743";
+ }
+ else if (strcmp(modem, "V.34_2800") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 20;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1680.0;
+ baud_rate = 2400.0*7.0/6.0;
+ rx_tag = "_2800_low_carrier";
+ tx_tag = "_2800";
+ }
+ else if (strcmp(modem, "V.34_2800_high") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 20;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1867.0;
+ baud_rate = 2400.0*7.0/6.0;
+ rx_tag = "_2800_high_carrier";
+ tx_tag = "_2800";
+ }
+ else if (strcmp(modem, "V.34_3000") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 8;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1800.0;
+ baud_rate = 2400.0*5.0/4.0;
+ rx_tag = "_3000_low_carrier";
+ tx_tag = "_3000";
+ }
+ else if (strcmp(modem, "V.34_3000_high") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 8;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 2000.0;
+ baud_rate = 2400.0*5.0/4.0;
+ rx_tag = "_3000_high_carrier";
+ tx_tag = "_3000";
+ }
+ else if (strcmp(modem, "V.34_3200") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 5;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1829.0;
+ baud_rate = 2400.0*4.0/3.0;
+ rx_tag = "_3200_low_carrier";
+ tx_tag = "_3200";
+ }
+ else if (strcmp(modem, "V.34_3200_high") == 0)
+ {
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 5;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ carrier = 1920.0;
+ baud_rate = 2400.0*4.0/3.0;
+ rx_tag = "_3200_high_carrier";
+ tx_tag = "_3200";
+ }
+ else if (strcmp(modem, "V.34_3429") == 0)
+ {
+ /* There is only one carrier frequency defined for this baud rate */
+ rx_coeff_sets = 192;
+ rx_coeffs_per_filter = 27;
+ rx_excess_bandwidth = 0.25;
+ tx_coeff_sets = 7;
+ tx_coeffs_per_filter = 9;
+ tx_excess_bandwidth = 0.12;
+ //carrier = 1959.0;
+ carrier = 1959.0;
+ baud_rate = 2400.0*10.0/7.0;
+ rx_tag = "_3429";
+ tx_tag = "_3429";
+ }
else
{
usage();
/* CNG is 0.5s+-15% of 1100+-38Hz, 3s+-15% off, repeating.
CED is 0.2s silence, 3.3+-0.7s of 2100+-15Hz, and 75+-20ms of silence.
-
+
+ Calling tone is 0.5s-0.7s of 1300Hz+-15Hz, 1.5s-2.0s off, repeating.
+
ANS is 3.3+-0.7s of 2100+-15Hz.
-
+
ANS/ is 3.3+-0.7s of 2100+-15Hz, with phase reversals (180+-10 degrees, hopping in <1ms) every 450+-25ms.
ANSam/ is 2100+-1Hz, with phase reversals (180+-10 degrees, hopping in <1ms) every 450+-25ms, and AM with a sinewave of 15+-0.1Hz.
return "FAX preamble";
case MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE:
return "FAX CED or preamble";
+ case MODEM_CONNECT_TONES_CALLING_TONE:
+ return "Calling tone";
}
return "???";
}
}
s->duration_timer -= len;
break;
+ case MODEM_CONNECT_TONES_CALLING_TONE:
+ for ( ; i < len; i++)
+ {
+ if (s->duration_timer > ms_to_samples(2000))
+ {
+ if ((xlen = i + s->duration_timer - ms_to_samples(2000)) > len)
+ xlen = len;
+ s->duration_timer -= (xlen - i);
+ for ( ; i < xlen; i++)
+ amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->level, 0);
+ }
+ if (s->duration_timer > 0)
+ {
+ if ((xlen = i + s->duration_timer) > len)
+ xlen = len;
+ s->duration_timer -= (xlen - i);
+ memset(amp + i, 0, sizeof(int16_t)*(xlen - i));
+ i = xlen;
+ }
+ if (s->duration_timer == 0)
+ s->duration_timer = ms_to_samples(600 + 2000);
+ }
+ break;
}
return len;
}
s->mod_phase = 0;
s->hop_timer = ms_to_samples(450);
break;
+ case MODEM_CONNECT_TONES_CALLING_TONE:
+ /* 0.6s of 1300Hz+-15Hz + 2.0s of silence repeating. */
+ s->tone_phase_rate = dds_phase_rate(1300.0);
+ s->level = dds_scaling_dbm0(-11);
+ s->duration_timer = ms_to_samples(600 + 2000);
+ s->mod_phase_rate = 0;
+ s->tone_phase = 0;
+ s->mod_phase = 0;
+ s->mod_level = 0;
+ s->hop_timer = 0;
+ break;
default:
if (alloced)
free(s);
famp = amp[i];
/* A Cauer notch at 1100Hz, spread just wide enough to meet our detection bandwidth
criteria. */
+ /* Poles 0.736618498*exp(+-1047/4000 * PI * j)
+ Zeroes exp(+-1099.5 * PI * j) */
v1 = 0.792928f*famp + 1.0018744927985f*s->znotch_1 - 0.54196833412465f*s->znotch_2;
famp = v1 - 1.2994747954630f*s->znotch_1 + s->znotch_2;
s->znotch_2 = s->znotch_1;
{
famp = amp[i];
/* A Cauer bandpass at 15Hz, with which we demodulate the AM signal. */
+ /* Poles 0.9983989*exp(+-15/4000 * PI * j)
+ Zeroes exp(0 * PI * j) */
v1 = fabs(famp) + 1.996667f*s->z15hz_1 - 0.9968004f*s->z15hz_2;
filtered = 0.001599787f*(v1 - s->z15hz_2);
s->z15hz_2 = s->z15hz_1;
//printf("%9.1f %10.4f %9d %9d\n", famp, filtered, s->am_level, s->channel_level);
/* A Cauer notch at 2100Hz, spread just wide enough to meet our detection bandwidth
criteria. */
- /* This is actually centred at 2095Hz, but gets the balance we want, due
- to the asymmetric walls of the notch */
+ /* Poles 0.7144255*exp(+-2105.612/4000 * PI * j)
+ Zeroes exp(+-2099.9 * PI * j) */
v1 = 0.76000f*famp - 0.1183852f*s->znotch_1 - 0.5104039f*s->znotch_2;
famp = v1 + 0.1567596f*s->znotch_1 + s->znotch_2;
s->znotch_2 = s->znotch_1;
}
}
break;
+ case MODEM_CONNECT_TONES_CALLING_TONE:
+ for (i = 0; i < len; i++)
+ {
+ famp = amp[i];
+ /* A Cauer notch at 1300Hz, spread just wide enough to meet our detection bandwidth
+ criteria. */
+ /* Poles 0.736618498*exp(+-1247/4000 * PI * j)
+ Zeroes exp(+-1299.5 * PI * j) */
+ v1 = 0.755582f*famp + 0.820887174515f*s->znotch_1 - 0.541968324778f*s->znotch_2;
+ famp = v1 - 1.0456667108f*s->znotch_1 + s->znotch_2;
+ s->znotch_2 = s->znotch_1;
+ s->znotch_1 = v1;
+ notched = (int16_t) lfastrintf(famp);
+
+ /* Estimate the overall energy in the channel, and the energy in
+ the notch (i.e. overall channel energy - tone energy => noise).
+ Use abs instead of multiply for speed (is it really faster?). */
+ s->channel_level += ((abs(amp[i]) - s->channel_level) >> 5);
+ s->notch_level += ((abs(notched) - s->notch_level) >> 5);
+ if (s->channel_level > 70 && s->notch_level*6 < s->channel_level)
+ {
+ /* There is adequate energy in the channel, and it is mostly at 1300Hz. */
+ if (s->tone_present != MODEM_CONNECT_TONES_CALLING_TONE)
+ {
+ if (++s->tone_cycle_duration >= ms_to_samples(415))
+ report_tone_state(s, MODEM_CONNECT_TONES_CALLING_TONE, lfastrintf(log10f(s->channel_level/32768.0f)*20.0f + DBM0_MAX_POWER + 0.8f));
+ }
+ }
+ else
+ {
+ /* If the signal looks wrong, even for a moment, we consider this the
+ end of the tone. */
+ if (s->tone_present == MODEM_CONNECT_TONES_CALLING_TONE)
+ report_tone_state(s, MODEM_CONNECT_TONES_NONE, -99);
+ s->tone_cycle_duration = 0;
+ }
+ }
+ break;
}
return 0;
}
/*! \brief CED tone is the same as ANS tone. FAX preamble in a string of V.21 HDLC flag octets.
This is only valid as a tone type to receive. It is never reported as a detected tone
type. The report will either be for FAX preamble or CED/ANS tone. */
- MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE = 7
+ MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE = 7,
+ /*! \brief Calling tone is a pure 1300Hz tone, in 0.6s bursts, with 2s silences in between. The
+ bursts repeat for as long as is required. */
+ MODEM_CONNECT_TONES_CALLING_TONE = 8
};
/*! \brief FAX CED tone is the same as ANS tone. */
#if !defined(_SPANDSP_PRIVATE_SUPER_TONE_RX_H_)
#define _SPANDSP_PRIVATE_SUPER_TONE_RX_H_
-#define BINS 128
+#define SUPER_TONE_BINS 128
struct super_tone_rx_segment_s
{
{
int used_frequencies;
int monitored_frequencies;
- int pitches[BINS/2][2];
+ int pitches[SUPER_TONE_BINS/2][2];
int tones;
super_tone_rx_segment_t **tone_list;
int *tone_segs;
int detected_tone;
int rotation;
tone_report_func_t tone_callback;
- void (*segment_callback)(void *data, int f1, int f2, int duration);
+ tone_segment_func_t segment_callback;
void *callback_data;
super_tone_rx_segment_t segments[11];
goertzel_state_t state[];
uint32_t guard_phase;
/*! \brief The update rate for the phase of the guard tone (i.e. the DDS increment). */
int32_t guard_phase_rate;
- float guard_level;
+ float guard_tone_gain;
/*! \brief The current fractional phase of the baud timing. */
int baud_phase;
/*! \brief The code number for the current position in the constellation. */
{
int status;
int modem_connect_tone;
+ int send_ci;
+ int v92;
int call_function;
unsigned int modulations;
int protocol;
#include "spandsp/private/super_tone_rx.h"
#if defined(SPANDSP_USE_FIXED_POINT)
-#define DETECTION_THRESHOLD 16439 /* -42dBm0 [((BINS*BINS*32768.0/(1.4142*128.0))*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2] */
+#define DETECTION_THRESHOLD 16439 /* -42dBm0 [((SUPER_TONE_BINS*SUPER_TONE_BINS*32768.0/(1.4142*128.0))*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2] */
#define TONE_TWIST 4 /* 6dB */
#define TONE_TO_TOTAL_ENERGY 64 /* -3dB */
#else
-#define DETECTION_THRESHOLD 2104205.6f /* -42dBm0 [((BINS*BINS*32768.0/1.4142)*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2] */
+#define DETECTION_THRESHOLD 2104205.6f /* -42dBm0 [((SUPER_TONE_BINS*SUPER_TONE_BINS*32768.0/1.4142)*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2] */
#define TONE_TWIST 3.981f /* 6dB */
#define TONE_TO_TOTAL_ENERGY 1.995f /* 3dB */
#endif
/* Merge these two */
desc->pitches[desc->used_frequencies][0] = freq;
desc->pitches[desc->used_frequencies][1] = i;
- make_goertzel_descriptor(&desc->desc[desc->pitches[i][1]], (float) (freq + desc->pitches[i][0])/2, BINS);
+ make_goertzel_descriptor(&desc->desc[desc->pitches[i][1]], (float) (freq + desc->pitches[i][0])/2, SUPER_TONE_BINS);
desc->used_frequencies++;
return desc->pitches[i][1];
}
{
desc->desc = (goertzel_descriptor_t *) realloc(desc->desc, (desc->monitored_frequencies + 5)*sizeof(goertzel_descriptor_t));
}
- make_goertzel_descriptor(&desc->desc[desc->monitored_frequencies++], (float) freq, BINS);
+ make_goertzel_descriptor(&desc->desc[desc->monitored_frequencies++], (float) freq, SUPER_TONE_BINS);
desc->used_frequencies++;
return desc->pitches[i][1];
}
j = (rotation + steps - 2)%steps;
if (pattern[j].f1 != test[8].f1 || pattern[j].f2 != test[8].f2)
return 0;
- if (pattern[j].min_duration > test[8].min_duration*BINS
+ if (pattern[j].min_duration > test[8].min_duration*SUPER_TONE_BINS
||
- pattern[j].max_duration < test[8].min_duration*BINS)
+ pattern[j].max_duration < test[8].min_duration*SUPER_TONE_BINS)
{
return 0;
}
j = (rotation + steps - 1)%steps;
if (pattern[j].f1 != test[9].f1 || pattern[j].f2 != test[9].f2)
return 0;
- if (pattern[j].max_duration < test[9].min_duration*BINS)
+ if (pattern[j].max_duration < test[9].min_duration*SUPER_TONE_BINS)
return 0;
}
else
j = i + 10 - steps;
if (pattern[i].f1 != test[j].f1 || pattern[i].f2 != test[j].f2)
return 0;
- if (pattern[i].min_duration > test[j].min_duration*BINS
+ if (pattern[i].min_duration > test[j].min_duration*SUPER_TONE_BINS
||
- pattern[i].max_duration < test[j].min_duration*BINS)
+ pattern[i].max_duration < test[j].min_duration*SUPER_TONE_BINS)
{
return 0;
}
int k1;
int k2;
#if defined(SPANDSP_USE_FIXED_POINT)
- int32_t res[BINS/2];
+ int32_t res[SUPER_TONE_BINS/2];
#else
- float res[BINS/2];
+ float res[SUPER_TONE_BINS/2];
#endif
for (i = 0; i < s->desc->monitored_frequencies; i++)
s->segment_callback(s->callback_data,
s->segments[9].f1,
s->segments[9].f2,
- s->segments[9].min_duration*BINS/8);
+ s->segments[9].min_duration*SUPER_TONE_BINS/8);
}
memcpy (&s->segments[0], &s->segments[1], 9*sizeof(s->segments[0]));
s->segments[9].f1 = k1;
s->energy += xamp*xamp;
#endif
}
- if (s->state[0].current_sample >= BINS)
+ if (s->state[0].current_sample >= SUPER_TONE_BINS)
{
/* We have finished a Goertzel block. */
super_tone_chunk(s);
/* There is a handler queued, so that is the next one. */
set_tx_handler(s, t->next_tx_handler, t->next_tx_user_data);
set_next_tx_handler(s, NULL, NULL);
- if (t->tx_handler == (span_tx_handler_t) &(silence_gen)
+ if (t->tx_handler == (span_tx_handler_t) &silence_gen
||
- t->tx_handler == (span_tx_handler_t) &(tone_gen))
+ t->tx_handler == (span_tx_handler_t) &tone_gen)
{
set_rx_active(s, TRUE);
}
{"##8", 'W'},
{"##9", 'Z'},
{"##0", ' '},
-#if defined(WIN32) || ( defined(__SVR4) && defined (__sun))
+#if defined(WIN32) || (defined(__SVR4) && defined (__sun))
{"#*1", 'X'}, // (Note 1) 111 1011
{"#*2", 'X'}, // (Note 1) 111 1100
{"#*3", 'X'}, // (Note 1) 111 1101
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
+#include "spandsp/fast_convert.h"
+#include "spandsp/math_fixed.h"
+#include "spandsp/saturated.h"
#include "spandsp/complex.h"
#include "spandsp/vector_float.h"
#include "spandsp/complex_vector_float.h"
#include "v22bis_rx_2400_floating_rrc.h"
#endif
-#define ms_to_symbols(t) (((t)*600)/1000)
+#define ms_to_symbols(t) (((t)*600)/1000)
/*! The adaption rate coefficient for the equalizer */
-#define EQUALIZER_DELTA 0.25f
+#define EQUALIZER_DELTA 0.25f
/*! The number of phase shifted coefficient set for the pulse shaping/bandpass filter */
-#define PULSESHAPER_COEFF_SETS 12
+#define PULSESHAPER_COEFF_SETS 12
/*
The basic method used by the V.22bis receiver is:
static complexf_t training_get(v22bis_state_t *s)
{
- complexf_t z;
int bits;
+ static const complexf_t zero = {0.0f, 0.0f};
/* V.22bis training sequence */
switch (s->tx.training)
case V22BIS_TX_TRAINING_STAGE_INITIAL_SILENCE:
/* Silence */
s->tx.constellation_state = 0;
- z = complex_setf(0.0f, 0.0f);
- break;
+ return zero;
case V22BIS_TX_TRAINING_STAGE_U11:
/* Send continuous unscrambled ones at 1200bps (i.e. 270 degree phase steps). */
/* Only the answering modem sends unscrambled ones. It is the first thing exchanged between the modems. */
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[3]) & 3;
- z = v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
- break;
+ return v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
case V22BIS_TX_TRAINING_STAGE_U0011:
/* Continuous unscrambled double dibit 00 11 at 1200bps. This is termed the S1 segment in
the V.22bis spec. It is only sent to request or accept 2400bps mode, and lasts 100+-3ms. After this
timed burst, we unconditionally change to sending scrambled ones at 1200bps. */
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[3*(s->tx.training_count & 1)]) & 3;
- z = v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
if (++s->tx.training_count >= ms_to_symbols(100))
{
span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting S11 after U0011\n");
s->tx.training = V22BIS_TX_TRAINING_STAGE_TIMED_S11;
}
}
- break;
+ return v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
case V22BIS_TX_TRAINING_STAGE_TIMED_S11:
/* A timed period of scrambled ones at 1200bps. */
if (++s->tx.training_count >= ms_to_symbols(756))
bits = scramble(s, 1);
bits = (bits << 1) | scramble(s, 1);
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[bits]) & 3;
- z = v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
- break;
+ return v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
case V22BIS_TX_TRAINING_STAGE_S1111:
/* Scrambled ones at 2400bps. We send a timed 200ms burst, and switch to normal operation at 2400bps */
bits = scramble(s, 1);
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[bits]) & 3;
bits = scramble(s, 1);
bits = (bits << 1) | scramble(s, 1);
- z = v22bis_constellation[(s->tx.constellation_state << 2) | bits];
if (++s->tx.training_count >= ms_to_symbols(200))
{
/* We have completed training. Now handle some real work. */
v22bis_report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED);
s->tx.current_get_bit = s->get_bit;
}
- break;
- case V22BIS_TX_TRAINING_STAGE_PARKED:
- default:
- z = complex_setf(0.0f, 0.0f);
- break;
+ return v22bis_constellation[(s->tx.constellation_state << 2) | bits];
}
- return z;
+ return zero;
}
/*- End of function --------------------------------------------------------*/
x.im += tx_pulseshaper[39 - s->tx.baud_phase][i]*s->tx.rrc_filter[i + s->tx.rrc_filter_step].im;
}
/* Now create and modulate the carrier */
- z = dds_complexf(&(s->tx.carrier_phase), s->tx.carrier_phase_rate);
+ z = dds_complexf(&s->tx.carrier_phase, s->tx.carrier_phase_rate);
famp = (x.re*z.re - x.im*z.im)*s->tx.gain;
if (s->tx.guard_phase_rate && (s->tx.rrc_filter[s->tx.rrc_filter_step].re != 0.0f || s->tx.rrc_filter[s->tx.rrc_filter_step].im != 0.0f))
{
/* Add the guard tone */
- famp += dds_modf(&(s->tx.guard_phase), s->tx.guard_phase_rate, s->tx.guard_level, 0);
+ famp += dds_modf(&s->tx.guard_phase, s->tx.guard_phase_rate, s->tx.guard_tone_gain, 0);
}
/* Don't bother saturating. We should never clip. */
amp[sample] = (int16_t) lfastrintf(famp);
l = 1.6f*powf(10.0f, (power - 1.0f - DBM0_MAX_POWER)/20.0f);
s->tx.gain = l*32768.0f/(TX_PULSESHAPER_GAIN*3.0f);
l = powf(10.0f, (power - 1.0f - 3.0f - DBM0_MAX_POWER)/20.0f);
- s->tx.guard_level = l*32768.0f;
+ s->tx.guard_tone_gain = l*32768.0f;
}
else if(s->tx.guard_phase_rate == dds_phase_ratef(1800.0f))
{
l = 1.6f*powf(10.0f, (power - 1.0f - 1.0f - DBM0_MAX_POWER)/20.0f);
s->tx.gain = l*32768.0f/(TX_PULSESHAPER_GAIN*3.0f);
l = powf(10.0f, (power - 1.0f - 6.0f - DBM0_MAX_POWER)/20.0f);
- s->tx.guard_level = l*32768.0f;
+ s->tx.guard_tone_gain = l*32768.0f;
}
else
{
l = 1.6f*powf(10.0f, (power - DBM0_MAX_POWER)/20.0f);
s->tx.gain = l*32768.0f/(TX_PULSESHAPER_GAIN*3.0f);
- s->tx.guard_level = 0;
+ s->tx.guard_tone_gain = 0;
}
}
/*- End of function --------------------------------------------------------*/
enum
{
- V8_WAIT_1S,
+ V8_WAIT_1S, /* Start point when sending CI */
+ V8_AWAIT_ANSAM, /* Start point when sending initial silence */
V8_CI_ON,
V8_CI_OFF,
V8_HEARD_ANSAM,
enum
{
V8_CI_SYNC_OCTET = 0x00,
- V8_CM_JM_SYNC_OCTET = 0xE0
+ V8_CM_JM_SYNC_OCTET = 0xE0,
+ V8_V92_SYNC_OCTET = 0x55
};
SPAN_DECLARE(const char *) v8_call_function_to_str(int call_function)
}
/*- End of function --------------------------------------------------------*/
-static void v8_send_ci(v8_state_t *s)
+static void send_v92(v8_state_t *s)
+{
+ int i;
+
+ if (s->result.v92 >= 0)
+ {
+ /* Send 2 V.92 packets */
+ for (i = 0; i < 2; i++)
+ {
+ v8_put_preamble(s);
+ v8_put_byte(s, V8_V92_SYNC_OCTET);
+ v8_put_byte(s, s->result.v92);
+ }
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void send_ci(v8_state_t *s)
{
int i;
if ((s->negotiation_timer -= len) > 0)
break;
fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]);
- v8_send_ci(s);
+ send_ci(s);
s->state = V8_CI_ON;
s->fsk_tx_on = TRUE;
break;
{
/* Try again */
fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]);
- v8_send_ci(s);
+ send_ci(s);
s->state = V8_CI_ON;
s->fsk_tx_on = TRUE;
}
}
break;
+ case V8_AWAIT_ANSAM:
+ residual_samples = modem_connect_tones_rx(&s->ansam_rx, amp, len);
+ /* Check if an ANSam or ANSam/ tone has been detected */
+ if ((tone = modem_connect_tones_rx_get(&s->ansam_rx)) != MODEM_CONNECT_TONES_NONE)
+ handle_modem_connect_tone(s, tone);
+ break;
case V8_HEARD_ANSAM:
/* We have heard the ANSam or ANSam/ signal, but we still need to wait for the
end of the Te timeout period to comply with the spec. */
v8_decode_init(s);
s->negotiation_timer = ms_to_samples(5000);
fsk_tx_restart(&s->v21tx, &preset_fsk_specs[FSK_V21CH1]);
+ send_v92(s);
send_cm_jm(s);
s->state = V8_CM_ON;
s->fsk_tx_on = TRUE;
s->calling_party = calling_party;
if (s->calling_party)
{
- s->state = V8_WAIT_1S;
- s->negotiation_timer = ms_to_samples(1000);
- s->ci_count = 0;
+ if (s->result.send_ci)
+ {
+ s->state = V8_WAIT_1S;
+ s->negotiation_timer = ms_to_samples(1000);
+ s->ci_count = 0;
+ }
+ else
+ {
+ s->state = V8_AWAIT_ANSAM;
+ }
modem_connect_tones_rx_init(&s->ansam_rx, MODEM_CONNECT_TONES_ANS_PR, NULL, NULL);
fsk_tx_init(&s->v21tx, &preset_fsk_specs[FSK_V21CH1], get_bit, s);
}
SPAN_DECLARE(void) vec_negatef(float z[], const float x[], int n)
{
int i;
- static const uint32_t mask = 0x80000000;
- static const float *fmask = (float *) &mask;
+ static const uint32_t mask = 0x80000000;
+ static const float *fmask = (float *) &mask;
__m128 n1;
__m128 n2;
for (i -= 4; i >= 0; i -= 4)
{
n1 = _mm_loadu_ps(x + i);
- n1 = _mm_xor_ps(n1, n2);
+ n1 = _mm_xor_ps(n1, n2);
_mm_storeu_ps(z + i, n1);
}
}
typedef struct faxtester_state_s faxtester_state_t;
-typedef void (faxtester_flush_handler_t)(faxtester_state_t *s, void *user_data, int which);
+typedef void (*faxtester_flush_handler_t)(faxtester_state_t *s, void *user_data, int which);
/*!
FAX tester real time frame handler.
\param msg The HDLC message.
\param len The length of the message.
*/
-typedef void (faxtester_real_time_frame_handler_t)(faxtester_state_t *s,
- void *user_data,
- int direction,
- const uint8_t *msg,
- int len);
+typedef void (*faxtester_real_time_frame_handler_t)(faxtester_state_t *s,
+ void *user_data,
+ int direction,
+ const uint8_t *msg,
+ int len);
-typedef void (faxtester_front_end_step_complete_handler_t)(faxtester_state_t *s, void *user_data);
+typedef void (*faxtester_front_end_step_complete_handler_t)(faxtester_state_t *s, void *user_data);
/*!
FAX tester descriptor.
/*! \brief Pointer to our current step in the test. */
xmlNodePtr cur;
- faxtester_flush_handler_t *flush_handler;
+ faxtester_flush_handler_t flush_handler;
void *flush_user_data;
/*! \brief A pointer to a callback routine to be called when frames are
exchanged. */
- faxtester_real_time_frame_handler_t *real_time_frame_handler;
+ faxtester_real_time_frame_handler_t real_time_frame_handler;
/*! \brief An opaque pointer supplied in real time frame callbacks. */
void *real_time_frame_user_data;
- faxtester_front_end_step_complete_handler_t *front_end_step_complete_handler;
+ faxtester_front_end_step_complete_handler_t front_end_step_complete_handler;
void *front_end_step_complete_user_data;
- faxtester_front_end_step_complete_handler_t *front_end_step_timeout_handler;
+ faxtester_front_end_step_complete_handler_t front_end_step_timeout_handler;
void *front_end_step_timeout_user_data;
const uint8_t *image_buffer;
void faxtester_send_hdlc_msg(faxtester_state_t *s, const uint8_t *msg, int len, int crc_ok);
-void faxtester_set_flush_handler(faxtester_state_t *s, faxtester_flush_handler_t *handler, void *user_data);
+void faxtester_set_flush_handler(faxtester_state_t *s, faxtester_flush_handler_t handler, void *user_data);
/*! Select whether silent audio will be sent when FAX transmit is idle.
\brief Select whether silent audio will be sent when FAX transmit is idle.
*/
void faxtester_set_tep_mode(faxtester_state_t *s, int use_tep);
-void faxtester_set_real_time_frame_handler(faxtester_state_t *s, faxtester_real_time_frame_handler_t *handler, void *user_data);
+void faxtester_set_real_time_frame_handler(faxtester_state_t *s, faxtester_real_time_frame_handler_t handler, void *user_data);
-void faxtester_set_front_end_step_complete_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t *handler, void *user_data);
+void faxtester_set_front_end_step_complete_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t handler, void *user_data);
-void faxtester_set_front_end_step_timeout_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t *handler, void *user_data);
+void faxtester_set_front_end_step_timeout_handler(faxtester_state_t *s, faxtester_front_end_step_complete_handler_t handler, void *user_data);
void faxtester_set_timeout(faxtester_state_t *s, int timeout);
#define UDPTL_BUF_MASK 15
-typedef int (udptl_rx_packet_handler_t) (void *user_data, const uint8_t msg[], int len, int seq_no);
+typedef int (*udptl_rx_packet_handler_t) (void *user_data, const uint8_t msg[], int len, int seq_no);
typedef struct
{
struct udptl_state_s
{
- udptl_rx_packet_handler_t *rx_packet_handler;
+ udptl_rx_packet_handler_t rx_packet_handler;
void *user_data;
/*! This option indicates the error correction scheme used in transmitted UDPTL