18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SpanDSP - a series of DSP components for telephony
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * echo.c - A line echo canceller.  This code is being developed
68c2ecf20Sopenharmony_ci *          against and partially complies with G168.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Written by Steve Underwood <steveu@coppice.org>
98c2ecf20Sopenharmony_ci *         and David Rowe <david_at_rowetel_dot_com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (C) 2001 Steve Underwood and 2007 David Rowe
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * All rights reserved.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#ifndef __ECHO_H
178c2ecf20Sopenharmony_ci#define __ECHO_H
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ciLine echo cancellation for voice
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciWhat does it do?
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciThis module aims to provide G.168-2002 compliant echo cancellation, to remove
258c2ecf20Sopenharmony_cielectrical echoes (e.g. from 2-4 wire hybrids) from voice calls.
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciHow does it work?
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciThe heart of the echo cancellor is FIR filter. This is adapted to match the
308c2ecf20Sopenharmony_ciecho impulse response of the telephone line. It must be long enough to
318c2ecf20Sopenharmony_ciadequately cover the duration of that impulse response. The signal transmitted
328c2ecf20Sopenharmony_cito the telephone line is passed through the FIR filter. Once the FIR is
338c2ecf20Sopenharmony_ciproperly adapted, the resulting output is an estimate of the echo signal
348c2ecf20Sopenharmony_cireceived from the line. This is subtracted from the received signal. The result
358c2ecf20Sopenharmony_ciis an estimate of the signal which originated at the far end of the line, free
368c2ecf20Sopenharmony_cifrom echos of our own transmitted signal.
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciThe least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and
398c2ecf20Sopenharmony_ciwas introduced in 1960. It is the commonest form of filter adaption used in
408c2ecf20Sopenharmony_cithings like modem line equalisers and line echo cancellers. There it works very
418c2ecf20Sopenharmony_ciwell.  However, it only works well for signals of constant amplitude. It works
428c2ecf20Sopenharmony_civery poorly for things like speech echo cancellation, where the signal level
438c2ecf20Sopenharmony_civaries widely.  This is quite easy to fix. If the signal level is normalised -
448c2ecf20Sopenharmony_cisimilar to applying AGC - LMS can work as well for a signal of varying
458c2ecf20Sopenharmony_ciamplitude as it does for a modem signal. This normalised least mean squares
468c2ecf20Sopenharmony_ci(NLMS) algorithm is the commonest one used for speech echo cancellation. Many
478c2ecf20Sopenharmony_ciother algorithms exist - e.g. RLS (essentially the same as Kalman filtering),
488c2ecf20Sopenharmony_ciFAP, etc. Some perform significantly better than NLMS.  However, factors such
498c2ecf20Sopenharmony_cias computational complexity and patents favour the use of NLMS.
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciA simple refinement to NLMS can improve its performance with speech. NLMS tends
528c2ecf20Sopenharmony_cito adapt best to the strongest parts of a signal. If the signal is white noise,
538c2ecf20Sopenharmony_cithe NLMS algorithm works very well. However, speech has more low frequency than
548c2ecf20Sopenharmony_cihigh frequency content. Pre-whitening (i.e. filtering the signal to flatten its
558c2ecf20Sopenharmony_cispectrum) the echo signal improves the adapt rate for speech, and ensures the
568c2ecf20Sopenharmony_cifinal residual signal is not heavily biased towards high frequencies. A very
578c2ecf20Sopenharmony_cilow complexity filter is adequate for this, so pre-whitening adds little to the
588c2ecf20Sopenharmony_cicompute requirements of the echo canceller.
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciAn FIR filter adapted using pre-whitened NLMS performs well, provided certain
618c2ecf20Sopenharmony_ciconditions are met:
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci    - The transmitted signal has poor self-correlation.
648c2ecf20Sopenharmony_ci    - There is no signal being generated within the environment being
658c2ecf20Sopenharmony_ci      cancelled.
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ciThe difficulty is that neither of these can be guaranteed.
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ciIf the adaption is performed while transmitting noise (or something fairly
708c2ecf20Sopenharmony_cinoise like, such as voice) the adaption works very well. If the adaption is
718c2ecf20Sopenharmony_ciperformed while transmitting something highly correlative (typically narrow
728c2ecf20Sopenharmony_ciband energy such as signalling tones or DTMF), the adaption can go seriously
738c2ecf20Sopenharmony_ciwrong. The reason is there is only one solution for the adaption on a near
748c2ecf20Sopenharmony_cirandom signal - the impulse response of the line. For a repetitive signal,
758c2ecf20Sopenharmony_cithere are any number of solutions which converge the adaption, and nothing
768c2ecf20Sopenharmony_ciguides the adaption to choose the generalised one. Allowing an untrained
778c2ecf20Sopenharmony_cicanceller to converge on this kind of narrowband energy probably a good thing,
788c2ecf20Sopenharmony_cisince at least it cancels the tones. Allowing a well converged canceller to
798c2ecf20Sopenharmony_cicontinue converging on such energy is just a way to ruin its generalised
808c2ecf20Sopenharmony_ciadaption. A narrowband detector is needed, so adapation can be suspended at
818c2ecf20Sopenharmony_ciappropriate times.
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ciThe adaption process is based on trying to eliminate the received signal. When
848c2ecf20Sopenharmony_cithere is any signal from within the environment being cancelled it may upset
858c2ecf20Sopenharmony_cithe adaption process. Similarly, if the signal we are transmitting is small,
868c2ecf20Sopenharmony_cinoise may dominate and disturb the adaption process. If we can ensure that the
878c2ecf20Sopenharmony_ciadaption is only performed when we are transmitting a significant signal level,
888c2ecf20Sopenharmony_ciand the environment is not, things will be OK. Clearly, it is easy to tell when
898c2ecf20Sopenharmony_ciwe are sending a significant signal. Telling, if the environment is generating
908c2ecf20Sopenharmony_cia significant signal, and doing it with sufficient speed that the adaption will
918c2ecf20Sopenharmony_cinot have diverged too much more we stop it, is a little harder.
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ciThe key problem in detecting when the environment is sourcing significant
948c2ecf20Sopenharmony_cienergy is that we must do this very quickly. Given a reasonably long sample of
958c2ecf20Sopenharmony_cithe received signal, there are a number of strategies which may be used to
968c2ecf20Sopenharmony_ciassess whether that signal contains a strong far end component. However, by the
978c2ecf20Sopenharmony_citime that assessment is complete the far end signal will have already caused
988c2ecf20Sopenharmony_cimajor mis-convergence in the adaption process. An assessment algorithm is
998c2ecf20Sopenharmony_cineeded which produces a fairly accurate result from a very short burst of far
1008c2ecf20Sopenharmony_ciend energy.
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciHow do I use it?
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciThe echo cancellor processes both the transmit and receive streams sample by
1058c2ecf20Sopenharmony_cisample. The processing function is not declared inline. Unfortunately,
1068c2ecf20Sopenharmony_cicancellation requires many operations per sample, so the call overhead is only
1078c2ecf20Sopenharmony_cia minor burden.
1088c2ecf20Sopenharmony_ci*/
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#include "fir.h"
1118c2ecf20Sopenharmony_ci#include "oslec.h"
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/*
1148c2ecf20Sopenharmony_ci    G.168 echo canceller descriptor. This defines the working state for a line
1158c2ecf20Sopenharmony_ci    echo canceller.
1168c2ecf20Sopenharmony_ci*/
1178c2ecf20Sopenharmony_cistruct oslec_state {
1188c2ecf20Sopenharmony_ci	int16_t tx;
1198c2ecf20Sopenharmony_ci	int16_t rx;
1208c2ecf20Sopenharmony_ci	int16_t clean;
1218c2ecf20Sopenharmony_ci	int16_t clean_nlp;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	int nonupdate_dwell;
1248c2ecf20Sopenharmony_ci	int curr_pos;
1258c2ecf20Sopenharmony_ci	int taps;
1268c2ecf20Sopenharmony_ci	int log2taps;
1278c2ecf20Sopenharmony_ci	int adaption_mode;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	int cond_met;
1308c2ecf20Sopenharmony_ci	int32_t pstates;
1318c2ecf20Sopenharmony_ci	int16_t adapt;
1328c2ecf20Sopenharmony_ci	int32_t factor;
1338c2ecf20Sopenharmony_ci	int16_t shift;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/* Average levels and averaging filter states */
1368c2ecf20Sopenharmony_ci	int ltxacc;
1378c2ecf20Sopenharmony_ci	int lrxacc;
1388c2ecf20Sopenharmony_ci	int lcleanacc;
1398c2ecf20Sopenharmony_ci	int lclean_bgacc;
1408c2ecf20Sopenharmony_ci	int ltx;
1418c2ecf20Sopenharmony_ci	int lrx;
1428c2ecf20Sopenharmony_ci	int lclean;
1438c2ecf20Sopenharmony_ci	int lclean_bg;
1448c2ecf20Sopenharmony_ci	int lbgn;
1458c2ecf20Sopenharmony_ci	int lbgn_acc;
1468c2ecf20Sopenharmony_ci	int lbgn_upper;
1478c2ecf20Sopenharmony_ci	int lbgn_upper_acc;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* foreground and background filter states */
1508c2ecf20Sopenharmony_ci	struct fir16_state_t fir_state;
1518c2ecf20Sopenharmony_ci	struct fir16_state_t fir_state_bg;
1528c2ecf20Sopenharmony_ci	int16_t *fir_taps16[2];
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* DC blocking filter states */
1558c2ecf20Sopenharmony_ci	int tx_1;
1568c2ecf20Sopenharmony_ci	int tx_2;
1578c2ecf20Sopenharmony_ci	int rx_1;
1588c2ecf20Sopenharmony_ci	int rx_2;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* optional High Pass Filter states */
1618c2ecf20Sopenharmony_ci	int32_t xvtx[5];
1628c2ecf20Sopenharmony_ci	int32_t yvtx[5];
1638c2ecf20Sopenharmony_ci	int32_t xvrx[5];
1648c2ecf20Sopenharmony_ci	int32_t yvrx[5];
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Parameters for the optional Hoth noise generator */
1678c2ecf20Sopenharmony_ci	int cng_level;
1688c2ecf20Sopenharmony_ci	int cng_rndnum;
1698c2ecf20Sopenharmony_ci	int cng_filter;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* snapshot sample of coeffs used for development */
1728c2ecf20Sopenharmony_ci	int16_t *snapshot;
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci#endif /* __ECHO_H */
176