xref: /openbmc/linux/drivers/misc/echo/echo.h (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*82c29810SThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-only */
26e2055a9SGreg Kroah-Hartman /*
36e2055a9SGreg Kroah-Hartman  * SpanDSP - a series of DSP components for telephony
46e2055a9SGreg Kroah-Hartman  *
56e2055a9SGreg Kroah-Hartman  * echo.c - A line echo canceller.  This code is being developed
66e2055a9SGreg Kroah-Hartman  *          against and partially complies with G168.
76e2055a9SGreg Kroah-Hartman  *
86e2055a9SGreg Kroah-Hartman  * Written by Steve Underwood <steveu@coppice.org>
96e2055a9SGreg Kroah-Hartman  *         and David Rowe <david_at_rowetel_dot_com>
106e2055a9SGreg Kroah-Hartman  *
116e2055a9SGreg Kroah-Hartman  * Copyright (C) 2001 Steve Underwood and 2007 David Rowe
126e2055a9SGreg Kroah-Hartman  *
136e2055a9SGreg Kroah-Hartman  * All rights reserved.
146e2055a9SGreg Kroah-Hartman  */
156e2055a9SGreg Kroah-Hartman 
166e2055a9SGreg Kroah-Hartman #ifndef __ECHO_H
176e2055a9SGreg Kroah-Hartman #define __ECHO_H
186e2055a9SGreg Kroah-Hartman 
196e2055a9SGreg Kroah-Hartman /*
206e2055a9SGreg Kroah-Hartman Line echo cancellation for voice
216e2055a9SGreg Kroah-Hartman 
226e2055a9SGreg Kroah-Hartman What does it do?
236e2055a9SGreg Kroah-Hartman 
246e2055a9SGreg Kroah-Hartman This module aims to provide G.168-2002 compliant echo cancellation, to remove
256e2055a9SGreg Kroah-Hartman electrical echoes (e.g. from 2-4 wire hybrids) from voice calls.
266e2055a9SGreg Kroah-Hartman 
276e2055a9SGreg Kroah-Hartman How does it work?
286e2055a9SGreg Kroah-Hartman 
296e2055a9SGreg Kroah-Hartman The heart of the echo cancellor is FIR filter. This is adapted to match the
306e2055a9SGreg Kroah-Hartman echo impulse response of the telephone line. It must be long enough to
316e2055a9SGreg Kroah-Hartman adequately cover the duration of that impulse response. The signal transmitted
326e2055a9SGreg Kroah-Hartman to the telephone line is passed through the FIR filter. Once the FIR is
336e2055a9SGreg Kroah-Hartman properly adapted, the resulting output is an estimate of the echo signal
346e2055a9SGreg Kroah-Hartman received from the line. This is subtracted from the received signal. The result
356e2055a9SGreg Kroah-Hartman is an estimate of the signal which originated at the far end of the line, free
366e2055a9SGreg Kroah-Hartman from echos of our own transmitted signal.
376e2055a9SGreg Kroah-Hartman 
386e2055a9SGreg Kroah-Hartman The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and
396e2055a9SGreg Kroah-Hartman was introduced in 1960. It is the commonest form of filter adaption used in
406e2055a9SGreg Kroah-Hartman things like modem line equalisers and line echo cancellers. There it works very
416e2055a9SGreg Kroah-Hartman well.  However, it only works well for signals of constant amplitude. It works
426e2055a9SGreg Kroah-Hartman very poorly for things like speech echo cancellation, where the signal level
436e2055a9SGreg Kroah-Hartman varies widely.  This is quite easy to fix. If the signal level is normalised -
446e2055a9SGreg Kroah-Hartman similar to applying AGC - LMS can work as well for a signal of varying
456e2055a9SGreg Kroah-Hartman amplitude as it does for a modem signal. This normalised least mean squares
466e2055a9SGreg Kroah-Hartman (NLMS) algorithm is the commonest one used for speech echo cancellation. Many
476e2055a9SGreg Kroah-Hartman other algorithms exist - e.g. RLS (essentially the same as Kalman filtering),
486e2055a9SGreg Kroah-Hartman FAP, etc. Some perform significantly better than NLMS.  However, factors such
496e2055a9SGreg Kroah-Hartman as computational complexity and patents favour the use of NLMS.
506e2055a9SGreg Kroah-Hartman 
516e2055a9SGreg Kroah-Hartman A simple refinement to NLMS can improve its performance with speech. NLMS tends
526e2055a9SGreg Kroah-Hartman to adapt best to the strongest parts of a signal. If the signal is white noise,
536e2055a9SGreg Kroah-Hartman the NLMS algorithm works very well. However, speech has more low frequency than
546e2055a9SGreg Kroah-Hartman high frequency content. Pre-whitening (i.e. filtering the signal to flatten its
556e2055a9SGreg Kroah-Hartman spectrum) the echo signal improves the adapt rate for speech, and ensures the
566e2055a9SGreg Kroah-Hartman final residual signal is not heavily biased towards high frequencies. A very
576e2055a9SGreg Kroah-Hartman low complexity filter is adequate for this, so pre-whitening adds little to the
586e2055a9SGreg Kroah-Hartman compute requirements of the echo canceller.
596e2055a9SGreg Kroah-Hartman 
606e2055a9SGreg Kroah-Hartman An FIR filter adapted using pre-whitened NLMS performs well, provided certain
616e2055a9SGreg Kroah-Hartman conditions are met:
626e2055a9SGreg Kroah-Hartman 
636e2055a9SGreg Kroah-Hartman     - The transmitted signal has poor self-correlation.
646e2055a9SGreg Kroah-Hartman     - There is no signal being generated within the environment being
656e2055a9SGreg Kroah-Hartman       cancelled.
666e2055a9SGreg Kroah-Hartman 
676e2055a9SGreg Kroah-Hartman The difficulty is that neither of these can be guaranteed.
686e2055a9SGreg Kroah-Hartman 
696e2055a9SGreg Kroah-Hartman If the adaption is performed while transmitting noise (or something fairly
706e2055a9SGreg Kroah-Hartman noise like, such as voice) the adaption works very well. If the adaption is
716e2055a9SGreg Kroah-Hartman performed while transmitting something highly correlative (typically narrow
726e2055a9SGreg Kroah-Hartman band energy such as signalling tones or DTMF), the adaption can go seriously
736e2055a9SGreg Kroah-Hartman wrong. The reason is there is only one solution for the adaption on a near
746e2055a9SGreg Kroah-Hartman random signal - the impulse response of the line. For a repetitive signal,
756e2055a9SGreg Kroah-Hartman there are any number of solutions which converge the adaption, and nothing
766e2055a9SGreg Kroah-Hartman guides the adaption to choose the generalised one. Allowing an untrained
776e2055a9SGreg Kroah-Hartman canceller to converge on this kind of narrowband energy probably a good thing,
786e2055a9SGreg Kroah-Hartman since at least it cancels the tones. Allowing a well converged canceller to
796e2055a9SGreg Kroah-Hartman continue converging on such energy is just a way to ruin its generalised
806e2055a9SGreg Kroah-Hartman adaption. A narrowband detector is needed, so adapation can be suspended at
816e2055a9SGreg Kroah-Hartman appropriate times.
826e2055a9SGreg Kroah-Hartman 
836e2055a9SGreg Kroah-Hartman The adaption process is based on trying to eliminate the received signal. When
846e2055a9SGreg Kroah-Hartman there is any signal from within the environment being cancelled it may upset
856e2055a9SGreg Kroah-Hartman the adaption process. Similarly, if the signal we are transmitting is small,
866e2055a9SGreg Kroah-Hartman noise may dominate and disturb the adaption process. If we can ensure that the
876e2055a9SGreg Kroah-Hartman adaption is only performed when we are transmitting a significant signal level,
886e2055a9SGreg Kroah-Hartman and the environment is not, things will be OK. Clearly, it is easy to tell when
896e2055a9SGreg Kroah-Hartman we are sending a significant signal. Telling, if the environment is generating
906e2055a9SGreg Kroah-Hartman a significant signal, and doing it with sufficient speed that the adaption will
916e2055a9SGreg Kroah-Hartman not have diverged too much more we stop it, is a little harder.
926e2055a9SGreg Kroah-Hartman 
936e2055a9SGreg Kroah-Hartman The key problem in detecting when the environment is sourcing significant
946e2055a9SGreg Kroah-Hartman energy is that we must do this very quickly. Given a reasonably long sample of
956e2055a9SGreg Kroah-Hartman the received signal, there are a number of strategies which may be used to
966e2055a9SGreg Kroah-Hartman assess whether that signal contains a strong far end component. However, by the
976e2055a9SGreg Kroah-Hartman time that assessment is complete the far end signal will have already caused
986e2055a9SGreg Kroah-Hartman major mis-convergence in the adaption process. An assessment algorithm is
996e2055a9SGreg Kroah-Hartman needed which produces a fairly accurate result from a very short burst of far
1006e2055a9SGreg Kroah-Hartman end energy.
1016e2055a9SGreg Kroah-Hartman 
1026e2055a9SGreg Kroah-Hartman How do I use it?
1036e2055a9SGreg Kroah-Hartman 
1046e2055a9SGreg Kroah-Hartman The echo cancellor processes both the transmit and receive streams sample by
1056e2055a9SGreg Kroah-Hartman sample. The processing function is not declared inline. Unfortunately,
1066e2055a9SGreg Kroah-Hartman cancellation requires many operations per sample, so the call overhead is only
1076e2055a9SGreg Kroah-Hartman a minor burden.
1086e2055a9SGreg Kroah-Hartman */
1096e2055a9SGreg Kroah-Hartman 
1106e2055a9SGreg Kroah-Hartman #include "fir.h"
1116e2055a9SGreg Kroah-Hartman #include "oslec.h"
1126e2055a9SGreg Kroah-Hartman 
1136e2055a9SGreg Kroah-Hartman /*
1146e2055a9SGreg Kroah-Hartman     G.168 echo canceller descriptor. This defines the working state for a line
1156e2055a9SGreg Kroah-Hartman     echo canceller.
1166e2055a9SGreg Kroah-Hartman */
1176e2055a9SGreg Kroah-Hartman struct oslec_state {
1186e2055a9SGreg Kroah-Hartman 	int16_t tx;
1196e2055a9SGreg Kroah-Hartman 	int16_t rx;
1206e2055a9SGreg Kroah-Hartman 	int16_t clean;
1216e2055a9SGreg Kroah-Hartman 	int16_t clean_nlp;
1226e2055a9SGreg Kroah-Hartman 
1236e2055a9SGreg Kroah-Hartman 	int nonupdate_dwell;
1246e2055a9SGreg Kroah-Hartman 	int curr_pos;
1256e2055a9SGreg Kroah-Hartman 	int taps;
1266e2055a9SGreg Kroah-Hartman 	int log2taps;
1276e2055a9SGreg Kroah-Hartman 	int adaption_mode;
1286e2055a9SGreg Kroah-Hartman 
1296e2055a9SGreg Kroah-Hartman 	int cond_met;
1306e2055a9SGreg Kroah-Hartman 	int32_t pstates;
1316e2055a9SGreg Kroah-Hartman 	int16_t adapt;
1326e2055a9SGreg Kroah-Hartman 	int32_t factor;
1336e2055a9SGreg Kroah-Hartman 	int16_t shift;
1346e2055a9SGreg Kroah-Hartman 
1356e2055a9SGreg Kroah-Hartman 	/* Average levels and averaging filter states */
1366e2055a9SGreg Kroah-Hartman 	int ltxacc;
1376e2055a9SGreg Kroah-Hartman 	int lrxacc;
1386e2055a9SGreg Kroah-Hartman 	int lcleanacc;
1396e2055a9SGreg Kroah-Hartman 	int lclean_bgacc;
1406e2055a9SGreg Kroah-Hartman 	int ltx;
1416e2055a9SGreg Kroah-Hartman 	int lrx;
1426e2055a9SGreg Kroah-Hartman 	int lclean;
1436e2055a9SGreg Kroah-Hartman 	int lclean_bg;
1446e2055a9SGreg Kroah-Hartman 	int lbgn;
1456e2055a9SGreg Kroah-Hartman 	int lbgn_acc;
1466e2055a9SGreg Kroah-Hartman 	int lbgn_upper;
1476e2055a9SGreg Kroah-Hartman 	int lbgn_upper_acc;
1486e2055a9SGreg Kroah-Hartman 
1496e2055a9SGreg Kroah-Hartman 	/* foreground and background filter states */
1506e2055a9SGreg Kroah-Hartman 	struct fir16_state_t fir_state;
1516e2055a9SGreg Kroah-Hartman 	struct fir16_state_t fir_state_bg;
1526e2055a9SGreg Kroah-Hartman 	int16_t *fir_taps16[2];
1536e2055a9SGreg Kroah-Hartman 
1546e2055a9SGreg Kroah-Hartman 	/* DC blocking filter states */
1556e2055a9SGreg Kroah-Hartman 	int tx_1;
1566e2055a9SGreg Kroah-Hartman 	int tx_2;
1576e2055a9SGreg Kroah-Hartman 	int rx_1;
1586e2055a9SGreg Kroah-Hartman 	int rx_2;
1596e2055a9SGreg Kroah-Hartman 
1606e2055a9SGreg Kroah-Hartman 	/* optional High Pass Filter states */
1616e2055a9SGreg Kroah-Hartman 	int32_t xvtx[5];
1626e2055a9SGreg Kroah-Hartman 	int32_t yvtx[5];
1636e2055a9SGreg Kroah-Hartman 	int32_t xvrx[5];
1646e2055a9SGreg Kroah-Hartman 	int32_t yvrx[5];
1656e2055a9SGreg Kroah-Hartman 
1666e2055a9SGreg Kroah-Hartman 	/* Parameters for the optional Hoth noise generator */
1676e2055a9SGreg Kroah-Hartman 	int cng_level;
1686e2055a9SGreg Kroah-Hartman 	int cng_rndnum;
1696e2055a9SGreg Kroah-Hartman 	int cng_filter;
1706e2055a9SGreg Kroah-Hartman 
1716e2055a9SGreg Kroah-Hartman 	/* snapshot sample of coeffs used for development */
1726e2055a9SGreg Kroah-Hartman 	int16_t *snapshot;
1736e2055a9SGreg Kroah-Hartman };
1746e2055a9SGreg Kroah-Hartman 
1756e2055a9SGreg Kroah-Hartman #endif /* __ECHO_H */
176