SPAN_DECLARE(int) r2_mf_rx(r2_mf_rx_state_t *s, const int16_t amp[], int samples)
{
#if defined(SPANDSP_USE_FIXED_POINT)
    int32_t energy[6];
    int16_t xamp;
#else
    float energy[6];
    float xamp;
#endif
    int i;
    int j;
    int sample;
    int best;
    int second_best;
    int hit;
    int hit_digit;
    int limit;

    hit = 0;
    hit_digit = 0;
    for (sample = 0;  sample < samples;  sample = limit)
    {
        if ((samples - sample) >= (R2_MF_SAMPLES_PER_BLOCK - s->current_sample))
            limit = sample + (R2_MF_SAMPLES_PER_BLOCK - s->current_sample);
        else
            limit = samples;
        for (j = sample;  j < limit;  j++)
        {
            xamp = goertzel_preadjust_amp(amp[j]);
            goertzel_samplex(&s->out[0], xamp);
            goertzel_samplex(&s->out[1], xamp);
            goertzel_samplex(&s->out[2], xamp);
            goertzel_samplex(&s->out[3], xamp);
            goertzel_samplex(&s->out[4], xamp);
            goertzel_samplex(&s->out[5], xamp);
        }
        s->current_sample += (limit - sample);
        if (s->current_sample < R2_MF_SAMPLES_PER_BLOCK)
            continue;

        /* We are at the end of an MF detection block */
        /* Find the two highest energies */
        energy[0] = goertzel_result(&s->out[0]);
        energy[1] = goertzel_result(&s->out[1]);
        if (energy[0] > energy[1])
        {
            best = 0;
            second_best = 1;
        }
        else
        {
            best = 1;
            second_best = 0;
        }
        
        for (i = 2;  i < 6;  i++)
        {
            energy[i] = goertzel_result(&s->out[i]);
            if (energy[i] >= energy[best])
            {
                second_best = best;
                best = i;
            }
            else if (energy[i] >= energy[second_best])
            {
                second_best = i;
            }
        }
        /* Basic signal level and twist tests */
        hit = FALSE;
        if (energy[best] >= R2_MF_THRESHOLD
            &&
            energy[second_best] >= R2_MF_THRESHOLD
            &&
            energy[best] < energy[second_best]*R2_MF_TWIST
            &&
            energy[best]*R2_MF_TWIST > energy[second_best])
        {
            /* Relative peak test */
            hit = TRUE;
            for (i = 0;  i < 6;  i++)
            {
                if (i != best  &&  i != second_best)
                {
                    if (energy[i]*R2_MF_RELATIVE_PEAK >= energy[second_best])
                    {
                        /* The best two are not clearly the best */
                        hit = FALSE;
                        break;
                    }
                }
            }
        }
        if (hit)
        {
            /* Get the values into ascending order */
            if (second_best < best)
            {
                i = best;
                best = second_best;
                second_best = i;
            }
            best = best*5 + second_best - 1;
            hit_digit = r2_mf_positions[best];
        }
        else
        {
            hit_digit = 0;
        }
        if (s->current_digit != hit_digit  &&  s->callback)
        {
            i = (hit_digit)  ?  -10  :  -99;
            s->callback(s->callback_data, hit_digit, i, 0);
        }
        s->current_digit = hit_digit;
        s->current_sample = 0;
    }
    return 0;
}
Example #2
0
SPAN_DECLARE(int) ademco_contactid_sender_rx(ademco_contactid_sender_state_t *s, const int16_t amp[], int samples)
{
#if defined(SPANDSP_USE_FIXED_POINT)
    int32_t energy_1400;
    int32_t energy_2300;
    int16_t xamp;
#else
    float energy_1400;
    float energy_2300;
    float xamp;
#endif
    int sample;
    int limit;
    int hit;
    int j;

    for (sample = 0;  sample < samples;  sample = limit)
    {
        if ((samples - sample) >= (GOERTZEL_SAMPLES_PER_BLOCK - s->current_sample))
            limit = sample + (GOERTZEL_SAMPLES_PER_BLOCK - s->current_sample);
        else
            limit = samples;
        for (j = sample;  j < limit;  j++)
        {
            xamp = amp[j];
            xamp = goertzel_preadjust_amp(xamp);
#if defined(SPANDSP_USE_FIXED_POINT)
            s->energy += ((int32_t) xamp*xamp);
#else
            s->energy += xamp*xamp;
#endif
            goertzel_samplex(&s->tone_1400, xamp);
            goertzel_samplex(&s->tone_2300, xamp);
        }
        s->current_sample += (limit - sample);
        if (s->current_sample < GOERTZEL_SAMPLES_PER_BLOCK)
            continue;

        energy_1400 = goertzel_result(&s->tone_1400);
        energy_2300 = goertzel_result(&s->tone_2300);
        hit = 0;
        if (energy_1400 > DETECTION_THRESHOLD  ||  energy_2300 > DETECTION_THRESHOLD)
        {
            if (energy_1400 > energy_2300)
            {
                if (energy_1400 > TONE_TO_TOTAL_ENERGY*s->energy)
                    hit = 1;
            }
            else
            {
                if (energy_2300 > TONE_TO_TOTAL_ENERGY*s->energy)
                    hit = 2;
            }
        }
        if (hit != s->in_tone  &&  hit == s->last_hit)
        {
            /* We have two successive indications that something has changed to a
               specific new state. */
            switch (s->tone_state)
            {
            case 0:
                if (hit == 1)
                {
                    span_log(&s->logging, SPAN_LOG_FLOW, "Receiving initial 1400Hz\n");
                    s->in_tone = hit;
                    s->tone_state = 1;
                    s->duration = 0;
                }
                break;
            case 1:
                /* We are looking for a burst of 1400Hz which is 100ms +- 5% long */
                if (hit == 0)
                {
                    if (s->duration < ms_to_samples(70)  ||  s->duration > ms_to_samples(130))
                    {
                        span_log(&s->logging, SPAN_LOG_FLOW, "Bad initial 1400Hz tone duration\n");
                        s->tone_state = 0;
                    }
                    else
                    {
                        span_log(&s->logging, SPAN_LOG_FLOW, "Received 1400Hz tone\n");
                        s->tone_state = 2;
                    }
                    s->in_tone = hit;
                    s->duration = 0;
                }
                break;
            case 2:
                /* We are looking for 100ms +-5% of silence after the 1400Hz tone */
                if (s->duration < ms_to_samples(70)  ||  s->duration > ms_to_samples(130))
                {
                    span_log(&s->logging, SPAN_LOG_FLOW, "Bad silence length\n");
                    s->tone_state = 0;
                    s->in_tone = hit;
                }
                else if (hit == 2)
                {
                    span_log(&s->logging, SPAN_LOG_FLOW, "Received silence\n");
                    s->tone_state = 3;
                    s->in_tone = hit;
                }
                else
                {
                    s->tone_state = 0;
                    s->in_tone = 0;
                }
                s->duration = 0;
                break;
            case 3:
                /* We are looking for a burst of 2300Hz which is 100ms +- 5% long */
                if (hit == 0)
                {
                    if (s->duration < ms_to_samples(70)  ||  s->duration > ms_to_samples(130))
                    {
                        span_log(&s->logging, SPAN_LOG_FLOW, "Bad initial 2300Hz tone duration\n");
                        s->tone_state = 0;
                    }
                    else
                    {
                        span_log(&s->logging, SPAN_LOG_FLOW, "Received 2300Hz\n");
                        if (s->callback)
                            s->callback(s->callback_user_data, -1, 0, 0);
                        s->tone_state = 4;
                        /* Release the transmit side, and it will time the 250ms post tone delay */
                        s->clear_to_send = true;
                        s->tries = 0;
                        if (s->tx_digits_len)
                            s->timer = ms_to_samples(3000);
                    }
                    s->in_tone = hit;
                    s->duration = 0;
                }
                break;
            case 4:
                if (hit == 1)
                {
                    span_log(&s->logging, SPAN_LOG_FLOW, "Receiving kissoff\n");
                    s->tone_state = 5;
                    s->in_tone = hit;
                    s->duration = 0;
                }
                break;
            case 5:
                if (hit == 0)
                {
                    s->busy = false;
                    if (s->duration < ms_to_samples(400)  ||  s->duration > ms_to_samples(1500))
                    {
                        span_log(&s->logging, SPAN_LOG_FLOW, "Bad kissoff duration %d\n", s->duration);
                        if (++s->tries < 4)
                        {
                            dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len);
                            s->timer = ms_to_samples(3000);
                            s->tone_state = 4;
                        }
                        else
                        {
                            s->timer = 0;
                            if (s->callback)
                                s->callback(s->callback_user_data, false, 0, 0);
                        }
                    }
                    else
                    {
                        span_log(&s->logging, SPAN_LOG_FLOW, "Received good kissoff\n");
                        s->clear_to_send = true;
                        s->tx_digits_len = 0;
                        if (s->callback)
                            s->callback(s->callback_user_data, true, 0, 0);
                        s->tone_state = 4;
                        s->clear_to_send = true;
                        s->tries = 0;
                        if (s->tx_digits_len)
                            s->timer = ms_to_samples(3000);
                    }
                    s->in_tone = hit;
                    s->duration = 0;
                }
                break;
            }
        }
        s->last_hit = hit;
        s->duration += GOERTZEL_SAMPLES_PER_BLOCK;
        if (s->timer > 0)
        {
            s->timer -= GOERTZEL_SAMPLES_PER_BLOCK;
            if (s->timer <= 0)
            {
                span_log(&s->logging, SPAN_LOG_FLOW, "Timer expired\n");
                if (s->tone_state == 4  &&  s->tx_digits_len)
                {
                    if (++s->tries < 4)
                    {
                        dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len);
                        s->timer = ms_to_samples(3000);
                    }
                    else
                    {
                        s->timer = 0;
                        if (s->callback)
                            s->callback(s->callback_user_data, false, 0, 0);
                    }
                }
            }
        }
        s->energy = 0;
        s->current_sample = 0;
    }
    return 0;
}
Example #3
0
static void super_tone_chunk(super_tone_rx_state_t *s)
{
    int i;
    int j;
    int k1;
    int k2;
#if defined(SPANDSP_USE_FIXED_POINT)
    int32_t res[SUPER_TONE_BINS/2];
#else
    float res[SUPER_TONE_BINS/2];
#endif

    for (i = 0;  i < s->desc->monitored_frequencies;  i++)
        res[i] = goertzel_result(&s->state[i]);
    /* Find our two best monitored frequencies, which also have adequate energy. */
    if (s->energy < DETECTION_THRESHOLD)
    {
        k1 = -1;
        k2 = -1;
    }
    else
    {
        if (res[0] > res[1])
        {
            k1 = 0;
            k2 = 1;
        }
        else
        {
            k1 = 1;
            k2 = 0;
        }
        for (j = 2;  j < s->desc->monitored_frequencies;  j++)
        {
            if (res[j] >= res[k1])
            {
                k2 = k1;
                k1 = j;
            }
            else if (res[j] >= res[k2])
            {
                k2 = j;
            }
        }
        if ((res[k1] + res[k2]) < TONE_TO_TOTAL_ENERGY*s->energy)
        {
            k1 = -1;
            k2 = -1;
        }
        else if (res[k1] > TONE_TWIST*res[k2])
        {
            k2 = -1;
        }
        else if (k2 < k1)
        {
            j = k1;
            k1 = k2;
            k2 = j;
        }
    }
    /* See if this differs from last time. */
    if (k1 != s->segments[10].f1  ||  k2 != s->segments[10].f2)
    {
        /* It is different, but this might just be a transitional quirk, or
           a one shot hiccup (eg due to noise). Only if this same thing is
           seen a second time should we change state. */
        s->segments[10].f1 = k1;
        s->segments[10].f2 = k2;
        /* While things are hopping around, consider this a continuance of the
           previous state. */
        s->segments[9].min_duration++;
    }
    else
    {
        if (k1 != s->segments[9].f1  ||  k2 != s->segments[9].f2)
        {
            if (s->detected_tone >= 0)
            {
                /* Test for the continuance of the existing tone pattern, based on our new knowledge of an
                   entire segment length. */
                if (!test_cadence(s->desc->tone_list[s->detected_tone], -s->desc->tone_segs[s->detected_tone], s->segments, s->rotation++))
                {
                    s->detected_tone = -1;
                    s->tone_callback(s->callback_data, s->detected_tone, -10, 0);
                }
            }
            if (s->segment_callback)
            {
                s->segment_callback(s->callback_data,
                                    s->segments[9].f1,
                                    s->segments[9].f2,
                                    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->segments[9].f2 = k2;
            s->segments[9].min_duration = 1;
        }
        else
        {
            /* This is a continuance of the previous state */
            if (s->detected_tone >= 0)
            {
                /* Test for the continuance of the existing tone pattern. We must do this here, so we can sense the
                   discontinuance of the tone on an excessively long segment. */
                if (!test_cadence(s->desc->tone_list[s->detected_tone], s->desc->tone_segs[s->detected_tone], s->segments, s->rotation))
                {
                    s->detected_tone = -1;
                    s->tone_callback(s->callback_data, s->detected_tone, -10, 0);
                }
            }
            s->segments[9].min_duration++;
        }
    }
    if (s->detected_tone < 0)
    {
        /* Test for the start of any of the monitored tone patterns */
        for (j = 0;  j < s->desc->tones;  j++)
        {
            if (test_cadence(s->desc->tone_list[j], s->desc->tone_segs[j], s->segments, -1))
            {
                s->detected_tone = j;
                s->rotation = 0;
                s->tone_callback(s->callback_data, s->detected_tone, -10, 0);
                break;
            }
        }
    }
#if defined(SPANDSP_USE_FIXED_POINT)
    s->energy = 0;
#else
    s->energy = 0.0f;
#endif
}
SPAN_DECLARE(int) bell_mf_rx(bell_mf_rx_state_t *s, const int16_t amp[], int samples)
{
#if defined(SPANDSP_USE_FIXED_POINT)
    int32_t energy[6];
    int16_t xamp;
#else
    float energy[6];
    float xamp;
#endif
    int i;
    int j;
    int sample;
    int best;
    int second_best;
    int limit;
    uint8_t hit;

    hit = 0;
    for (sample = 0;  sample < samples;  sample = limit)
    {
        if ((samples - sample) >= (BELL_MF_SAMPLES_PER_BLOCK - s->current_sample))
            limit = sample + (BELL_MF_SAMPLES_PER_BLOCK - s->current_sample);
        else
            limit = samples;
        for (j = sample;  j < limit;  j++)
        {
            xamp = goertzel_preadjust_amp(amp[j]);
            goertzel_samplex(&s->out[0], xamp);
            goertzel_samplex(&s->out[1], xamp);
            goertzel_samplex(&s->out[2], xamp);
            goertzel_samplex(&s->out[3], xamp);
            goertzel_samplex(&s->out[4], xamp);
            goertzel_samplex(&s->out[5], xamp);
        }
        s->current_sample += (limit - sample);
        if (s->current_sample < BELL_MF_SAMPLES_PER_BLOCK)
            continue;

        /* We are at the end of an MF detection block */
        /* Find the two highest energies. The spec says to look for
           two tones and two tones only. Taking this literally -ie
           only two tones pass the minimum threshold - doesn't work
           well. The sinc function mess, due to rectangular windowing
           ensure that! Find the two highest energies and ensure they
           are considerably stronger than any of the others. */
        energy[0] = goertzel_result(&s->out[0]);
        energy[1] = goertzel_result(&s->out[1]);
        if (energy[0] > energy[1])
        {
            best = 0;
            second_best = 1;
        }
        else
        {
            best = 1;
            second_best = 0;
        }
        for (i = 2;  i < 6;  i++)
        {
            energy[i] = goertzel_result(&s->out[i]);
            if (energy[i] >= energy[best])
            {
                second_best = best;
                best = i;
            }
            else if (energy[i] >= energy[second_best])
            {
                second_best = i;
            }
        }
        /* Basic signal level and twist tests */
        hit = 0;
        if (energy[best] >= BELL_MF_THRESHOLD
            &&
            energy[second_best] >= BELL_MF_THRESHOLD
            &&
            energy[best] < energy[second_best]*BELL_MF_TWIST
            &&
            energy[best]*BELL_MF_TWIST > energy[second_best])
        {
            /* Relative peak test */
            hit = 'X';
            for (i = 0;  i < 6;  i++)
            {
                if (i != best  &&  i != second_best)
                {
                    if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best])
                    {
                        /* The best two are not clearly the best */
                        hit = 0;
                        break;
                    }
                }
            }
        }
        if (hit)
        {
            /* Get the values into ascending order */
            if (second_best < best)
            {
                i = best;
                best = second_best;
                second_best = i;
            }
            best = best*5 + second_best - 1;
            hit = bell_mf_positions[best];
            /* Look for two successive similar results */
            /* The logic in the next test is:
               For KP we need 4 successive identical clean detects, with
               two blocks of something different preceeding it. For anything
               else we need two successive identical clean detects, with
               two blocks of something different preceeding it. */
            if (hit == s->hits[4]
                &&
                hit == s->hits[3]
                &&
                   ((hit != '*'  &&  hit != s->hits[2]  &&  hit != s->hits[1])
                    ||
                    (hit == '*'  &&  hit == s->hits[2]  &&  hit != s->hits[1]  &&  hit != s->hits[0])))
            {
                if (s->current_digits < MAX_BELL_MF_DIGITS)
                {
                    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->hits[0] = s->hits[1];
        s->hits[1] = s->hits[2];
        s->hits[2] = s->hits[3];
        s->hits[3] = s->hits[4];
        s->hits[4] = hit;
        s->current_sample = 0;
    }
    if (s->current_digits  &&  s->digits_callback)
    {
        s->digits_callback(s->digits_callback_data, s->digits, s->current_digits);
        s->digits[0] = '\0';
        s->current_digits = 0;
    }
    return 0;
}
Example #5
0
int r2_mf_rx(r2_mf_rx_state_t *s, const int16_t amp[], int samples)
{
    float energy[6];
    float famp;
    float v1;
    int i;
    int j;
    int sample;
    int best;
    int second_best;
    int hit;
    int hit_char;
    int limit;

    hit = 0;
    hit_char = 0;
    for (sample = 0;  sample < samples;  sample = limit)
    {
        if ((samples - sample) >= (s->samples - s->current_sample))
            limit = sample + (s->samples - s->current_sample);
        else
            limit = samples;
        for (j = sample;  j < limit;  j++)
        {
            famp = amp[j];
    
            /* With GCC 2.95, the following unrolled code seems to take about 35%
               (rough estimate) as long as a neat little 0-5 loop */
            v1 = s->out[0].v2;
            s->out[0].v2 = s->out[0].v3;
            s->out[0].v3 = s->out[0].fac*s->out[0].v2 - v1 + famp;
    
            v1 = s->out[1].v2;
            s->out[1].v2 = s->out[1].v3;
            s->out[1].v3 = s->out[1].fac*s->out[1].v2 - v1 + famp;
    
            v1 = s->out[2].v2;
            s->out[2].v2 = s->out[2].v3;
            s->out[2].v3 = s->out[2].fac*s->out[2].v2 - v1 + famp;
    
            v1 = s->out[3].v2;
            s->out[3].v2 = s->out[3].v3;
            s->out[3].v3 = s->out[3].fac*s->out[3].v2 - v1 + famp;
    
            v1 = s->out[4].v2;
            s->out[4].v2 = s->out[4].v3;
            s->out[4].v3 = s->out[4].fac*s->out[4].v2 - v1 + famp;
    
            v1 = s->out[5].v2;
            s->out[5].v2 = s->out[5].v3;
            s->out[5].v3 = s->out[5].fac*s->out[5].v2 - v1 + famp;
        }
        s->current_sample += (limit - sample);
        if (s->current_sample < s->samples)
            continue;

        /* We are at the end of an MF detection block */
        /* Find the two highest energies */
        energy[0] = goertzel_result(&s->out[0]);
        energy[1] = goertzel_result(&s->out[1]);
        if (energy[0] > energy[1])
        {
            best = 0;
            second_best = 1;
        }
        else
        {
            best = 1;
            second_best = 0;
        }
        
        for (i = 2;  i < 6;  i++)
        {
            energy[i] = goertzel_result(&s->out[i]);
            if (energy[i] >= energy[best])
            {
                second_best = best;
                best = i;
            }
            else if (energy[i] >= energy[second_best])
            {
                second_best = i;
            }
        }
        /* Basic signal level and twist tests */
        hit = FALSE;
        if (energy[best] >= R2_MF_THRESHOLD
            &&
            energy[second_best] >= R2_MF_THRESHOLD
            &&
            energy[best] < energy[second_best]*R2_MF_TWIST
            &&
            energy[best]*R2_MF_TWIST > energy[second_best])
        {
            /* Relative peak test */
            hit = TRUE;
            for (i = 0;  i < 6;  i++)
            {
                if (i != best  &&  i != second_best)
                {
                    if (energy[i]*R2_MF_RELATIVE_PEAK >= energy[second_best])
                    {
                        /* The best two are not clearly the best */
                        hit = FALSE;
                        break;
                    }
                }
            }
        }
        if (hit)
        {
            /* Get the values into ascending order */
            if (second_best < best)
            {
                i = best;
                best = second_best;
                second_best = i;
            }
            best = best*5 + second_best - 1;
            hit_char = r2_mf_positions[best];
        }
        else
        {
            hit_char = 0;
        }

        /* Reinitialise the detector for the next block */
        if (s->fwd)
        {
            for (i = 0;  i < 6;  i++)
                goertzel_reset(&s->out[i]);
        }
        else
        {
            for (i = 0;  i < 6;  i++)
                goertzel_reset(&s->out[i]);
        }
        s->current_sample = 0;
    }
    return hit_char;
}
Example #6
0
int bell_mf_rx(bell_mf_rx_state_t *s, const int16_t amp[], int samples)
{
    float energy[6];
    float famp;
    float v1;
    int i;
    int j;
    int sample;
    int best;
    int second_best;
    int limit;
    uint8_t hit;

    hit = 0;
    for (sample = 0;  sample < samples;  sample = limit)
    {
        if ((samples - sample) >= (120 - s->current_sample))
            limit = sample + (120 - s->current_sample);
        else
            limit = samples;
        for (j = sample;  j < limit;  j++)
        {
            famp = amp[j];
    
            /* With GCC 2.95, the following unrolled code seems to take about 35%
               (rough estimate) as long as a neat little 0-5 loop */
            v1 = s->out[0].v2;
            s->out[0].v2 = s->out[0].v3;
            s->out[0].v3 = s->out[0].fac*s->out[0].v2 - v1 + famp;
    
            v1 = s->out[1].v2;
            s->out[1].v2 = s->out[1].v3;
            s->out[1].v3 = s->out[1].fac*s->out[1].v2 - v1 + famp;
    
            v1 = s->out[2].v2;
            s->out[2].v2 = s->out[2].v3;
            s->out[2].v3 = s->out[2].fac*s->out[2].v2 - v1 + famp;
    
            v1 = s->out[3].v2;
            s->out[3].v2 = s->out[3].v3;
            s->out[3].v3 = s->out[3].fac*s->out[3].v2 - v1 + famp;
    
            v1 = s->out[4].v2;
            s->out[4].v2 = s->out[4].v3;
            s->out[4].v3 = s->out[4].fac*s->out[4].v2 - v1 + famp;
    
            v1 = s->out[5].v2;
            s->out[5].v2 = s->out[5].v3;
            s->out[5].v3 = s->out[5].fac*s->out[5].v2 - v1 + famp;
        }
        s->current_sample += (limit - sample);
        if (s->current_sample < 120)
            continue;

        /* We are at the end of an MF detection block */
        /* Find the two highest energies. The spec says to look for
           two tones and two tones only. Taking this literally -ie
           only two tones pass the minimum threshold - doesn't work
           well. The sinc function mess, due to rectangular windowing
           ensure that! Find the two highest energies and ensure they
           are considerably stronger than any of the others. */
        energy[0] = goertzel_result(&s->out[0]);
        energy[1] = goertzel_result(&s->out[1]);
        if (energy[0] > energy[1])
        {
            best = 0;
            second_best = 1;
        }
        else
        {
            best = 1;
            second_best = 0;
        }
        for (i = 2;  i < 6;  i++)
        {
            energy[i] = goertzel_result(&s->out[i]);
            if (energy[i] >= energy[best])
            {
                second_best = best;
                best = i;
            }
            else if (energy[i] >= energy[second_best])
            {
                second_best = i;
            }
        }
        /* Basic signal level and twist tests */
        hit = 0;
        if (energy[best] >= BELL_MF_THRESHOLD
            &&
            energy[second_best] >= BELL_MF_THRESHOLD
            &&
            energy[best] < energy[second_best]*BELL_MF_TWIST
            &&
            energy[best]*BELL_MF_TWIST > energy[second_best])
        {
            /* Relative peak test */
            hit = 'X';
            for (i = 0;  i < 6;  i++)
            {
                if (i != best  &&  i != second_best)
                {
                    if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best])
                    {
                        /* The best two are not clearly the best */
                        hit = 0;
                        break;
                    }
                }
            }
        }
        if (hit)
        {
            /* Get the values into ascending order */
            if (second_best < best)
            {
                i = best;
                best = second_best;
                second_best = i;
            }
            best = best*5 + second_best - 1;
            hit = bell_mf_positions[best];
            /* Look for two successive similar results */
            /* The logic in the next test is:
               For KP we need 4 successive identical clean detects, with
               two blocks of something different preceeding it. For anything
               else we need two successive identical clean detects, with
               two blocks of something different preceeding it. */
            if (hit == s->hits[4]
                &&
                hit == s->hits[3]
                &&
                   ((hit != '*'  &&  hit != s->hits[2]  &&  hit != s->hits[1])
                    ||
                    (hit == '*'  &&  hit == s->hits[2]  &&  hit != s->hits[1]  &&  hit != s->hits[0])))
            {
                if (s->current_digits < MAX_BELL_MF_DIGITS)
                {
                    s->digits[s->current_digits++] = (char) hit;
                    s->digits[s->current_digits] = '\0';
                    if (s->callback)
                    {
                        s->callback(s->callback_data, s->digits, s->current_digits);
                        s->current_digits = 0;
                    }
                }
                else
                {
                    s->lost_digits++;
                }
            }
        }
        s->hits[0] = s->hits[1];
        s->hits[1] = s->hits[2];
        s->hits[2] = s->hits[3];
        s->hits[3] = s->hits[4];
        s->hits[4] = hit;
        /* Reinitialise the detector for the next block */
        for (i = 0;  i < 6;  i++)
            goertzel_reset(&s->out[i]);
        s->current_sample = 0;
    }
    if (s->current_digits  &&  s->callback)
    {
        s->callback(s->callback_data, s->digits, s->current_digits);
        s->digits[0] = '\0';
        s->current_digits = 0;
    }
    return 0;
}
Example #7
0
File: dsp.c Project: OPSF/uClinux
static int dtmf_detect (dtmf_detect_state_t *s, int16_t amp[], int samples, 
		 int digitmode, int *writeback, int faxdetect)
{
	float row_energy[4];
	float col_energy[4];
#ifdef FAX_DETECT
	float fax_energy;
#ifdef OLD_DSP_ROUTINES
	float fax_energy_2nd;
#endif	
#endif /* FAX_DETECT */
	float famp;
	float v1;
	int i;
	int j;
	int sample;
	int best_row;
	int best_col;
	int hit;
	int limit;

	hit = 0;
	for (sample = 0;  sample < samples;  sample = limit) {
		/* 102 is optimised to meet the DTMF specs. */
		if ((samples - sample) >= (102 - s->current_sample))
			limit = sample + (102 - s->current_sample);
		else
			limit = samples;
#if defined(USE_3DNOW)
		_dtmf_goertzel_update (s->row_out, amp + sample, limit - sample);
		_dtmf_goertzel_update (s->col_out, amp + sample, limit - sample);
#ifdef OLD_DSP_ROUTINES
		_dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample);
		_dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample);
#endif		
		/* XXX Need to fax detect for 3dnow too XXX */
		#warning "Fax Support Broken"
#else
		/* The following unrolled loop takes only 35% (rough estimate) of the 
		   time of a rolled loop on the machine on which it was developed */
		for (j=sample;j<limit;j++) {
			famp = amp[j];
			s->energy += famp*famp;
			/* With GCC 2.95, the following unrolled code seems to take about 35%
			   (rough estimate) as long as a neat little 0-3 loop */
			v1 = s->row_out[0].v2;
			s->row_out[0].v2 = s->row_out[0].v3;
			s->row_out[0].v3 = s->row_out[0].fac*s->row_out[0].v2 - v1 + famp;
			v1 = s->col_out[0].v2;
			s->col_out[0].v2 = s->col_out[0].v3;
			s->col_out[0].v3 = s->col_out[0].fac*s->col_out[0].v2 - v1 + famp;
			v1 = s->row_out[1].v2;
			s->row_out[1].v2 = s->row_out[1].v3;
			s->row_out[1].v3 = s->row_out[1].fac*s->row_out[1].v2 - v1 + famp;
			v1 = s->col_out[1].v2;
			s->col_out[1].v2 = s->col_out[1].v3;
			s->col_out[1].v3 = s->col_out[1].fac*s->col_out[1].v2 - v1 + famp;
			v1 = s->row_out[2].v2;
			s->row_out[2].v2 = s->row_out[2].v3;
			s->row_out[2].v3 = s->row_out[2].fac*s->row_out[2].v2 - v1 + famp;
			v1 = s->col_out[2].v2;
			s->col_out[2].v2 = s->col_out[2].v3;
			s->col_out[2].v3 = s->col_out[2].fac*s->col_out[2].v2 - v1 + famp;
			v1 = s->row_out[3].v2;
			s->row_out[3].v2 = s->row_out[3].v3;
			s->row_out[3].v3 = s->row_out[3].fac*s->row_out[3].v2 - v1 + famp;
			v1 = s->col_out[3].v2;
			s->col_out[3].v2 = s->col_out[3].v3;
			s->col_out[3].v3 = s->col_out[3].fac*s->col_out[3].v2 - v1 + famp;
#ifdef FAX_DETECT
			/* Update fax tone */
			v1 = s->fax_tone.v2;
			s->fax_tone.v2 = s->fax_tone.v3;
			s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp;
#endif /* FAX_DETECT */
#ifdef OLD_DSP_ROUTINES
			v1 = s->col_out2nd[0].v2;
			s->col_out2nd[0].v2 = s->col_out2nd[0].v3;
			s->col_out2nd[0].v3 = s->col_out2nd[0].fac*s->col_out2nd[0].v2 - v1 + famp;
			v1 = s->row_out2nd[0].v2;
			s->row_out2nd[0].v2 = s->row_out2nd[0].v3;
			s->row_out2nd[0].v3 = s->row_out2nd[0].fac*s->row_out2nd[0].v2 - v1 + famp;
			v1 = s->col_out2nd[1].v2;
			s->col_out2nd[1].v2 = s->col_out2nd[1].v3;
			s->col_out2nd[1].v3 = s->col_out2nd[1].fac*s->col_out2nd[1].v2 - v1 + famp;
			v1 = s->row_out2nd[1].v2;
			s->row_out2nd[1].v2 = s->row_out2nd[1].v3;
			s->row_out2nd[1].v3 = s->row_out2nd[1].fac*s->row_out2nd[1].v2 - v1 + famp;
			v1 = s->col_out2nd[2].v2;
			s->col_out2nd[2].v2 = s->col_out2nd[2].v3;
			s->col_out2nd[2].v3 = s->col_out2nd[2].fac*s->col_out2nd[2].v2 - v1 + famp;
			v1 = s->row_out2nd[2].v2;
			s->row_out2nd[2].v2 = s->row_out2nd[2].v3;
			s->row_out2nd[2].v3 = s->row_out2nd[2].fac*s->row_out2nd[2].v2 - v1 + famp;
			v1 = s->col_out2nd[3].v2;
			s->col_out2nd[3].v2 = s->col_out2nd[3].v3;
			s->col_out2nd[3].v3 = s->col_out2nd[3].fac*s->col_out2nd[3].v2 - v1 + famp;
			v1 = s->row_out2nd[3].v2;
			s->row_out2nd[3].v2 = s->row_out2nd[3].v3;
			s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp;
#ifdef FAX_DETECT
			/* Update fax tone */            
			v1 = s->fax_tone.v2;
			s->fax_tone2nd.v2 = s->fax_tone2nd.v3;
			s->fax_tone2nd.v3 = s->fax_tone2nd.fac*s->fax_tone2nd.v2 - v1 + famp;
#endif /* FAX_DETECT */
#endif
		}
#endif
		s->current_sample += (limit - sample);
		if (s->current_sample < 102) {
			if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
				/* If we had a hit last time, go ahead and clear this out since likely it
				   will be another hit */
				for (i=sample;i<limit;i++) 
					amp[i] = 0;
				*writeback = 1;
			}
			continue;
		}
