comparison spandsp-0.0.6pre17/src/super_tone_rx.c @ 4:26cd8f1ef0b1

import spandsp-0.0.6pre17
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 15:50:58 +0200
parents
children
comparison
equal deleted inserted replaced
3:c6c5a16ce2f2 4:26cd8f1ef0b1
1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * super_tone_rx.c - Flexible telephony supervisory tone detection.
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2003 Steve Underwood
9 *
10 * All rights reserved.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 2.1,
14 * as published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 * $Id: super_tone_rx.c,v 1.33.4.1 2009/12/19 09:47:56 steveu Exp $
26 */
27
28 /*! \file */
29
30 #if defined(HAVE_CONFIG_H)
31 #include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <ctype.h>
39 #include <time.h>
40 #include <inttypes.h>
41 #if defined(HAVE_TGMATH_H)
42 #include <tgmath.h>
43 #endif
44 #if defined(HAVE_MATH_H)
45 #include <math.h>
46 #endif
47 #include "floating_fudge.h"
48
49 #include "spandsp/telephony.h"
50 #include "spandsp/fast_convert.h"
51 #include "spandsp/complex.h"
52 #include "spandsp/vector_float.h"
53 #include "spandsp/complex_vector_float.h"
54 #include "spandsp/tone_detect.h"
55 #include "spandsp/tone_generate.h"
56 #include "spandsp/super_tone_rx.h"
57
58 #include "spandsp/private/super_tone_rx.h"
59
60 #if defined(SPANDSP_USE_FIXED_POINT)
61 #define DETECTION_THRESHOLD 16439 /* -42dBm0 */
62 #define TONE_TWIST 4 /* 6dB */
63 #define TONE_TO_TOTAL_ENERGY 64 /* -3dB */
64 #else
65 #define DETECTION_THRESHOLD 269338317.0f /* -42dBm0 [((128.0*32768.0/1.4142)*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2 => 269338317.0] */
66 #define TONE_TWIST 3.981f /* 6dB */
67 #define TONE_TO_TOTAL_ENERGY 1.995f /* 3dB */
68 #define DTMF_TO_TOTAL_ENERGY 64.152f /* -3dB [BINS*10^(-3/10.0)] */
69 #endif
70
71 static int add_super_tone_freq(super_tone_rx_descriptor_t *desc, int freq)
72 {
73 int i;
74
75 if (freq == 0)
76 return -1;
77 /* Look for an existing frequency */
78 for (i = 0; i < desc->used_frequencies; i++)
79 {
80 if (desc->pitches[i][0] == freq)
81 return desc->pitches[i][1];
82 }
83 /* Look for an existing tone which is very close. We may need to merge
84 the detectors. */
85 for (i = 0; i < desc->used_frequencies; i++)
86 {
87 if ((desc->pitches[i][0] - 10) <= freq && freq <= (desc->pitches[i][0] + 10))
88 {
89 /* Merge these two */
90 desc->pitches[desc->used_frequencies][0] = freq;
91 desc->pitches[desc->used_frequencies][1] = i;
92 make_goertzel_descriptor(&desc->desc[desc->pitches[i][1]], (float) (freq + desc->pitches[i][0])/2, BINS);
93 desc->used_frequencies++;
94 return desc->pitches[i][1];
95 }
96 }
97 desc->pitches[i][0] = freq;
98 desc->pitches[i][1] = desc->monitored_frequencies;
99 if (desc->monitored_frequencies%5 == 0)
100 {
101 desc->desc = (goertzel_descriptor_t *) realloc(desc->desc, (desc->monitored_frequencies + 5)*sizeof(goertzel_descriptor_t));
102 }
103 make_goertzel_descriptor(&desc->desc[desc->monitored_frequencies++], (float) freq, BINS);
104 desc->used_frequencies++;
105 return desc->pitches[i][1];
106 }
107 /*- End of function --------------------------------------------------------*/
108
109 SPAN_DECLARE(int) super_tone_rx_add_tone(super_tone_rx_descriptor_t *desc)
110 {
111 if (desc->tones%5 == 0)
112 {
113 desc->tone_list = (super_tone_rx_segment_t **) realloc(desc->tone_list, (desc->tones + 5)*sizeof(super_tone_rx_segment_t *));
114 desc->tone_segs = (int *) realloc(desc->tone_segs, (desc->tones + 5)*sizeof(int));
115 }
116 desc->tone_list[desc->tones] = NULL;
117 desc->tone_segs[desc->tones] = 0;
118 desc->tones++;
119 return desc->tones - 1;
120 }
121 /*- End of function --------------------------------------------------------*/
122
123 SPAN_DECLARE(int) super_tone_rx_add_element(super_tone_rx_descriptor_t *desc,
124 int tone,
125 int f1,
126 int f2,
127 int min,
128 int max)
129 {
130 int step;
131
132 step = desc->tone_segs[tone];
133 if (step%5 == 0)
134 {
135 desc->tone_list[tone] = (super_tone_rx_segment_t *) realloc(desc->tone_list[tone], (step + 5)*sizeof(super_tone_rx_segment_t));
136 }
137 desc->tone_list[tone][step].f1 = add_super_tone_freq(desc, f1);
138 desc->tone_list[tone][step].f2 = add_super_tone_freq(desc, f2);
139 desc->tone_list[tone][step].min_duration = min*8;
140 desc->tone_list[tone][step].max_duration = (max == 0) ? 0x7FFFFFFF : max*8;
141 desc->tone_segs[tone]++;
142 return step;
143 }
144 /*- End of function --------------------------------------------------------*/
145
146 static int test_cadence(super_tone_rx_segment_t *pattern,
147 int steps,
148 super_tone_rx_segment_t *test,
149 int rotation)
150 {
151 int i;
152 int j;
153
154 if (rotation >= 0)
155 {
156 /* Check only for the sustaining of a tone in progress. This means
157 we only need to check each block if the latest step is compatible
158 with the tone template. */
159 if (steps < 0)
160 {
161 /* A -ve value for steps indicates we just changed step, and need to
162 check the last one ended within spec. If we don't do this
163 extra test a low duration segment might be accepted as OK. */
164 steps = -steps;
165 j = (rotation + steps - 2)%steps;
166 if (pattern[j].f1 != test[8].f1 || pattern[j].f2 != test[8].f2)
167 return 0;
168 if (pattern[j].min_duration > test[8].min_duration*BINS
169 ||
170 pattern[j].max_duration < test[8].min_duration*BINS)
171 {
172 return 0;
173 }
174 }
175 j = (rotation + steps - 1)%steps;
176 if (pattern[j].f1 != test[9].f1 || pattern[j].f2 != test[9].f2)
177 return 0;
178 if (pattern[j].max_duration < test[9].min_duration*BINS)
179 return 0;
180 }
181 else
182 {
183 /* Look for a complete template match. */
184 for (i = 0; i < steps; i++)
185 {
186 j = i + 10 - steps;
187 if (pattern[i].f1 != test[j].f1 || pattern[i].f2 != test[j].f2)
188 return 0;
189 if (pattern[i].min_duration > test[j].min_duration*BINS
190 ||
191 pattern[i].max_duration < test[j].min_duration*BINS)
192 {
193 return 0;
194 }
195 }
196 }
197 return 1;
198 }
199 /*- End of function --------------------------------------------------------*/
200
201 SPAN_DECLARE(super_tone_rx_descriptor_t *) super_tone_rx_make_descriptor(super_tone_rx_descriptor_t *desc)
202 {
203 if (desc == NULL)
204 {
205 if ((desc = (super_tone_rx_descriptor_t *) malloc(sizeof(*desc))) == NULL)
206 return NULL;
207 }
208 desc->tone_list = NULL;
209 desc->tone_segs = NULL;
210
211 desc->used_frequencies = 0;
212 desc->monitored_frequencies = 0;
213 desc->desc = NULL;
214 desc->tones = 0;
215 return desc;
216 }
217 /*- End of function --------------------------------------------------------*/
218
219 SPAN_DECLARE(int) super_tone_rx_free_descriptor(super_tone_rx_descriptor_t *desc)
220 {
221 int i;
222
223 if (desc)
224 {
225 for (i = 0; i < desc->tones; i++)
226 {
227 if (desc->tone_list[i])
228 free(desc->tone_list[i]);
229 }
230 if (desc->tone_list)
231 free(desc->tone_list);
232 if (desc->tone_segs)
233 free(desc->tone_segs);
234 if (desc->desc)
235 free(desc->desc);
236 free(desc);
237 }
238 return 0;
239 }
240 /*- End of function --------------------------------------------------------*/
241
242 SPAN_DECLARE(void) super_tone_rx_segment_callback(super_tone_rx_state_t *s,
243 void (*callback)(void *data, int f1, int f2, int duration))
244 {
245 s->segment_callback = callback;
246 }
247 /*- End of function --------------------------------------------------------*/
248
249 SPAN_DECLARE(super_tone_rx_state_t *) super_tone_rx_init(super_tone_rx_state_t *s,
250 super_tone_rx_descriptor_t *desc,
251 tone_report_func_t callback,
252 void *user_data)
253 {
254 int i;
255
256 if (desc == NULL)
257 return NULL;
258 if (callback == NULL)
259 return NULL;
260 if (s == NULL)
261 {
262 if ((s = (super_tone_rx_state_t *) malloc(sizeof(*s) + desc->monitored_frequencies*sizeof(goertzel_state_t))) == NULL)
263 return NULL;
264 }
265
266 for (i = 0; i < 11; i++)
267 {
268 s->segments[i].f1 = -1;
269 s->segments[i].f2 = -1;
270 s->segments[i].min_duration = 0;
271 }
272 s->segment_callback = NULL;
273 s->tone_callback = callback;
274 s->callback_data = user_data;
275 if (desc)
276 s->desc = desc;
277 s->detected_tone = -1;
278 s->energy = 0.0f;
279 for (i = 0; i < desc->monitored_frequencies; i++)
280 goertzel_init(&s->state[i], &s->desc->desc[i]);
281 return s;
282 }
283 /*- End of function --------------------------------------------------------*/
284
285 SPAN_DECLARE(int) super_tone_rx_release(super_tone_rx_state_t *s)
286 {
287 return 0;
288 }
289 /*- End of function --------------------------------------------------------*/
290
291 SPAN_DECLARE(int) super_tone_rx_free(super_tone_rx_state_t *s)
292 {
293 if (s)
294 free(s);
295 return 0;
296 }
297 /*- End of function --------------------------------------------------------*/
298
299 static void super_tone_chunk(super_tone_rx_state_t *s)
300 {
301 int i;
302 int j;
303 int k1;
304 int k2;
305 #if defined(SPANDSP_USE_FIXED_POINT)
306 int32_t res[BINS/2];
307 #else
308 float res[BINS/2];
309 #endif
310
311 for (i = 0; i < s->desc->monitored_frequencies; i++)
312 res[i] = goertzel_result(&s->state[i]);
313 /* Find our two best monitored frequencies, which also have adequate energy. */
314 if (s->energy < DETECTION_THRESHOLD)
315 {
316 k1 = -1;
317 k2 = -1;
318 }
319 else
320 {
321 if (res[0] > res[1])
322 {
323 k1 = 0;
324 k2 = 1;
325 }
326 else
327 {
328 k1 = 1;
329 k2 = 0;
330 }
331 for (j = 2; j < s->desc->monitored_frequencies; j++)
332 {
333 if (res[j] >= res[k1])
334 {
335 k2 = k1;
336 k1 = j;
337 }
338 else if (res[j] >= res[k2])
339 {
340 k2 = j;
341 }
342 }
343 if ((res[k1] + res[k2]) < TONE_TO_TOTAL_ENERGY*s->energy)
344 {
345 k1 = -1;
346 k2 = -1;
347 }
348 else if (res[k1] > TONE_TWIST*res[k2])
349 {
350 k2 = -1;
351 }
352 else if (k2 < k1)
353 {
354 j = k1;
355 k1 = k2;
356 k2 = j;
357 }
358 }
359 /* See if this differs from last time. */
360 if (k1 != s->segments[10].f1 || k2 != s->segments[10].f2)
361 {
362 /* It is different, but this might just be a transitional quirk, or
363 a one shot hiccup (eg due to noise). Only if this same thing is
364 seen a second time should we change state. */
365 s->segments[10].f1 = k1;
366 s->segments[10].f2 = k2;
367 /* While things are hopping around, consider this a continuance of the
368 previous state. */
369 s->segments[9].min_duration++;
370 }
371 else
372 {
373 if (k1 != s->segments[9].f1 || k2 != s->segments[9].f2)
374 {
375 if (s->detected_tone >= 0)
376 {
377 /* Test for the continuance of the existing tone pattern, based on our new knowledge of an
378 entire segment length. */
379 if (!test_cadence(s->desc->tone_list[s->detected_tone], -s->desc->tone_segs[s->detected_tone], s->segments, s->rotation++))
380 {
381 s->detected_tone = -1;
382 s->tone_callback(s->callback_data, s->detected_tone, -10, 0);
383 }
384 }
385 if (s->segment_callback)
386 {
387 s->segment_callback(s->callback_data,
388 s->segments[9].f1,
389 s->segments[9].f2,
390 s->segments[9].min_duration*BINS/8);
391 }
392 memcpy (&s->segments[0], &s->segments[1], 9*sizeof(s->segments[0]));
393 s->segments[9].f1 = k1;
394 s->segments[9].f2 = k2;
395 s->segments[9].min_duration = 1;
396 }
397 else
398 {
399 /* This is a continuance of the previous state */
400 if (s->detected_tone >= 0)
401 {
402 /* Test for the continuance of the existing tone pattern. We must do this here, so we can sense the
403 discontinuance of the tone on an excessively long segment. */
404 if (!test_cadence(s->desc->tone_list[s->detected_tone], s->desc->tone_segs[s->detected_tone], s->segments, s->rotation))
405 {
406 s->detected_tone = -1;
407 s->tone_callback(s->callback_data, s->detected_tone, -10, 0);
408 }
409 }
410 s->segments[9].min_duration++;
411 }
412 }
413 if (s->detected_tone < 0)
414 {
415 /* Test for the start of any of the monitored tone patterns */
416 for (j = 0; j < s->desc->tones; j++)
417 {
418 if (test_cadence(s->desc->tone_list[j], s->desc->tone_segs[j], s->segments, -1))
419 {
420 s->detected_tone = j;
421 s->rotation = 0;
422 s->tone_callback(s->callback_data, s->detected_tone, -10, 0);
423 break;
424 }
425 }
426 }
427 #if defined(SPANDSP_USE_FIXED_POINT)
428 s->energy = 0;
429 #else
430 s->energy = 0.0f;
431 #endif
432 }
433 /*- End of function --------------------------------------------------------*/
434
435 SPAN_DECLARE(int) super_tone_rx(super_tone_rx_state_t *s, const int16_t amp[], int samples)
436 {
437 int i;
438 int x;
439 int sample;
440 #if defined(SPANDSP_USE_FIXED_POINT)
441 int16_t xamp;
442 #else
443 float xamp;
444 #endif
445
446 x = 0;
447 for (sample = 0; sample < samples; sample += x)
448 {
449 for (i = 0; i < s->desc->monitored_frequencies; i++)
450 x = goertzel_update(&s->state[i], amp + sample, samples - sample);
451 for (i = 0; i < x; i++)
452 {
453 xamp = goertzel_preadjust_amp(amp[sample + i]);
454 #if defined(SPANDSP_USE_FIXED_POINT)
455 s->energy += ((int32_t) xamp*xamp);
456 #else
457 s->energy += xamp*xamp;
458 #endif
459 }
460 if (s->state[0].current_sample >= BINS)
461 {
462 /* We have finished a Goertzel block. */
463 super_tone_chunk(s);
464 s->energy = 0;
465 }
466 }
467 return samples;
468 }
469 /*- End of function --------------------------------------------------------*/
470 /*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.