Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/modem_echo.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/src/modem_echo.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,178 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * modem_echo.c - An echo cancellor, suitable for electrical echos in GSTN modems + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2001, 2003, 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 Lesser General Public License version 2.1, + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: modem_echo.c,v 1.26 2009/09/22 13:11:04 steveu Exp $ + */ + +/*! \file */ + +/* The FIR taps must be adapted as 32 bit values, to get the necessary finesse + in the adaption process. However, they are applied as 16 bit values (bits 30-15 + of the 32 bit values) in the FIR. For the working 16 bit values, we need 4 sets. + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#if defined(HAVE_TGMATH_H) +#include <tgmath.h> +#endif +#if defined(HAVE_MATH_H) +#include <math.h> +#endif +#include "floating_fudge.h" + +#include "spandsp/telephony.h" +#include "spandsp/bit_operations.h" +#include "spandsp/dc_restore.h" +#include "spandsp/modem_echo.h" + +#include "spandsp/private/modem_echo.h" + +SPAN_DECLARE(modem_echo_can_state_t *) modem_echo_can_create(int len) +{ + modem_echo_can_state_t *ec; + + if ((ec = (modem_echo_can_state_t *) malloc(sizeof(*ec))) == NULL) + return NULL; + memset(ec, 0, sizeof(*ec)); + ec->taps = len; + ec->curr_pos = ec->taps - 1; + if ((ec->fir_taps32 = (int32_t *) malloc(ec->taps*sizeof(int32_t))) == NULL) + { + free(ec); + return NULL; + } + memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t)); + if ((ec->fir_taps16 = (int16_t *) malloc(ec->taps*sizeof(int16_t))) == NULL) + { + free(ec->fir_taps32); + free(ec); + return NULL; + } + memset(ec->fir_taps16, 0, ec->taps*sizeof(int16_t)); + if (fir16_create(&ec->fir_state, ec->fir_taps16, ec->taps) == NULL) + { + free(ec->fir_taps16); + free(ec->fir_taps32); + free(ec); + return NULL; + } + return ec; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) modem_echo_can_free(modem_echo_can_state_t *ec) +{ + fir16_free(&ec->fir_state); + free(ec->fir_taps32); + free(ec->fir_taps16); + free(ec); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) modem_echo_can_flush(modem_echo_can_state_t *ec) +{ + ec->tx_power = 0; + + fir16_flush(&ec->fir_state); + ec->fir_state.curr_pos = ec->taps - 1; + memset(ec->fir_taps32, 0, ec->taps*sizeof(int32_t)); + memset(ec->fir_taps16, 0, ec->taps*sizeof(int16_t)); + ec->curr_pos = ec->taps - 1; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) modem_echo_can_adaption_mode(modem_echo_can_state_t *ec, int adapt) +{ + ec->adapt = adapt; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int16_t) modem_echo_can_update(modem_echo_can_state_t *ec, int16_t tx, int16_t rx) +{ + int32_t echo_value; + int clean_rx; + int shift; + int i; + int offset1; + int offset2; + + /* Evaluate the echo - i.e. apply the FIR filter */ + /* Assume the gain of the FIR does not exceed unity. Exceeding unity + would seem like a rather poor thing for an echo cancellor to do :) + This means we can compute the result with a total disregard for + overflows. 16bits x 16bits -> 31bits, so no overflow can occur in + any multiply. While accumulating we may overflow and underflow the + 32 bit scale often. However, if the gain does not exceed unity, + everything should work itself out, and the final result will be + OK, without any saturation logic. */ + /* Overflow is very much possible here, and we do nothing about it because + of the compute costs */ + echo_value = fir16(&ec->fir_state, tx); + + /* And the answer is..... */ + clean_rx = rx - echo_value; + //printf("%8d %8d %8d %8d\n", tx, rx, echo_value, clean_rx); + if (ec->adapt) + { + /* Calculate short term power levels using very simple single pole IIRs */ + /* TODO: Is the nasty modulus approach the fastest, or would a real + tx*tx power calculation actually be faster? Using the squares + makes the numbers grow a lot! */ + ec->tx_power += ((tx*tx - ec->tx_power) >> 5); + + shift = 1; + /* Update the FIR taps */ + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + for (i = ec->taps - 1; i >= offset1; i--) + { + /* Leak to avoid the coefficients drifting beyond the ability of the + adaption process to bring them back under control. */ + ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 23); + ec->fir_taps32[i] += (ec->fir_state.history[i - offset1]*clean_rx) >> shift; + ec->fir_taps16[i] = (int16_t) (ec->fir_taps32[i] >> 15); + } + for ( ; i >= 0; i--) + { + ec->fir_taps32[i] -= (ec->fir_taps32[i] >> 23); + ec->fir_taps32[i] += (ec->fir_state.history[i + offset2]*clean_rx) >> shift; + ec->fir_taps16[i] = (int16_t) (ec->fir_taps32[i] >> 15); + } + } + + /* Roll around the rolling buffer */ + if (ec->curr_pos <= 0) + ec->curr_pos = ec->taps; + ec->curr_pos--; + return (int16_t) clean_rx; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/