miércoles, julio 29, 2009

Asterisk DTMF Alarms Telecare and other industrial appliances

Alarms, Tele care devices and other industrial appliances, use DTMF tones based protocol for controlling the communication or for transmit information. In a lot of scenarios Asterisk can be a great "bridge" server to connect this kind of appliances, but in some cases we can find some weakness/problems related with Asterisk implementation or with VoIP.

The goal of this article is trying to expose some problems we can encounter connecting this kind of devices to Asterisk and if applicable the solutions we can use. In a lot of cases this solutions include adapting the Asterisk code.

Asterisk implementation problems (for DTMFs exchange):

1) DTMFs/DTMFs Gaps timings
Asterisk defines minimal DTMF tone duration and minimal amount of time between two consecutive DTMFs tones. Using this constants Asterisk can decide to ignore a DTMF or to regenerate a DTMF tone with a greater duration than the original.
The related code can be found at main/channel.c.
The normal values defined are:
#define AST_MIN_DTMF_DURATION 80
#define AST_MIN_DTMF_GAP 45
For resolve some problems we found with some Telecare devices (sending DTMF with 50 ms duration), we changed this values to:
#define AST_MIN_DTMF_DURATION 30
#define AST_MIN_DTMF_GAP 20
This problem can be detected with Asterisk 1.4.x and 1.6.x versions.

2) Initial weird sound at DTMFs tone beginning
When the DTMF are sent inband, Asterisk is always listening the audio channel to detect the DTMFs and execute the corresponding action. Sometimes the action is to activate a feature, or select an option in an IVR or in our case to regenerate the DTMF tone in the output channel. In the last mentioned case, at the output channel we can hear the original RTP of the DTMF (a few milliseconds), followed by few milliseconds of silence, and followed with the regenerated DTMF tone.
The first few milliseconds is the time used by the DSP (main/dsp.c) code to detect the DTMF, the silence is the time between the DSP module detect the DTMF and the regenerated DTMF tone begin.
For example, if we use RFC2833 for DTMF signaling, we can receive a few milliseconds of RTP original DTMF, followed by the DTMF tone as RFC2833 signal, so we can hear a "little" weird sound at the begining of the DTMF tone.

The first and second periods can be improved (reduced) changing the Asterisk DSP code, but it seems a difficult patch. Other possibility is to inhibit Asterisk from detecting DTMFs in inband audio. Of course it can be easy to avoid the DTMF detection globally (for example following http://astrecipes.net/index.php?n=248), but a better solution can be some kind of DTMF detection/regeneration configuration at channel level. Anyone can help me to implement this patch???

We don't have any proof that this "little" weird sound affect the DTMFs exchange with the devices we usually use, but of course, it won't be a great surprise if in the future we have problems with other devices.
As far as we know, this problem can be detected with Asterisk 1.4.x and 1.6.x versions.

3) Incorrect regeneration of DTMFs when conversion from inband to rfc2833
We take the following scenario:
  • Tlf A (DTMFs inband) (SIP/MGCP/ZAP/ etc... Always having the DTMFs in audio, codec G711 a or u law)
  • Tlf B (DTMFs rfc2833)
  • Asterisk bridges channels Tlf A and Tlf B

In this scenario and independent who initiates the call, if on the channel using inband a DTMF is received, on the receiving channel (Tlf B) we hear the original RTP of the DTMF (a short time, just the same as described in point 2), followed by silence for the whole duration of the DTMF, and followed (when the received DTMF ends on the inband channel) with the regenerated DTMF tone in rfc2833 for a fixed time (100 ms).

E.g. If on channel A we receive inband a 2 seconds long DTMF (keep the button 2 secs pushed), we hear on channel B (rfc2833) the short tone when the DTMF begins, 2 seconds of silence and then the DTMF tone of 100 ms.

Looking at traces in the Asterisk it looks like the Asterisk only detects the end of the DTMF and not the beginning.
DTMF end '1' received on SIP/mta419-1-081efc60, duration 0 ms
DTMF begin emulation of '1' with duration 100 queued on SIP/mta419-1-081efc60
DTMF end emulation of '1' queued on SIP/mta419-1-081efc60

DTMF end '2' received on SIP/mta419-1-081efc60, duration 0 ms
DTMF begin emulation of '2' with duration 100 queued on SIP/mta419-1-081efc60
DTMF end emulation of '2' queued on SIP/mta419-1-081efc60

In this same call a DTMF generated by B (rfc2833) is heard perfectly on side A (inband) (not taking into account the initial short tone)
DTMF begin '1' received on SIP/mta419-2-081adcd0
DTMF begin passthrough '1' on SIP/mta419-2-081adcd0
DTMF end '1' received on SIP/mta419-2-081adcd0, duration 5170 ms
DTMF end accepted with begin '1' on SIP/mta419-2-081adcd0
DTMF end passthrough '1' on SIP/mta419-2-081adcd0

This behavior was verified in versions Asterisk 1.4.17, 1.4.24-rsp, 1.4-svn-trunk and 1.6.0, Although it looks like it has been fixed in 1.6.1, and now we were trying to determine at what point in the code it has been fixed to try to backport the fix to the 1.4.x code.

4) Asterisk detects a valid DTMF tone as a DTMF echo, and ignores it.
Thanks to Iñaki Civico for reporting the problem and the possible solution.
In this case we can find at the Asterisk logs the message "Ignore potential DTMF echo from ..." This message indicates that Asterisk thinks that there is no enough separation between the end of the previous DTMF and the DTMF we are trying to send now.

The related code can be found at main/rtp.c
static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type)
{
if (((ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && type == AST_FRAME_DTMF_END) ||
(type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) {
ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr));
...
}
The rtp->dtmfmute is initiated to 500ms in the same file:
int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit)
{
...
rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
..
}

int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit)
{
...
rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
...
}
Of course, to avoid this problem we can change the initialization of rtp->dtmfmute to a smaller amount of time.
This problem can be detected with Asterisk 1.4.x and 1.6.x versions.


This Article is published at oss.alea-soluciones.com AsteriskDTMF