Bug in DTMF Decode sample?

I built the DTM Detection sample, unmodified, and downloaded it to a Fastrack Xtend with R7.4a.

It detects DTMF, but almost always gives a “double” trigger - a short false trigger, followed by the true symbol; eg,

The DTMF spec requires a minimum of 40ms before a valid symbol is recognised, so these can’t be right (at only 10 or 20ms) - so there must be a bug somewhere…

Yes, it’d be easy enough to work-around by having the application ignore any “too-short” symbols - but it shouldn’t be necessary!

Hiya,

I know this doesn’t help the fact that the DTMF example is misbehaving, but…

I don’t recall seeing the issue that you are having, but below is the DTMF setup code that I’m using without any troubles.

void voice_dtmfSubscribe( void )
{
        s32 ret;
        s32 bufferSize;

        s32 rawSampleDuration;
        s32 dtmfDuration;

        // subscribe to Audio Stream
        Voice_DtmfAudioStreamHandle = adl_audioSubscribe( ADL_AUDIO_VOICE_CALL_RX,
                                voice_dtmfAudioEventHandler, ADL_AUDIO_RESOURCE_OPTION_FORBID_PREEMPTION );

        ret = adl_audioGetOption( Voice_DtmfAudioStreamHandle,
                        ADL_AUDIO_RAW_DTMF_SAMPLE_DURATION, &rawSampleDuration);
        TRACE ((TRACE_VOICE_BASE, "DTMF: audioGetOption returns [%d], dtmf duration = [%d]", ret, rawSampleDuration));

        dtmfDuration = rawSampleDuration * ADL_AUDIO_MAX_DTMF_PER_FRAME * 10;

        // configure buffer and stream options
    ret = adl_audioSetOption ( Voice_DtmfAudioStreamHandle,
                                ADL_AUDIO_DTMF_DETECT_BLANK_DURATION, dtmfDuration  );
        TRACE ((TRACE_VOICE_BASE, "DTMF: audioSetOption returns [%d], duration = [%d]", ret, dtmfDuration));

        ret = adl_audioGetOption( Voice_DtmfAudioStreamHandle,
                        ADL_AUDIO_DTMF_PROCESSED_STREAM_BUFFER_SIZE, &bufferSize);
        TRACE ((TRACE_VOICE_BASE, "DTMF: audioGetOption returns [%d], buffer = [%d]", ret, bufferSize));

        // Buffer Memory Allocation
        if (Voice_DtmfStreamBuffer != NULL)
        {
                adl_memRelease( Voice_DtmfStreamBuffer );
        }
        Voice_DtmfStreamBuffer = adl_memGet( bufferSize );

        // now connect to the audio stream
        ret = adl_audioStreamListen(Voice_DtmfAudioStreamHandle, ADL_AUDIO_DTMF,
                                        Voice_DtmfLowIrqHandle, Voice_DtmfHighIrqHandle, Voice_DtmfStreamBuffer );
        TRACE ((TRACE_VOICE_BASE, "DTMF audioStreamListen returns [%d]", ret));

        return;
}

Note that I am setting the DTMF duration and the blank duration myself…can’t remember why this was done, but it did come out of one of the app notes or the ADL guide. 8)

Hope this helps some.

ciao, Dave

The Guide has this to say:

Is that what you were thinking of?

It is typically unhelpful in giving no guidance whatsoever on how to choose a suitable multiple! :unamused: :angry:

Your multiple seems to be ten - was there any particular reason for that

The sample is certainly not enforcing the highlighted requirement!

As in:

dtmfDuration = rawSampleDuration * ADL_AUDIO_MAX_DTMF_PER_FRAME * 10;

        // configure buffer and stream options
    ret = adl_audioSetOption ( Voice_DtmfAudioStreamHandle,
                                ADL_AUDIO_DTMF_DETECT_BLANK_DURATION, dtmfDuration  );

ie, you’re setting the blank duration to:
rawSampleDuration * ADL_AUDIO_MAX_DTMF_PER_FRAME * 10

  • On both my Fastrack Supreme and Xtend, rawSampleDuration = 10

  • ADL_AUDIO_MAX_DTMF_PER_FRAME is #defined as 2 in adl_audio.h

So the blank duration comes out to 10 * 2 * 10 = 200

So I put AT+BLANK=200 into the sample, and the behaviour remains the same - still the double triggering!

