comparison spandsp-0.0.3/spandsp-0.0.3/src/v27ter_tx.c @ 5:f762bf195c4b

import spandsp-0.0.3
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 16:00:21 +0200
parents
children
comparison
equal deleted inserted replaced
4:26cd8f1ef0b1 5:f762bf195c4b
1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * v27ter_tx.c - ITU V.27ter modem transmit part
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 General Public License version 2, as
14 * 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 General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 * $Id: v27ter_tx.c,v 1.48 2006/11/28 16:59:57 steveu Exp $
26 */
27
28 /*! \file */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <stdio.h>
35 #include <inttypes.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #if defined(HAVE_TGMATH_H)
39 #include <tgmath.h>
40 #endif
41 #if defined(HAVE_MATH_H)
42 #include <math.h>
43 #endif
44
45 #include "spandsp/telephony.h"
46 #include "spandsp/logging.h"
47 #include "spandsp/complex.h"
48 #include "spandsp/vector_float.h"
49 #include "spandsp/complex_vector_float.h"
50 #include "spandsp/async.h"
51 #include "spandsp/dds.h"
52 #include "spandsp/power_meter.h"
53
54 #include "spandsp/v27ter_tx.h"
55
56 #define CARRIER_NOMINAL_FREQ 1800.0f
57
58 /* Segments of the training sequence */
59 /* V.27ter defines a long and a short sequence. FAX doesn't use the
60 short sequence, so it is not implemented here. */
61 #define V27TER_TRAINING_SEG_1 0
62 #define V27TER_TRAINING_SEG_2 (V27TER_TRAINING_SEG_1 + 320)
63 #define V27TER_TRAINING_SEG_3 (V27TER_TRAINING_SEG_2 + 32)
64 #define V27TER_TRAINING_SEG_4 (V27TER_TRAINING_SEG_3 + 50)
65 #define V27TER_TRAINING_SEG_5 (V27TER_TRAINING_SEG_4 + 1074)
66 #define V27TER_TRAINING_END (V27TER_TRAINING_SEG_5 + 8)
67 #define V27TER_TRAINING_SHUTDOWN_END (V27TER_TRAINING_END + 32)
68
69 /* Raised root cosine pulse shaping; Beta = 0.5; 4 symbols either
70 side of the centre. */
71 /* Created with mkshape -r 0.025 0.5 181 -l and then split up */
72 #define PULSESHAPER_2400_GAIN (19.972065748f/20.0f)
73 #define PULSESHAPER_2400_COEFF_SETS 20
74 static const float pulseshaper_2400[PULSESHAPER_2400_COEFF_SETS][V27TER_TX_FILTER_STEPS] =
75 {
76 {
77 0.0050051219f, /* Filter 0 */
78 0.0107180844f,
79 -0.0150077814f,
80 -0.0750272071f,
81 0.5786341413f,
82 0.5786341413f,
83 -0.0750272071f,
84 -0.0150077814f,
85 0.0107180844f
86 },
87 {
88 0.0036624469f, /* Filter 1 */
89 0.0131516633f,
90 -0.0107913392f,
91 -0.0957820135f,
92 0.6671466059f,
93 0.4891745311f,
94 -0.0541239470f,
95 -0.0179109014f,
96 0.0079099936f
97 },
98 {
99 0.0020204744f, /* Filter 2 */
100 0.0150588729f,
101 -0.0053908083f,
102 -0.1154114754f,
103 0.7528295479f,
104 0.4006032722f,
105 -0.0339459430f,
106 -0.0194500407f,
107 0.0048904515f
108 },
109 {
110 0.0001596234f, /* Filter 3 */
111 0.0163079778f,
112 0.0009858079f,
113 -0.1328632049f,
114 0.8338068363f,
115 0.3146585634f,
116 -0.0152415667f,
117 -0.0196492903f,
118 0.0018247182f
119 },
120 {
121 -0.0018233575f, /* Filter 4 */
122 0.0167957238f,
123 0.0080554403f,
124 -0.1470417557f,
125 0.9082626683f,
126 0.2329352195f,
127 0.0013822552f,
128 -0.0186004475f,
129 -0.0011283792f
130 },
131 {
132 -0.0038199491f, /* Filter 5 */
133 0.0164546659f,
134 0.0154676597f,
135 -0.1568448230f,
136 0.9744947974f,
137 0.1568443643f,
138 0.0154698286f,
139 -0.0164532877f,
140 -0.0038242967f
141 },
142 {
143 -0.0057152767f, /* Filter 6 */
144 0.0152590213f,
145 0.0228163087f,
146 -0.1612020164f,
147 1.0309651039f,
148 0.0875801110f,
149 0.0267201501f,
150 -0.0134037738f,
151 -0.0061394831f
152 },
153 {
154 -0.0073941287f, /* Filter 7 */
155 0.0132286539f,
156 0.0296547979f,
157 -0.1591148676f,
158 1.0763457753f,
159 0.0260941722f,
160 0.0349842710f,
161 -0.0096808822f,
162 -0.0079766730f
163 },
164 {
165 -0.0087472825f, /* Filter 8 */
166 0.0104308721f,
167 0.0355146231f,
168 -0.1496966290f,
169 1.1095595051f,
170 -0.0269209682f,
171 0.0402570324f,
172 -0.0055327477f,
173 -0.0092685626f
174 },
175 {
176 -0.0096778115f, /* Filter 9 */
177 0.0069798134f,
178 0.0399264862f,
179 -0.1322103702f,
180 1.1298123136f,
181 -0.0710400038f,
182 0.0426638320f,
183 -0.0012128224f,
184 -0.0099797659f
185 },
186 {
187 -0.0101070340f, /* Filter 10 */
188 0.0030333009f,
189 0.0424432507f,
190 -0.1061038872f,
191 1.1366178484f,
192 -0.1061038872f,
193 0.0424432507f,
194 0.0030333009f,
195 -0.0101070340f
196 },
197 {
198 -0.0099797659f, /* Filter 11 */
199 -0.0012128224f,
200 0.0426638320f,
201 -0.0710400038f,
202 1.1298123136f,
203 -0.1322103702f,
204 0.0399264862f,
205 0.0069798134f,
206 -0.0096778115f
207 },
208 {
209 -0.0092685626f, /* Filter 12 */
210 -0.0055327477f,
211 0.0402570324f,
212 -0.0269209682f,
213 1.1095595051f,
214 -0.1496966290f,
215 0.0355146231f,
216 0.0104308721f,
217 -0.0087472825f
218 },
219 {
220 -0.0079766730f, /* Filter 13 */
221 -0.0096808822f,
222 0.0349842710f,
223 0.0260941722f,
224 1.0763457753f,
225 -0.1591148676f,
226 0.0296547979f,
227 0.0132286539f,
228 -0.0073941287f
229 },
230 {
231 -0.0061394831f, /* Filter 14 */
232 -0.0134037738f,
233 0.0267201501f,
234 0.0875801110f,
235 1.0309651039f,
236 -0.1612020164f,
237 0.0228163087f,
238 0.0152590213f,
239 -0.0057152767f
240 },
241 {
242 -0.0038242967f, /* Filter 15 */
243 -0.0164532877f,
244 0.0154698286f,
245 0.1568443643f,
246 0.9744947974f,
247 -0.1568448230f,
248 0.0154676597f,
249 0.0164546659f,
250 -0.0038199491f
251 },
252 {
253 -0.0011283792f, /* Filter 16 */
254 -0.0186004475f,
255 0.0013822552f,
256 0.2329352195f,
257 0.9082626683f,
258 -0.1470417557f,
259 0.0080554403f,
260 0.0167957238f,
261 -0.0018233575f
262 },
263 {
264 0.0018247182f, /* Filter 17 */
265 -0.0196492903f,
266 -0.0152415667f,
267 0.3146585634f,
268 0.8338068363f,
269 -0.1328632049f,
270 0.0009858079f,
271 0.0163079778f,
272 0.0001596234f
273 },
274 {
275 0.0048904515f, /* Filter 18 */
276 -0.0194500407f,
277 -0.0339459430f,
278 0.4006032722f,
279 0.7528295479f,
280 -0.1154114754f,
281 -0.0053908083f,
282 0.0150588729f,
283 0.0020204744f
284 },
285 {
286 0.0079099936f, /* Filter 19 */
287 -0.0179109014f,
288 -0.0541239470f,
289 0.4891745311f,
290 0.6671466059f,
291 -0.0957820135f,
292 -0.0107913392f,
293 0.0131516633f,
294 0.0036624469f
295 },
296 };
297
298 /* Raised root cosine pulse shaping; Beta = 0.5; 4 symbols either
299 side of the centre. */
300 /* Created with mkshape -r 0.1 0.5 45 -l and then split up */
301 #define PULSESHAPER_4800_GAIN (4.9913162900f/5.0f)
302 #define PULSESHAPER_4800_COEFF_SETS 5
303 static const float pulseshaper_4800[PULSESHAPER_4800_COEFF_SETS][V27TER_TX_FILTER_STEPS] =
304 {
305 {
306 0.0020173211f, /* Filter 0 */
307 0.0150576434f,
308 -0.0053888047f,
309 -0.1154099010f,
310 0.7528286821f,
311 0.4006013374f,
312 -0.0339462085f,
313 -0.0194477281f,
314 0.0048918464f
315 },
316 {
317 -0.0057162575f, /* Filter 1 */
318 0.0152563286f,
319 0.0228163350f,
320 -0.1612000503f,
321 1.0309660372f,
322 0.0875788553f,
323 0.0267182476f,
324 -0.0134032156f,
325 -0.0061365979f
326 },
327 {
328 -0.0101052019f, /* Filter 2 */
329 0.0030314952f,
330 0.0424414442f,
331 -0.1061032862f,
332 1.1366196464f,
333 -0.1061032862f,
334 0.0424414442f,
335 0.0030314952f,
336 -0.0101052019f
337 },
338 {
339 -0.0061365979f, /* Filter 3 */
340 -0.0134032156f,
341 0.0267182476f,
342 0.0875788553f,
343 1.0309660372f,
344 -0.1612000503f,
345 0.0228163350f,
346 0.0152563286f,
347 -0.0057162575f
348 },
349 {
350 0.0048918464f, /* Filter 4 */
351 -0.0194477281f,
352 -0.0339462085f,
353 0.4006013374f,
354 0.7528286821f,
355 -0.1154099010f,
356 -0.0053888047f,
357 0.0150576434f,
358 0.0020173211f
359 },
360 };
361
362 static int fake_get_bit(void *user_data)
363 {
364 return 1;
365 }
366 /*- End of function --------------------------------------------------------*/
367
368 static __inline__ int scramble(v27ter_tx_state_t *s, int in_bit)
369 {
370 int out_bit;
371
372 /* This scrambler is really quite messy to implement. There seems to be no efficient shortcut */
373 out_bit = (in_bit ^ (s->scramble_reg >> 5) ^ (s->scramble_reg >> 6)) & 1;
374 if (s->scrambler_pattern_count >= 33)
375 {
376 out_bit ^= 1;
377 s->scrambler_pattern_count = 0;
378 }
379 else
380 {
381 if ((((s->scramble_reg >> 7) ^ out_bit) & ((s->scramble_reg >> 8) ^ out_bit) & ((s->scramble_reg >> 11) ^ out_bit) & 1))
382 s->scrambler_pattern_count = 0;
383 else
384 s->scrambler_pattern_count++;
385 }
386 s->scramble_reg = (s->scramble_reg << 1) | out_bit;
387 return out_bit;
388 }
389 /*- End of function --------------------------------------------------------*/
390
391 static __inline__ int get_scrambled_bit(v27ter_tx_state_t *s)
392 {
393 int bit;
394
395 if ((bit = s->current_get_bit(s->user_data)) == PUTBIT_END_OF_DATA)
396 {
397 /* End of real data. Switch to the fake get_bit routine, until we
398 have shut down completely. */
399 s->current_get_bit = fake_get_bit;
400 s->in_training = TRUE;
401 bit = 1;
402 }
403 return scramble(s, bit);
404 }
405 /*- End of function --------------------------------------------------------*/
406
407 static complexf_t getbaud(v27ter_tx_state_t *s)
408 {
409 static const int phase_steps_4800[8] =
410 {
411 1, 0, 2, 3, 6, 7, 5, 4
412 };
413 static const int phase_steps_2400[4] =
414 {
415 0, 2, 6, 4
416 };
417 static const complexf_t constellation[8] =
418 {
419 { 1.414f, 0.0f}, /* 0deg */
420 { 1.0f, 1.0f}, /* 45deg */
421 { 0.0f, 1.414f}, /* 90deg */
422 {-1.0f, 1.0f}, /* 135deg */
423 {-1.414f, 0.0f}, /* 180deg */
424 {-1.0f, -1.0f}, /* 225deg */
425 { 0.0f, -1.414f}, /* 270deg */
426 { 1.0f, -1.0f} /* 315deg */
427 };
428 int bits;
429
430 if (s->in_training)
431 {
432 /* Send the training sequence */
433 if (++s->training_step <= V27TER_TRAINING_SEG_5)
434 {
435 if (s->training_step <= V27TER_TRAINING_SEG_4)
436 {
437 if (s->training_step <= V27TER_TRAINING_SEG_2)
438 {
439 /* Segment 1: Unmodulated carrier (talker echo protection) */
440 return constellation[0];
441 }
442 if (s->training_step <= V27TER_TRAINING_SEG_3)
443 {
444 /* Segment 2: Silence */
445 return complex_setf(0.0, 0.0);
446 }
447 /* Segment 3: Regular reversals... */
448 s->constellation_state = (s->constellation_state + 4) & 7;
449 return constellation[s->constellation_state];
450 }
451 /* Segment 4: Scrambled reversals... */
452 /* Apply the 1 + x^-6 + x^-7 scrambler. We want every third
453 bit from the scrambler. */
454 bits = get_scrambled_bit(s) << 2;
455 get_scrambled_bit(s);
456 get_scrambled_bit(s);
457 s->constellation_state = (s->constellation_state + bits) & 7;
458 return constellation[s->constellation_state];
459 }
460 /* We should be in the block of test ones, or shutdown ones, if we get here. */
461 /* There is no graceful shutdown procedure defined for V.27ter. Just
462 send some ones, to ensure we get the real data bits through, even
463 with bad ISI. */
464 if (s->training_step == V27TER_TRAINING_END + 1)
465 {
466 /* End of the last segment - segment 5: All ones */
467 /* Switch from the fake get_bit routine, to the user supplied real
468 one, and we are up and running. */
469 s->current_get_bit = s->get_bit;
470 s->in_training = FALSE;
471 }
472 }
473 /* 4800bps uses 8 phases. 2400bps uses 4 phases. */
474 if (s->bit_rate == 4800)
475 {
476 bits = get_scrambled_bit(s);
477 bits = (bits << 1) | get_scrambled_bit(s);
478 bits = (bits << 1) | get_scrambled_bit(s);
479 bits = phase_steps_4800[bits];
480 }
481 else
482 {
483 bits = get_scrambled_bit(s);
484 bits = (bits << 1) | get_scrambled_bit(s);
485 bits = phase_steps_2400[bits];
486 }
487 s->constellation_state = (s->constellation_state + bits) & 7;
488 return constellation[s->constellation_state];
489 }
490 /*- End of function --------------------------------------------------------*/
491
492 int v27ter_tx(v27ter_tx_state_t *s, int16_t amp[], int len)
493 {
494 complexf_t x;
495 complexf_t z;
496 int i;
497 int sample;
498
499 if (s->training_step >= V27TER_TRAINING_SHUTDOWN_END)
500 {
501 /* Once we have sent the shutdown symbols, we stop sending completely. */
502 return 0;
503 }
504 /* The symbol rates for the two bit rates are different. This makes it difficult to
505 merge both generation procedures into a single efficient loop. We do not bother
506 trying. We use two independent loops, filter coefficients, etc. */
507 if (s->bit_rate == 4800)
508 {
509 for (sample = 0; sample < len; sample++)
510 {
511 if (++s->baud_phase >= 5)
512 {
513 s->baud_phase -= 5;
514 s->rrc_filter[s->rrc_filter_step] =
515 s->rrc_filter[s->rrc_filter_step + V27TER_TX_FILTER_STEPS] = getbaud(s);
516 if (++s->rrc_filter_step >= V27TER_TX_FILTER_STEPS)
517 s->rrc_filter_step = 0;
518 }
519 /* Root raised cosine pulse shaping at baseband */
520 x.re = 0.0f;
521 x.im = 0.0f;
522 for (i = 0; i < V27TER_TX_FILTER_STEPS; i++)
523 {
524 x.re += pulseshaper_4800[4 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re;
525 x.im += pulseshaper_4800[4 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im;
526 }
527 /* Now create and modulate the carrier */
528 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate);
529 /* Don't bother saturating. We should never clip. */
530 amp[sample] = (int16_t) lrintf((x.re*z.re - x.im*z.im)*s->gain_4800);
531 }
532 }
533 else
534 {
535 for (sample = 0; sample < len; sample++)
536 {
537 if ((s->baud_phase += 3) >= 20)
538 {
539 s->baud_phase -= 20;
540 s->rrc_filter[s->rrc_filter_step] =
541 s->rrc_filter[s->rrc_filter_step + V27TER_TX_FILTER_STEPS] = getbaud(s);
542 if (++s->rrc_filter_step >= V27TER_TX_FILTER_STEPS)
543 s->rrc_filter_step = 0;
544 }
545 /* Root raised cosine pulse shaping at baseband */
546 x.re = 0.0f;
547 x.im = 0.0f;
548 for (i = 0; i < V27TER_TX_FILTER_STEPS; i++)
549 {
550 x.re += pulseshaper_2400[19 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re;
551 x.im += pulseshaper_2400[19 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im;
552 }
553 /* Now create and modulate the carrier */
554 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate);
555 /* Don't bother saturating. We should never clip. */
556 amp[sample] = (int16_t) lrintf((x.re*z.re - x.im*z.im)*s->gain_2400);
557 }
558 }
559 return sample;
560 }
561 /*- End of function --------------------------------------------------------*/
562
563 void v27ter_tx_power(v27ter_tx_state_t *s, float power)
564 {
565 float l;
566
567 l = powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*32768.0f;
568 s->gain_2400 = l/PULSESHAPER_2400_GAIN;
569 s->gain_4800 = l/PULSESHAPER_4800_GAIN;
570 }
571 /*- End of function --------------------------------------------------------*/
572
573 void v27ter_tx_set_get_bit(v27ter_tx_state_t *s, get_bit_func_t get_bit, void *user_data)
574 {
575 if (s->get_bit == s->current_get_bit)
576 s->current_get_bit = get_bit;
577 s->get_bit = get_bit;
578 s->user_data = user_data;
579 }
580 /*- End of function --------------------------------------------------------*/
581
582 int v27ter_tx_restart(v27ter_tx_state_t *s, int rate, int tep)
583 {
584 if (rate != 4800 && rate != 2400)
585 return -1;
586 s->bit_rate = rate;
587 cvec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0]));
588 s->rrc_filter_step = 0;
589 s->scramble_reg = 0x3C;
590 s->scrambler_pattern_count = 0;
591 s->in_training = TRUE;
592 s->training_step = (tep) ? V27TER_TRAINING_SEG_1 : V27TER_TRAINING_SEG_2;
593 s->carrier_phase = 0;
594 s->baud_phase = 0;
595 s->constellation_state = 0;
596 s->current_get_bit = fake_get_bit;
597 return 0;
598 }
599 /*- End of function --------------------------------------------------------*/
600
601 v27ter_tx_state_t *v27ter_tx_init(v27ter_tx_state_t *s, int rate, int tep, get_bit_func_t get_bit, void *user_data)
602 {
603 if (s == NULL)
604 {
605 if ((s = (v27ter_tx_state_t *) malloc(sizeof(*s))) == NULL)
606 return NULL;
607 }
608 memset(s, 0, sizeof(*s));
609 s->get_bit = get_bit;
610 s->user_data = user_data;
611 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ);
612 v27ter_tx_power(s, -14.0f);
613 v27ter_tx_restart(s, rate, tep);
614 return s;
615 }
616 /*- End of function --------------------------------------------------------*/
617
618 int v27ter_tx_release(v27ter_tx_state_t *s)
619 {
620 free(s);
621 return 0;
622 }
623 /*- End of function --------------------------------------------------------*/
624 /*- End of file ------------------------------------------------------------*/

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