From b4c7cf4a3365a7bdc05b1ac146f8f48b5666ce8d Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Thu, 30 Oct 2025 23:30:14 +0100 Subject: [PATCH] analyzers: define sos filter Signed-off-by: Timofey Titovets --- scripts/motan/analyzers.py | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/scripts/motan/analyzers.py b/scripts/motan/analyzers.py index 91678012f..c3476b931 100644 --- a/scripts/motan/analyzers.py +++ b/scripts/motan/analyzers.py @@ -197,6 +197,77 @@ class GenSmoothed: return data AHandlers["smooth"] = GenSmoothed +class GenSOSFilter: + ParametersMin = 5 + ParametersMax = 6 + DataSets = [ + ('sos(,{filt,filtfilt},highpass,,)', + 'SOS highpass filtered dataset'), + ('sos(,{filt,filtfilt},lowpass,,)', + 'SOS lowpass filtered dataset'), + ('sos(,{filt,filtfilt},bandpass,,,)', + 'SOS lowpass filtered dataset'), + ('sos(,{filt,filtfilt},notch,,)', + 'SOS notch filtered dataset'), + ] + def __init__(self, amanager, name_parts): + self.amanager = amanager + self.source = name_parts[1] + + from numpy import array + from scipy.signal import sosfiltfilt, sosfilt, sosfilt_zi + self.array = array + self.type = name_parts[2] + if self.type == "filt": + self.sosfilt = sosfilt + self.sosfilt_zi = sosfilt_zi + elif self.type == "filtfilt": + self.sosfilt = sosfiltfilt + else: + amanager.error("Unknown sos type '%s'" % (self.type,)) + + filter_type = name_parts[3] + amanager.setup_dataset(self.source) + inv_seg_time = 1.0 / amanager.get_segment_time() + + if filter_type in ('highpass', 'lowpass', 'bandpass'): + from scipy.signal import butter + order = int(name_parts[4]) + cutoff = float(name_parts[5]) + desc = "%.fHz" % (cutoff) + if filter_type == 'bandpass': + cutoff = (cutoff, float(name_parts[6])) + desc = "%.1f..%.1fHz" % cutoff + self.sos = butter(order, cutoff, filter_type, output='sos', + fs=inv_seg_time) + self.filter_desc = "%s %s order %d" % (filter_type, desc, order) + elif filter_type == 'notch': + from scipy.signal import iirnotch, tf2sos + freq = float(name_parts[4]) + quality = float(name_parts[5]) + b, a = iirnotch(freq, Q=quality, fs=inv_seg_time) + self.sos = tf2sos(b, a) + self.filter_desc = "notch %.1fHz Q: %.1f" % (freq, quality) + else: + raise amanager.error("Unknown filter type '%s'" % (filter_type,)) + + def get_label(self): + label = self.amanager.get_label(self.source) + lname = "SOS %s (%s)" % (self.filter_desc, label['label']) + return {'label': lname, 'units': label['units']} + + def generate_data(self): + data = self.amanager.get_datasets()[self.source] + data_array = self.array(data) + if self.type == "filt": + zi = self.sosfilt_zi(self.sos) * data_array[0] + filtered, _ = self.sosfilt(self.sos, data_array, zi=zi) + else: + filtered = self.sosfilt(self.sos, data_array) + return filtered.tolist() + +AHandlers["sos"] = GenSOSFilter + # Calculate a kinematic stepper position from the toolhead requested position class GenKinematicPosition: ParametersMin = ParametersMax = 1