From 22dbdf102983ae6bef685d93fb60613f9e7aec4e Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sat, 4 Apr 2026 20:32:11 -0400 Subject: [PATCH] trigger_analog: Store pre-generated 25hz lowpass filter (at 250sps) Store the 12 constants needed for the common probe_eddy_current "tap" filter in trigger_analog.py code. This avoids requiring "tap" users to have the scipy python package installed. Signed-off-by: Kevin O'Connor --- docs/Eddy_Probe.md | 6 ------ klippy/extras/trigger_analog.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/Eddy_Probe.md b/docs/Eddy_Probe.md index 14bc1e7e8..d7753c5b0 100644 --- a/docs/Eddy_Probe.md +++ b/docs/Eddy_Probe.md @@ -116,12 +116,6 @@ Practically, it ensures that the Eddy's output data absolute value change per second (velocity) is high enough - higher than the noise level, and that upon collision it always decreases by at least this value. -Before setting it to any other value, it is necessary to install `scipy`: - -```bash -~/klippy-env/bin/pip install scipy -``` - The suggested calibration routine works as follows: 1. Home Z 2. Place the toolhead at the center of the bed. diff --git a/klippy/extras/trigger_analog.py b/klippy/extras/trigger_analog.py index 27f4133ad..1c1ea3412 100644 --- a/klippy/extras/trigger_analog.py +++ b/klippy/extras/trigger_analog.py @@ -25,6 +25,33 @@ def to_fixed_32(value, frac_bits=0): fixed_val = int(value * (2**frac_bits)) return assert_is_int32(fixed_val, frac_bits) +# Pre-generated SOS filters (avoid Scipy package for common installs) +GeneratedSOS = { + ('lowpass', 10.0, 4): [ + [0.004824343357716228, 0.009648686715432456, 0.004824343357716228, + 1.0, -1.0485995763626117, 0.2961403575616696], + [1.0, 2.0, 1.0, 1.0, -1.3209134308194264, 0.6327387928852766], + ], +} + +# Helper tool to pre-generate SOS filters. Run with something like: +# python -c 'import trigger_analog as m; m.pre_gen_filt("lowpass", 250, 25, 4)' +def pre_gen_filt(btype, sps, freq, order): + global GeneratedSOS + GeneratedSOS = {} + # Create filter + df = DigitalFilter(sps, ImportError) + fs = df._butter(freq, btype, order) + # Write filter info to stdout + msgs = [] + msgs.append(" ('%s', %s, %d): [" % (btype, repr(float(sps)/freq), order)) + for data in fs: + coeffs = ", ".join([repr(float(c)) for c in data]) + msgs.append(" [%s]," % coeffs,) + msgs.append(" ],") + msgs.append("") + import sys + sys.stdout.write("\n".join(msgs)) # Digital filter designer and container class DigitalFilter: @@ -59,6 +86,9 @@ class DigitalFilter: return self.initial_state = signal.sosfilt_zi(self.filter_sections) def _butter(self, frequency, btype, order): + key = (btype, float(self.sample_frequency)/frequency, int(order)) + if key in GeneratedSOS: + return GeneratedSOS[key] signal = self.get_scipy_signal() return signal.butter(order, Wn=frequency, btype=btype, fs=self.sample_frequency, output='sos')