Mercurial > hg > audiostuff
comparison spandsp-0.0.3/spandsp-0.0.3/src/v29tx.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 * v29tx.c - ITU V.29 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: v29tx.c,v 1.58 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/v29tx.h" | |
55 | |
56 #define CARRIER_NOMINAL_FREQ 1700.0f | |
57 | |
58 /* Segments of the training sequence */ | |
59 #define V29_TRAINING_SEG_TEP 0 | |
60 #define V29_TRAINING_SEG_1 (V29_TRAINING_SEG_TEP + 480) | |
61 #define V29_TRAINING_SEG_2 (V29_TRAINING_SEG_1 + 48) | |
62 #define V29_TRAINING_SEG_3 (V29_TRAINING_SEG_2 + 128) | |
63 #define V29_TRAINING_SEG_4 (V29_TRAINING_SEG_3 + 384) | |
64 #define V29_TRAINING_END (V29_TRAINING_SEG_4 + 48) | |
65 #define V29_TRAINING_SHUTDOWN_END (V29_TRAINING_END + 32) | |
66 | |
67 /* Raised root cosine pulse shaping; Beta = 0.25; 4 symbols either | |
68 side of the centre. */ | |
69 /* Created with mkshape -r 0.05 0.25 91 -l and then split up */ | |
70 #define PULSESHAPER_GAIN (9.9888356312f/10.0f) | |
71 #define PULSESHAPER_COEFF_SETS 10 | |
72 | |
73 static const float pulseshaper[PULSESHAPER_COEFF_SETS][V29_TX_FILTER_STEPS] = | |
74 { | |
75 { | |
76 -0.0029426223f, /* Filter 0 */ | |
77 -0.0183060118f, | |
78 0.0653192857f, | |
79 -0.1703207714f, | |
80 0.6218069936f, | |
81 0.6218069936f, | |
82 -0.1703207714f, | |
83 0.0653192857f, | |
84 -0.0183060118f | |
85 }, | |
86 { | |
87 0.0031876922f, /* Filter 1 */ | |
88 -0.0300884145f, | |
89 0.0832744718f, | |
90 -0.1974255221f, | |
91 0.7664229820f, | |
92 0.4670580725f, | |
93 -0.1291107519f, | |
94 0.0424189243f, | |
95 -0.0059810465f | |
96 }, | |
97 { | |
98 0.0097229236f, /* Filter 2 */ | |
99 -0.0394811291f, | |
100 0.0931039664f, | |
101 -0.2043906784f, | |
102 0.8910868760f, | |
103 0.3122713836f, | |
104 -0.0802880559f, | |
105 0.0179050490f, | |
106 0.0052057308f | |
107 }, | |
108 { | |
109 0.0156117223f, /* Filter 3 */ | |
110 -0.0447125347f, | |
111 0.0922040267f, | |
112 -0.1862939416f, | |
113 0.9870942864f, | |
114 0.1669790517f, | |
115 -0.0301581072f, | |
116 -0.0051358510f, | |
117 0.0139350286f | |
118 }, | |
119 { | |
120 0.0197702545f, /* Filter 4 */ | |
121 -0.0443470335f, | |
122 0.0789538534f, | |
123 -0.1399184160f, | |
124 1.0476130256f, | |
125 0.0393903028f, | |
126 0.0157339854f, | |
127 -0.0241879599f, | |
128 0.0193774571f | |
129 }, | |
130 { | |
131 0.0212455717f, /* Filter 5 */ | |
132 -0.0375307894f, | |
133 0.0530516472f, | |
134 -0.0642195521f, | |
135 1.0682849922f, | |
136 -0.0642195521f, | |
137 0.0530516472f, | |
138 -0.0375307894f, | |
139 0.0212455717f | |
140 }, | |
141 { | |
142 0.0193774571f, /* Filter 6 */ | |
143 -0.0241879599f, | |
144 0.0157339854f, | |
145 0.0393903028f, | |
146 1.0476130256f, | |
147 -0.1399184160f, | |
148 0.0789538534f, | |
149 -0.0443470335f, | |
150 0.0197702545f | |
151 }, | |
152 { | |
153 0.0139350286f, /* Filter 7 */ | |
154 -0.0051358510f, | |
155 -0.0301581072f, | |
156 0.1669790517f, | |
157 0.9870942864f, | |
158 -0.1862939416f, | |
159 0.0922040267f, | |
160 -0.0447125347f, | |
161 0.0156117223f | |
162 }, | |
163 { | |
164 0.0052057308f, /* Filter 8 */ | |
165 0.0179050490f, | |
166 -0.0802880559f, | |
167 0.3122713836f, | |
168 0.8910868760f, | |
169 -0.2043906784f, | |
170 0.0931039664f, | |
171 -0.0394811291f, | |
172 0.0097229236f | |
173 }, | |
174 { | |
175 -0.0059810465f, /* Filter 9 */ | |
176 0.0424189243f, | |
177 -0.1291107519f, | |
178 0.4670580725f, | |
179 0.7664229820f, | |
180 -0.1974255221f, | |
181 0.0832744718f, | |
182 -0.0300884145f, | |
183 0.0031876922f | |
184 }, | |
185 }; | |
186 | |
187 static int fake_get_bit(void *user_data) | |
188 { | |
189 return 1; | |
190 } | |
191 /*- End of function --------------------------------------------------------*/ | |
192 | |
193 static __inline__ int get_scrambled_bit(v29_tx_state_t *s) | |
194 { | |
195 int bit; | |
196 int out_bit; | |
197 | |
198 if ((bit = s->current_get_bit(s->user_data)) == PUTBIT_END_OF_DATA) | |
199 { | |
200 /* End of real data. Switch to the fake get_bit routine, until we | |
201 have shut down completely. */ | |
202 s->current_get_bit = fake_get_bit; | |
203 s->in_training = TRUE; | |
204 bit = 1; | |
205 } | |
206 out_bit = (bit ^ (s->scramble_reg >> 17) ^ (s->scramble_reg >> 22)) & 1; | |
207 s->scramble_reg = (s->scramble_reg << 1) | out_bit; | |
208 return out_bit; | |
209 } | |
210 /*- End of function --------------------------------------------------------*/ | |
211 | |
212 static __inline__ complexf_t getbaud(v29_tx_state_t *s) | |
213 { | |
214 static const int phase_steps_9600[8] = | |
215 { | |
216 1, 0, 2, 3, 6, 7, 5, 4 | |
217 }; | |
218 static const int phase_steps_4800[4] = | |
219 { | |
220 0, 2, 6, 4 | |
221 }; | |
222 static const complexf_t constellation[16] = | |
223 { | |
224 { 3.0, 0.0}, /* 0deg low */ | |
225 { 1.0, 1.0}, /* 45deg low */ | |
226 { 0.0, 3.0}, /* 90deg low */ | |
227 {-1.0, 1.0}, /* 135deg low */ | |
228 {-3.0, 0.0}, /* 180deg low */ | |
229 {-1.0, -1.0}, /* 225deg low */ | |
230 { 0.0, -3.0}, /* 270deg low */ | |
231 { 1.0, -1.0}, /* 315deg low */ | |
232 { 5.0, 0.0}, /* 0deg high */ | |
233 { 3.0, 3.0}, /* 45deg high */ | |
234 { 0.0, 5.0}, /* 90deg high */ | |
235 {-3.0, 3.0}, /* 135deg high */ | |
236 {-5.0, 0.0}, /* 180deg high */ | |
237 {-3.0, -3.0}, /* 225deg high */ | |
238 { 0.0, -5.0}, /* 270deg high */ | |
239 { 3.0, -3.0} /* 315deg high */ | |
240 }; | |
241 static const complexf_t abab[6] = | |
242 { | |
243 { 3.0, -3.0}, /* 315deg high 9600 */ | |
244 {-3.0, 0.0}, /* 180deg low */ | |
245 { 1.0, -1.0}, /* 315deg low 7200 */ | |
246 {-3.0, 0.0}, /* 180deg low */ | |
247 { 0.0, -3.0}, /* 270deg low 4800 */ | |
248 {-3.0, 0.0} /* 180deg low */ | |
249 }; | |
250 static const complexf_t cdcd[6] = | |
251 { | |
252 { 3.0, 0.0}, /* 0deg low 9600 */ | |
253 {-3.0, 3.0}, /* 135deg high */ | |
254 { 3.0, 0.0}, /* 0deg low 7200 */ | |
255 {-1.0, 1.0}, /* 135deg low */ | |
256 { 3.0, 0.0}, /* 0deg low 4800 */ | |
257 { 0.0, 3.0} /* 90deg low */ | |
258 }; | |
259 int bits; | |
260 int amp; | |
261 int bit; | |
262 | |
263 if (s->in_training) | |
264 { | |
265 /* Send the training sequence */ | |
266 if (++s->training_step <= V29_TRAINING_SEG_4) | |
267 { | |
268 if (s->training_step <= V29_TRAINING_SEG_3) | |
269 { | |
270 if (s->training_step <= V29_TRAINING_SEG_1) | |
271 { | |
272 /* Optional segment: Unmodulated carrier (talker echo protection) */ | |
273 return constellation[0]; | |
274 } | |
275 if (s->training_step <= V29_TRAINING_SEG_2) | |
276 { | |
277 /* Segment 1: silence */ | |
278 return complex_setf(0.0f, 0.0f); | |
279 } | |
280 /* Segment 2: ABAB... */ | |
281 return abab[(s->training_step & 1) + s->training_offset]; | |
282 } | |
283 /* Segment 3: CDCD... */ | |
284 /* Apply the 1 + x^-6 + x^-7 training scrambler */ | |
285 bit = s->training_scramble_reg & 1; | |
286 s->training_scramble_reg >>= 1; | |
287 s->training_scramble_reg |= (((bit ^ s->training_scramble_reg) & 1) << 6); | |
288 return cdcd[bit + s->training_offset]; | |
289 } | |
290 /* We should be in the block of test ones, or shutdown ones, if we get here. */ | |
291 /* There is no graceful shutdown procedure defined for V.29. Just | |
292 send some ones, to ensure we get the real data bits through, even | |
293 with bad ISI. */ | |
294 if (s->training_step == V29_TRAINING_END + 1) | |
295 { | |
296 /* Switch from the fake get_bit routine, to the user supplied real | |
297 one, and we are up and running. */ | |
298 s->current_get_bit = s->get_bit; | |
299 s->in_training = FALSE; | |
300 } | |
301 } | |
302 /* 9600bps uses the full constellation. | |
303 7200bps uses only the first half of the full constellation. | |
304 4800bps uses the smaller constellation. */ | |
305 amp = 0; | |
306 /* We only use an amplitude bit at 9600bps */ | |
307 if (s->bit_rate == 9600 && get_scrambled_bit(s)) | |
308 amp = 8; | |
309 /*endif*/ | |
310 bits = get_scrambled_bit(s); | |
311 bits = (bits << 1) | get_scrambled_bit(s); | |
312 if (s->bit_rate == 4800) | |
313 { | |
314 bits = phase_steps_4800[bits]; | |
315 } | |
316 else | |
317 { | |
318 bits = (bits << 1) | get_scrambled_bit(s); | |
319 bits = phase_steps_9600[bits]; | |
320 } | |
321 s->constellation_state = (s->constellation_state + bits) & 7; | |
322 return constellation[amp | s->constellation_state]; | |
323 } | |
324 /*- End of function --------------------------------------------------------*/ | |
325 | |
326 int v29_tx(v29_tx_state_t *s, int16_t amp[], int len) | |
327 { | |
328 complexf_t x; | |
329 complexf_t z; | |
330 int i; | |
331 int sample; | |
332 | |
333 if (s->training_step >= V29_TRAINING_SHUTDOWN_END) | |
334 { | |
335 /* Once we have sent the shutdown symbols, we stop sending completely. */ | |
336 return 0; | |
337 } | |
338 for (sample = 0; sample < len; sample++) | |
339 { | |
340 if ((s->baud_phase += 3) >= 10) | |
341 { | |
342 s->baud_phase -= 10; | |
343 s->rrc_filter[s->rrc_filter_step] = | |
344 s->rrc_filter[s->rrc_filter_step + V29_TX_FILTER_STEPS] = getbaud(s); | |
345 if (++s->rrc_filter_step >= V29_TX_FILTER_STEPS) | |
346 s->rrc_filter_step = 0; | |
347 } | |
348 /* Root raised cosine pulse shaping at baseband */ | |
349 x.re = 0.0f; | |
350 x.im = 0.0f; | |
351 for (i = 0; i < V29_TX_FILTER_STEPS; i++) | |
352 { | |
353 x.re += pulseshaper[9 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re; | |
354 x.im += pulseshaper[9 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im; | |
355 } | |
356 /* Now create and modulate the carrier */ | |
357 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate); | |
358 /* Don't bother saturating. We should never clip. */ | |
359 amp[sample] = (int16_t) lrintf((x.re*z.re - x.im*z.im)*s->gain); | |
360 } | |
361 return sample; | |
362 } | |
363 /*- End of function --------------------------------------------------------*/ | |
364 | |
365 static void set_working_gain(v29_tx_state_t *s) | |
366 { | |
367 switch (s->bit_rate) | |
368 { | |
369 case 9600: | |
370 s->gain = 0.387f*s->base_gain; | |
371 break; | |
372 case 7200: | |
373 s->gain = 0.605f*s->base_gain; | |
374 break; | |
375 case 4800: | |
376 s->gain = 0.470f*s->base_gain; | |
377 break; | |
378 default: | |
379 break; | |
380 } | |
381 } | |
382 /*- End of function --------------------------------------------------------*/ | |
383 | |
384 void v29_tx_power(v29_tx_state_t *s, float power) | |
385 { | |
386 /* The constellation does not maintain constant average power as we change bit rates. | |
387 We need to scale the gain we get here by a bit rate specific scaling factor each | |
388 time we restart the modem. */ | |
389 s->base_gain = powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*32768.0f/PULSESHAPER_GAIN; | |
390 set_working_gain(s); | |
391 } | |
392 /*- End of function --------------------------------------------------------*/ | |
393 | |
394 void v29_tx_set_get_bit(v29_tx_state_t *s, get_bit_func_t get_bit, void *user_data) | |
395 { | |
396 if (s->get_bit == s->current_get_bit) | |
397 s->current_get_bit = get_bit; | |
398 s->get_bit = get_bit; | |
399 s->user_data = user_data; | |
400 } | |
401 /*- End of function --------------------------------------------------------*/ | |
402 | |
403 int v29_tx_restart(v29_tx_state_t *s, int rate, int tep) | |
404 { | |
405 span_log(&s->logging, SPAN_LOG_FLOW, "Restarting V.29\n"); | |
406 s->bit_rate = rate; | |
407 set_working_gain(s); | |
408 switch (s->bit_rate) | |
409 { | |
410 case 9600: | |
411 s->training_offset = 0; | |
412 break; | |
413 case 7200: | |
414 s->training_offset = 2; | |
415 break; | |
416 case 4800: | |
417 s->training_offset = 4; | |
418 break; | |
419 default: | |
420 return -1; | |
421 } | |
422 cvec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
423 s->rrc_filter_step = 0; | |
424 s->scramble_reg = 0; | |
425 s->training_scramble_reg = 0x2A; | |
426 s->in_training = TRUE; | |
427 s->training_step = (tep) ? V29_TRAINING_SEG_TEP : V29_TRAINING_SEG_1; | |
428 s->carrier_phase = 0; | |
429 s->baud_phase = 0; | |
430 s->constellation_state = 0; | |
431 s->current_get_bit = fake_get_bit; | |
432 return 0; | |
433 } | |
434 /*- End of function --------------------------------------------------------*/ | |
435 | |
436 v29_tx_state_t *v29_tx_init(v29_tx_state_t *s, int rate, int tep, get_bit_func_t get_bit, void *user_data) | |
437 { | |
438 if (s == NULL) | |
439 { | |
440 if ((s = (v29_tx_state_t *) malloc(sizeof(*s))) == NULL) | |
441 return NULL; | |
442 } | |
443 memset(s, 0, sizeof(*s)); | |
444 s->get_bit = get_bit; | |
445 s->user_data = user_data; | |
446 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
447 v29_tx_power(s, -14.0f); | |
448 v29_tx_restart(s, rate, tep); | |
449 return s; | |
450 } | |
451 /*- End of function --------------------------------------------------------*/ | |
452 | |
453 int v29_tx_release(v29_tx_state_t *s) | |
454 { | |
455 free(s); | |
456 return 0; | |
457 } | |
458 /*- End of function --------------------------------------------------------*/ | |
459 /*- End of file ------------------------------------------------------------*/ |