#ifdef FAX_DETECT
		/* Detect the fax energy, too */
		fax_energy = goertzel_result(&s->fax_tone);
#endif
		/* We are at the end of a DTMF detection block */
		/* Find the peak row and the peak column */
		row_energy[0] = goertzel_result (&s->row_out[0]);
		col_energy[0] = goertzel_result (&s->col_out[0]);

		for (best_row = best_col = 0, i = 1;  i < 4;  i++) {
			row_energy[i] = goertzel_result (&s->row_out[i]);
			if (row_energy[i] > row_energy[best_row])
				best_row = i;
			col_energy[i] = goertzel_result (&s->col_out[i]);
			if (col_energy[i] > col_energy[best_col])
				best_col = i;
		}
		hit = 0;
		/* Basic signal level test and the twist test */
		if (row_energy[best_row] >= DTMF_THRESHOLD && 
		    col_energy[best_col] >= DTMF_THRESHOLD &&
		    col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST &&
		    col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) {
			/* Relative peak test */
			for (i = 0;  i < 4;  i++) {
				if ((i != best_col &&
				    col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) ||
				    (i != best_row 
				     && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) {
					break;
				}
			}
#ifdef OLD_DSP_ROUTINES
			/* ... and second harmonic test */
			if (i >= 4 && 
			    (row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy &&
                	    goertzel_result(&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col]
			    && goertzel_result(&s->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) {
#else
			/* ... and fraction of total energy test */
			if (i >= 4 &&
			    (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy) {
#endif
				/* Got a hit */
				hit = dtmf_positions[(best_row << 2) + best_col];
				if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) {
					/* Zero out frame data if this is part DTMF */
					for (i=sample;i<limit;i++) 
						amp[i] = 0;
					*writeback = 1;
				}
				/* Look for two successive similar results */
				/* The logic in the next test is:
				   We need two successive identical clean detects, with
				   something different preceeding it. This can work with
				   back to back differing digits. More importantly, it
				   can work with nasty phones that give a very wobbly start
				   to a digit */
#ifdef OLD_DSP_ROUTINES
				if (hit == s->hit3  &&  s->hit3 != s->hit2) {
					s->mhit = hit;
					s->digit_hits[(best_row << 2) + best_col]++;
					s->detected_digits++;
					if (s->current_digits < MAX_DTMF_DIGITS) {
						s->digits[s->current_digits++] = hit;
						s->digits[s->current_digits] = '\0';
					} else {
						s->lost_digits++;
					}
				}
#else				
				if (hit == s->hits[2]  &&  hit != s->hits[1]  &&  hit != s->hits[0]) {
					s->mhit = hit;
					s->digit_hits[(best_row << 2) + best_col]++;
					s->detected_digits++;
					if (s->current_digits < MAX_DTMF_DIGITS) {
						s->digits[s->current_digits++] = hit;
						s->digits[s->current_digits] = '\0';
					} else {
						s->lost_digits++;
					}
				}
#endif
			}
		} 
#ifdef FAX_DETECT
		if (!hit && (fax_energy >= FAX_THRESHOLD) && 
			(fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy) &&
			(faxdetect)) {
#if 0
			printf("Fax energy/Second Harmonic: %f\n", fax_energy);
#endif					
			/* XXX Probably need better checking than just this the energy XXX */
			hit = 'f';
			s->fax_hits++;
		} else {
			if (s->fax_hits > 5) {
				hit = 'f';
				s->mhit = 'f';
				s->detected_digits++;
				if (s->current_digits < MAX_DTMF_DIGITS) {
					s->digits[s->current_digits++] = hit;
					s->digits[s->current_digits] = '\0';
				} else {
					s->lost_digits++;
				}
			}
			s->fax_hits = 0;
		}
#endif /* FAX_DETECT */
#ifdef OLD_DSP_ROUTINES
		s->hit1 = s->hit2;
		s->hit2 = s->hit3;
		s->hit3 = hit;
#else
		s->hits[0] = s->hits[1];
		s->hits[1] = s->hits[2];
		s->hits[2] = hit;
#endif		
		/* Reinitialise the detector for the next block */
		for (i = 0;  i < 4;  i++) {
			goertzel_reset(&s->row_out[i]);
			goertzel_reset(&s->col_out[i]);
#ifdef OLD_DSP_ROUTINES
			goertzel_reset(&s->row_out2nd[i]);
			goertzel_reset(&s->col_out2nd[i]);
#endif			
		}
#ifdef FAX_DETECT
		goertzel_reset (&s->fax_tone);
#ifdef OLD_DSP_ROUTINES
		goertzel_reset (&s->fax_tone2nd);
#endif			
#endif
		s->energy = 0.0;
		s->current_sample = 0;
	}
	if ((!s->mhit) || (s->mhit != hit)) {
		s->mhit = 0;
		return(0);
	}
	return (hit);
}

/* MF goertzel size */
#ifdef OLD_DSP_ROUTINES
#define	MF_GSIZE 160
#else
#define MF_GSIZE 120
#endif

static int mf_detect (mf_detect_state_t *s, int16_t amp[],
                 int samples, int digitmode, int *writeback)
{
#ifdef OLD_DSP_ROUTINES
	float tone_energy[6];
	int best1;
	int best2;
	float max;
	int sofarsogood;
#else
	float energy[6];
	int best;
	int second_best;
#endif
	float famp;
	float v1;
	int i;
	int j;
	int sample;
	int hit;
	int limit;

	hit = 0;
	for (sample = 0;  sample < samples;  sample = limit) {
		/* 80 is optimised to meet the MF specs. */
		if ((samples - sample) >= (MF_GSIZE - s->current_sample))
			limit = sample + (MF_GSIZE - s->current_sample);
		else
			limit = samples;
#if defined(USE_3DNOW)
		_dtmf_goertzel_update (s->row_out, amp + sample, limit - sample);
		_dtmf_goertzel_update (s->col_out, amp + sample, limit - sample);
#ifdef OLD_DSP_ROUTINES
		_dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample);
		_dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample);
#endif
		/* XXX Need to fax detect for 3dnow too XXX */
		#warning "Fax Support Broken"
#else
		/* The following unrolled loop takes only 35% (rough estimate) of the 
		   time of a rolled loop on the machine on which it was developed */
		for (j = sample;  j < limit;  j++) {
			famp = amp[j];
#ifdef OLD_DSP_ROUTINES
			s->energy += famp*famp;
#endif
			/* With GCC 2.95, the following unrolled code seems to take about 35%
			   (rough estimate) as long as a neat little 0-3 loop */
			v1 = s->tone_out[0].v2;
			s->tone_out[0].v2 = s->tone_out[0].v3;
			s->tone_out[0].v3 = s->tone_out[0].fac*s->tone_out[0].v2 - v1 + famp;
			v1 = s->tone_out[1].v2;
			s->tone_out[1].v2 = s->tone_out[1].v3;
			s->tone_out[1].v3 = s->tone_out[1].fac*s->tone_out[1].v2 - v1 + famp;
			v1 = s->tone_out[2].v2;
			s->tone_out[2].v2 = s->tone_out[2].v3;
			s->tone_out[2].v3 = s->tone_out[2].fac*s->tone_out[2].v2 - v1 + famp;
			v1 = s->tone_out[3].v2;
			s->tone_out[3].v2 = s->tone_out[3].v3;
			s->tone_out[3].v3 = s->tone_out[3].fac*s->tone_out[3].v2 - v1 + famp;
			v1 = s->tone_out[4].v2;
			s->tone_out[4].v2 = s->tone_out[4].v3;
			s->tone_out[4].v3 = s->tone_out[4].fac*s->tone_out[4].v2 - v1 + famp;
			v1 = s->tone_out[5].v2;
			s->tone_out[5].v2 = s->tone_out[5].v3;
			s->tone_out[5].v3 = s->tone_out[5].fac*s->tone_out[5].v2 - v1 + famp;
#ifdef OLD_DSP_ROUTINES
			v1 = s->tone_out2nd[0].v2;
			s->tone_out2nd[0].v2 = s->tone_out2nd[0].v3;
			s->tone_out2nd[0].v3 = s->tone_out2nd[0].fac*s->tone_out2nd[0].v2 - v1 + famp;
			v1 = s->tone_out2nd[1].v2;
			s->tone_out2nd[1].v2 = s->tone_out2nd[1].v3;
			s->tone_out2nd[1].v3 = s->tone_out2nd[1].fac*s->tone_out2nd[1].v2 - v1 + famp;
			v1 = s->tone_out2nd[2].v2;
			s->tone_out2nd[2].v2 = s->tone_out2nd[2].v3;
			s->tone_out2nd[2].v3 = s->tone_out2nd[2].fac*s->tone_out2nd[2].v2 - v1 + famp;
			v1 = s->tone_out2nd[3].v2;
			s->tone_out2nd[3].v2 = s->tone_out2nd[3].v3;
			s->tone_out2nd[3].v3 = s->tone_out2nd[3].fac*s->tone_out2nd[3].v2 - v1 + famp;
			v1 = s->tone_out2nd[4].v2;
			s->tone_out2nd[4].v2 = s->tone_out2nd[4].v3;
			s->tone_out2nd[4].v3 = s->tone_out2nd[4].fac*s->tone_out2nd[2].v2 - v1 + famp;
			v1 = s->tone_out2nd[3].v2;
			s->tone_out2nd[5].v2 = s->tone_out2nd[6].v3;
			s->tone_out2nd[5].v3 = s->tone_out2nd[6].fac*s->tone_out2nd[3].v2 - v1 + famp;
#endif
		}
#endif
		s->current_sample += (limit - sample);
		if (s->current_sample < MF_GSIZE) {
			if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
				/* If we had a hit last time, go ahead and clear this out since likely it
				   will be another hit */
				for (i=sample;i<limit;i++) 
					amp[i] = 0;
				*writeback = 1;
			}
			continue;
		}
#ifdef OLD_DSP_ROUTINES		
		/* We're at the end of an MF detection block.  Go ahead and calculate
		   all the energies. */
		for (i=0;i<6;i++) {
			tone_energy[i] = goertzel_result(&s->tone_out[i]);
		}
		/* Find highest */
		best1 = 0;
		max = tone_energy[0];
		for (i=1;i<6;i++) {
			if (tone_energy[i] > max) {
				max = tone_energy[i];
				best1 = i;
			}
		}

		/* Find 2nd highest */
		if (best1) {
			max = tone_energy[0];
			best2 = 0;
		} else {
			max = tone_energy[1];
			best2 = 1;
		}

		for (i=0;i<6;i++) {
			if (i == best1) continue;
			if (tone_energy[i] > max) {
				max = tone_energy[i];
				best2 = i;
			}
		}
		hit = 0;
		if (best1 != best2) 
			sofarsogood=1;
		else 
			sofarsogood=0;
		/* Check for relative energies */
		for (i=0;i<6;i++) {
			if (i == best1) 
				continue;
			if (i == best2) 
				continue;
			if (tone_energy[best1] < tone_energy[i] * MF_RELATIVE_PEAK) {
				sofarsogood = 0;
				break;
			}
			if (tone_energy[best2] < tone_energy[i] * MF_RELATIVE_PEAK) {
				sofarsogood = 0;
				break;
			}
		}
		
		if (sofarsogood) {
			/* Check for 2nd harmonic */
			if (goertzel_result(&s->tone_out2nd[best1]) * MF_2ND_HARMONIC > tone_energy[best1]) 
				sofarsogood = 0;
			else if (goertzel_result(&s->tone_out2nd[best2]) * MF_2ND_HARMONIC > tone_energy[best2])
				sofarsogood = 0;
		}
		if (sofarsogood) {
			hit = mf_hit[best1][best2];
			if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) {
				/* Zero out frame data if this is part DTMF */
				for (i=sample;i<limit;i++) 
					amp[i] = 0;
				*writeback = 1;
			}
			/* Look for two consecutive clean hits */
			if ((hit == s->hit3) && (s->hit3 != s->hit2)) {
				s->mhit = hit;
				s->detected_digits++;
				if (s->current_digits < MAX_DTMF_DIGITS - 2) {
					s->digits[s->current_digits++] = hit;
					s->digits[s->current_digits] = '\0';
				} else {
					s->lost_digits++;
				}
			}
		}
		
		s->hit1 = s->hit2;
		s->hit2 = s->hit3;
		s->hit3 = hit;
		/* Reinitialise the detector for the next block */
		for (i = 0;  i < 6;  i++) {
			goertzel_reset(&s->tone_out[i]);
			goertzel_reset(&s->tone_out2nd[i]);
		}
		s->energy = 0.0;
		s->current_sample = 0;
	}
