Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v29tx.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 * 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 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: v29tx.c,v 1.89 2009/06/02 16:03:56 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/v29tx.h" | |
57 | |
58 #include "spandsp/private/logging.h" | |
59 #include "spandsp/private/v29tx.h" | |
60 | |
61 #include "v29tx_constellation_maps.h" | |
62 #if defined(SPANDSP_USE_FIXED_POINT) | |
63 #include "v29tx_fixed_rrc.h" | |
64 #else | |
65 #include "v29tx_floating_rrc.h" | |
66 #endif | |
67 | |
68 /*! The nominal frequency of the carrier, in Hertz */ | |
69 #define CARRIER_NOMINAL_FREQ 1700.0f | |
70 | |
71 /* Segments of the training sequence */ | |
72 /*! The start of the optional TEP, that may preceed the actual training, in symbols */ | |
73 #define V29_TRAINING_SEG_TEP 0 | |
74 /*! The start of training segment 1, in symbols */ | |
75 #define V29_TRAINING_SEG_1 (V29_TRAINING_SEG_TEP + 480) | |
76 /*! The start of training segment 2, in symbols */ | |
77 #define V29_TRAINING_SEG_2 (V29_TRAINING_SEG_1 + 48) | |
78 /*! The start of training segment 3, in symbols */ | |
79 #define V29_TRAINING_SEG_3 (V29_TRAINING_SEG_2 + 128) | |
80 /*! The start of training segment 4, in symbols */ | |
81 #define V29_TRAINING_SEG_4 (V29_TRAINING_SEG_3 + 384) | |
82 /*! The end of the training, in symbols */ | |
83 #define V29_TRAINING_END (V29_TRAINING_SEG_4 + 48) | |
84 /*! The end of the shutdown sequence, in symbols */ | |
85 #define V29_TRAINING_SHUTDOWN_END (V29_TRAINING_END + 32) | |
86 | |
87 static int fake_get_bit(void *user_data) | |
88 { | |
89 return 1; | |
90 } | |
91 /*- End of function --------------------------------------------------------*/ | |
92 | |
93 static __inline__ int get_scrambled_bit(v29_tx_state_t *s) | |
94 { | |
95 int bit; | |
96 int out_bit; | |
97 | |
98 if ((bit = s->current_get_bit(s->get_bit_user_data)) == SIG_STATUS_END_OF_DATA) | |
99 { | |
100 /* End of real data. Switch to the fake get_bit routine, until we | |
101 have shut down completely. */ | |
102 if (s->status_handler) | |
103 s->status_handler(s->status_user_data, SIG_STATUS_END_OF_DATA); | |
104 s->current_get_bit = fake_get_bit; | |
105 s->in_training = TRUE; | |
106 bit = 1; | |
107 } | |
108 out_bit = (bit ^ (s->scramble_reg >> 17) ^ (s->scramble_reg >> 22)) & 1; | |
109 s->scramble_reg = (s->scramble_reg << 1) | out_bit; | |
110 return out_bit; | |
111 } | |
112 /*- End of function --------------------------------------------------------*/ | |
113 | |
114 #if defined(SPANDSP_USE_FIXED_POINT) | |
115 static __inline__ complexi16_t getbaud(v29_tx_state_t *s) | |
116 #else | |
117 static __inline__ complexf_t getbaud(v29_tx_state_t *s) | |
118 #endif | |
119 { | |
120 static const int phase_steps_9600[8] = | |
121 { | |
122 1, 0, 2, 3, 6, 7, 5, 4 | |
123 }; | |
124 static const int phase_steps_4800[4] = | |
125 { | |
126 0, 2, 6, 4 | |
127 }; | |
128 #if defined(SPANDSP_USE_FIXED_POINT) | |
129 static const complexi16_t zero = {0, 0}; | |
130 #else | |
131 static const complexf_t zero = {0.0f, 0.0f}; | |
132 #endif | |
133 int bits; | |
134 int amp; | |
135 int bit; | |
136 | |
137 if (s->in_training) | |
138 { | |
139 /* Send the training sequence */ | |
140 if (++s->training_step <= V29_TRAINING_SEG_4) | |
141 { | |
142 if (s->training_step <= V29_TRAINING_SEG_3) | |
143 { | |
144 if (s->training_step <= V29_TRAINING_SEG_1) | |
145 { | |
146 /* Optional segment: Unmodulated carrier (talker echo protection) */ | |
147 return v29_9600_constellation[0]; | |
148 } | |
149 if (s->training_step <= V29_TRAINING_SEG_2) | |
150 { | |
151 /* Segment 1: silence */ | |
152 return zero; | |
153 } | |
154 /* Segment 2: ABAB... */ | |
155 return v29_abab_constellation[(s->training_step & 1) + s->training_offset]; | |
156 } | |
157 /* Segment 3: CDCD... */ | |
158 /* Apply the 1 + x^-6 + x^-7 training scrambler */ | |
159 bit = s->training_scramble_reg & 1; | |
160 s->training_scramble_reg >>= 1; | |
161 s->training_scramble_reg |= (((bit ^ s->training_scramble_reg) & 1) << 6); | |
162 return v29_cdcd_constellation[bit + s->training_offset]; | |
163 } | |
164 /* We should be in the block of test ones, or shutdown ones, if we get here. */ | |
165 /* There is no graceful shutdown procedure defined for V.29. Just | |
166 send some ones, to ensure we get the real data bits through, even | |
167 with bad ISI. */ | |
168 if (s->training_step == V29_TRAINING_END + 1) | |
169 { | |
170 /* Switch from the fake get_bit routine, to the user supplied real | |
171 one, and we are up and running. */ | |
172 s->current_get_bit = s->get_bit; | |
173 s->in_training = FALSE; | |
174 } | |
175 if (s->training_step == V29_TRAINING_SHUTDOWN_END) | |
176 { | |
177 if (s->status_handler) | |
178 s->status_handler(s->status_user_data, SIG_STATUS_SHUTDOWN_COMPLETE); | |
179 } | |
180 } | |
181 /* 9600bps uses the full constellation. | |
182 7200bps uses only the first half of the full constellation. | |
183 4800bps uses the smaller constellation. */ | |
184 amp = 0; | |
185 /* We only use an amplitude bit at 9600bps */ | |
186 if (s->bit_rate == 9600 && get_scrambled_bit(s)) | |
187 amp = 8; | |
188 /*endif*/ | |
189 bits = get_scrambled_bit(s); | |
190 bits = (bits << 1) | get_scrambled_bit(s); | |
191 if (s->bit_rate == 4800) | |
192 { | |
193 bits = phase_steps_4800[bits]; | |
194 } | |
195 else | |
196 { | |
197 bits = (bits << 1) | get_scrambled_bit(s); | |
198 bits = phase_steps_9600[bits]; | |
199 } | |
200 s->constellation_state = (s->constellation_state + bits) & 7; | |
201 return v29_9600_constellation[amp | s->constellation_state]; | |
202 } | |
203 /*- End of function --------------------------------------------------------*/ | |
204 | |
205 SPAN_DECLARE_NONSTD(int) v29_tx(v29_tx_state_t *s, int16_t amp[], int len) | |
206 { | |
207 #if defined(SPANDSP_USE_FIXED_POINT) | |
208 complexi_t x; | |
209 complexi_t z; | |
210 #else | |
211 complexf_t x; | |
212 complexf_t z; | |
213 #endif | |
214 int i; | |
215 int sample; | |
216 | |
217 if (s->training_step >= V29_TRAINING_SHUTDOWN_END) | |
218 { | |
219 /* Once we have sent the shutdown symbols, we stop sending completely. */ | |
220 return 0; | |
221 } | |
222 for (sample = 0; sample < len; sample++) | |
223 { | |
224 if ((s->baud_phase += 3) >= 10) | |
225 { | |
226 s->baud_phase -= 10; | |
227 s->rrc_filter[s->rrc_filter_step] = | |
228 s->rrc_filter[s->rrc_filter_step + V29_TX_FILTER_STEPS] = getbaud(s); | |
229 if (++s->rrc_filter_step >= V29_TX_FILTER_STEPS) | |
230 s->rrc_filter_step = 0; | |
231 } | |
232 /* Root raised cosine pulse shaping at baseband */ | |
233 #if defined(SPANDSP_USE_FIXED_POINT) | |
234 x = complex_seti(0, 0); | |
235 for (i = 0; i < V29_TX_FILTER_STEPS; i++) | |
236 { | |
237 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; | |
238 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; | |
239 } | |
240 /* Now create and modulate the carrier */ | |
241 x.re >>= 4; | |
242 x.im >>= 4; | |
243 z = dds_complexi(&(s->carrier_phase), s->carrier_phase_rate); | |
244 /* Don't bother saturating. We should never clip. */ | |
245 i = (x.re*z.re - x.im*z.im) >> 15; | |
246 amp[sample] = (int16_t) ((i*s->gain) >> 15); | |
247 #else | |
248 x = complex_setf(0.0f, 0.0f); | |
249 for (i = 0; i < V29_TX_FILTER_STEPS; i++) | |
250 { | |
251 x.re += tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re; | |
252 x.im += tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im; | |
253 } | |
254 /* Now create and modulate the carrier */ | |
255 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate); | |
256 /* Don't bother saturating. We should never clip. */ | |
257 amp[sample] = (int16_t) lfastrintf((x.re*z.re - x.im*z.im)*s->gain); | |
258 #endif | |
259 } | |
260 return sample; | |
261 } | |
262 /*- End of function --------------------------------------------------------*/ | |
263 | |
264 static void set_working_gain(v29_tx_state_t *s) | |
265 { | |
266 #if defined(SPANDSP_USE_FIXED_POINT) | |
267 switch (s->bit_rate) | |
268 { | |
269 case 9600: | |
270 s->gain = 0.387f*s->base_gain*16.0f*32767.0f/30672.52f; | |
271 break; | |
272 case 7200: | |
273 s->gain = 0.605f*s->base_gain*16.0f*32767.0f/30672.52f; | |
274 break; | |
275 case 4800: | |
276 s->gain = 0.470f*s->base_gain*16.0f*32767.0f/30672.52f; | |
277 break; | |
278 default: | |
279 break; | |
280 } | |
281 #else | |
282 switch (s->bit_rate) | |
283 { | |
284 case 9600: | |
285 s->gain = 0.387f*s->base_gain; | |
286 break; | |
287 case 7200: | |
288 s->gain = 0.605f*s->base_gain; | |
289 break; | |
290 case 4800: | |
291 s->gain = 0.470f*s->base_gain; | |
292 break; | |
293 default: | |
294 break; | |
295 } | |
296 #endif | |
297 } | |
298 /*- End of function --------------------------------------------------------*/ | |
299 | |
300 SPAN_DECLARE(void) v29_tx_power(v29_tx_state_t *s, float power) | |
301 { | |
302 /* The constellation does not maintain constant average power as we change bit rates. | |
303 We need to scale the gain we get here by a bit rate specific scaling factor each | |
304 time we restart the modem. */ | |
305 s->base_gain = powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*32768.0f/TX_PULSESHAPER_GAIN; | |
306 set_working_gain(s); | |
307 } | |
308 /*- End of function --------------------------------------------------------*/ | |
309 | |
310 SPAN_DECLARE(void) v29_tx_set_get_bit(v29_tx_state_t *s, get_bit_func_t get_bit, void *user_data) | |
311 { | |
312 if (s->get_bit == s->current_get_bit) | |
313 s->current_get_bit = get_bit; | |
314 s->get_bit = get_bit; | |
315 s->get_bit_user_data = user_data; | |
316 } | |
317 /*- End of function --------------------------------------------------------*/ | |
318 | |
319 SPAN_DECLARE(void) v29_tx_set_modem_status_handler(v29_tx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
320 { | |
321 s->status_handler = handler; | |
322 s->status_user_data = user_data; | |
323 } | |
324 /*- End of function --------------------------------------------------------*/ | |
325 | |
326 SPAN_DECLARE(logging_state_t *) v29_tx_get_logging_state(v29_tx_state_t *s) | |
327 { | |
328 return &s->logging; | |
329 } | |
330 /*- End of function --------------------------------------------------------*/ | |
331 | |
332 SPAN_DECLARE(int) v29_tx_restart(v29_tx_state_t *s, int bit_rate, int tep) | |
333 { | |
334 span_log(&s->logging, SPAN_LOG_FLOW, "Restarting V.29\n"); | |
335 s->bit_rate = bit_rate; | |
336 set_working_gain(s); | |
337 switch (s->bit_rate) | |
338 { | |
339 case 9600: | |
340 s->training_offset = 0; | |
341 break; | |
342 case 7200: | |
343 s->training_offset = 2; | |
344 break; | |
345 case 4800: | |
346 s->training_offset = 4; | |
347 break; | |
348 default: | |
349 return -1; | |
350 } | |
351 #if defined(SPANDSP_USE_FIXED_POINT) | |
352 memset(s->rrc_filter, 0, sizeof(s->rrc_filter)); | |
353 #else | |
354 cvec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
355 #endif | |
356 s->rrc_filter_step = 0; | |
357 s->scramble_reg = 0; | |
358 s->training_scramble_reg = 0x2A; | |
359 s->in_training = TRUE; | |
360 s->training_step = (tep) ? V29_TRAINING_SEG_TEP : V29_TRAINING_SEG_1; | |
361 s->carrier_phase = 0; | |
362 s->baud_phase = 0; | |
363 s->constellation_state = 0; | |
364 s->current_get_bit = fake_get_bit; | |
365 return 0; | |
366 } | |
367 /*- End of function --------------------------------------------------------*/ | |
368 | |
369 SPAN_DECLARE(v29_tx_state_t *) v29_tx_init(v29_tx_state_t *s, int bit_rate, int tep, get_bit_func_t get_bit, void *user_data) | |
370 { | |
371 switch (bit_rate) | |
372 { | |
373 case 9600: | |
374 case 7200: | |
375 case 4800: | |
376 break; | |
377 default: | |
378 return NULL; | |
379 } | |
380 if (s == NULL) | |
381 { | |
382 if ((s = (v29_tx_state_t *) malloc(sizeof(*s))) == NULL) | |
383 return NULL; | |
384 } | |
385 memset(s, 0, sizeof(*s)); | |
386 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
387 span_log_set_protocol(&s->logging, "V.29 TX"); | |
388 s->get_bit = get_bit; | |
389 s->get_bit_user_data = user_data; | |
390 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
391 v29_tx_power(s, -14.0f); | |
392 v29_tx_restart(s, bit_rate, tep); | |
393 return s; | |
394 } | |
395 /*- End of function --------------------------------------------------------*/ | |
396 | |
397 SPAN_DECLARE(int) v29_tx_release(v29_tx_state_t *s) | |
398 { | |
399 return 0; | |
400 } | |
401 /*- End of function --------------------------------------------------------*/ | |
402 | |
403 SPAN_DECLARE(int) v29_tx_free(v29_tx_state_t *s) | |
404 { | |
405 free(s); | |
406 return 0; | |
407 } | |
408 /*- End of function --------------------------------------------------------*/ | |
409 /*- End of file ------------------------------------------------------------*/ |