Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/fsk.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 * fsk.c - FSK modem transmit and receive parts | |
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: fsk.c,v 1.60 2009/11/02 13:25:20 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 <inttypes.h> | |
36 #include <string.h> | |
37 #if defined(HAVE_TGMATH_H) | |
38 #include <tgmath.h> | |
39 #endif | |
40 #if defined(HAVE_MATH_H) | |
41 #include <math.h> | |
42 #endif | |
43 #include "floating_fudge.h" | |
44 #include <assert.h> | |
45 | |
46 #include "spandsp/telephony.h" | |
47 #include "spandsp/complex.h" | |
48 #include "spandsp/dds.h" | |
49 #include "spandsp/power_meter.h" | |
50 #include "spandsp/async.h" | |
51 #include "spandsp/fsk.h" | |
52 | |
53 #include "spandsp/private/fsk.h" | |
54 | |
55 const fsk_spec_t preset_fsk_specs[] = | |
56 { | |
57 { | |
58 "V21 ch 1", | |
59 1080 + 100, | |
60 1080 - 100, | |
61 -14, | |
62 -30, | |
63 300*100 | |
64 }, | |
65 { | |
66 "V21 ch 2", | |
67 1750 + 100, | |
68 1750 - 100, | |
69 -14, | |
70 -30, | |
71 300*100 | |
72 }, | |
73 { | |
74 "V23 ch 1", | |
75 2100, | |
76 1300, | |
77 -14, | |
78 -30, | |
79 1200*100 | |
80 }, | |
81 { | |
82 "V23 ch 2", | |
83 450, | |
84 390, | |
85 -14, | |
86 -30, | |
87 75*100 | |
88 }, | |
89 { | |
90 "Bell103 ch 1", | |
91 2125 - 100, | |
92 2125 + 100, | |
93 -14, | |
94 -30, | |
95 300*100 | |
96 }, | |
97 { | |
98 "Bell103 ch 2", | |
99 1170 - 100, | |
100 1170 + 100, | |
101 -14, | |
102 -30, | |
103 300*100 | |
104 }, | |
105 { | |
106 "Bell202", | |
107 2200, | |
108 1200, | |
109 -14, | |
110 -30, | |
111 1200*100 | |
112 }, | |
113 { | |
114 "Weitbrecht 45.45", /* Used for TDD (Telecoms Device for the Deaf) */ | |
115 1800, | |
116 1400, | |
117 -14, | |
118 -30, | |
119 4545 | |
120 }, | |
121 { | |
122 "Weitbrecht 50", /* Used for TDD (Telecoms Device for the Deaf) */ | |
123 1800, | |
124 1400, | |
125 -14, | |
126 -30, | |
127 5000 | |
128 } | |
129 }; | |
130 | |
131 SPAN_DECLARE(int) fsk_tx_restart(fsk_tx_state_t *s, const fsk_spec_t *spec) | |
132 { | |
133 s->baud_rate = spec->baud_rate; | |
134 s->phase_rates[0] = dds_phase_rate((float) spec->freq_zero); | |
135 s->phase_rates[1] = dds_phase_rate((float) spec->freq_one); | |
136 s->scaling = dds_scaling_dbm0((float) spec->tx_level); | |
137 /* Initialise fractional sample baud generation. */ | |
138 s->phase_acc = 0; | |
139 s->baud_frac = 0; | |
140 s->current_phase_rate = s->phase_rates[1]; | |
141 | |
142 s->shutdown = FALSE; | |
143 return 0; | |
144 } | |
145 /*- End of function --------------------------------------------------------*/ | |
146 | |
147 SPAN_DECLARE(fsk_tx_state_t *) fsk_tx_init(fsk_tx_state_t *s, | |
148 const fsk_spec_t *spec, | |
149 get_bit_func_t get_bit, | |
150 void *user_data) | |
151 { | |
152 if (s == NULL) | |
153 { | |
154 if ((s = (fsk_tx_state_t *) malloc(sizeof(*s))) == NULL) | |
155 return NULL; | |
156 } | |
157 memset(s, 0, sizeof(*s)); | |
158 | |
159 s->get_bit = get_bit; | |
160 s->get_bit_user_data = user_data; | |
161 fsk_tx_restart(s, spec); | |
162 return s; | |
163 } | |
164 /*- End of function --------------------------------------------------------*/ | |
165 | |
166 SPAN_DECLARE(int) fsk_tx_release(fsk_tx_state_t *s) | |
167 { | |
168 return 0; | |
169 } | |
170 /*- End of function --------------------------------------------------------*/ | |
171 | |
172 SPAN_DECLARE(int) fsk_tx_free(fsk_tx_state_t *s) | |
173 { | |
174 free(s); | |
175 return 0; | |
176 } | |
177 /*- End of function --------------------------------------------------------*/ | |
178 | |
179 SPAN_DECLARE_NONSTD(int) fsk_tx(fsk_tx_state_t *s, int16_t amp[], int len) | |
180 { | |
181 int sample; | |
182 int bit; | |
183 | |
184 if (s->shutdown) | |
185 return 0; | |
186 /* Make the transitions between 0 and 1 phase coherent, but instantaneous | |
187 jumps. There is currently no interpolation for bauds that end mid-sample. | |
188 Mainstream users will not care. Some specialist users might have a problem | |
189 with them, if they care about accurate transition timing. */ | |
190 for (sample = 0; sample < len; sample++) | |
191 { | |
192 if ((s->baud_frac += s->baud_rate) >= SAMPLE_RATE*100) | |
193 { | |
194 s->baud_frac -= SAMPLE_RATE*100; | |
195 if ((bit = s->get_bit(s->get_bit_user_data)) == SIG_STATUS_END_OF_DATA) | |
196 { | |
197 if (s->status_handler) | |
198 s->status_handler(s->status_user_data, SIG_STATUS_END_OF_DATA); | |
199 if (s->status_handler) | |
200 s->status_handler(s->status_user_data, SIG_STATUS_SHUTDOWN_COMPLETE); | |
201 s->shutdown = TRUE; | |
202 break; | |
203 } | |
204 s->current_phase_rate = s->phase_rates[bit & 1]; | |
205 } | |
206 amp[sample] = dds_mod(&(s->phase_acc), s->current_phase_rate, s->scaling, 0); | |
207 } | |
208 return sample; | |
209 } | |
210 /*- End of function --------------------------------------------------------*/ | |
211 | |
212 SPAN_DECLARE(void) fsk_tx_power(fsk_tx_state_t *s, float power) | |
213 { | |
214 s->scaling = dds_scaling_dbm0(power); | |
215 } | |
216 /*- End of function --------------------------------------------------------*/ | |
217 | |
218 SPAN_DECLARE(void) fsk_tx_set_get_bit(fsk_tx_state_t *s, get_bit_func_t get_bit, void *user_data) | |
219 { | |
220 s->get_bit = get_bit; | |
221 s->get_bit_user_data = user_data; | |
222 } | |
223 /*- End of function --------------------------------------------------------*/ | |
224 | |
225 SPAN_DECLARE(void) fsk_tx_set_modem_status_handler(fsk_tx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
226 { | |
227 s->status_handler = handler; | |
228 s->status_user_data = user_data; | |
229 } | |
230 /*- End of function --------------------------------------------------------*/ | |
231 | |
232 SPAN_DECLARE(void) fsk_rx_signal_cutoff(fsk_rx_state_t *s, float cutoff) | |
233 { | |
234 /* The 6.04 allows for the gain of the DC blocker */ | |
235 s->carrier_on_power = (int32_t) (power_meter_level_dbm0(cutoff + 2.5f - 6.04f)); | |
236 s->carrier_off_power = (int32_t) (power_meter_level_dbm0(cutoff - 2.5f - 6.04f)); | |
237 } | |
238 /*- End of function --------------------------------------------------------*/ | |
239 | |
240 SPAN_DECLARE(float) fsk_rx_signal_power(fsk_rx_state_t *s) | |
241 { | |
242 return power_meter_current_dbm0(&s->power); | |
243 } | |
244 /*- End of function --------------------------------------------------------*/ | |
245 | |
246 SPAN_DECLARE(void) fsk_rx_set_put_bit(fsk_rx_state_t *s, put_bit_func_t put_bit, void *user_data) | |
247 { | |
248 s->put_bit = put_bit; | |
249 s->put_bit_user_data = user_data; | |
250 } | |
251 /*- End of function --------------------------------------------------------*/ | |
252 | |
253 SPAN_DECLARE(void) fsk_rx_set_modem_status_handler(fsk_rx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
254 { | |
255 s->status_handler = handler; | |
256 s->status_user_data = user_data; | |
257 } | |
258 /*- End of function --------------------------------------------------------*/ | |
259 | |
260 SPAN_DECLARE(int) fsk_rx_restart(fsk_rx_state_t *s, const fsk_spec_t *spec, int framing_mode) | |
261 { | |
262 int chop; | |
263 | |
264 s->baud_rate = spec->baud_rate; | |
265 s->framing_mode = framing_mode; | |
266 fsk_rx_signal_cutoff(s, (float) spec->min_level); | |
267 | |
268 /* Detect by correlating against the tones we want, over a period | |
269 of one baud. The correlation must be quadrature. */ | |
270 | |
271 /* First we need the quadrature tone generators to correlate | |
272 against. */ | |
273 s->phase_rate[0] = dds_phase_rate((float) spec->freq_zero); | |
274 s->phase_rate[1] = dds_phase_rate((float) spec->freq_one); | |
275 s->phase_acc[0] = 0; | |
276 s->phase_acc[1] = 0; | |
277 s->last_sample = 0; | |
278 | |
279 /* The correlation should be over one baud. */ | |
280 s->correlation_span = SAMPLE_RATE*100/spec->baud_rate; | |
281 /* But limit it for very slow baud rates, so we do not overflow our | |
282 buffer. */ | |
283 if (s->correlation_span > FSK_MAX_WINDOW_LEN) | |
284 s->correlation_span = FSK_MAX_WINDOW_LEN; | |
285 | |
286 /* We need to scale, to avoid overflow in the correlation. */ | |
287 s->scaling_shift = 0; | |
288 chop = s->correlation_span; | |
289 while (chop != 0) | |
290 { | |
291 s->scaling_shift++; | |
292 chop >>= 1; | |
293 } | |
294 | |
295 /* Initialise the baud/bit rate tracking. */ | |
296 s->baud_phase = 0; | |
297 s->frame_state = 0; | |
298 s->frame_bits = 0; | |
299 s->last_bit = 0; | |
300 | |
301 /* Initialise a power detector, so sense when a signal is present. */ | |
302 power_meter_init(&(s->power), 4); | |
303 s->signal_present = 0; | |
304 return 0; | |
305 } | |
306 /*- End of function --------------------------------------------------------*/ | |
307 | |
308 SPAN_DECLARE(fsk_rx_state_t *) fsk_rx_init(fsk_rx_state_t *s, | |
309 const fsk_spec_t *spec, | |
310 int framing_mode, | |
311 put_bit_func_t put_bit, | |
312 void *user_data) | |
313 { | |
314 if (s == NULL) | |
315 { | |
316 if ((s = (fsk_rx_state_t *) malloc(sizeof(*s))) == NULL) | |
317 return NULL; | |
318 } | |
319 memset(s, 0, sizeof(*s)); | |
320 | |
321 s->put_bit = put_bit; | |
322 s->put_bit_user_data = user_data; | |
323 fsk_rx_restart(s, spec, framing_mode); | |
324 return s; | |
325 } | |
326 /*- End of function --------------------------------------------------------*/ | |
327 | |
328 SPAN_DECLARE(int) fsk_rx_release(fsk_rx_state_t *s) | |
329 { | |
330 return 0; | |
331 } | |
332 /*- End of function --------------------------------------------------------*/ | |
333 | |
334 SPAN_DECLARE(int) fsk_rx_free(fsk_rx_state_t *s) | |
335 { | |
336 free(s); | |
337 return 0; | |
338 } | |
339 /*- End of function --------------------------------------------------------*/ | |
340 | |
341 static void report_status_change(fsk_rx_state_t *s, int status) | |
342 { | |
343 if (s->status_handler) | |
344 s->status_handler(s->status_user_data, status); | |
345 else if (s->put_bit) | |
346 s->put_bit(s->put_bit_user_data, status); | |
347 } | |
348 /*- End of function --------------------------------------------------------*/ | |
349 | |
350 SPAN_DECLARE_NONSTD(int) fsk_rx(fsk_rx_state_t *s, const int16_t *amp, int len) | |
351 { | |
352 int buf_ptr; | |
353 int baudstate; | |
354 int i; | |
355 int j; | |
356 int16_t x; | |
357 int32_t dot; | |
358 int32_t sum[2]; | |
359 int32_t power; | |
360 complexi_t ph; | |
361 | |
362 buf_ptr = s->buf_ptr; | |
363 | |
364 for (i = 0; i < len; i++) | |
365 { | |
366 /* The *totally* asynchronous character to character behaviour of these | |
367 modems, when carrying async. data, seems to force a sample by sample | |
368 approach. */ | |
369 for (j = 0; j < 2; j++) | |
370 { | |
371 s->dot[j].re -= s->window[j][buf_ptr].re; | |
372 s->dot[j].im -= s->window[j][buf_ptr].im; | |
373 | |
374 ph = dds_complexi(&(s->phase_acc[j]), s->phase_rate[j]); | |
375 s->window[j][buf_ptr].re = (ph.re*amp[i]) >> s->scaling_shift; | |
376 s->window[j][buf_ptr].im = (ph.im*amp[i]) >> s->scaling_shift; | |
377 | |
378 s->dot[j].re += s->window[j][buf_ptr].re; | |
379 s->dot[j].im += s->window[j][buf_ptr].im; | |
380 | |
381 dot = s->dot[j].re >> 15; | |
382 sum[j] = dot*dot; | |
383 dot = s->dot[j].im >> 15; | |
384 sum[j] += dot*dot; | |
385 } | |
386 /* If there isn't much signal, don't demodulate - it will only produce | |
387 useless junk results. */ | |
388 /* There should be no DC in the signal, but sometimes there is. | |
389 We need to measure the power with the DC blocked, but not using | |
390 a slow to respond DC blocker. Use the most elementary HPF. */ | |
391 x = amp[i] >> 1; | |
392 power = power_meter_update(&(s->power), x - s->last_sample); | |
393 s->last_sample = x; | |
394 if (s->signal_present) | |
395 { | |
396 /* Look for power below turn-off threshold to turn the carrier off */ | |
397 if (power < s->carrier_off_power) | |
398 { | |
399 if (--s->signal_present <= 0) | |
400 { | |
401 /* Count down a short delay, to ensure we push the last | |
402 few bits through the filters before stopping. */ | |
403 report_status_change(s, SIG_STATUS_CARRIER_DOWN); | |
404 s->baud_phase = 0; | |
405 continue; | |
406 } | |
407 } | |
408 } | |
409 else | |
410 { | |
411 /* Look for power exceeding turn-on threshold to turn the carrier on */ | |
412 if (power < s->carrier_on_power) | |
413 { | |
414 s->baud_phase = 0; | |
415 continue; | |
416 } | |
417 if (s->baud_phase < (s->correlation_span >> 1) - 30) | |
418 { | |
419 s->baud_phase++; | |
420 continue; | |
421 } | |
422 s->signal_present = 1; | |
423 /* Initialise the baud/bit rate tracking. */ | |
424 s->baud_phase = 0; | |
425 s->frame_state = 0; | |
426 s->frame_bits = 0; | |
427 s->last_bit = 0; | |
428 report_status_change(s, SIG_STATUS_CARRIER_UP); | |
429 } | |
430 /* Non-coherent FSK demodulation by correlation with the target tones | |
431 over a one baud interval. The slow V.xx specs. are too open ended | |
432 to allow anything fancier to be used. The dot products are calculated | |
433 using a sliding window approach, so the compute load is not that great. */ | |
434 | |
435 baudstate = (sum[0] < sum[1]); | |
436 switch (s->framing_mode) | |
437 { | |
438 case FSK_FRAME_MODE_SYNC: | |
439 /* Synchronous serial operation - e.g. for HDLC */ | |
440 if (s->last_bit != baudstate) | |
441 { | |
442 /* On a transition we check our timing */ | |
443 s->last_bit = baudstate; | |
444 /* For synchronous use (e.g. HDLC channels in FAX modems), nudge | |
445 the baud phase gently, trying to keep it centred on the bauds. */ | |
446 if (s->baud_phase < (SAMPLE_RATE*50)) | |
447 s->baud_phase += (s->baud_rate >> 3); | |
448 else | |
449 s->baud_phase -= (s->baud_rate >> 3); | |
450 } | |
451 if ((s->baud_phase += s->baud_rate) >= (SAMPLE_RATE*100)) | |
452 { | |
453 /* We should be in the middle of a baud now, so report the current | |
454 state as the next bit */ | |
455 s->baud_phase -= (SAMPLE_RATE*100); | |
456 s->put_bit(s->put_bit_user_data, baudstate); | |
457 } | |
458 break; | |
459 case FSK_FRAME_MODE_ASYNC: | |
460 /* Fully asynchronous mode */ | |
461 if (s->last_bit != baudstate) | |
462 { | |
463 /* On a transition we check our timing */ | |
464 s->last_bit = baudstate; | |
465 /* For async. operation, believe transitions completely, and | |
466 sample appropriately. This allows instant start on the first | |
467 transition. */ | |
468 /* We must now be about half way to a sampling point. We do not do | |
469 any fractional sample estimation of the transitions, so this is | |
470 the most accurate baud alignment we can do. */ | |
471 s->baud_phase = SAMPLE_RATE*50; | |
472 } | |
473 if ((s->baud_phase += s->baud_rate) >= (SAMPLE_RATE*100)) | |
474 { | |
475 /* We should be in the middle of a baud now, so report the current | |
476 state as the next bit */ | |
477 s->baud_phase -= (SAMPLE_RATE*100); | |
478 s->put_bit(s->put_bit_user_data, baudstate); | |
479 } | |
480 break; | |
481 default: | |
482 /* Gather the specified number of bits, with robust checking to ensure reasonable voice immunity. | |
483 The first bit should be a start bit (0), and the last bit should be a stop bit (1) */ | |
484 if (s->frame_state == 0) | |
485 { | |
486 /* Looking for the start of a zero bit, which hopefully the start of a start bit */ | |
487 if (baudstate == 0) | |
488 { | |
489 s->baud_phase = SAMPLE_RATE*(100 - 40)/2; | |
490 s->frame_state = -1; | |
491 s->frame_bits = 0; | |
492 s->last_bit = -1; | |
493 } | |
494 } | |
495 else if (s->frame_state == -1) | |
496 { | |
497 /* Look for a continuous zero from the start of the start bit until | |
498 beyond the middle */ | |
499 if (baudstate != 0) | |
500 { | |
501 /* If we aren't looking at a stable start bit, restart */ | |
502 s->frame_state = 0; | |
503 } | |
504 else | |
505 { | |
506 s->baud_phase += s->baud_rate; | |
507 if (s->baud_phase >= SAMPLE_RATE*100) | |
508 { | |
509 s->frame_state = 1; | |
510 s->last_bit = baudstate; | |
511 } | |
512 } | |
513 } | |
514 else | |
515 { | |
516 s->baud_phase += s->baud_rate; | |
517 if (s->baud_phase >= SAMPLE_RATE*(100 - 40)) | |
518 { | |
519 if (s->last_bit < 0) | |
520 s->last_bit = baudstate; | |
521 /* Look for the bit being consistent over the central 20% of the bit time. */ | |
522 if (s->last_bit != baudstate) | |
523 { | |
524 s->frame_state = 0; | |
525 } | |
526 else if (s->baud_phase >= SAMPLE_RATE*100) | |
527 { | |
528 /* We should be in the middle of a baud now, so report the current | |
529 state as the next bit */ | |
530 if (s->last_bit == baudstate) | |
531 { | |
532 s->frame_bits |= (baudstate << s->framing_mode); | |
533 s->frame_bits >>= 1; | |
534 s->baud_phase -= (SAMPLE_RATE*100); | |
535 if (++s->frame_state > s->framing_mode) | |
536 { | |
537 /* Check we have a stop bit */ | |
538 if (baudstate == 1) | |
539 { | |
540 /* Check we have a start bit */ | |
541 if ((s->frame_bits & 1) == 0) | |
542 { | |
543 /* Drop the start bit, and pass the rest back */ | |
544 s->frame_bits >>= 1; | |
545 s->put_bit(s->put_bit_user_data, s->frame_bits); | |
546 } | |
547 } | |
548 s->frame_state = 0; | |
549 } | |
550 } | |
551 else | |
552 { | |
553 s->frame_state = 0; | |
554 } | |
555 s->last_bit = -1; | |
556 } | |
557 } | |
558 } | |
559 break; | |
560 } | |
561 if (++buf_ptr >= s->correlation_span) | |
562 buf_ptr = 0; | |
563 } | |
564 s->buf_ptr = buf_ptr; | |
565 return 0; | |
566 } | |
567 /*- End of function --------------------------------------------------------*/ | |
568 | |
569 SPAN_DECLARE(int) fsk_rx_fillin(fsk_rx_state_t *s, int len) | |
570 { | |
571 /* The valid choice here is probably to do nothing. We don't change state | |
572 (i.e carrier on<->carrier off), and we'll just output less bits than we | |
573 should. */ | |
574 /* TODO: Advance the symbol phase the appropriate amount */ | |
575 return 0; | |
576 } | |
577 /*- End of function --------------------------------------------------------*/ | |
578 /*- End of file ------------------------------------------------------------*/ |