forked from Mirrors/freeswitch
fc7a74905b
test suite is now mostly OK with valgrind.
577 lines
15 KiB
C
577 lines
15 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
#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
|
|
#if defined(HAVE_STDBOOL_H)
|
|
#include <stdbool.h>
|
|
#else
|
|
#include "spandsp/stdbool.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)
|
|
{
|
|
tone_gen_descriptor_init(&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)
|
|
{
|
|
tone_gen_descriptor_init(&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);
|
|
tone_gen_descriptor_init(&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_free(one_way_line_model_state_t *s)
|
|
{
|
|
codec_munge_free(s->munge);
|
|
free(s);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(both_ways_line_model_state_t *) 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)
|
|
{
|
|
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 */
|
|
s->line1.near_co_hybrid_echo = pow(10, echo_level_co1/20.0f);
|
|
s->line2.near_co_hybrid_echo = pow(10, echo_level_co2/20.0f);
|
|
s->line1.near_cpe_hybrid_echo = pow(10, echo_level_cpe1/20.0f);
|
|
s->line2.near_cpe_hybrid_echo = pow(10, echo_level_cpe2/20.0f);
|
|
|
|
return s;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) both_ways_line_model_free(both_ways_line_model_state_t *s)
|
|
{
|
|
codec_munge_free(s->line1.munge);
|
|
codec_munge_free(s->line2.munge);
|
|
free(s);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
/*- End of file ------------------------------------------------------------*/
|