Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/v27ter_tx.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 * 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 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: v27ter_tx.c,v 1.76 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/v27ter_tx.h" | |
57 | |
58 #include "spandsp/private/logging.h" | |
59 #include "spandsp/private/v27ter_tx.h" | |
60 | |
61 #if defined(SPANDSP_USE_FIXED_POINT) | |
62 #include "v27ter_tx_4800_fixed_rrc.h" | |
63 #include "v27ter_tx_2400_fixed_rrc.h" | |
64 #else | |
65 #include "v27ter_tx_4800_floating_rrc.h" | |
66 #include "v27ter_tx_2400_floating_rrc.h" | |
67 #endif | |
68 | |
69 /*! The nominal frequency of the carrier, in Hertz */ | |
70 #define CARRIER_NOMINAL_FREQ 1800.0f | |
71 | |
72 /* Segments of the training sequence */ | |
73 /* V.27ter defines a long and a short sequence. FAX doesn't use the | |
74 short sequence, so it is not implemented here. */ | |
75 /*! The start of training segment 1, in symbols */ | |
76 #define V27TER_TRAINING_SEG_1 0 | |
77 /*! The start of training segment 2, in symbols */ | |
78 #define V27TER_TRAINING_SEG_2 (V27TER_TRAINING_SEG_1 + 320) | |
79 /*! The start of training segment 3, in symbols */ | |
80 #define V27TER_TRAINING_SEG_3 (V27TER_TRAINING_SEG_2 + 32) | |
81 /*! The start of training segment 4, in symbols */ | |
82 #define V27TER_TRAINING_SEG_4 (V27TER_TRAINING_SEG_3 + 50) | |
83 /*! The start of training segment 5, in symbols */ | |
84 #define V27TER_TRAINING_SEG_5 (V27TER_TRAINING_SEG_4 + 1074) | |
85 /*! The end of the training, in symbols */ | |
86 #define V27TER_TRAINING_END (V27TER_TRAINING_SEG_5 + 8) | |
87 /*! The end of the shutdown sequence, in symbols */ | |
88 #define V27TER_TRAINING_SHUTDOWN_END (V27TER_TRAINING_END + 32) | |
89 | |
90 static int fake_get_bit(void *user_data) | |
91 { | |
92 return 1; | |
93 } | |
94 /*- End of function --------------------------------------------------------*/ | |
95 | |
96 static __inline__ int scramble(v27ter_tx_state_t *s, int in_bit) | |
97 { | |
98 int out_bit; | |
99 | |
100 /* This scrambler is really quite messy to implement. There seems to be no efficient shortcut */ | |
101 out_bit = (in_bit ^ (s->scramble_reg >> 5) ^ (s->scramble_reg >> 6)) & 1; | |
102 if (s->scrambler_pattern_count >= 33) | |
103 { | |
104 out_bit ^= 1; | |
105 s->scrambler_pattern_count = 0; | |
106 } | |
107 else | |
108 { | |
109 if ((((s->scramble_reg >> 7) ^ out_bit) & ((s->scramble_reg >> 8) ^ out_bit) & ((s->scramble_reg >> 11) ^ out_bit) & 1)) | |
110 s->scrambler_pattern_count = 0; | |
111 else | |
112 s->scrambler_pattern_count++; | |
113 } | |
114 s->scramble_reg = (s->scramble_reg << 1) | out_bit; | |
115 return out_bit; | |
116 } | |
117 /*- End of function --------------------------------------------------------*/ | |
118 | |
119 static __inline__ int get_scrambled_bit(v27ter_tx_state_t *s) | |
120 { | |
121 int bit; | |
122 | |
123 if ((bit = s->current_get_bit(s->get_bit_user_data)) == SIG_STATUS_END_OF_DATA) | |
124 { | |
125 /* End of real data. Switch to the fake get_bit routine, until we | |
126 have shut down completely. */ | |
127 if (s->status_handler) | |
128 s->status_handler(s->status_user_data, SIG_STATUS_END_OF_DATA); | |
129 s->current_get_bit = fake_get_bit; | |
130 s->in_training = TRUE; | |
131 bit = 1; | |
132 } | |
133 return scramble(s, bit); | |
134 } | |
135 /*- End of function --------------------------------------------------------*/ | |
136 | |
137 #if defined(SPANDSP_USE_FIXED_POINT) | |
138 static complexi16_t getbaud(v27ter_tx_state_t *s) | |
139 #else | |
140 static complexf_t getbaud(v27ter_tx_state_t *s) | |
141 #endif | |
142 { | |
143 static const int phase_steps_4800[8] = | |
144 { | |
145 1, 0, 2, 3, 6, 7, 5, 4 | |
146 }; | |
147 static const int phase_steps_2400[4] = | |
148 { | |
149 0, 2, 6, 4 | |
150 }; | |
151 #if defined(SPANDSP_USE_FIXED_POINT) | |
152 static const complexi16_t constellation[8] = | |
153 { | |
154 { 1414, 0000}, /* 0deg */ | |
155 { 1000, 1000}, /* 45deg */ | |
156 { 0000, 1414}, /* 90deg */ | |
157 {-1000, 1000}, /* 135deg */ | |
158 {-1414, 0000}, /* 180deg */ | |
159 {-1000, -1000}, /* 225deg */ | |
160 { 0000, -1414}, /* 270deg */ | |
161 { 1000, -1000} /* 315deg */ | |
162 }; | |
163 static const complexi16_t zero = {0, 0}; | |
164 #else | |
165 static const complexf_t constellation[8] = | |
166 { | |
167 { 1.414f, 0.0f}, /* 0deg */ | |
168 { 1.0f, 1.0f}, /* 45deg */ | |
169 { 0.0f, 1.414f}, /* 90deg */ | |
170 {-1.0f, 1.0f}, /* 135deg */ | |
171 {-1.414f, 0.0f}, /* 180deg */ | |
172 {-1.0f, -1.0f}, /* 225deg */ | |
173 { 0.0f, -1.414f}, /* 270deg */ | |
174 { 1.0f, -1.0f} /* 315deg */ | |
175 }; | |
176 static const complexf_t zero = {0.0f, 0.0f}; | |
177 #endif | |
178 int bits; | |
179 | |
180 if (s->in_training) | |
181 { | |
182 /* Send the training sequence */ | |
183 if (++s->training_step <= V27TER_TRAINING_SEG_5) | |
184 { | |
185 if (s->training_step <= V27TER_TRAINING_SEG_4) | |
186 { | |
187 if (s->training_step <= V27TER_TRAINING_SEG_2) | |
188 { | |
189 /* Segment 1: Unmodulated carrier (talker echo protection) */ | |
190 return constellation[0]; | |
191 } | |
192 if (s->training_step <= V27TER_TRAINING_SEG_3) | |
193 { | |
194 /* Segment 2: Silence */ | |
195 return zero; | |
196 } | |
197 /* Segment 3: Regular reversals... */ | |
198 s->constellation_state = (s->constellation_state + 4) & 7; | |
199 return constellation[s->constellation_state]; | |
200 } | |
201 /* Segment 4: Scrambled reversals... */ | |
202 /* Apply the 1 + x^-6 + x^-7 scrambler. We want every third | |
203 bit from the scrambler. */ | |
204 bits = get_scrambled_bit(s) << 2; | |
205 get_scrambled_bit(s); | |
206 get_scrambled_bit(s); | |
207 s->constellation_state = (s->constellation_state + bits) & 7; | |
208 return constellation[s->constellation_state]; | |
209 } | |
210 /* We should be in the block of test ones, or shutdown ones, if we get here. */ | |
211 /* There is no graceful shutdown procedure defined for V.27ter. Just | |
212 send some ones, to ensure we get the real data bits through, even | |
213 with bad ISI. */ | |
214 if (s->training_step == V27TER_TRAINING_END + 1) | |
215 { | |
216 /* End of the last segment - segment 5: All ones */ | |
217 /* Switch from the fake get_bit routine, to the user supplied real | |
218 one, and we are up and running. */ | |
219 s->current_get_bit = s->get_bit; | |
220 s->in_training = FALSE; | |
221 } | |
222 if (s->training_step == V27TER_TRAINING_SHUTDOWN_END) | |
223 { | |
224 if (s->status_handler) | |
225 s->status_handler(s->status_user_data, SIG_STATUS_SHUTDOWN_COMPLETE); | |
226 } | |
227 } | |
228 /* 4800bps uses 8 phases. 2400bps uses 4 phases. */ | |
229 if (s->bit_rate == 4800) | |
230 { | |
231 bits = get_scrambled_bit(s); | |
232 bits = (bits << 1) | get_scrambled_bit(s); | |
233 bits = (bits << 1) | get_scrambled_bit(s); | |
234 bits = phase_steps_4800[bits]; | |
235 } | |
236 else | |
237 { | |
238 bits = get_scrambled_bit(s); | |
239 bits = (bits << 1) | get_scrambled_bit(s); | |
240 bits = phase_steps_2400[bits]; | |
241 } | |
242 s->constellation_state = (s->constellation_state + bits) & 7; | |
243 return constellation[s->constellation_state]; | |
244 } | |
245 /*- End of function --------------------------------------------------------*/ | |
246 | |
247 SPAN_DECLARE_NONSTD(int) v27ter_tx(v27ter_tx_state_t *s, int16_t amp[], int len) | |
248 { | |
249 #if defined(SPANDSP_USE_FIXED_POINT) | |
250 complexi_t x; | |
251 complexi_t z; | |
252 #else | |
253 complexf_t x; | |
254 complexf_t z; | |
255 #endif | |
256 int i; | |
257 int sample; | |
258 | |
259 if (s->training_step >= V27TER_TRAINING_SHUTDOWN_END) | |
260 { | |
261 /* Once we have sent the shutdown symbols, we stop sending completely. */ | |
262 return 0; | |
263 } | |
264 /* The symbol rates for the two bit rates are different. This makes it difficult to | |
265 merge both generation procedures into a single efficient loop. We do not bother | |
266 trying. We use two independent loops, filter coefficients, etc. */ | |
267 if (s->bit_rate == 4800) | |
268 { | |
269 for (sample = 0; sample < len; sample++) | |
270 { | |
271 if (++s->baud_phase >= 5) | |
272 { | |
273 s->baud_phase -= 5; | |
274 s->rrc_filter[s->rrc_filter_step] = | |
275 s->rrc_filter[s->rrc_filter_step + V27TER_TX_FILTER_STEPS] = getbaud(s); | |
276 if (++s->rrc_filter_step >= V27TER_TX_FILTER_STEPS) | |
277 s->rrc_filter_step = 0; | |
278 } | |
279 /* Root raised cosine pulse shaping at baseband */ | |
280 #if defined(SPANDSP_USE_FIXED_POINT) | |
281 x = complex_seti(0, 0); | |
282 for (i = 0; i < V27TER_TX_FILTER_STEPS; i++) | |
283 { | |
284 x.re += (int32_t) tx_pulseshaper_4800[TX_PULSESHAPER_4800_COEFF_SETS - 1 - s->baud_phase][i]*(int32_t) s->rrc_filter[i + s->rrc_filter_step].re; | |
285 x.im += (int32_t) tx_pulseshaper_4800[TX_PULSESHAPER_4800_COEFF_SETS - 1 - s->baud_phase][i]*(int32_t) s->rrc_filter[i + s->rrc_filter_step].im; | |
286 } | |
287 /* Now create and modulate the carrier */ | |
288 x.re >>= 14; | |
289 x.im >>= 14; | |
290 z = dds_complexi(&(s->carrier_phase), s->carrier_phase_rate); | |
291 /* Don't bother saturating. We should never clip. */ | |
292 i = (x.re*z.re - x.im*z.im) >> 15; | |
293 amp[sample] = (int16_t) ((i*s->gain_4800) >> 15); | |
294 #else | |
295 x = complex_setf(0.0f, 0.0f); | |
296 for (i = 0; i < V27TER_TX_FILTER_STEPS; i++) | |
297 { | |
298 x.re += tx_pulseshaper_4800[TX_PULSESHAPER_4800_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re; | |
299 x.im += tx_pulseshaper_4800[TX_PULSESHAPER_4800_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im; | |
300 } | |
301 /* Now create and modulate the carrier */ | |
302 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate); | |
303 /* Don't bother saturating. We should never clip. */ | |
304 amp[sample] = (int16_t) lfastrintf((x.re*z.re - x.im*z.im)*s->gain_4800); | |
305 #endif | |
306 } | |
307 } | |
308 else | |
309 { | |
310 for (sample = 0; sample < len; sample++) | |
311 { | |
312 if ((s->baud_phase += 3) >= 20) | |
313 { | |
314 s->baud_phase -= 20; | |
315 s->rrc_filter[s->rrc_filter_step] = | |
316 s->rrc_filter[s->rrc_filter_step + V27TER_TX_FILTER_STEPS] = getbaud(s); | |
317 if (++s->rrc_filter_step >= V27TER_TX_FILTER_STEPS) | |
318 s->rrc_filter_step = 0; | |
319 } | |
320 /* Root raised cosine pulse shaping at baseband */ | |
321 #if defined(SPANDSP_USE_FIXED_POINT) | |
322 x = complex_seti(0, 0); | |
323 for (i = 0; i < V27TER_TX_FILTER_STEPS; i++) | |
324 { | |
325 x.re += (int32_t) tx_pulseshaper_2400[TX_PULSESHAPER_2400_COEFF_SETS - 1 - s->baud_phase][i]*(int32_t) s->rrc_filter[i + s->rrc_filter_step].re; | |
326 x.im += (int32_t) tx_pulseshaper_2400[TX_PULSESHAPER_2400_COEFF_SETS - 1 - s->baud_phase][i]*(int32_t) s->rrc_filter[i + s->rrc_filter_step].im; | |
327 } | |
328 /* Now create and modulate the carrier */ | |
329 x.re >>= 14; | |
330 x.im >>= 14; | |
331 z = dds_complexi(&(s->carrier_phase), s->carrier_phase_rate); | |
332 /* Don't bother saturating. We should never clip. */ | |
333 i = (x.re*z.re - x.im*z.im) >> 15; | |
334 amp[sample] = (int16_t) ((i*s->gain_2400) >> 15); | |
335 #else | |
336 x = complex_setf(0.0f, 0.0f); | |
337 for (i = 0; i < V27TER_TX_FILTER_STEPS; i++) | |
338 { | |
339 x.re += tx_pulseshaper_2400[TX_PULSESHAPER_2400_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].re; | |
340 x.im += tx_pulseshaper_2400[TX_PULSESHAPER_2400_COEFF_SETS - 1 - s->baud_phase][i]*s->rrc_filter[i + s->rrc_filter_step].im; | |
341 } | |
342 /* Now create and modulate the carrier */ | |
343 z = dds_complexf(&(s->carrier_phase), s->carrier_phase_rate); | |
344 /* Don't bother saturating. We should never clip. */ | |
345 amp[sample] = (int16_t) lfastrintf((x.re*z.re - x.im*z.im)*s->gain_2400); | |
346 #endif | |
347 } | |
348 } | |
349 return sample; | |
350 } | |
351 /*- End of function --------------------------------------------------------*/ | |
352 | |
353 SPAN_DECLARE(void) v27ter_tx_power(v27ter_tx_state_t *s, float power) | |
354 { | |
355 float l; | |
356 | |
357 l = powf(10.0f, (power - DBM0_MAX_POWER)/20.0f)*32768.0f; | |
358 #if defined(SPANDSP_USE_FIXED_POINT) | |
359 s->gain_2400 = 16.0f*1.024f*(32767.0f/28828.51f)*l/TX_PULSESHAPER_2400_GAIN; | |
360 s->gain_4800 = 16.0f*1.024f*(32767.0f/28828.46f)*l/TX_PULSESHAPER_4800_GAIN; | |
361 #else | |
362 s->gain_2400 = l/TX_PULSESHAPER_2400_GAIN; | |
363 s->gain_4800 = l/TX_PULSESHAPER_4800_GAIN; | |
364 #endif | |
365 } | |
366 /*- End of function --------------------------------------------------------*/ | |
367 | |
368 SPAN_DECLARE(void) v27ter_tx_set_get_bit(v27ter_tx_state_t *s, get_bit_func_t get_bit, void *user_data) | |
369 { | |
370 if (s->get_bit == s->current_get_bit) | |
371 s->current_get_bit = get_bit; | |
372 s->get_bit = get_bit; | |
373 s->get_bit_user_data = user_data; | |
374 } | |
375 /*- End of function --------------------------------------------------------*/ | |
376 | |
377 SPAN_DECLARE(void) v27ter_tx_set_modem_status_handler(v27ter_tx_state_t *s, modem_tx_status_func_t handler, void *user_data) | |
378 { | |
379 s->status_handler = handler; | |
380 s->status_user_data = user_data; | |
381 } | |
382 /*- End of function --------------------------------------------------------*/ | |
383 | |
384 SPAN_DECLARE(logging_state_t *) v27ter_tx_get_logging_state(v27ter_tx_state_t *s) | |
385 { | |
386 return &s->logging; | |
387 } | |
388 /*- End of function --------------------------------------------------------*/ | |
389 | |
390 SPAN_DECLARE(int) v27ter_tx_restart(v27ter_tx_state_t *s, int bit_rate, int tep) | |
391 { | |
392 if (bit_rate != 4800 && bit_rate != 2400) | |
393 return -1; | |
394 s->bit_rate = bit_rate; | |
395 #if defined(SPANDSP_USE_FIXED_POINT) | |
396 memset(s->rrc_filter, 0, sizeof(s->rrc_filter)); | |
397 #else | |
398 cvec_zerof(s->rrc_filter, sizeof(s->rrc_filter)/sizeof(s->rrc_filter[0])); | |
399 #endif | |
400 s->rrc_filter_step = 0; | |
401 s->scramble_reg = 0x3C; | |
402 s->scrambler_pattern_count = 0; | |
403 s->in_training = TRUE; | |
404 s->training_step = (tep) ? V27TER_TRAINING_SEG_1 : V27TER_TRAINING_SEG_2; | |
405 s->carrier_phase = 0; | |
406 s->baud_phase = 0; | |
407 s->constellation_state = 0; | |
408 s->current_get_bit = fake_get_bit; | |
409 return 0; | |
410 } | |
411 /*- End of function --------------------------------------------------------*/ | |
412 | |
413 SPAN_DECLARE(v27ter_tx_state_t *) v27ter_tx_init(v27ter_tx_state_t *s, int bit_rate, int tep, get_bit_func_t get_bit, void *user_data) | |
414 { | |
415 switch (bit_rate) | |
416 { | |
417 case 4800: | |
418 case 2400: | |
419 break; | |
420 default: | |
421 return NULL; | |
422 } | |
423 if (s == NULL) | |
424 { | |
425 if ((s = (v27ter_tx_state_t *) malloc(sizeof(*s))) == NULL) | |
426 return NULL; | |
427 } | |
428 memset(s, 0, sizeof(*s)); | |
429 span_log_init(&s->logging, SPAN_LOG_NONE, NULL); | |
430 span_log_set_protocol(&s->logging, "V.27ter TX"); | |
431 s->get_bit = get_bit; | |
432 s->get_bit_user_data = user_data; | |
433 s->carrier_phase_rate = dds_phase_ratef(CARRIER_NOMINAL_FREQ); | |
434 v27ter_tx_power(s, -14.0f); | |
435 v27ter_tx_restart(s, bit_rate, tep); | |
436 return s; | |
437 } | |
438 /*- End of function --------------------------------------------------------*/ | |
439 | |
440 SPAN_DECLARE(int) v27ter_tx_release(v27ter_tx_state_t *s) | |
441 { | |
442 return 0; | |
443 } | |
444 /*- End of function --------------------------------------------------------*/ | |
445 | |
446 SPAN_DECLARE(int) v27ter_tx_free(v27ter_tx_state_t *s) | |
447 { | |
448 free(s); | |
449 return 0; | |
450 } | |
451 /*- End of function --------------------------------------------------------*/ | |
452 /*- End of file ------------------------------------------------------------*/ |