Tone Detection

The following functions support programmable tone detection:

vpb_settonedet()
vpb_gettonedet()
vpb_debug_tonedet()

When a tone is detected, a VPB_TONEDETECT event is placed on the event queue with the tone identifier placed in the data element of the event structure. There are 3 pre-defined tones, VPB_DIAL (dial tone), VPB_RINGBACK (ringback), and VPB_BUSY (busy tone). These are configured for standard tones and may be redefined if desired to suit the actual switch to which the VPB is connected. Up to VPB_MD (a #define in vpbapi.h) tone detectors can be defined for each channel of each VPB. Counting the 3 pre-defined tones, VPB_MD-3 new user defined tones are available for the user, or the pre-defined tones may be redefined to give a total of VPB_MD user defined tones. To redefine a pre-defined tone, just specify the pre-defined tone identifier in the tone_id parameter of the VPB_DETECT structure when programming the tone.

Note: The VPB_MD (#define in vpbapi.h) is set by Voicetronix and should not be modified by the user. Up to ten programmable tones (including the pre-defined tones) are available for the user. The tone detectors of each channel are independent of the tone detectors of other channels. Thus each channel must be programmed independently with the vpb_settonedet() API function.

Programming the Tone Detectors

Tone detectors (either DIAL, BUSY, RINGBACK, GRUNT, or user [5..9] tone detectors) can be programmed at driver start up using the VPB_TONE env. variable. The main reason for this feature is to allow users to program the busy tone detector for different PBX models without having to modify and recompile any code.

The format of the VPB_TONE env. variable is described in src/envtone.cpp.

The tone detectors can also be programmed using the VPBAPI function calls. For more information on this and tone detection in general please see the next section.

To determine the parameters of a tone:

  1. Sample the tone using unittest/recwav. For example to sample the busy tone, dial the port, start recwav, then hangup the phone. To sample dial tone just run recwav without dialling the port from another extension. Run recwave for about 30 seconds on pulsed tones so that you get at least 10 repetitions - this helps later with testing.

  2. Use a waveform editor to view the tone and determine it's frequency and on-time (on-time is only valid for pulsed tones such as busy only, dial tones usually stay on). I use a Windows shareware editor called Goldwave. You can determine the frequency of the tone in Goldwave by clicking on the waveform window in the "Device Controls window", this will give you a spectral view of the tone. Each division is 400Hz. There may be similar tools available for Linux.

  3. For a pulsed tone, determine the frequency and on-time. For continuous tone such as dial, just the frequency is required.

  4. To test your tone detector, the unittest/tonedebug program is useful. This program can play your sampled tone, while at the same time it tries to detect the tone using the parameters you have determined. These parameters can be entered on the command line of tonedebug, so testing variations of the tone detector parameters is easy. Note that this program must run without any phone lines connected to the card - it replays the tone you sampled, while simultaneously sampling the same signal to the prototype tone detector you are testing.

  5. Try setting the bandwidth to 100Hz, if that doesn't work, try 200Hz.

  6. Once you are happy with the tone detector (for example, it detects all 10 repetitions of the tones you sampled), you can then set the env. variable to describe the tone, and run your main application.

Examples

Dial tone

The frequency is 500Hz, and we select a bandwidth of 100Hz. We would like the tone detector to "fire" after 1 second (1000ms) of continuous dial tone. The dial tone we sampled with recwav is in the file mydial.wav.

In the unittest dir:

$ ./tonedebug -c 500 100 1000 -w mydial.wav -t dial

which will produce:

[00] Tone Detect: Dial
[00] Play End

Busy tone

The frequency is 425Hz, and we select a bandwidth of 200Hz. By inspecting the wave file we determine that this pulsed tone is on for 375ms. The wave file is called mybusy.wav.

$ ./tonedebug -p 425 200 375 -w mybusy.wav -t busy

which will produce something like:

[00] Tone Detect: Grunt
[00] Tone Detect: Busy
[00] Tone Detect: Busy
[00] Tone Detect: Busy
[00] Play End

In this case, all three busy tones sampled were detected OK. The "grunt" tone is a special tone detector that just senses energy on the line.

To then set the busy tone detector using an env. variable (bash shell):

$ export VPB_TONE=BUSY,P,425,200,375

See also src/envtone.cpp for more information.

More Detail on Programming the Tone Detectors

The tone detection algorithm is split into two phases. The first phase checks an incoming signal to determine if its signal parameters fit the templates defined in the tone detector, for example, the frequency, bandwidth, level, and signal to noise ratio (SNR). The second phase looks at the on-off timing of the signal, or cadence information. If both the signal parameters and the cadence parameters match, an event is posted.

Each programmable tone has a unique identifier, i.e. a 16 bit integer that is placed in the data element of the event when the tone is detected. The value of the tone identifier is not important, as long as it is unique. If vpb_settonedet() is called with a non-unique identifier (one which has been used before), then that programmable tone is redefined. If vpb_settonedet() is called with a unique identifier (one which has not been used before), then a new programmable tone is defined.

The structure VPB_DETECT is used to define a programmable tone. Single or dual frequencies can be specified, each with independent frequency, amplitude, and bandwidth. In addition, the allowable twist for dual tones (difference in level between the two tones) can be defined. Attempting to define a tone outside of the tone detector limits will cause an error.

Signal Detection Algorithm

For a single frequency tone to pass the signal processing phase, the following conditions must be true:

  1. The level of the tone in the band specified by the frequency (freq1) and bandwidth (bandwidth1) must be above the minimum level (minlevel1).

  2. The signal energy must be greater than the rest of the energy in the received signal by the specified signal to noise ratio (snr).

  3. If the above conditions are met, then the signal is declared detected and is passed to the cadence detection stage for cadence analysis. For a dual frequency tone to pass the signal processing phase, the following conditions must be true:

  4. The level of the first frequency component in the band specified by the frequency (freq1) and bandwidth (bandwidth1) must be above the minimum level (minlevel1).

  5. The level of the second frequency component in the band specified by the frequency (freq2) and bandwidth (bandwidth2) must be above the minimum level (minlevel2).

  6. The total signal energy in both bands must be greater than the rest of the energy in the received signal by the specified signal to noise ratio (snr).

  7. The difference in the energies of the two frequency components must be within the allowed twist.

If the above conditions are met, then the signal is declared detected and is passed to the cadence detection stage for cadence analysis.

Cadence Detection Algorithm

The cadence detection parameters of the tone detector are defined by constructing a state machine for each tone detector. The state machine is defined by a state transition table (stran), and the number of states (nstates). The state transition table consists of an array of VPB_STRAN structures. The state machine begins in state 0 and progresses to the next state when the state transition requirements are met for that state. When the final state transition occurs, an event of type VPB_TONEDETECT is posted on the API event queue. A maximum of VPB_MS (#define in vpbapi.h) cadence states are allowed for each state machine.

There are three types of state transitions:

VPB_TIMER. A state transition occurs when no change (transition) in the signal occurs for the fire time (tfire) in milliseconds. If a transition occurs before time tfire, the state machine is reset to state 0. The VPB_TIMER transition provides a way to measure the on time of signals without cadence, for example continuous dial tones.
VPB_DELAY. It waits the desired time, tfire ms and goes to the next state. The VPB_DELAY transition provides a way to wait a desired time irrespective of any signal transitions.
VPB_RISING. A state transition occurs on the rising edge of the signal detection, that is, when the signal goes from a non-detected to detected state. The state transition occurs if the time spent in this state is between the minimal (tmin) and maximum (tmax) time in milliseconds. If at the rising edge the time spent in this state is not between tmin and tmax ms, the state machine is reset to state 0. If tmin and tmax are both set to 0, then a state transition occurs on the rising edge of the signal, regardless of the time spent in this state. The VPB_RISING transition provides a way to determine how long the signal was off.
VPB_FALLING. A state transition occurs on the falling edge of the signal detection, that is when the signal goes from a detected to non-detected state. The state transition occurs if the time spent in this state is between tmin and tmax ms. If at the falling edge the time spent in this state is not between tmin and tmax ms, the state machine is reset to state 0. If tmin and tmax are both set to 0, then a state transition occurs on the falling edge of the signal, regardless of the time spent in this state. The VPB_FALLING transition provides a way to determine how long the signal was on.

Programmable Tone Detector Tips

The cadence state machine approach has the advantage that non-periodic or variable cadences may be detected, for example a tone that has a long on period, then a short on period. To detect several cycles of a periodic cadence pattern, for example 3 on/off cycles of a busy tone, just repeat the cadence state detector rising and falling edge transitions the desired number of times, up to the VPB_MS (#define in vpbapi.h) limit.

When designing/debugging a tone detector, start with a simple cadence detector, for example just a single rising edge state. Adjust the signal detection parameters first, then add and test the extra cadence states one at a time. This will identify any problems with the last cadence state added. Don't forget to modify the nstates parameter in the VPB_DETECT structure as extra states are added.

When designing/debugging a tone detector, start with wide bandwidths (several hundred Hz), and low level thresholds (say -40 dB), then gradually tighten (reduce bandwidth, increase level) these parameters to increase the robustness of the tone detector. Amplitude modulated signals have wider bandwidths than pure sinusoids, and their amplitudes move up and down with the modulation frequency. Amplitude modulated signals may therefore need lower level thresholds than unmodulated tones. As a rule of thumb, use a bandwidth about four times as wide as the modulating frequency, for example, 100 Hz for a 25 Hz modulating frequency. The level threshold may need to be very low to detect the signal through the low level periods, so try -40 dB as a starting point for the level parameters.

Programmable Tone Detector Examples

Continuous dial tone at 425 Hz, amplitude modulated at 25 Hz

This discussion refers to the toned_dial variable below.

This tone is amplitude modulated at 25 Hz, which spreads the bandwidth approximately 25 Hz either side of the main frequency component. A bandwidth of 100 Hz is used to be sure that all of the dial tone energy is captured, although only 50 Hz is strictly necessary. The level of amplitude modulated signals can also be quite low (it varies up and down with the modulating signal), so a low amplitude threshold of -40dB is used. The tone contains only a single frequency component so the 2nd frequency parameters are set to 0, which instructs the tone detector software to ignore the 2nd frequency.

The signal to noise ratio (SNR) was set to 10 dB so that the detector requires most of the energy in the signal to be contained in the 100 Hz bandwidth. This helps to reduce false triggering, for example by a speech signal that may have energy around 400 Hz.

There are two cadence states specified. The first looks for a rising edge in the signal detection logic. No time parameters are specified, so only the rising edge (from non-detected to detected signal condition) is required to move the state machine to the second state.

The second state is a VPB_TIMER state. This timer "fires" when the signal has been detected for 2000 ms. If the signal disappears in this time, the state machine is reset to the first state and no event is posted. If the signal continues uninterrupted for 2000 ms, an event is posted to the API event queue and the state machine resets itself to state 0. The event will contain VPB_DIAL in the data member of the VPB_EVENT structure.

Note that the timer state provides an additional level of "filtering", as the signal must be continuous and uninterrupted for 2000 ms. Any non-valid signals are unlikely to meet the signal detection conditions for this time, and will be discarded by the state machine.

Example 4. toned_dial for 425Hz continuous dial tone, modulated at 25Hz


static VPB_DETECT toned_dial = {
        2,        // number of cadence states
        VPB_DIAL, // tone id
        1,        // number of tones
        425,      // freq1
        100,      // bandwidth1
        0,        // freq2, N/A
        0,        // bandwidth2, N/A
        -40,      // level1
        0,        // level2, N/A
        0,        // twist, N/A
        10,       // SNR
        40,       // glitch duration 40ms

        VPB_RISING,   // state 0
        0,
        0,
        0,

        VPB_TIMER,    // state 1
        2000,
        0,
        0
};

Austel Busy Tone 425 Hz or 400 Hz, 375ms on, 375 ms off

This discussion refers to the toned_austel_busy variable below.

The Austel busy tone has two possible frequencies, 400 or 425 Hz, so a bandwidth of 100 Hz has been used to make sure both possibilities are within the bandwidth of the signal detector. As the signal is a single unmodulated tone, the amplitude will be relatively stable, so we can set a fairly high cut off level of -20 dB. The cadence state machine has 3 states, which detect a single on/off cycle of the busy tone. The first state detects the first rising edge, with no time parameters. The second state waits for the falling edge, and checks that the signal on time was between 300 and 450 ms. The third state then waits for the next rising edge, and checks if its off time was between 300 and 450 ms. If so, then a VPB_AUSTEL_BUSY event is posted to the API queue. If any of these conditions fail then the state machine resets and no events are generated.

Example 5. Austel busy tone


static VPB_DETECT toned_austel_busy = {
        3,               // number of cadence states
        VPB_AUSTEL_BUSY, // tone id
        1,               // number of tones
        425,             // freq1
        100,             // bandwidth1
        0,               // freq2, N/A
        0,               // bandwidth2:N/A
        -20,             // level1
        0,               // level2, N/A
        0,               // twist:N/A
        10,              // SNR: 10dB
        40,              // glitch duration 40ms
        VPB_RISING,      // state
        0
        0,
        0,
        0,
        VPB_FALLING,     // state 1
        0,
        300,
        450,
        VPB_RISING,      // state 2
        0,
        300,
        450,
};

DTMF tone detection

This example demonstrates the ability of the programmable tone detector to detect tones with two frequency components, in this case the DTMF tone for 1, composed of 697 Hz, and 1209 Hz. The variable toned_dtmf1 below illustrates the configuration for this tone.

The signal detection parameters are straightforward, both frequencies have the same level thresholds. The twist parameter specifies the maximum permissible difference in the magnitudes of the two frequency components of the tones, in this case it is 10 dB. The cadence state machine looks for a rising edge, then the tone must be present for a minimum of 100 ms before a VPB_TONEDETECT event with the data member set to DTMF_1 (user #defined to a unique tone identifier) is posted.

Example 6. DTMF tone detector


static VPB_DETECT toned_dtmf1 = {
        2,          // number of cadence states
        DTMF_1,     // tone id
        2,          // number of tones
        697,        // f1: centre frequency
        100,        // bw1: bandwidth
        1209,       // f2: centre frequency
        100,        // bw2: bandwidth
        -20,        // A1: -20 dBm0
        -20,        // A2: -20 dBm0
        10,         // twist: 10 dB
        10,         // SNR: 10 dB
        40          // glitch duration 40ms

        VPB_RISING, // state 0
        0,
        0,
        0,

        VPB_TIMER,  // state 1
        100,
        0,
        0
};

Example 4 - Grunt Detector used to detect wide band signals such as voice

This discussion refers to the toned_grunt variable below.

Voice signals have a wide band signal characteristic, with most of the energy located between 50-2000Hz. The grunt detector looks for wide band energy between 500Hz and 3500Hz. The lower frequency band (500Hz) was selected as not to overlap with other telephony signals, such as dial and busy tones which have their energy centred around 400Hz. The cadence state machine has 2 states to detect a wideband signal. The first state waits for 1 second, ignoring any signal transitions. The second state checks for a rising edge in the signal with an off time between 0-40 ms. If so, then a VPB_GRUNT event is posted to the API queue. If any of these conditions fail then the state machine resets and no events are generated.

Example 7. DTMF tone detector


static VPB_DETECT toned_grunt = {
        2,           // number of cadence states
        VPB_GRUNT,   // tone id
        1,           // number of tones
        2000,        // freq1
        3000,        // bandwidth1
        0,           // freq2, N/A
        0,           // bandwidth2: N/A
        -40,         // level1 (-40 dB)
        0,           // level2, N/A
        0,           // twist: N/A
        0,           // SNR: N/A
        40           // glitch duration 40ms

        VPB_DELAY,   // state 0
        1000,
        0,
        0,

        VPB_RISING,  // state 1
        0,
        40,
        0
};