And the Raw detection doesn’t seem to work at all!

OK: I’ve taken davidc’s initialisation, and just the minimal IRQ and Event handlers - and I still get the double-detection. :angry:

Here it is:

#include "adl_global.h"

/******************************************************************************
 * Open-AT mandatory Variables
 *****************************************************************************/
const u16 wm_apmCustomStackSize 	  = 4096*3;
const u32 wm_apmIRQLowLevelStackSize  = 4096;
const u32 wm_apmIRQHighLevelStackSize = 4096;


/******************************************************************************
 * Globals
 *****************************************************************************/
s32	voice_DtmfAudioStreamHandle;	// Handle for the Audio Subscription
s32 voice_DTMFLowIrqHandle;			// Handle for the Low-Level  IRQ
s32 voice_DTMFHighIrqHandle;		// Handle for the High-Level IRQ

adl_audioPostProcessedDecoder_t * postProcessedDTMF;   // Post-processed DTMF

void * pDtmfStreamBuffer;	// Audio stream buffer

ascii dtmfResultStr [300];	// For tracing


/******************************************************************************
 * Audio Event Handler
 *****************************************************************************/
void voice_dtmfAudioEventHandler( s32 audioHandle, adl_audioEvents_e audioEvent )
{
	TRACE(( 1, "Audio Ev: %d", audioEvent ));
}


/******************************************************************************
 * Low-Level IRQ Handler:
 *
 * Retrieves the post-processed DTMF Data, and checks the duration;
 * If the duration is too short to be valid, it is ignored (returns FALSE);
 * If the duration is valid, returns TRUE so that the High-Level IRQ Handler
 * will be called to deal with it.
 *****************************************************************************/
bool voice_DtmfLowIrqHandler( adl_irqID_e src, adl_irqNotificationLevel_e lvl, adl_irqEventData_t *Data )
{
    TRACE(( 1, "LoIRQ: Ctx=%p Inst=%d Usr=%p Src=%p Buf=%p",
							Data->Context,
							Data->Instance,
							Data->UserData,
							Data->SourceData,
							((adl_audioStream_t*)Data->SourceData)->DataBuffer
    ));

    if( ((adl_audioPostProcessedDecoder_t*)((adl_audioStream_t*)Data->SourceData)->DataBuffer)->Duration > 4 )
    {	// Valid DTMF duration;
    	// Cause ADL to call the High level IRQ handler.
		return TRUE;
    }
    else
    {	// Invalid DTMF duration (too short);
    	// Do not cause ADL to call the High level IRQ handler (ie, ignore it!)

    	// retrieve DTMF - just for the Trace
        postProcessedDTMF =  ((adl_audioPostProcessedDecoder_t*)((adl_audioStream_t*)Data->SourceData)->DataBuffer) ;

        TRACE(( 1, "   PP-DTMF: %c; dur=%d ms - IGNORE!",
    							postProcessedDTMF->DecodedDTMF,
    							(postProcessedDTMF->Duration*10)
    	));
    	return FALSE;
    }
}


/******************************************************************************
 * High-Level IRQ Handler:
 *
 * Outputs the received DTMF symbol in an AT Response
 * (assumes that the Low-Level IRQ Hndler has already extracted the data)
 *****************************************************************************/
bool voice_DtmfHighIrqHandler( adl_irqID_e src, adl_irqNotificationLevel_e lvl, adl_irqEventData_t *Data )
{
	// retrieve DTMF
    postProcessedDTMF =  ((adl_audioPostProcessedDecoder_t*)((adl_audioStream_t*)Data->SourceData)->DataBuffer) ;

    wm_sprintf ( dtmfResultStr, "Post-proc DTMF: '%c'; duration: %d ms\r\n", postProcessedDTMF->DecodedDTMF, (postProcessedDTMF->Duration*10) );
    adl_atSendResponse( ADL_AT_RSP, dtmfResultStr );
    TRACE(( 1, dtmfResultStr ));

    return FALSE;	// The return value is not used for a High-Level handler
}

/******************************************************************************
 * Open-AT Entry Point
 *
 *****************************************************************************/
void adl_main ( adl_InitType_e initType )
{
	s32 result;	// For API return result codes

	// Values for the Audio option Get/Set functions;
	// they all require a type of s32.
	s32 rawSampleDuration;
	s32 dtmfBlankDuration;
	s32 bufferSize;

    TRACE(( 1, "\n" ));
    TRACE(( 1, "Embedded Application: Main; Init=%d\n", initType ));

    /* DTMF decoding test application */
    TRACE(( 1, "DTMF Listener" ));
    TRACE(( 1, __DATE__ ));
    TRACE(( 1, __TIME__ ));


	// Subscribe the IRQ Handlers
	voice_DTMFLowIrqHandle  = adl_irqSubscribe( voice_DtmfLowIrqHandler,  ADL_IRQ_NOTIFY_LOW_LEVEL,  ADL_IRQ_PRIORITY_LOW_LEVEL,  1 );
	voice_DTMFHighIrqHandle = adl_irqSubscribe( voice_DtmfHighIrqHandler, ADL_IRQ_NOTIFY_HIGH_LEVEL, ADL_IRQ_PRIORITY_HIGH_LEVEL, 1 );

	// Subscribe to Audio Stream
    voice_DtmfAudioStreamHandle = adl_audioSubscribe
    (
    	ADL_AUDIO_VOICE_CALL_RX,					// Audio Stream ("Resource")
        voice_dtmfAudioEventHandler, 				// Event Handler
        ADL_AUDIO_RESOURCE_OPTION_FORBID_PREEMPTION // Options: this subscription "owns" the stream exclusively.
    );
    TRACE(( 1, "Audio Handle: %d", voice_DtmfAudioStreamHandle ));

    // Get the DTMF sample size
    result =  adl_audioGetOption
	(
		voice_DtmfAudioStreamHandle, 		// The subscribed resource handle
		ADL_AUDIO_RAW_DTMF_SAMPLE_DURATION,	// The required parameter to get
		&rawSampleDuration					// Will receive the requested value
	);
	TRACE((1, "audioGetOption=%d, sample duration: %d", result, rawSampleDuration));

	// Set the DTMF Blank Duration:
	// The ADL User Guide says this must be a multiple of ADL_AUDIO_MAX_DTMF_PER_FRAME and
	// the raw sample duration (obtained above). Unhelpfully, it doesn't give any advice on
	// what multiple to use - ten is, apparently, arbitrary.
	dtmfBlankDuration = rawSampleDuration * ADL_AUDIO_MAX_DTMF_PER_FRAME * 10;
	result = adl_audioSetOption
	(
		voice_DtmfAudioStreamHandle,			// The subscribed resource handle
		ADL_AUDIO_DTMF_DETECT_BLANK_DURATION, 	// The required parameter to get
		dtmfBlankDuration  						// The value to set
	);
	TRACE(( 1, "audioSetOption= %d, blank duration: %d", result, dtmfBlankDuration ));

	// Get the required buffer size
	result = adl_audioGetOption
    (
    	voice_DtmfAudioStreamHandle,
        ADL_AUDIO_DTMF_PROCESSED_STREAM_BUFFER_SIZE,
        &bufferSize
    );
    TRACE(( 1, "audioGetOption=%d, buffer: %d", result, bufferSize ));

    // Allocate a buffer of the indicated size
    pDtmfStreamBuffer = adl_memGet( bufferSize );
    TRACE(( 1, "pDtmfStreamBuffer=%p", pDtmfStreamBuffer ));

    // Start listening to the Audio Stream
    result = adl_audioStreamListen
	(
		voice_DtmfAudioStreamHandle,	// The subscribed resource handle
		ADL_AUDIO_DTMF,					// The required audio format
		voice_DTMFLowIrqHandle, 		// The Low-level  IRQ handle
		voice_DTMFHighIrqHandle, 		// The High-level IRQ handle
		pDtmfStreamBuffer 				// The Stream Buffer;
										// The post-processed DTMF data will be delivered
										// to the IRQ Handlers in this buffer.
	);
    TRACE ((1, "audioStreamListen=%d", result));

    // Received DTMF data will now be passed to the Low-Level IRQ Handler...
}

Note that the Low-Level IRQ Handler just checks for too-short DTMF durations, and only causes the High-Level IRQ Handler to be called when the duration is valid

The source file is also attached - as it can be rather hard to read in the little code window on this forum! :unamused:
adl_main.zip (2.22 KB)

Example TMT Trace:

Hiya,

I’ve just built your example code and loaded it into a Q2686G. The only change I made was to comment out the adl_atSendResponse() in the High level interrupt routine (because this has caused me issues in the past).

