0
$\begingroup$

I have implemented a modified linear prediction coding function like this:

def modified_lpc_process(s: NDArray, p: int, fs: int) -> NDArray:
a = lpc_coefficients(s, p)
a_hat = modified_pole_coeffs(a, fs=fs)
# a_hat = a # debug line
s_hat = numpy.zeros(len(s))

for n, sn in enumerate(s):
    as_accum = 0
    for k in range(len(a)):
        if n - 1 - k < 0:
            break
        as_accum += a[k] * s[n - 1 - k]
    un = sn + as_accum

    as_hat_accum = 0
    for k in range(len(a_hat)):
        if n - 1 - k < 0:
            break
        as_hat_accum += a_hat[k] * s_hat[n - 1 - k]

    s_hat[n] = -as_hat_accum + un
    round(s_hat[n], 8)

return s_hat

Which essentially implements the maths here:

\begin{align} u[n] &= s[n] + \sum_{k=1}^p a_k s[n-k]\\ \hat{s}[n] &= - \sum_{k=1}^p \hat{a}_k \hat{s}[n-k] + u[n] \end{align}

I have noticed that the floating point error in this operation quickly blows up and I end up with sample values too large. In an effort to debug, I've set the modified pole coefficients a_hat to be the same with a, expecting to get the same value for s_hat and s; but the same issue is still there. I've also just realised that in floating point, adding a number and subtracting the same number you end up with slightly different results, which in recursive applications like this can accumulate to large differences.

I wonder what the best practice is in this case to mitigate this error accumulation?

$\endgroup$
3
  • $\begingroup$ Do you know where your poles and zeros are? And may I modify the question to use more common (dare I say "standard") notation in the DSP lit? It's quite likely that people will suggest factoring your IIR filter into 2nd-order stages. Otherwise, if your floating-point wordsize is decent and your poles are not directly on (or outside) the unit circle, there shouldn't be a nasty rounding error accumulation. $\endgroup$ Commented Mar 5 at 0:04
  • $\begingroup$ Looking at this a second time, I noticed that this is about Linear Predictive Coding. It might be that your LPC filter is unstable. How long is it? len(s)? $\endgroup$ Commented Mar 5 at 1:55
  • $\begingroup$ So my filter length len(a) currently is 10, and I’ve made sure the poles are inside the unit circle by nudging the ones outside to have a magnitude of 0.95. The length of s is the current buffer so right now it is 240; but I’ve tried with smaller sizes and it still blows up. I’ll have a double check on my logic there for pole calculation $\endgroup$
    – jh0427
    Commented Mar 5 at 9:09

1 Answer 1

0
$\begingroup$

I think I fixed it. It is indeed because of having unstable poles, but I was only looking at each individual transfer function instead of the overall transfer function.

In LPC the individual transfer functions are in the form of

$$\frac{1}{1+\sum_{k=1}^{p}a_kz^{-k}}$$

However the overall transfer in my case, combining both lines of equations for $u[n]$ and $\hat{s}[n]$ becomes: $$ \frac{1+\sum_{k=1}^{p}a_kz^{-k}}{1+\sum_{k=1}^{p}\hat{a}_kz^{-k}} $$

So I needed to correct the poles for this denominator, which has a different sign for the sum term than the general LPC transfer function.

I believe this is because I am using the original signal as input, as opposed to traditional LPC which uses a generated excitation, therefore the transfer function has a different sign.

$\endgroup$
2
  • $\begingroup$ How big is $p$? $\endgroup$ Commented Apr 4 at 20:00
  • 1
    $\begingroup$ I think 10-12 is the order I chose when I did it. $\endgroup$
    – jh0427
    Commented Apr 9 at 21:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.