diff --git a/shaketune/helpers/resonance_test.py b/shaketune/helpers/resonance_test.py index 2538da7..d2c3736 100644 --- a/shaketune/helpers/resonance_test.py +++ b/shaketune/helpers/resonance_test.py @@ -22,8 +22,28 @@ from ..helpers.console_output import ConsoleOutput +# Shake&Tune: 3D printer analysis tools +# +# Adapted from Klipper's original resonance_tester.py file by Dmitry Butyugin +# Copyright (C) 2024 FĂ©lix Boisselier (Frix_x on Discord) +# Licensed under the GNU General Public License v3.0 (GPL-3.0) +# +# File: resonance_test.py +# Description: Contains functions to test the resonance frequency of the printer and its components +# by vibrating the toolhead in specific axis directions. This derive a bit from Klipper's +# implementation as there are a couple of changes: +# 1. Original code doesn't use euclidean distance with projection for the coordinates calculation. +# The new approach implemented here ensures that the vector's total length remains constant (= L), +# regardless of the direction components. It's especially important when the direction vector +# involves combinations of movements along multiple axes like for the diagonal belt tests. +# 2. Original code doesn't allow Z axis movements that was added in order to test the Z axis resonance +# or CoreXZ belts frequency profiles as well. +# 3. There is the possibility to do a real static frequency test by specifying a duration and a +# fixed frequency to maintain. + # This class is used to generate the base vibration test sequences +# Note: it's almost untouched from the original Klipper code from Dmitry Butyugin class BaseVibrationGenerator: def __init__(self, min_freq, max_freq, accel_per_hz, hz_per_sec): self.min_freq = min_freq @@ -66,6 +86,7 @@ def gen_test(self): # This class is a derivative of BaseVibrationGenerator that adds slow sweeping acceleration to the test sequences (new style) +# Note: it's almost untouched from the original Klipper code from Dmitry Butyugin class SweepingVibrationGenerator(BaseVibrationGenerator): def __init__(self, min_freq, max_freq, accel_per_hz, hz_per_sec, sweeping_accel=400.0, sweeping_period=1.2): super().__init__(min_freq, max_freq, accel_per_hz, hz_per_sec) @@ -192,10 +213,12 @@ def vibrate_axis(self, axis_direction, min_freq=None, max_freq=None, hz_per_sec= s_period = base_s_period s_accel = base_s_accel - if s_period > 0: - gen = SweepingVibrationGenerator(final_min_f, final_max_f, final_aph, final_hps, s_accel, s_period) - else: + if s_period == 0 or self.is_old_klipper: + ConsoleOutput.print('Using pulse-only test') gen = BaseVibrationGenerator(final_min_f, final_max_f, final_aph, final_hps) + else: + ConsoleOutput.print('Using pulse test with additional sweeping') + gen = SweepingVibrationGenerator(final_min_f, final_max_f, final_aph, final_hps, s_accel, s_period) test_seq = gen.gen_test() self._run_test_sequence(axis_direction, test_seq) @@ -216,7 +239,6 @@ def _run_test_sequence(self, axis_direction, test_seq): X, Y, Z, E = toolhead.get_position() old_max_accel = toolhead_info['max_accel'] - old_mcr = toolhead_info.get('minimum_cruise_ratio', 1.0) # Set velocity limits if test_seq: @@ -224,7 +246,12 @@ def _run_test_sequence(self, axis_direction, test_seq): else: max_accel = old_max_accel # no moves, just default - gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel:.3f} MINIMUM_CRUISE_RATIO=0') + if 'minimum_cruise_ratio' in toolhead_info: # minimum_cruise_ratio found: Klipper >= v0.12.0-239 + old_mcr = toolhead_info['minimum_cruise_ratio'] + gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel} MINIMUM_CRUISE_RATIO=0') + else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239 + old_mcr = None + gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel}') # Disable input shaper if present input_shaper = self.toolhead.printer.lookup_object('input_shaper', None) @@ -240,7 +267,7 @@ def _run_test_sequence(self, axis_direction, test_seq): for next_t, accel, freq in test_seq: t_seg = next_t - last_t - gcode.run_script_from_command(f'M204 S={abs(accel):.3f}') + toolhead.cmd_M204(gcode.create_gcode_command('M204', 'M204', {'S': abs(accel)})) v = last_v + accel * t_seg abs_v = abs(v) if abs_v < 1e-6: @@ -253,7 +280,8 @@ def _run_test_sequence(self, axis_direction, test_seq): dX, dY, dZ = self._project_distance(d, normalized_direction) nX, nY, nZ = X + dX, Y + dY, Z + dZ - toolhead.limit_next_junction_speed(abs_last_v) + if not self.is_old_klipper: + toolhead.limit_next_junction_speed(abs_last_v) # If direction changed sign, must pass through zero velocity if v * last_v < 0: @@ -278,13 +306,14 @@ def _run_test_sequence(self, axis_direction, test_seq): if last_v != 0.0: d_decel = -0.5 * last_v2 / old_max_accel if old_max_accel != 0 else 0 ddX, ddY, ddZ = self._project_distance(d_decel, normalized_direction) - gcode.run_script_from_command(f'M204 S={old_max_accel:.3f}') + toolhead.cmd_M204(gcode.create_gcode_command('M204', 'M204', {'S': old_max_accel})) toolhead.move([X + ddX, Y + ddY, Z + ddZ, E], abs(last_v)) - # Restore original limits - gcode.run_script_from_command( - f'SET_VELOCITY_LIMIT ACCEL={old_max_accel:.3f} MINIMUM_CRUISE_RATIO={old_mcr:.3f}' - ) + # Restore the previous acceleration values + if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239 + gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_max_accel} MINIMUM_CRUISE_RATIO={old_mcr}') + else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239 + gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_max_accel}') # Re-enable input shaper if disabled if input_shaper is not None: