dect
/
asterisk
Archived
13
0
Fork 0

do v21 detection instead of CED detection for the fax gateway

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@327769 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
mnicholson 2011-07-12 15:23:24 +00:00
parent d75b0b09d3
commit f4348dccf2
3 changed files with 170 additions and 45 deletions

View File

@ -45,7 +45,8 @@
#define DSP_FAXMODE_DETECT_CNG (1 << 0)
#define DSP_FAXMODE_DETECT_CED (1 << 1)
#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED)
#define DSP_FAXMODE_DETECT_V21 (1 << 2)
#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED | DSP_FAXMODE_DETECT_V21)
#define DSP_TONE_STATE_SILENCE 0
#define DSP_TONE_STATE_RINGING 1

View File

@ -244,6 +244,20 @@ typedef struct
} tone_detect_state_t;
typedef struct
{
int block_size;
goertzel_state_t tone;
float energy; /* Accumulated energy of the current block */
int samples_pending; /* Samples remain to complete the current block */
float threshold; /* Energy of the tone relative to energy from all other signals to consider a hit */
int hit_count;
int miss_count;
} v21_detect_state_t;
typedef struct
{
goertzel_state_t row_out[4];
@ -391,6 +405,7 @@ struct ast_dsp {
digit_detect_state_t digit_state;
tone_detect_state_t cng_tone_state;
tone_detect_state_t ced_tone_state;
v21_detect_state_t v21_state;
};
static void mute_fragment(struct ast_dsp *dsp, fragment_t *fragment)
@ -463,10 +478,55 @@ static void ast_tone_detect_init(tone_detect_state_t *s, int freq, int duration,
ast_debug(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required);
}
static void ast_v21_detect_init(v21_detect_state_t *s, unsigned int sample_rate)
{
float x;
int periods_in_block;
/* If we want to remove tone, it is important to have block size not
to exceed frame size. Otherwise by the moment tone is detected it is too late
to squelch it from previous frames. Block size is 20ms at the given sample rate.*/
s->block_size = (20 * sample_rate) / 1000;
periods_in_block = s->block_size * 1850 / sample_rate;
/* Make sure we will have at least 5 periods at target frequency for analisys.
This may make block larger than expected packet and will make squelching impossible
but at least we will be detecting the tone */
if (periods_in_block < 5)
periods_in_block = 5;
/* Now calculate final block size. It will contain integer number of periods */
s->block_size = periods_in_block * sample_rate / 1850;
goertzel_init(&s->tone, 1850.0, s->block_size, sample_rate);
s->samples_pending = s->block_size;
s->hit_count = 0;
s->miss_count = 0;
s->energy = 0.0;
/* We want tone energy to be amp decibels above the rest of the signal (the noise).
According to Parseval's theorem the energy computed in time domain equals to energy
computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result)
from the energy in the time domain we will get energy of the remaining signal (without the tone
we are detecting). We will be checking that
10*log(Ew / (Et - Ew)) > amp
Calculate threshold so that we will be actually checking
Ew > Et * threshold
*/
x = pow(10.0, 16 / 10.0);
s->threshold = x / (x + 1);
ast_debug(1, "Setup v21 detector, block_size=%d\n", s->block_size);
}
static void ast_fax_detect_init(struct ast_dsp *s)
{
ast_tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB, s->sample_rate);
ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB, s->sample_rate);
ast_v21_detect_init(&s->v21_state, s->sample_rate);
}
static void ast_dtmf_detect_init (dtmf_detect_state_t *s, unsigned int sample_rate)
@ -513,6 +573,89 @@ static void ast_digit_detect_init(digit_detect_state_t *s, int mf, unsigned int
}
}
/*! \brief Detect a v21 preamble.
* This code is derived from the tone_detect code and detects a pattern of 1850
* Hz tone found in a v21 preamble.
*/
static int v21_detect(struct ast_dsp *dsp, v21_detect_state_t *s, int16_t *amp, int samples)
{
float tone_energy;
int i;
int hit = 0;
int limit;
int res = 0;
int16_t *ptr;
int start, end;
for (start = 0; start < samples; start = end) {
/* Process in blocks. */
limit = samples - start;
if (limit > s->samples_pending) {
limit = s->samples_pending;
}
end = start + limit;
for (i = limit, ptr = amp ; i > 0; i--, ptr++) {
/* signed 32 bit int should be enough to suqare any possible signed 16 bit value */
s->energy += (int32_t) *ptr * (int32_t) *ptr;
goertzel_sample(&s->tone, *ptr);
}
s->samples_pending -= limit;
if (s->samples_pending) {
/* Finished incomplete (last) block */
break;
}
tone_energy = goertzel_result(&s->tone);
/* Scale to make comparable */
tone_energy *= 2.0;
s->energy *= s->block_size;
ast_debug(10, "v21 1850 Ew=%.2E, Et=%.2E, s/n=%10.2f\n", tone_energy, s->energy, tone_energy / (s->energy - tone_energy));
hit = 0;
if (tone_energy > s->energy * s->threshold) {
ast_debug(10, "Hit! count=%d; miss_count=%d\n", s->hit_count, s->miss_count);
hit = 1;
}
if (hit) {
if (s->hit_count == 0 || s->miss_count == 3) {
s->hit_count++;
} else {
s->hit_count = 0;
}
s->miss_count = 0;
} else {
s->miss_count++;
if (s->miss_count > 3) {
s->hit_count = 0;
}
}
if (s->hit_count == 4) {
ast_debug(1, "v21 preamble detected\n");
res = 1;
}
/* Reinitialise the detector for the next block */
goertzel_reset(&s->tone);
/* Advance to the next block */
s->energy = 0.0;
s->samples_pending = s->block_size;
amp += limit;
}
return res;
}
static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp, int samples)
{
float tone_energy;
@ -1441,6 +1584,10 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
if ((dsp->faxmode & DSP_FAXMODE_DETECT_CED) && tone_detect(dsp, &dsp->ced_tone_state, shortdata, len)) {
fax_digit = 'e';
}
if ((dsp->faxmode & DSP_FAXMODE_DETECT_V21) && v21_detect(dsp, &dsp->v21_state, shortdata, len)) {
fax_digit = 'g';
}
}
if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {

View File

@ -246,8 +246,6 @@ struct fax_gateway {
struct ast_fax_tech_token *token;
/*! \brief the start of our timeout counter */
struct timeval timeout_start;
/*! \brief the start of our ced timeout */
struct timeval ced_timeout_start;
/*! \brief DSP Processor */
struct ast_dsp *chan_dsp;
struct ast_dsp *peer_dsp;
@ -255,8 +253,8 @@ struct fax_gateway {
int framehook;
/*! \brief bridged */
int bridged:1;
/*! \brief 1 if the ced tone came from chan, 0 if it came from peer */
int ced_chan:1;
/*! \brief 1 if a v21 preamble has been detected */
int detected_v21:1;
/*! \brief a flag to track the state of our negotiation */
enum ast_t38_state t38_state;
/*! \brief original audio formats */
@ -273,7 +271,6 @@ static int fax_logger_level = -1;
#define RES_FAX_TIMEOUT 10000
#define FAX_GATEWAY_TIMEOUT RES_FAX_TIMEOUT
#define FAX_GATEWAY_CED_TIMEOUT 3000
/*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */
static struct {
@ -2412,10 +2409,10 @@ static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *detai
gateway->framehook = -1;
ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_CED);
ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21);
ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CED);
ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21);
details->caps = AST_FAX_TECH_GATEWAY;
if (!(gateway->s = fax_session_reserve(details, &gateway->token))) {
@ -2503,21 +2500,17 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
gateway->t38_state = T38_STATE_NEGOTIATING;
gateway->timeout_start = ast_tvnow();
gateway->ced_timeout_start.tv_sec = 0;
gateway->ced_timeout_start.tv_usec = 0;
ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name);
return fp;
}
static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
{
struct ast_frame *dfr = ast_frdup(f);
struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
struct ast_channel *other = (active == chan) ? peer : chan;
/* if we have already detected a CED tone, don't waste time here */
if (!ast_tvzero(gateway->ced_timeout_start)) {
if (gateway->detected_v21) {
return f;
}
@ -2529,17 +2522,13 @@ static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, str
return f;
}
if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'e') {
if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') {
gateway->detected_v21 = 1;
if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
if (ast_channel_get_t38_state(active) == T38_STATE_UNKNOWN) {
gateway->ced_timeout_start = ast_tvnow();
gateway->ced_chan = (active == chan);
ast_debug(1, "detected CED tone from %s; will schedule T.38 request on %s\n", active->name, other->name);
} else {
return fax_gateway_request_t38(gateway, chan, f);
}
ast_debug(1, "detected v21 preamble from %s\n", active->name);
return fax_gateway_request_t38(gateway, chan, f);
} else {
ast_debug(1, "detected CED tone on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
ast_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
}
}
@ -2593,9 +2582,6 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) {
enum ast_t38_state state = ast_channel_get_t38_state(other);
gateway->ced_timeout_start.tv_sec = 0;
gateway->ced_timeout_start.tv_usec = 0;
if (state == T38_STATE_UNKNOWN) {
/* we detected a request to negotiate T.38 and the
* other channel appears to support T.38, we'll pass
@ -2635,7 +2621,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
return &ast_null_frame;
} else if (gateway->t38_state == T38_STATE_NEGOTIATING) {
/* we got a request to negotiate T.38 after we already
* sent one to the other party based on CED tone
* sent one to the other party based on v21 preamble
* detection. We'll just pretend we passed this request
* through in the first place. */
@ -2643,12 +2629,12 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
gateway->t38_state = T38_STATE_UNKNOWN;
gateway->timeout_start = ast_tvnow();
ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on CED detection\n", active->name);
ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble detection\n", active->name);
ao2_ref(details, -1);
return &ast_null_frame;
} else if (gateway->t38_state == T38_STATE_NEGOTIATED) {
/* we got a request to negotiate T.38 after we already
* sent one to the other party based on CED tone
* sent one to the other party based on v21 preamble
* detection and received a response. We need to
* respond to this and shut down the gateway. */
@ -2869,7 +2855,8 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
gateway->timeout_start = ast_tvnow();
/* we are bridged, change r/w formats to SLIN for CED detection and T.30 */
/* we are bridged, change r/w formats to SLIN for v21 preamble
* detection and T.30 */
ast_format_copy(&gateway->chan_read_format, &chan->readformat);
ast_format_copy(&gateway->chan_write_format, &chan->readformat);
@ -2944,20 +2931,10 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
return fax_gateway_detect_t38(gateway, chan, peer, active, f);
}
/* handle the ced timeout delay */
if (!ast_tvzero(gateway->ced_timeout_start)) {
if (ast_tvdiff_ms(ast_tvnow(), gateway->ced_timeout_start) > FAX_GATEWAY_CED_TIMEOUT) {
if (gateway->ced_chan && chan == active) {
return fax_gateway_request_t38(gateway, chan, f);
} else if (!gateway->ced_chan && peer == active) {
return fax_gateway_request_t38(gateway, chan, f);
}
}
} else if (gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
/* not in gateway mode and have not detected CED yet, listen
* for CED */
/* XXX this should detect a v21 preamble instead of CED */
return fax_gateway_detect_ced(gateway, chan, peer, active, f);
if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
/* not in gateway mode and have not detected v21 yet, listen
* for v21 */
return fax_gateway_detect_v21(gateway, chan, peer, active, f);
}
/* in gateway mode, gateway some packets */
@ -2981,7 +2958,7 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
}
/* force silence on the line if T.38 negotiation might be taking place */
if (!ast_tvzero(gateway->ced_timeout_start) || (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED)) {
if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) {
if (f->frametype == AST_FRAME_VOICE && f->subclass.format.id == AST_FORMAT_SLINEAR) {
short silence_buf[f->samples];
struct ast_frame silence_frame = {