Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v17tx.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 * v17tx.c - ITU V.17 modem transmit part | |
5 * | |
6 * Written by Steve Underwood <steveu@coppice.org> | |
7 * | |
8 * Copyright (C) 2004 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: v17tx.c,v 1.75.4.1 2009/12/24 16:52:30 steveu Exp $ | |
26 */ | |
27 | |
28 /*! \file */ | |
29 | |
30 #if defined(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 #include "floating_fudge.h" | |
45 | |
46 #include "spandsp/telephony.h" | |
47 #include "spandsp/fast_convert.h" | |
48 #include "spandsp/logging.h" | |
49 #include "spandsp/complex.h" | |
50 #include "spandsp/vector_float.h" | |
51 #include "spandsp/complex_vector_float.h" | |
52 #include "spandsp/async.h" | |
53 #include "spandsp/dds.h" | |
54 #include "spandsp/power_meter.h" | |
55 | |
56 #include "spandsp/v17tx.h" | |
57 | |
58 #include "spandsp/private/logging.h" | |
59 #include "spandsp/private/v17tx.h" | |
60 | |
61 #if defined(SPANDSP_USE_FIXED_POINT) | |
62 #define SPANDSP_USE_FIXED_POINTx | |
63 #endif | |
64 | |
65 #include "v17_v32bis_tx_constellation_maps.h" | |
66 #if defined(SPANDSP_USE_FIXED_POINT) | |
67 #include "v17_v32bis_tx_fixed_rrc.h" | |
68 #else | |
69 #include "v17_v32bis_tx_floating_rrc.h" | |
70 #endif | |
71 | |
72 /*! The nominal frequency of the carrier, in Hertz */ | |
73 #define CARRIER_NOMINAL_FREQ 1800.0f | |
74 | |
75 /* Segments of the training sequence */ | |
76 /*! The start of the optional TEP, that may preceed the actual training, in symbols */ | |
77 #define V17_TRAINING_SEG_TEP_A 0 | |
78 /*! The mid point of the optional TEP, that may preceed the actual training, in symbols */ | |
79 #define V17_TRAINING_SEG_TEP_B (V17_TRAINING_SEG_TEP_A + 480) | |
80 /*! The start of training segment 1, in symbols */ | |
81 #define V17_TRAINING_SEG_1 (V17_TRAINING_SEG_TEP_B + 48) | |
82 /*! The start of training segment 2, in symbols */ | |
83 #define V17_TRAINING_SEG_2 (V17_TRAINING_SEG_1 + 256) | |
84 /*! The start of training segment 3, in symbols */ | |
85 #define V17_TRAINING_SEG_3 (V17_TRAINING_SEG_2 + 2976) | |
86 /*! The start of training segment 4, in symbols */ | |
87 #define V17_TRAINING_SEG_4 (V17_TRAINING_SEG_3 + 64) | |
88 /*! The start of training segment 4 in short training mode, in symbols */ | |
89 #define V17_TRAINING_SHORT_SEG_4 (V17_TRAINING_SEG_2 + 38) | |
90 /*! The end of the training, in symbols */ | |
91 #define V17_TRAINING_END (V17_TRAINING_SEG_4 + 48) | |
92 #define V17_TRAINING_SHUTDOWN_A (V17_TRAINING_END + 32) | |
93 /*! The end of the shutdown sequence, in symbols */ | |
94 #define V17_TRAINING_SHUTDOWN_END (V17_TRAINING_SHUTDOWN_A + 48) | |
95 | |
96 /*! The 16 bit pattern used in the bridge section of the training sequence */ | |
97 #define V17_BRIDGE_WORD 0x8880 | |
98 | |
99 static __inline__ int scramble(v17_tx_state_t *s, int in_bit) | |
100 { | |
101 int out_bit; | |
102 | |
103 //out_bit = (in_bit ^ (s->scramble_reg >> s->scrambler_tap) ^ (s->scramble_reg >> (23 - 1))) & 1; | |
104 out_bit = (in_bit ^ (s->scramble_reg >> (18 - 1)) ^ (s->scramble_reg >> (23 - 1))) & 1; | |
105 s->scramble_reg = (s->scramble_reg << 1) | out_bit; | |
106 return out_bit; | |
107 } | |
108 /*- End of function --------------------------------------------------------*/ | |
109 | |
110 #if defined(SPANDSP_USE_FIXED_POINT) | |
111 static __inline__ complexi16_t training_get(v17_tx_state_t *s) | |
112 #else | |
113 static __inline__ complexf_t training_get(v17_tx_state_t *s) | |
114 #endif | |
115 { | |
116 static const int cdba_to_abcd[4] = | |
117 { | |
118 2, 3, 1, 0 | |
119 }; | |
120 static const int dibit_to_step[4] = | |
121 { | |
122 1, 0, 2, 3 | |
123 }; | |
124 #if defined(SPANDSP_USE_FIXED_POINT) | |
125 static const complexi16_t zero = {0, 0}; | |
126 #else | |
127 static const complexf_t zero = {0.0f, 0.0f}; | |
128 #endif | |
129 int bits; | |
130 int shift; | |
131 | |
132 if (++s->training_step <= V17_TRAINING_SEG_3) | |
133 { | |
134 if (s->training_step <= V17_TRAINING_SEG_2) | |
135 { | |
136 if (s->training_step <= V17_TRAINING_SEG_TEP_B) | |
137 { | |
138 /* Optional segment: Unmodulated carrier (talker echo protection) */ | |
139 return v17_v32bis_abcd_constellation[0]; | |
140 } | |
141 if (s->training_step <= V17_TRAINING_SEG_1) | |
142 { | |
143 /* Optional segment: silence (talker echo protection) */ | |
144 return zero; | |
145 } | |
146 /* Segment 1: ABAB... */ | |
147 return v17_v32bis_abcd_constellation[(s->training_step & 1) ^ 1]; | |
148 } | |
149 /* Segment 2: CDBA... */ | |
150 /* Apply the scrambler */ | |
151 bits = scramble(s, 1); | |
152 bits = (bits << 1) | scramble(s, 1); | |
153 s->constellation_state = cdba_to_abcd[bits]; | |
154 if (s->short_train && s->training_step == V17_TRAINING_SHORT_SEG_4) | |
155 { | |
156 /* Go straight to the ones test. */ | |
157 s->training_step = V17_TRAINING_SEG_4; | |
158 } | |
159 return v17_v32bis_abcd_constellation[s->constellation_state]; | |
160 } | |
161 /* Segment 3: Bridge... */ | |
162 shift = ((s->training_step - V17_TRAINING_SEG_3 - 1) & 0x7) << 1; | |
163 //span_log(&s->logging, SPAN_LOG_FLOW, "Seg 3 shift %d\n", shift); | |
164 bits = scramble(s, V17_BRIDGE_WORD >> shift); | |
165 bits = (bits << 1) | scramble(s, V17_BRIDGE_WORD >> (shift + 1)); | |
166 s->constellation_state = (s->constellation_state + dibit_to_step[bits]) & 3; | |
167 return v17_v32bis_abcd_constellation[s->constellation_state]; | |
168 } | |
169 /*- End of function --------------------------------------------------------*/ | |
170 | |
171 static __inline__ int diff_and_convolutional_encode(v17_tx_state_t *s, int q) | |
172 { | |
173 static const uint8_t v32bis_4800_differential_encoder[4][4] = | |
174 { | |
175 {2, 3, 0, 1}, | |
176 {0, 2, 1, 3}, | |
177 {3, 1, 2, 0}, | |
178 {1, 0, 3, 2} | |
179 }; | |
180 static const uint8_t v17_differential_encoder[4][4] = | |
181 { | |
182 {0, 1, 2, 3}, | |
183 {1, 2, 3, 0}, | |
184 {2, 3, 0, 1}, | |
185 {3, 0, 1, 2} | |
186 }; | |
187 static const uint8_t v17_convolutional_coder[8][4] = | |
188 { | |
189 {0, 2, 3, 1}, | |
190 {4, 7, 5, 6}, | |
191 {1, 3, 2, 0}, | |
192 {7, 4, 6, 5}, | |
193 {2, 0, 1, 3}, | |
194 {6, 5, 7, 4}, | |
195 {3, 1, 0, 2}, | |
196 {5, 6, 4, 7} | |
197 }; | |
198 | |
199 if (s->bits_per_symbol == 2) | |
200 { | |
201 /* 4800bps mode for V.32bis */ | |
202 /* There is no trellis. We just differentially encode. */ | |
203 s->diff = v32bis_4800_differential_encoder[s->diff][q & 0x03]; | |
204 return s->diff; | |
205 } | |
206 /* Differentially encode */ | |
207 s->diff = v17_differential_encoder[s->diff][q & 0x03]; | |
208 | |
209 /* Convolutionally encode the redundant bit */ | |
210 s->convolution = v17_convolutional_coder[s->convolution][s->diff]; | |
211 | |
212 /* The final result is the combination of some uncoded bits, 2 differentially | |
213 encoded bits, and the convolutionally encoded redundant bit. */ | |
214 return ((q << 1) & 0x78) | (s->diff << 1) | ((s->convolution >> 2) & 1); | |
215 } | |
216 /*- End of function --------------------------------------------------------*/ | |
217 | |
218 static int fake_get_bit(void *user_data) | |
219 { | |
220 return 1; | |
221 } | |
222 /*- End of function --------------------------------------------------------*/ | |
223 | |
224 #if defined(SPANDSP_USE_FIXED_POINT) | |
225 static __inline__ complexi16_t getbaud(v17_tx_state_t *s) | |
226 #else | |
227 static __inline__ complexf_t getbaud(v17_tx_state_t *s) | |
228 #endif | |
229 { | |
230 int i; | |
231 int bit; | |
232 int bits; | |
233 | |
234 if (s->in_training) | |
235 { | |
236 if (s->training_step <= V17_TRAINING_END) | |
237 { | |
238 /* Send the training sequence */ | |
239 if (s->training_step < V17_TRAINING_SEG_4) | |
240 return training_get(s); | |
241 /* The last step in training is to send some 1's */ | |
242 if (++s->training_step > V17_TRAINING_END) | |
243 { | |
244 /* Training finished - commence normal operation. */ | |
245 s->current_get_bit = s->get_bit; | |
246 s->in_training = FALSE; | |
247 } | |
248 } | |
249 else | |
250 { | |
251 if (++s->training_step > V17_TRAINING_SHUTDOWN_A) | |
252 { | |
253 /* The shutdown sequence is 32 bauds of all 1's, then 48 bauds | |
254 of silence */ | |
255 #if defined(SPANDSP_USE_FIXED_POINT) | |
256 return complex_seti16(0, 0); | |
257 #else | |
258 return complex_setf(0.0f, 0.0f); | |
259 #endif | |
260 } | |
261 if (s->training_step == V17_TRAINING_SHUTDOWN_END) | |
262 { | |
263 if (s->status_handler) | |
264 s->status_handler(s->status_user_data, SIG_STATUS_SHUTDOWN_COMPLETE); | |
265 } | |
266 } | |
267 } | |
268 bits = 0; | |
269 for (i = 0; i < s->bits_per_symbol; i++) | |
270 { | |
271 if ((bit = s->current_get_bit(s->get_bit_user_data)) == SIG_STATUS_END_OF_DATA) | |
272 { | |
273 /* End of real data. Switch to the fake get_bit routine, until we | |
274 have shut down completely. */ | |
275 if (s->status_handler) | |
276 s->status_handler(s->status_user_data, SIG_STATUS_END_OF_DATA); | |
277 s->current_get_bit = fake_get_bit; | |
278 s->in_training = TRUE; | |
279 bit = 1; | |
280 } | |
281 bits |= (scramble(s, bit) << i); | |
282 } | |
283 return s->constellation[diff_and_convolutional_encode(s, bits)]; | |
284 } | |
285 /*- End of function --------------------------------------------------------*/ | |
286 | |
287 SPAN_DECLARE_NONSTD(int) v17_tx(v17_tx_state_t *s, int16_t amp[], int len) | |
288 { | |
289 #if defined(SPANDSP_USE_FIXED_POINT) | |
290 complexi_t x; | |
291 complexi_t z; | |
292 #else | |
293 complexf_t x; | |
294 complexf_t z; | |
295 #endif | |
296 int i; | |
297 int sample; | |
298 | |
299 if (s->training_step >= V17_TRAINING_SHUTDOWN_END) | |
300 { | |
301 /* Once we have sent the shutdown sequence, we stop sending completely. */ | |
302 return 0; | |
303 } | |
304 for (sample = 0; sample < len; sample++) | |
305 { | |
306 if ((s->baud_phase += 3) >= 10) | |
307 { | |
308 s->baud_phase -= 10; | |
309 s->rrc_filter[s->rrc_filter_step] = | |
310 s->rrc_filter[s->rrc_filter_step + V17_TX_FILTER_STEPS] = getbaud(s); | |
311 if (++s->rrc_filter_step >= V17_TX_FILTER_STEPS) | |
312 s->rrc_filter_step = 0; | |
313 } | |
314 /* Root raised cosine pulse shaping at baseband */ | |
315 #if defined(SPANDSP_USE_FIXED_POINT) | |
316 x = complex_seti(0, 0); | |
317 for (i = 0; i < V17_TX_FILTER_STEPS; i++) | |
318 { | |
319 x.re += (int32_t) tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->baud_phase][i]*(int32_t) s->rrc_filter[i + s->rrc_filter_step].re; | |
320 x.im += (int32_t) tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->baud_phase][i]*(int32_t) s->rrc_filter[i + s->rrc_filter_step].im; | |
321 } | |
322 /* Now create and modulate the carrier */ | |
323 x.re >>= 4; | |
324 x.im >>= 4; | |
325 z = dds_complexi(&(s->carrier_phase), s->carrier_phase_rate); | |
326 /* Don't bother saturating. We should never clip. */ | |
327 i = (x.re*z.re - x.im*z.im) >> 15; | |
328 amp[sample] = (int16_t) ((i*s->gain) >> 15); | |
329 #else | |
330 x = complex_setf(0.0f, 0.0f); | |
331 for (i = 0; i < V17_TX_FILTER_STEPS; i++) | |
332 { | |
333 x.re += tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re; | |
334 x.im += tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im; | |
335 } | |
336 /* Now create and modulate the carrier */ | |
337 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate); | |
338 /* Don't bother saturating. We should never clip. */ | |
339 amp[sample] = (int16_t) lfastrintf((x.re*z.re - x.im*z.im)*s->gain); | |
340 #endif | |
341 } | |
342 return sample; | |
343 } | |
344 /*- End of function --------------------------------------------------------*/ | |
345 | |
346 SPAN_DECLARE(void) v17_tx_power(v17_tx_state_t *s, float power) | |
347 { | |
348 /* The constellation design seems to keep the average power the same, regardless | |
349 of which bit rate is in use. */ | |
350 #if defined(SPANDSP_USE_FIXED_POINT) | |
351 s->gain = 0.223f*powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*16.0f*(32767.0f/30672.52f)*32768.0f/TX_PULSESHAPER_GAIN; | |
352 #else | |
353 s->gain = 0.223f*powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*32768.0f/TX_PULSESHAPER_GAIN; | |
354 #endif | |
355 } | |
356 /*- End of function --------------------------------------------------------*/ | |
357 | |
358 SPAN_DECLARE(void) v17_tx_set_get_bit(v17_tx_state_t *s, get_bit_func_t get_bit, void *user_data) | |
359 { | |
360 if (s->get_bit == s->current_get_bit) | |
361 s->current_get_bit = get_bit; | |
362 s->get_bit = get_bit; | |
363 s->get_bit_user_data = user_data; | |
364 } | |
365 /*- End of function --------------------------------------------------------*/ | |
366 | |
367 SPAN_DECLARE(void) v17_tx_set_modem_status_handler(v17_tx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
368 { | |
369 s->status_handler = handler; | |
370 s->status_user_data = user_data; | |
371 } | |
372 /*- End of function --------------------------------------------------------*/ | |
373 | |
374 SPAN_DECLARE(logging_state_t *) v17_tx_get_logging_state(v17_tx_state_t *s) | |
375 { | |
376 return &s->logging; | |
377 } | |
378 /*- End of function --------------------------------------------------------*/ | |
379 | |
380 SPAN_DECLARE(int) v17_tx_restart(v17_tx_state_t *s, int bit_rate, int tep, int short_train) | |
381 { | |
382 switch (bit_rate) | |
383 { | |
384 case 14400: | |
385 s->bits_per_symbol = 6; | |
386 s->constellation = v17_v32bis_14400_constellation; | |
387 break; | |
388 case 12000: | |
389 s->bits_per_symbol = 5; | |
390 s->constellation = v17_v32bis_12000_constellation; | |
391 break; | |
392 case 9600: | |
393 s->bits_per_symbol = 4; | |
394 s->constellation = v17_v32bis_9600_constellation; | |
395 break; | |
396 case 7200: | |
397 s->bits_per_symbol = 3; | |
398 s->constellation = v17_v32bis_7200_constellation; | |
399 break; | |
400 case 4800: | |
401 /* This does not exist in the V.17 spec as a valid mode of operation. | |
402 However, it does exist in V.32bis, so it is here for completeness. */ | |
403 s->bits_per_symbol = 2; | |
404 s->constellation = v17_v32bis_4800_constellation; | |
405 break; | |
406 default: | |
407 return -1; | |
408 } | |
409 s->bit_rate = bit_rate; | |
410 /* NB: some modems seem to use 3 instead of 1 for long training */ | |
411 s->diff = (short_train) ? 0 : 1; | |
412 #if defined(SPANDSP_USE_FIXED_POINT) | |
413 memset(s->rrc_filter, 0, sizeof(s->rrc_filter)); | |
414 #else | |
415 cvec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
416 #endif | |
417 s->rrc_filter_step = 0; | |
418 s->convolution = 0; | |
419 s->scramble_reg = 0x2ECDD5; | |
420 s->in_training = TRUE; | |
421 s->short_train = short_train; | |
422 s->training_step = (tep) ? V17_TRAINING_SEG_TEP_A : V17_TRAINING_SEG_1; | |
423 s->carrier_phase = 0; | |
424 s->baud_phase = 0; | |
425 s->constellation_state = 0; | |
426 s->current_get_bit = fake_get_bit; | |
427 return 0; | |
428 } | |
429 /*- End of function --------------------------------------------------------*/ | |
430 | |
431 SPAN_DECLARE(v17_tx_state_t *) v17_tx_init(v17_tx_state_t *s, int bit_rate, int tep, get_bit_func_t get_bit, void *user_data) | |
432 { | |
433 switch (bit_rate) | |
434 { | |
435 case 14400: | |
436 case 12000: | |
437 case 9600: | |
438 case 7200: | |
439 case 4800: | |
440 /* 4800 is an extension of V.17, to provide full converage of the V.32bis modes */ | |
441 break; | |
442 default: | |
443 return NULL; | |
444 } | |
445 if (s == NULL) | |
446 { | |
447 if ((s = (v17_tx_state_t *) malloc(sizeof(*s))) == NULL) | |
448 return NULL; | |
449 } | |
450 memset(s, 0, sizeof(*s)); | |
451 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
452 span_log_set_protocol(&s->logging, "V.17 TX"); | |
453 s->get_bit = get_bit; | |
454 s->get_bit_user_data = user_data; | |
455 //s->scrambler_tap = 18 - 1; | |
456 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
457 v17_tx_power(s, -14.0f); | |
458 v17_tx_restart(s, bit_rate, tep, FALSE); | |
459 return s; | |
460 } | |
461 /*- End of function --------------------------------------------------------*/ | |
462 | |
463 SPAN_DECLARE(int) v17_tx_release(v17_tx_state_t *s) | |
464 { | |
465 return 0; | |
466 } | |
467 /*- End of function --------------------------------------------------------*/ | |
468 | |
469 SPAN_DECLARE(int) v17_tx_free(v17_tx_state_t *s) | |
470 { | |
471 free(s); | |
472 return 0; | |
473 } | |
474 /*- End of function --------------------------------------------------------*/ | |
475 /*- End of file ------------------------------------------------------------*/ |