Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/spandsp-sim/line_model.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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/spandsp-sim/line_model.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,568 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * line_model.c - Model a telephone line. + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2004 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: line_model.c,v 1.14 2009/09/23 16:02:59 steveu Exp $ + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <string.h> +#include <time.h> +#include <stdio.h> +#include <fcntl.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#define GEN_CONST +#include <math.h> +#endif +#include "floating_fudge.h" + +#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES +#include "spandsp.h" +#include "spandsp-sim.h" +#include "spandsp/g168models.h" + +#if !defined(NULL) +#define NULL (void *) 0 +#endif + +static const float null_line_model[] = +{ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 +}; + +SPAN_DECLARE_DATA const float *line_models[] = +{ + null_line_model, /* 0 */ + proakis_line_model, + ad_1_edd_1_model, + ad_1_edd_2_model, + ad_1_edd_3_model, + ad_5_edd_1_model, /* 5 */ + ad_5_edd_2_model, + ad_5_edd_3_model, + ad_6_edd_1_model, + ad_6_edd_2_model, + ad_6_edd_3_model, /* 10 */ + ad_7_edd_1_model, + ad_7_edd_2_model, + ad_7_edd_3_model, + ad_8_edd_1_model, + ad_8_edd_2_model, /* 15 */ + ad_8_edd_3_model, + ad_9_edd_1_model, + ad_9_edd_2_model, + ad_9_edd_3_model +}; + +static float calc_near_line_filter(one_way_line_model_state_t *s, float v) +{ + float sum; + int j; + int p; + + /* Add the sample in the filter buffer */ + p = s->near_buf_ptr; + s->near_buf[p] = v; + if (++p == s->near_filter_len) + p = 0; + s->near_buf_ptr = p; + + /* Apply the filter */ + sum = 0.0f; + for (j = 0; j < s->near_filter_len; j++) + { + sum += s->near_filter[j]*s->near_buf[p]; + if (++p >= s->near_filter_len) + p = 0; + } + + /* Add noise */ + sum += awgn(&s->near_noise); + + return sum; +} +/*- End of function --------------------------------------------------------*/ + +static float calc_far_line_filter(one_way_line_model_state_t *s, float v) +{ + float sum; + int j; + int p; + + /* Add the sample in the filter buffer */ + p = s->far_buf_ptr; + s->far_buf[p] = v; + if (++p == s->far_filter_len) + p = 0; + s->far_buf_ptr = p; + + /* Apply the filter */ + sum = 0.0f; + for (j = 0; j < s->far_filter_len; j++) + { + sum += s->far_filter[j]*s->far_buf[p]; + if (++p >= s->far_filter_len) + p = 0; + } + + /* Add noise */ + sum += awgn(&s->far_noise); + + return sum; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) one_way_line_model(one_way_line_model_state_t *s, + int16_t output[], + const int16_t input[], + int samples) +{ + int i; + float in; + float out; + float out1; + int16_t amp[1]; + + /* The path being modelled is: + terminal + | < hybrid + | + | < noise and filtering + | + | < hybrid + CO + | + | < A-law distortion + bulk delay + | + CO + | < hybrid + | + | < noise and filtering + | + | < hybrid + terminal + */ + for (i = 0; i < samples; i++) + { + in = input[i]; + + /* Near end analogue section */ + + /* Line model filters & noise */ + out = calc_near_line_filter(s, in); + + /* Long distance digital section */ + + amp[0] = out; + codec_munge(s->munge, amp, 1); + out = amp[0]; + /* Introduce the bulk delay of the long distance link. */ + out1 = s->bulk_delay_buf[s->bulk_delay_ptr]; + s->bulk_delay_buf[s->bulk_delay_ptr] = out; + out = out1; + if (++s->bulk_delay_ptr >= s->bulk_delay) + s->bulk_delay_ptr = 0; + + /* Far end analogue section */ + + /* Line model filters & noise */ + out = calc_far_line_filter(s, out); + + if (s->mains_interference) + { + tone_gen(&s->mains_tone, amp, 1); + out += amp[0]; + } + output[i] = out + s->dc_offset; + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) one_way_line_model_set_dc(one_way_line_model_state_t *s, float dc) +{ + s->dc_offset = dc; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) one_way_line_model_set_mains_pickup(one_way_line_model_state_t *s, int f, float level) +{ + tone_gen_descriptor_t mains_tone_desc; + + if (f) + { + make_tone_gen_descriptor(&mains_tone_desc, f, (int) (level - 10.0f), f*3, (int) level, 1, 0, 0, 0, TRUE); + tone_gen_init(&s->mains_tone, &mains_tone_desc); + } + s->mains_interference = f; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) 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) +{ + int i; + float in1; + float in2; + float out1; + float out2; + float tmp1; + float tmp2; + int16_t amp[1]; + + /* The path being modelled is: + terminal + | < hybrid echo + | + | < noise and filtering + | + | < hybrid echo + CO + | + | < A-law distortion + bulk delay + | + CO + | < hybrid echo + | + | < noise and filtering + | + | < hybrid echo + terminal + */ + for (i = 0; i < samples; i++) + { + in1 = input1[i]; + in2 = input2[i]; + + /* Near end analogue sections */ + /* Echo from each terminal's CO hybrid */ + tmp1 = in1 + s->fout2*s->line1.near_co_hybrid_echo; + tmp2 = in2 + s->fout1*s->line2.near_co_hybrid_echo; + + /* Line model filters & noise */ + s->fout1 = calc_near_line_filter(&s->line1, tmp1); + s->fout2 = calc_near_line_filter(&s->line2, tmp2); + + /* Long distance digital section */ + + /* Introduce distortion due to A-law or u-law munging. */ + amp[0] = s->fout1; + codec_munge(s->line1.munge, amp, 1); + s->fout1 = amp[0]; + + amp[0] = s->fout2; + codec_munge(s->line2.munge, amp, 1); + s->fout2 = amp[0]; + + /* Introduce the bulk delay of the long distance digital link. */ + out1 = s->line1.bulk_delay_buf[s->line1.bulk_delay_ptr]; + s->line1.bulk_delay_buf[s->line1.bulk_delay_ptr] = s->fout1; + s->fout1 = out1; + if (++s->line1.bulk_delay_ptr >= s->line1.bulk_delay) + s->line1.bulk_delay_ptr = 0; + + out2 = s->line2.bulk_delay_buf[s->line2.bulk_delay_ptr]; + s->line2.bulk_delay_buf[s->line2.bulk_delay_ptr] = s->fout2; + s->fout2 = out2; + if (++s->line2.bulk_delay_ptr >= s->line2.bulk_delay) + s->line2.bulk_delay_ptr = 0; + + /* Far end analogue sections */ + + /* Echo from each terminal's own hybrid */ + out1 += in2*s->line1.far_cpe_hybrid_echo; + out2 += in1*s->line2.far_cpe_hybrid_echo; + + /* Line model filters & noise */ + out1 = calc_far_line_filter(&s->line1, out1); + out2 = calc_far_line_filter(&s->line2, out2); + + output1[i] = fsaturate(out1 + s->line1.dc_offset); + output2[i] = fsaturate(out2 + s->line2.dc_offset); + } +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) both_ways_line_model_set_dc(both_ways_line_model_state_t *s, float dc1, float dc2) +{ + s->line1.dc_offset = dc1; + s->line2.dc_offset = dc2; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) both_ways_line_model_set_mains_pickup(both_ways_line_model_state_t *s, int f, float level1, float level2) +{ + tone_gen_descriptor_t mains_tone_desc; + + if (f) + { + make_tone_gen_descriptor(&mains_tone_desc, f, (int) (level1 - 10.0f), f*3, (int) level1, 1, 0, 0, 0, TRUE); + tone_gen_init(&s->line1.mains_tone, &mains_tone_desc); + make_tone_gen_descriptor(&mains_tone_desc, f, (int) (level2 - 10.0f), f*3, (int) level2, 1, 0, 0, 0, TRUE); + tone_gen_init(&s->line2.mains_tone, &mains_tone_desc); + } + s->line1.mains_interference = f; + s->line2.mains_interference = f; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(one_way_line_model_state_t *) one_way_line_model_init(int model, float noise, int codec, int rbs_pattern) +{ + one_way_line_model_state_t *s; + + if ((s = (one_way_line_model_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + memset(s, 0, sizeof(*s)); + + s->bulk_delay = 8; + s->bulk_delay_ptr = 0; + + s->munge = codec_munge_init(codec, rbs_pattern); + + s->near_filter = line_models[model]; + s->near_filter_len = 129; + + s->far_filter = line_models[model]; + s->far_filter_len = 129; + + /* Put half the noise in each analogue section */ + awgn_init_dbm0(&s->near_noise, 1234567, noise - 3.02f); + awgn_init_dbm0(&s->far_noise, 1234567, noise - 3.02f); + + s->dc_offset = 0.0f; + s->mains_interference = 0; + + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) one_way_line_model_release(one_way_line_model_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(both_ways_line_model_state_t *) both_ways_line_model_init(int model1, + float noise1, + int model2, + float noise2, + int codec, + int rbs_pattern) +{ + float echo_level; + both_ways_line_model_state_t *s; + + if ((s = (both_ways_line_model_state_t *) malloc(sizeof(*s))) == NULL) + return NULL; + memset(s, 0, sizeof(*s)); + + s->line1.munge = codec_munge_init(codec, rbs_pattern); + s->line2.munge = codec_munge_init(codec, rbs_pattern); + + s->line1.bulk_delay = 8; + s->line2.bulk_delay = 8; + + s->line1.bulk_delay_ptr = 0; + s->line2.bulk_delay_ptr = 0; + + s->line1.near_filter = line_models[model1]; + s->line1.near_filter_len = 129; + s->line2.near_filter = line_models[model2]; + s->line2.near_filter_len = 129; + + s->line1.far_filter = line_models[model1]; + s->line1.far_filter_len = 129; + s->line2.far_filter = line_models[model2]; + s->line2.far_filter_len = 129; + + /* Put half the noise in each analogue section */ + awgn_init_dbm0(&s->line1.near_noise, 1234567, noise1 - 3.02f); + awgn_init_dbm0(&s->line2.near_noise, 7654321, noise2 - 3.02f); + + awgn_init_dbm0(&s->line1.far_noise, 1234567, noise1 - 3.02f); + awgn_init_dbm0(&s->line2.far_noise, 7654321, noise2 - 3.02f); + + s->line1.dc_offset = 0.0f; + s->line2.dc_offset = 0.0f; + s->line1.mains_interference = 0; + s->line2.mains_interference = 0; + + /* Echos */ + echo_level = -15; /* in dB */ + s->line1.near_co_hybrid_echo = pow(10, echo_level/20.0f); + s->line2.near_co_hybrid_echo = pow(10, echo_level/20.0f); + s->line1.near_cpe_hybrid_echo = pow(10, echo_level/20.0f); + s->line2.near_cpe_hybrid_echo = pow(10, echo_level/20.0f); + + return s; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) both_ways_line_model_release(both_ways_line_model_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/