|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | +""" |
| 4 | +@author: Alexandru - George Rusu (2019). All Rights Reserved. |
| 5 | +""" |
| 6 | +import numpy as np |
| 7 | + |
| 8 | + |
| 9 | +class cir_buf: |
| 10 | + """ |
| 11 | + Class that defines the concept of circular buffer. |
| 12 | + """ |
| 13 | + def __init__(self, cblen): |
| 14 | + self.cblen = cblen |
| 15 | + self.cb = np.zeros([cblen, 1]) |
| 16 | + |
| 17 | + def cb_push(self, x): |
| 18 | + """ |
| 19 | + Pushes a sample in the circula buffer. |
| 20 | + :param x: input sample. |
| 21 | + """ |
| 22 | + self.cb = np.vstack((x, self.cb[0 : self.cblen - 1])) |
| 23 | + |
| 24 | + def dot_product(self): |
| 25 | + """ |
| 26 | + Performs the scalar product of the circular buffer and its self. |
| 27 | + """ |
| 28 | + return ((self.cb).T).dot(self.cb) |
| 29 | + |
| 30 | + |
| 31 | +class fir(cir_buf): |
| 32 | + """ |
| 33 | + Class that defines the FIR filter. |
| 34 | + """ |
| 35 | + def __init__(self, ford): |
| 36 | + self.ord = ford |
| 37 | + self.coeffs = np.zeros([ford, 1]) |
| 38 | + |
| 39 | + def ffir(self, x): |
| 40 | + """ |
| 41 | + Performs the filtering of an input signal of the same length. |
| 42 | + :param x: input circular buffer. |
| 43 | + """ |
| 44 | + return ((x.cb).T).dot(self.coeffs) |
| 45 | + |
| 46 | + |
| 47 | +class lmso(fir, cir_buf): |
| 48 | + def __init__(self, ford = 512, cst = 2.165e-6, lam = 0.95, sgmv = 0, sgmw = 0, lniproc = 2, alpha = 1.0, delta = 1e-6): |
| 49 | + """ |
| 50 | + Constructor that initializes lmso object parameters. |
| 51 | + :param ford: the length L of the adaptive filter, which is the same as for the input circular buffer. |
| 52 | + :param cst: small constant that initializes the MSD. |
| 53 | + :param lam: represents the forgetting factor of the exponential window. |
| 54 | + :param sgmv: represents the variance of the measurement noise. |
| 55 | + :param sgmw: represents the variance of the system noise. |
| 56 | + :param lniproc: how long the initialization process lasts [e.g., int(lniproc) * L]. |
| 57 | + :param alpha: the fixed step-size of the NLMS algorithm used in the initialization process |
| 58 | + for a period of int(lniproc) * L samples. |
| 59 | + :param delta: the regularization parameter of the NLMS algorithm used in the initialization |
| 60 | + process for a period of int(lniproc) * L samples. |
| 61 | + |
| 62 | + """ |
| 63 | + id_vec = np.ones([ford, 1]) |
| 64 | + id_matrix = np.diagflat(id_vec) |
| 65 | + |
| 66 | + self.h = fir(ford) |
| 67 | + self.c = np.sqrt(cst) * id_vec |
| 68 | + self.ccorr_mat = cst * id_matrix |
| 69 | + self.gamma_mat = self.ccorr_mat + sgmw * id_matrix |
| 70 | + |
| 71 | + self.x = cir_buf(ford) |
| 72 | + self.xcorr_mat = np.zeros([ford, ford]) |
| 73 | + |
| 74 | + self.msd = cst |
| 75 | + self.tmp = 0 |
| 76 | + self.sx = 0 |
| 77 | + self.sv = sgmv |
| 78 | + self.sw = sgmw |
| 79 | + self.sw_idmat = sgmw * id_matrix |
| 80 | + |
| 81 | + self.ln_init_proc = lniproc * ford |
| 82 | + self.alpha = alpha |
| 83 | + self.delta = delta |
| 84 | + self.lam = lam |
| 85 | + |
| 86 | + def lmso_w(self, echo): |
| 87 | + """ |
| 88 | + This function implements the white version of the LMSO algorithm. |
| 89 | + :param echo: echo sample. |
| 90 | + """ |
| 91 | + if(self.ln_init_proc == 0): |
| 92 | + self.msd = np.trace(self.ccorr_mat) + self.h.ord * self.sw |
| 93 | + self.ln_init_proc = -1 |
| 94 | + |
| 95 | + err = echo - self.h.ffir(self.x) |
| 96 | + if(self.ln_init_proc > 0): |
| 97 | + self.sx = self.x.dot_product() |
| 98 | + sz = self.alpha / (self.sx + self.delta) |
| 99 | + u = sz * err * self.x.cb |
| 100 | + self.c += u |
| 101 | + self.ccorr_mat = self.lam * self.ccorr_mat + (1 - self.lam) * (self.c).dot((self.c).T) |
| 102 | + self.ln_init_proc -= 1 |
| 103 | + else: |
| 104 | + self.sx = self.x.dot_product() / self.h.ord |
| 105 | + sz = self.msd / (self.msd * self.sx * (self.h.ord + 2) + self.h.ord * self.sv) |
| 106 | + u = sz * err * self.x.cb |
| 107 | + self.tmp = self.msd * (1 - sz * self.sx) |
| 108 | + self.msd = self.tmp + self.h.ord * self.sw |
| 109 | + |
| 110 | + self.h.coeffs += u |
| 111 | + return err |
| 112 | + |
| 113 | + def lmso_g(self, echo): |
| 114 | + """ |
| 115 | + This function implements the general version of the LMSO algorithm. |
| 116 | + :param echo: echo sample. |
| 117 | + """ |
| 118 | + self.xcorr_mat = self.lam * self.xcorr_mat + (1 - self.lam) * (self.x.cb).dot((self.x.cb).T) |
| 119 | + err = echo - self.h.ffir(self.x) |
| 120 | + |
| 121 | + if(self.ln_init_proc > 0): |
| 122 | + self.sx = self.x.dot_product() |
| 123 | + sz = self.alpha / (self.sx + self.delta) |
| 124 | + u = sz * err * self.x.cb |
| 125 | + self.c += u |
| 126 | + self.ccorr_mat = self.lam * self.ccorr_mat + (1 - self.lam) * (self.c).dot((self.c).T) |
| 127 | + self.ln_init_proc -= 1 |
| 128 | + else: |
| 129 | + self.sx = self.x.dot_product() / self.h.ord |
| 130 | + gr_prod = (self.gamma_mat).dot(self.xcorr_mat) |
| 131 | + rg_prod = (self.xcorr_mat).dot(self.gamma_mat) |
| 132 | + tp = np.trace(gr_prod) |
| 133 | + sz = tp / (self.h.ord * self.sx * tp + 2 * np.trace(gr_prod.dot(self.xcorr_mat)) \ |
| 134 | + + self.h.ord * self.sx * self.sv) |
| 135 | + u = sz * err * self.x.cb |
| 136 | + self.ccorr_mat = self.gamma_mat - sz * (gr_prod + rg_prod) \ |
| 137 | + + sz**2 * (2 * rg_prod.dot(self.xcorr_mat) + self.xcorr_mat * tp) \ |
| 138 | + + sz**2 * self.sv * self.xcorr_mat |
| 139 | + |
| 140 | + self.h.coeffs += u |
| 141 | + self.gamma_mat = self.ccorr_mat + self.sw_idmat |
| 142 | + return err |
| 143 | + |
0 commit comments