5
|
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 ------------------------------------------------------------*/
|