1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * line_model.c - Model a telephone line.
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 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
26 #if defined(HAVE_CONFIG_H)
27 #include "config.h"
28 #endif
29
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <inttypes.h>
33 #include <string.h>
34 #include <time.h>
35 #include <stdio.h>
36 #include <fcntl.h>
37 #if defined(HAVE_TGMATH_H)
38 #include <tgmath.h>
39 #endif
40 #if defined(HAVE_MATH_H)
41 #define GEN_CONST
42 #include <math.h>
43 #endif
44 #if defined(HAVE_STDBOOL_H)
45 #include <stdbool.h>
46 #else
47 #include "spandsp/stdbool.h"
48 #endif
49 #include "floating_fudge.h"
50
51 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
52 #include "spandsp.h"
53 #include "spandsp-sim.h"
54 #include "spandsp/g168models.h"
55
56 #if !defined(NULL)
57 #define NULL (void *) 0
58 #endif
59
60 static const float null_line_model[] =
61 {
62 0.0,
63 0.0,
64 0.0,
65 0.0,
66 0.0,
67 0.0,
68 0.0,
69 0.0,
70 0.0,
71 0.0,
72 0.0,
73 0.0,
74 0.0,
75 0.0,
76 0.0,
77 0.0,
78 0.0,
79 0.0,
80 0.0,
81 0.0,
82 0.0,
83 0.0,
84 0.0,
85 0.0,
86 0.0,
87 0.0,
88 0.0,
89 0.0,
90 0.0,
91 0.0,
92 0.0,
93 0.0,
94 0.0,
95 0.0,
96 0.0,
97 0.0,
98 0.0,
99 0.0,
100 0.0,
101 0.0,
102 0.0,
103 0.0,
104 0.0,
105 0.0,
106 0.0,
107 0.0,
108 0.0,
109 0.0,
110 0.0,
111 0.0,
112 0.0,
113 0.0,
114 0.0,
115 0.0,
116 0.0,
117 0.0,
118 0.0,
119 0.0,
120 0.0,
121 0.0,
122 0.0,
123 0.0,
124 0.0,
125 0.0,
126 0.0,
127 0.0,
128 0.0,
129 0.0,
130 0.0,
131 0.0,
132 0.0,
133 0.0,
134 0.0,
135 0.0,
136 0.0,
137 0.0,
138 0.0,
139 0.0,
140 0.0,
141 0.0,
142 0.0,
143 0.0,
144 0.0,
145 0.0,
146 0.0,
147 0.0,
148 0.0,
149 0.0,
150 0.0,
151 0.0,
152 0.0,
153 0.0,
154 0.0,
155 0.0,
156 0.0,
157 0.0,
158 0.0,
159 0.0,
160 0.0,
161 0.0,
162 0.0,
163 0.0,
164 0.0,
165 0.0,
166 0.0,
167 0.0,
168 0.0,
169 0.0,
170 0.0,
171 0.0,
172 0.0,
173 0.0,
174 0.0,
175 0.0,
176 0.0,
177 0.0,
178 0.0,
179 0.0,
180 0.0,
181 0.0,
182 0.0,
183 0.0,
184 0.0,
185 0.0,
186 0.0,
187 0.0,
188 0.0,
189 0.0,
190 1.0
191 };
192
193 SPAN_DECLARE_DATA const float *line_models[] =
194 {
195 null_line_model, /* 0 */
196 proakis_line_model,
197 ad_1_edd_1_model,
198 ad_1_edd_2_model,
199 ad_1_edd_3_model,
200 ad_5_edd_1_model, /* 5 */
201 ad_5_edd_2_model,
202 ad_5_edd_3_model,
203 ad_6_edd_1_model,
204 ad_6_edd_2_model,
205 ad_6_edd_3_model, /* 10 */
206 ad_7_edd_1_model,
207 ad_7_edd_2_model,
208 ad_7_edd_3_model,
209 ad_8_edd_1_model,
210 ad_8_edd_2_model, /* 15 */
211 ad_8_edd_3_model,
212 ad_9_edd_1_model,
213 ad_9_edd_2_model,
214 ad_9_edd_3_model
215 };
216
calc_near_line_filter(one_way_line_model_state_t * s,float v)217 static float calc_near_line_filter(one_way_line_model_state_t *s, float v)
218 {
219 float sum;
220 int j;
221 int p;
222
223 /* Add the sample in the filter buffer */
224 p = s->near_buf_ptr;
225 s->near_buf[p] = v;
226 if (++p == s->near_filter_len)
227 p = 0;
228 s->near_buf_ptr = p;
229
230 /* Apply the filter */
231 sum = 0.0f;
232 for (j = 0; j < s->near_filter_len; j++)
233 {
234 sum += s->near_filter[j]*s->near_buf[p];
235 if (++p >= s->near_filter_len)
236 p = 0;
237 }
238
239 /* Add noise */
240 sum += awgn(&s->near_noise);
241
242 return sum;
243 }
244 /*- End of function --------------------------------------------------------*/
245
calc_far_line_filter(one_way_line_model_state_t * s,float v)246 static float calc_far_line_filter(one_way_line_model_state_t *s, float v)
247 {
248 float sum;
249 int j;
250 int p;
251
252 /* Add the sample in the filter buffer */
253 p = s->far_buf_ptr;
254 s->far_buf[p] = v;
255 if (++p == s->far_filter_len)
256 p = 0;
257 s->far_buf_ptr = p;
258
259 /* Apply the filter */
260 sum = 0.0f;
261 for (j = 0; j < s->far_filter_len; j++)
262 {
263 sum += s->far_filter[j]*s->far_buf[p];
264 if (++p >= s->far_filter_len)
265 p = 0;
266 }
267
268 /* Add noise */
269 sum += awgn(&s->far_noise);
270
271 return sum;
272 }
273 /*- End of function --------------------------------------------------------*/
274
one_way_line_model(one_way_line_model_state_t * s,int16_t output[],const int16_t input[],int samples)275 SPAN_DECLARE(void) one_way_line_model(one_way_line_model_state_t *s,
276 int16_t output[],
277 const int16_t input[],
278 int samples)
279 {
280 int i;
281 float in;
282 float out;
283 float out1;
284 int16_t amp[1];
285
286 /* The path being modelled is:
287 terminal
288 | < hybrid
289 |
290 | < noise and filtering
291 |
292 | < hybrid
293 CO
294 |
295 | < A-law distortion + bulk delay
296 |
297 CO
298 | < hybrid
299 |
300 | < noise and filtering
301 |
302 | < hybrid
303 terminal
304 */
305 for (i = 0; i < samples; i++)
306 {
307 in = input[i];
308
309 /* Near end analogue section */
310
311 /* Line model filters & noise */
312 out = calc_near_line_filter(s, in);
313
314 /* Long distance digital section */
315
316 amp[0] = out;
317 codec_munge(s->munge, amp, 1);
318 out = amp[0];
319 /* Introduce the bulk delay of the long distance link. */
320 out1 = s->bulk_delay_buf[s->bulk_delay_ptr];
321 s->bulk_delay_buf[s->bulk_delay_ptr] = out;
322 out = out1;
323 if (++s->bulk_delay_ptr >= s->bulk_delay)
324 s->bulk_delay_ptr = 0;
325
326 /* Far end analogue section */
327
328 /* Line model filters & noise */
329 out = calc_far_line_filter(s, out);
330
331 if (s->mains_interference)
332 {
333 tone_gen(&s->mains_tone, amp, 1);
334 out += amp[0];
335 }
336 output[i] = out + s->dc_offset;
337 }
338 }
339 /*- End of function --------------------------------------------------------*/
340
one_way_line_model_set_dc(one_way_line_model_state_t * s,float dc)341 SPAN_DECLARE(void) one_way_line_model_set_dc(one_way_line_model_state_t *s, float dc)
342 {
343 s->dc_offset = dc;
344 }
345 /*- End of function --------------------------------------------------------*/
346
one_way_line_model_set_mains_pickup(one_way_line_model_state_t * s,int f,float level)347 SPAN_DECLARE(void) one_way_line_model_set_mains_pickup(one_way_line_model_state_t *s, int f, float level)
348 {
349 tone_gen_descriptor_t mains_tone_desc;
350
351 if (f)
352 {
353 tone_gen_descriptor_init(&mains_tone_desc, f, (int) (level - 10.0f), f*3, (int) level, 1, 0, 0, 0, true);
354 tone_gen_init(&s->mains_tone, &mains_tone_desc);
355 }
356 s->mains_interference = f;
357 }
358 /*- End of function --------------------------------------------------------*/
359
both_ways_line_model(both_ways_line_model_state_t * s,int16_t output1[],const int16_t input1[],int16_t output2[],const int16_t input2[],int samples)360 SPAN_DECLARE(void) both_ways_line_model(both_ways_line_model_state_t *s,
361 int16_t output1[],
362 const int16_t input1[],
363 int16_t output2[],
364 const int16_t input2[],
365 int samples)
366 {
367 int i;
368 float in1;
369 float in2;
370 float out1;
371 float out2;
372 float tmp1;
373 float tmp2;
374 int16_t amp[1];
375
376 /* The path being modelled is:
377 terminal
378 | < hybrid echo
379 |
380 | < noise and filtering
381 |
382 | < hybrid echo
383 CO
384 |
385 | < A-law distortion + bulk delay
386 |
387 CO
388 | < hybrid echo
389 |
390 | < noise and filtering
391 |
392 | < hybrid echo
393 terminal
394 */
395 for (i = 0; i < samples; i++)
396 {
397 in1 = input1[i];
398 in2 = input2[i];
399
400 /* Near end analogue sections */
401 /* Echo from each terminal's CO hybrid */
402 tmp1 = in1 + s->fout2*s->line1.near_co_hybrid_echo;
403 tmp2 = in2 + s->fout1*s->line2.near_co_hybrid_echo;
404
405 /* Line model filters & noise */
406 s->fout1 = calc_near_line_filter(&s->line1, tmp1);
407 s->fout2 = calc_near_line_filter(&s->line2, tmp2);
408
409 /* Long distance digital section */
410
411 /* Introduce distortion due to A-law or u-law munging. */
412 amp[0] = s->fout1;
413 codec_munge(s->line1.munge, amp, 1);
414 s->fout1 = amp[0];
415
416 amp[0] = s->fout2;
417 codec_munge(s->line2.munge, amp, 1);
418 s->fout2 = amp[0];
419
420 /* Introduce the bulk delay of the long distance digital link. */
421 out1 = s->line1.bulk_delay_buf[s->line1.bulk_delay_ptr];
422 s->line1.bulk_delay_buf[s->line1.bulk_delay_ptr] = s->fout1;
423 s->fout1 = out1;
424 if (++s->line1.bulk_delay_ptr >= s->line1.bulk_delay)
425 s->line1.bulk_delay_ptr = 0;
426
427 out2 = s->line2.bulk_delay_buf[s->line2.bulk_delay_ptr];
428 s->line2.bulk_delay_buf[s->line2.bulk_delay_ptr] = s->fout2;
429 s->fout2 = out2;
430 if (++s->line2.bulk_delay_ptr >= s->line2.bulk_delay)
431 s->line2.bulk_delay_ptr = 0;
432
433 /* Far end analogue sections */
434
435 /* Echo from each terminal's own hybrid */
436 out1 += in2*s->line1.far_cpe_hybrid_echo;
437 out2 += in1*s->line2.far_cpe_hybrid_echo;
438
439 /* Line model filters & noise */
440 out1 = calc_far_line_filter(&s->line1, out1);
441 out2 = calc_far_line_filter(&s->line2, out2);
442
443 output1[i] = fsaturate(out1 + s->line1.dc_offset);
444 output2[i] = fsaturate(out2 + s->line2.dc_offset);
445 }
446 }
447 /*- End of function --------------------------------------------------------*/
448
both_ways_line_model_set_dc(both_ways_line_model_state_t * s,float dc1,float dc2)449 SPAN_DECLARE(void) both_ways_line_model_set_dc(both_ways_line_model_state_t *s, float dc1, float dc2)
450 {
451 s->line1.dc_offset = dc1;
452 s->line2.dc_offset = dc2;
453 }
454 /*- End of function --------------------------------------------------------*/
455
both_ways_line_model_set_mains_pickup(both_ways_line_model_state_t * s,int f,float level1,float level2)456 SPAN_DECLARE(void) both_ways_line_model_set_mains_pickup(both_ways_line_model_state_t *s, int f, float level1, float level2)
457 {
458 tone_gen_descriptor_t mains_tone_desc;
459
460 if (f)
461 {
462 tone_gen_descriptor_init(&mains_tone_desc, f, (int) (level1 - 10.0f), f*3, (int) level1, 1, 0, 0, 0, true);
463 tone_gen_init(&s->line1.mains_tone, &mains_tone_desc);
464 tone_gen_descriptor_init(&mains_tone_desc, f, (int) (level2 - 10.0f), f*3, (int) level2, 1, 0, 0, 0, true);
465 tone_gen_init(&s->line2.mains_tone, &mains_tone_desc);
466 }
467 s->line1.mains_interference = f;
468 s->line2.mains_interference = f;
469 }
470 /*- End of function --------------------------------------------------------*/
471
one_way_line_model_init(int model,float noise,int codec,int rbs_pattern)472 SPAN_DECLARE(one_way_line_model_state_t *) one_way_line_model_init(int model, float noise, int codec, int rbs_pattern)
473 {
474 one_way_line_model_state_t *s;
475
476 if ((s = (one_way_line_model_state_t *) malloc(sizeof(*s))) == NULL)
477 return NULL;
478 memset(s, 0, sizeof(*s));
479
480 s->bulk_delay = 8;
481 s->bulk_delay_ptr = 0;
482
483 s->munge = codec_munge_init(codec, rbs_pattern);
484
485 s->near_filter = line_models[model];
486 s->near_filter_len = 129;
487
488 s->far_filter = line_models[model];
489 s->far_filter_len = 129;
490
491 /* Put half the noise in each analogue section */
492 awgn_init_dbm0(&s->near_noise, 1234567, noise - 3.02f);
493 awgn_init_dbm0(&s->far_noise, 1234567, noise - 3.02f);
494
495 s->dc_offset = 0.0f;
496 s->mains_interference = 0;
497
498 return s;
499 }
500 /*- End of function --------------------------------------------------------*/
501
one_way_line_model_free(one_way_line_model_state_t * s)502 SPAN_DECLARE(int) one_way_line_model_free(one_way_line_model_state_t *s)
503 {
504 codec_munge_free(s->munge);
505 free(s);
506 return 0;
507 }
508 /*- End of function --------------------------------------------------------*/
509
both_ways_line_model_init(int model1,float noise1,float echo_level_cpe1,float echo_level_co1,int model2,float noise2,float echo_level_cpe2,float echo_level_co2,int codec,int rbs_pattern)510 SPAN_DECLARE(both_ways_line_model_state_t *) both_ways_line_model_init(int model1,
511 float noise1,
512 float echo_level_cpe1,
513 float echo_level_co1,
514 int model2,
515 float noise2,
516 float echo_level_cpe2,
517 float echo_level_co2,
518 int codec,
519 int rbs_pattern)
520 {
521 both_ways_line_model_state_t *s;
522
523 if ((s = (both_ways_line_model_state_t *) malloc(sizeof(*s))) == NULL)
524 return NULL;
525 memset(s, 0, sizeof(*s));
526
527 s->line1.munge = codec_munge_init(codec, rbs_pattern);
528 s->line2.munge = codec_munge_init(codec, rbs_pattern);
529
530 s->line1.bulk_delay = 8;
531 s->line2.bulk_delay = 8;
532
533 s->line1.bulk_delay_ptr = 0;
534 s->line2.bulk_delay_ptr = 0;
535
536 s->line1.near_filter = line_models[model1];
537 s->line1.near_filter_len = 129;
538 s->line2.near_filter = line_models[model2];
539 s->line2.near_filter_len = 129;
540
541 s->line1.far_filter = line_models[model1];
542 s->line1.far_filter_len = 129;
543 s->line2.far_filter = line_models[model2];
544 s->line2.far_filter_len = 129;
545
546 /* Put half the noise in each analogue section */
547 awgn_init_dbm0(&s->line1.near_noise, 1234567, noise1 - 3.02f);
548 awgn_init_dbm0(&s->line2.near_noise, 7654321, noise2 - 3.02f);
549
550 awgn_init_dbm0(&s->line1.far_noise, 1234567, noise1 - 3.02f);
551 awgn_init_dbm0(&s->line2.far_noise, 7654321, noise2 - 3.02f);
552
553 s->line1.dc_offset = 0.0f;
554 s->line2.dc_offset = 0.0f;
555 s->line1.mains_interference = 0;
556 s->line2.mains_interference = 0;
557
558 /* Echos */
559 s->line1.near_co_hybrid_echo = pow(10, echo_level_co1/20.0f);
560 s->line2.near_co_hybrid_echo = pow(10, echo_level_co2/20.0f);
561 s->line1.near_cpe_hybrid_echo = pow(10, echo_level_cpe1/20.0f);
562 s->line2.near_cpe_hybrid_echo = pow(10, echo_level_cpe2/20.0f);
563
564 return s;
565 }
566 /*- End of function --------------------------------------------------------*/
567
both_ways_line_model_free(both_ways_line_model_state_t * s)568 SPAN_DECLARE(int) both_ways_line_model_free(both_ways_line_model_state_t *s)
569 {
570 codec_munge_free(s->line1.munge);
571 codec_munge_free(s->line2.munge);
572 free(s);
573 return 0;
574 }
575 /*- End of function --------------------------------------------------------*/
576 /*- End of file ------------------------------------------------------------*/
577