I cannot duplicate the ‘double detection’ issue that you are seeing.

Just out of interest, ATI9 returns the following for my module:

ati9
"DWL","V08b0g","","Wavecom",54680,"071609 15:36","16bb6f5c","00010000"
"FW","B74s00gg.Q2686G","R74a00gg.Q2686G","Wavecom",2106632,"102809 12:57","7c687cd0","00020000"
"OAT","","","",77616,"060710 16:37","cb146736","00260000"
 -"M2M Studio","1.1.1.200911160856-R3160"
 -"Open AT Embedded Software Suite package","2.31.0.RC2"
 -"Open AT OS Package","6.31.0.03"
 -"Open AT Firmware Package","7.4.0.a.200912041916"

And I was using a HTC HD2 phone to send the DTMF by tapping on the touch screen…

Now I’m wondering if any of the following are valid:

  1. Are we using different versions of OpenAT or Firmware (etc)?
  2. How different is the Fastrack Xtend Hardware to the Q2686G?
  3. Can you try a different module? (e.g. Fastrack Supreme?)
  4. Can you try a different phone or modem to send the DTMF to your Xtend in case there is an issue with the DTMF being sent? Although, as you note each DTMF tone should be a minimum of 40mS so the Sierra module shouldn’t catch tones shorter than this…

Hope this helps some.

ciao, Dave

I’ll give that a try; however, that is in the Sierra Wireless sample - so if it is what causes the problem, then that is a bug in the sample! :unamused:

I have tried using:

  • My POTS landline phone - so that’s real, analogue in-band signalling;
  • A GSM Phone - so that’s network-generated;
  • Skype - no idea what that does!

And they all exhibit the same behaviour.

I’ll check…

I don’t know - but I thought this was all digital - within the module?

Yes, a Supreme exhibits the same behaviour.

See above! 8)

One thing I haven’t tried is using a different GSM network…

Hiya,

Exactly - but are you surprised? :confused:

That doesn’t sound good. I’ve just checked with a POTS landline and didn’t see any difference to my tests with the GSM mobile.

I would have thought so too - but it may depend on how the voice stream is being processed in different processors/hardware.

Have you got the facilities to connect a microphone or handset up to your module? If you can, and have an old tone dialer in your collection of spares it might be worth seeing if the same issue arises when connecting to the Microphone Audio Stream. If it does, then we know it’s a processing issue. I know it’s clutching at straws, but it’s probably worth a try - just for completeness.

Good thought - all my testing has been on the same network.

Maybe it’s time to contact your Distributor :unamused:

ciao, Dave

Don’t get me started… :angry:

I have done - they are looking into it…

Hiya,

Just tried your code with a mobile from the Virgin Network (carried over Optus here in Oz), and still don’t see the issue you are having.

Just out of interest, have you tried the DTMF RAW mode to see what the modem is decoding?

ciao, Dave

Results on all 4 UK networks as follows (all pre-pay):

  • Virgin (T-Mobile): Exhibits the problem

  • Vodafone: Exhibits the problem (used in the original tests);

  • Orange: No problem;

  • O2: No problem.

So it does seem to be network-related: Vodafone & T-Mobile have the problem; Orange & O2 don’t.

In all cases, the results are the same irrespective of the originator of the call (Skype/GSM/POTS).

So, does anyone know what Vodafone & T-Mobile have in common, that’s different to what Orange & O2 have in common :question:

The “raw” mode didn’t seem to work at all in the sample - which is one reason why I took it out of my “cut-down” version!
I might now try it again on the “good” networks…

Hiya,

Oh blast.

That’s not good.

I’ll see if I can find someone with a Vodaphone SIM here in Oz and give it a try and see if the problem can be replicated here too. I have tested the following networks here in Oz:
Telstra (Post-paid)
Virgin (Pre-paid, used Optus for connectivity).

After your findings I think I’ll look into buying pre-paid SIMs for each of the networks here in Oz and testing my software. At the very least I’m going to put in your ‘short pulse’ trap in my DTMF Low Level interrupt.

We shouldn’t have to do this.

ciao, Dave

You may send the royalties to my PayPal account… 8)

I’ve tried taking that out - I still get the problem.

I tried using the AT+WDDM command - the results are the same; eg,

So it’s not just an Open-AT thing