#else
		/* We're at the end of an MF detection block.  */
		/* Find the two highest energies. The spec says to look for
		   two tones and two tones only. Taking this literally -ie
		   only two tones pass the minimum threshold - doesn't work
		   well. The sinc function mess, due to rectangular windowing
		   ensure that! Find the two highest energies and ensure they
		   are considerably stronger than any of the others. */
		energy[0] = goertzel_result(&s->tone_out[0]);
		energy[1] = goertzel_result(&s->tone_out[1]);
		if (energy[0] > energy[1]) {
			best = 0;
			second_best = 1;
		} else {
			best = 1;
			second_best = 0;
		}
		/*endif*/
		for (i=2;i<6;i++) {
			energy[i] = goertzel_result(&s->tone_out[i]);
			if (energy[i] >= energy[best]) {
				second_best = best;
				best = i;
			} else if (energy[i] >= energy[second_best]) {
				second_best = i;
			}
		}
		/* Basic signal level and twist tests */
		hit = 0;
		if (energy[best] >= BELL_MF_THRESHOLD && energy[second_best] >= BELL_MF_THRESHOLD
	            && energy[best] < energy[second_best]*BELL_MF_TWIST
	            && energy[best]*BELL_MF_TWIST > energy[second_best]) {
			/* Relative peak test */
			hit = -1;
			for (i=0;i<6;i++) {
				if (i != best && i != second_best) {
					if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best]) {
						/* The best two are not clearly the best */
						hit = 0;
						break;
					}
				}
			}
		}
		if (hit) {
			/* Get the values into ascending order */
			if (second_best < best) {
				i = best;
				best = second_best;
				second_best = i;
			}
			best = best*5 + second_best - 1;
			hit = bell_mf_positions[best];
			/* Look for two successive similar results */
			/* The logic in the next test is:
			   For KP we need 4 successive identical clean detects, with
			   two blocks of something different preceeding it. For anything
			   else we need two successive identical clean detects, with
			   two blocks of something different preceeding it. */
			if (hit == s->hits[4] && hit == s->hits[3] &&
			   ((hit != '*' && hit != s->hits[2] && hit != s->hits[1])||
			    (hit == '*' && hit == s->hits[2] && hit != s->hits[1] && 
			    hit != s->hits[0]))) {
				s->detected_digits++;
				if (s->current_digits < MAX_DTMF_DIGITS) {
					s->digits[s->current_digits++] = hit;
					s->digits[s->current_digits] = '\0';
				} else {
					s->lost_digits++;
				}
			}
		} else {
			hit = 0;
		}
		s->hits[0] = s->hits[1];
		s->hits[1] = s->hits[2];
		s->hits[2] = s->hits[3];
		s->hits[3] = s->hits[4];
		s->hits[4] = hit;
		/* Reinitialise the detector for the next block */
		for (i = 0;  i < 6;  i++)
			goertzel_reset(&s->tone_out[i]);
		s->current_sample = 0;
	}
