Suggested
This comes with a lot of caveats. Since you don't have test usage, I haven't been able to test it, so I don't know whether it's valid. You're going to want to develop a numerical test suite to ensure that it's calculating the right thing. I also assumed that there's no need to hard-code for 3rd- or 6th-order filters, so just added an n
. Finally: I don't have micropython, so this is written naively, assuming that standard Python usage is valid.
from array import array
from math import pi, acos
from typing import Sequence, Iterable
class EWMA:
def __init__(self, coeff: Sequence[float], initial_value: float, n: int = None):
nc = len(coeff)
if n:
self.n = n
if nc > n:
raise ValueError(f'len(coeff)={nc} > n={n}')
else:
self.n = nc # default to the length of the coefficients
# ewma states for coefficient optimization
self.last_ewma = array('f', (0 for _ in range(self.n)))
# default coefficients to 1.0 so the order can be from 0 - n
# since cascade elements will pass input signal to output with a=1
self.coeff = array('f', (1 for _ in range(self.n)))
self.coeff[:nc] = coeff
self.states = array('f', (0 for _ in range(1 + self.n)))
def preload(self, value: float):
self.states[:] = value
@staticmethod
def ewma(alpha: float, this: float, last: float) -> float:
"""
calculate single EWMA element
:param alpha: filter coefficient
:param this: current input sample
:param last: last output sample from this stage (feedback)
:return: EWMA result
"""
return alpha*this + (1 - alpha)*last
def calculate(self, input_value: float) -> float:
"""
calculate nth order cascade ewma
:param input_value: Raw input sample
:return: output of nth cascade element
"""
self.states[0] = input_value
for i, (c, s) in enumerate(zip(self.coeff, self.states[:-1])):
self.states[i + 1] = self.ewma(c, s, self.states[i + 1])
return self.get_last_output()
def get_last_output(self) -> float:
return self.states[-1]
def model_ewma_preload(self, v: float):
self.last_ewma[:] = v
def model_ewma(self, y0: float, coeffs: Sequence[float]) -> float:
"""
ewma nth order for IIR Model Fitting via SciPy Optimize
:param y0: The input value
:param coeffs: Sequence of coefficients
:return: IIR output
"""
prev = y0
for i, (c, e) in enumerate(zip(coeffs, self.last_ewma)):
new = self.ewma(c, prev, e)
self.last_ewma[i] = new
prev = new
return prev
def get_cutoff(self, fs: float = 1) -> array:
return array(
'f',
(
fs*pi/2 * acos(1 - c**2/2/(1 - c))
for c in self.coeff
)
)
def apply_to_data(self, data: Iterable[float]) -> array:
return array('f', (self.calculate(d) for d in data))