Example #8
0
SPAN_DECLARE(int) dtmf_rx(dtmf_rx_state_t *s, const int16_t amp[], int samples)
{
#if defined(SPANDSP_USE_FIXED_POINT)
    int32_t row_energy[4];
    int32_t col_energy[4];
    int16_t xamp;
    float famp;
#else
    float row_energy[4];
    float col_energy[4];
    float xamp;
    float famp;
#endif
    float v1;
    int i;
    int j;
    int sample;
    int best_row;
    int best_col;
    int limit;
    uint8_t hit;

    hit = 0;
    for (sample = 0;  sample < samples;  sample = limit)
    {
        /* The block length is optimised to meet the DTMF specs. */
        if ((samples - sample) >= (DTMF_SAMPLES_PER_BLOCK - s->current_sample))
            limit = sample + (DTMF_SAMPLES_PER_BLOCK - s->current_sample);
        else
            limit = samples;
        /* The following unrolled loop takes only 35% (rough estimate) of the
           time of a rolled loop on the machine on which it was developed */
        for (j = sample;  j < limit;  j++)
        {
            xamp = amp[j];
            if (s->filter_dialtone)
            {
                famp = xamp;
                /* Sharp notches applied at 350Hz and 440Hz - the two common dialtone frequencies.
                   These are rather high Q, to achieve the required narrowness, without using lots of
                   sections. */
                v1 = 0.98356f*famp + 1.8954426f*s->z350[0] - 0.9691396f*s->z350[1];
                famp = v1 - 1.9251480f*s->z350[0] + s->z350[1];
                s->z350[1] = s->z350[0];
                s->z350[0] = v1;

                v1 = 0.98456f*famp + 1.8529543f*s->z440[0] - 0.9691396f*s->z440[1];
                famp = v1 - 1.8819938f*s->z440[0] + s->z440[1];
                s->z440[1] = s->z440[0];
                s->z440[0] = v1;
                xamp = famp;
            }
            xamp = goertzel_preadjust_amp(xamp);
#if defined(SPANDSP_USE_FIXED_POINT)
            s->energy += ((int32_t) xamp*xamp);
#else
            s->energy += xamp*xamp;
#endif
            goertzel_samplex(&s->row_out[0], xamp);
            goertzel_samplex(&s->col_out[0], xamp);
            goertzel_samplex(&s->row_out[1], xamp);
            goertzel_samplex(&s->col_out[1], xamp);
            goertzel_samplex(&s->row_out[2], xamp);
            goertzel_samplex(&s->col_out[2], xamp);
            goertzel_samplex(&s->row_out[3], xamp);
            goertzel_samplex(&s->col_out[3], xamp);
        }
        if (s->duration < INT_MAX - (limit - sample))
            s->duration += (limit - sample);
        s->current_sample += (limit - sample);
        if (s->current_sample < DTMF_SAMPLES_PER_BLOCK)
            continue;

        /* We are at the end of a DTMF detection block */
        /* Find the peak row and the peak column */
        row_energy[0] = goertzel_result(&s->row_out[0]);
        best_row = 0;
        col_energy[0] = goertzel_result(&s->col_out[0]);
        best_col = 0;
        for (i = 1;  i < 4;  i++)
        {
            row_energy[i] = goertzel_result(&s->row_out[i]);
            if (row_energy[i] > row_energy[best_row])
                best_row = i;
            col_energy[i] = goertzel_result(&s->col_out[i]);
            if (col_energy[i] > col_energy[best_col])
                best_col = i;
        }
        hit = 0;
        /* Basic signal level test and the twist test */
        if (row_energy[best_row] >= s->threshold
            &&
            col_energy[best_col] >= s->threshold)
        {
            if (col_energy[best_col] < row_energy[best_row]*s->reverse_twist
                &&
                col_energy[best_col]*s->normal_twist > row_energy[best_row])
            {
                /* Relative peak test ... */
                for (i = 0;  i < 4;  i++)
                {
                    if ((i != best_col  &&  col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col])
                        ||
                        (i != best_row  &&  row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row]))
                    {
                        break;
                    }
                }
                /* ... and fraction of total energy test */
                if (i >= 4
                    &&
                    (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy)
                {
                    /* Got a hit */
                    hit = dtmf_positions[(best_row << 2) + best_col];
                }
            }
            if (span_log_test(&s->logging, SPAN_LOG_FLOW))
            {
                /* Log information about the quality of the signal, to aid analysis of detection problems */
                /* Logging at this point filters the total no-hoper frames out of the log, and leaves
                   anything which might feasibly be a DTMF digit. The log will then contain a list of the
                   total, row and coloumn power levels for detailed analysis of detection problems. */
                span_log(&s->logging,
                         SPAN_LOG_FLOW,
                         "Potentially '%c' - total %.2fdB, row %.2fdB, col %.2fdB, duration %d - %s\n",
                         dtmf_positions[(best_row << 2) + best_col],
                         log10f(s->energy)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER,
                         log10f(row_energy[best_row]/DTMF_TO_TOTAL_ENERGY)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER,
                         log10f(col_energy[best_col]/DTMF_TO_TOTAL_ENERGY)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER,
                         s->duration,
                         (hit)  ?  "hit"  :  "miss");
            }
        }
        /* The logic in the next test should ensure the following for different successive hit patterns:
                -----ABB = start of digit B.
                ----B-BB = start of digit B
                ----A-BB = start of digit B
                BBBBBABB = still in digit B.
                BBBBBB-- = end of digit B
                BBBBBBC- = end of digit B
                BBBBACBB = B ends, then B starts again.
                BBBBBBCC = B ends, then C starts.
                BBBBBCDD = B ends, then D starts.
           This can work with:
                - Back to back differing digits. Back-to-back digits should
                  not happen. The spec. says there should be a gap between digits.
                  However, many real phones do not impose a gap, and rolling across
                  the keypad can produce little or no gap.
                - It tolerates nasty phones that give a very wobbly start to a digit.
                - VoIP can give sample slips. The phase jumps that produces will cause
                  the block it is in to give no detection. This logic will ride over a
                  single missed block, and not falsely declare a second digit. If the
                  hiccup happens in the wrong place on a minimum length digit, however
                  we would still fail to detect that digit. Could anything be done to
                  deal with that? Packet loss is clearly a no-go zone.
                  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  &&  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)
            {
                /* 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;
                }
            }
            else
            {
                if (hit)
                {
                    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_callback(s->digits_callback_data, s->digits, s->current_digits);
                            s->current_digits = 0;
                        }
                    }
                    else
                    {
                        s->lost_digits++;
                    }
                }
            }
            s->in_digit = hit;
        }
        s->last_hit = hit;
#if defined(SPANDSP_USE_FIXED_POINT)
        s->energy = 0;
#else
        s->energy = 0.0f;
#endif
        s->current_sample = 0;
    }
    if (s->current_digits  &&  s->digits_callback)
    {
        s->digits_callback(s->digits_callback_data, s->digits, s->current_digits);
        s->digits[0] = '\0';
        s->current_digits = 0;
    }
    return 0;
}
Example #9
0
int CGoertzelDTMFFax::fax_detect
(
	double					amp[], 
	int						samples
)
{
	double fax_energy;
	double fax_energy_2nd;
    double famp;
    float v1;
    int i;
    int j;
    int sample;
    int hit;
    int limit;
 
    hit = 0;
    for (sample = 0;  sample < samples;  sample = limit) 
	{
       /* 102 is optimised to meet the DTMF specs. */
       if ((samples - sample) >= (102 - state->current_sample))
          limit = sample + (102 - state->current_sample);
       else
          limit = samples;

       for (j=sample;j<limit;j++) 
	   {
          famp = amp[j];
          state->energy += famp*famp;

		  //goertzel_sample(&(state->fax_tone), famp);
          v1 = state->fax_tone.v2;
          state->fax_tone.v2 = state->fax_tone.v3;
          state->fax_tone.v3 = state->fax_tone.fac*state->fax_tone.v2 - v1 + famp;

		  //goertzel_sample(&(state->fax_tone2nd), famp);
          v1 = state->fax_tone.v2;
          state->fax_tone2nd.v2 = state->fax_tone2nd.v3;
          state->fax_tone2nd.v3 = state->fax_tone2nd.fac*state->fax_tone2nd.v2 - v1 + famp;

       }

	   state->current_sample += (limit - sample);
         
       /* Detect the fax energy */
       fax_energy = goertzel_result(&state->fax_tone);
	   
	   hit = 0;

       if ((fax_energy >= FAX_THRESHOLD) && 
          (fax_energy >= DTMF_TO_TOTAL_ENERGY*state->energy)) 
	   {               
		    
		    //Bug here...
		    //Test the 2nd harmonic too
		    
			/*fax_energy_2nd = goertzel_result(&state->fax_tone2nd);
			if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy)
			{
			
			  Probably need better checking than just this the energy
			}*/
			
			hit = 'f';
			state->fax_hits++;

			//Enough of it...
			if (state->fax_hits > 5) 
			{
				break;
			}
       }

       goertzel_reset (&state->fax_tone);
       goertzel_reset (&state->fax_tone2nd);

       state->energy = 0.0;
       state->current_sample = 0;
    }

    return (hit);
}