From 474053b82501e057c8b440e9b8d6a1a86c436920 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Thu, 15 Jan 2026 11:34:49 +0100 Subject: [PATCH 01/25] Refactoring of ResonanceGenerator class and files --- Marlin/Configuration_adv.h | 2 +- Marlin/src/feature/e_parser.cpp | 8 ++++---- Marlin/src/feature/e_parser.h | 4 ++-- .../resonance}/resonance_generator.cpp | 12 +++++++----- .../resonance}/resonance_generator.h | 8 +++++--- .../{ft_motion => resonance}/M495_M496.cpp | 18 +++++++++--------- Marlin/src/gcode/gcode.cpp | 2 +- Marlin/src/gcode/gcode.h | 6 +++--- Marlin/src/inc/SanityCheck.h | 4 ++-- Marlin/src/lcd/menu/menu_motion.cpp | 12 ++++++------ Marlin/src/module/ft_motion.cpp | 19 +++++++------------ Marlin/src/module/ft_motion.h | 9 ++++----- .../module/ft_motion/trajectory_generator.h | 2 +- buildroot/tests/I3DBEEZ9_V1 | 2 +- buildroot/tests/STM32F103RC_btt | 2 +- ini/features.ini | 2 +- 16 files changed, 55 insertions(+), 57 deletions(-) rename Marlin/src/{module/ft_motion => feature/resonance}/resonance_generator.cpp (93%) rename Marlin/src/{module/ft_motion => feature/resonance}/resonance_generator.h (94%) rename Marlin/src/gcode/feature/{ft_motion => resonance}/M495_M496.cpp (92%) diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 84fbd6f8ed..cee7941fa5 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1204,7 +1204,7 @@ #define FTM_SHAPING_ZETA_E 0.03f // Zeta used by input shapers for E axis #define FTM_SHAPING_V_TOL_E 0.05f // Vibration tolerance used by EI input shapers for E axis - //#define FTM_RESONANCE_TEST // Sine sweep motion for resonance study + //#define RESONANCE_TEST // Sine sweep motion for resonance study //#define FTM_SMOOTHING // Smoothing can reduce artifacts and make steppers quieter // on sharp corners, but too much will round corners. diff --git a/Marlin/src/feature/e_parser.cpp b/Marlin/src/feature/e_parser.cpp index 3744870164..ca67427788 100644 --- a/Marlin/src/feature/e_parser.cpp +++ b/Marlin/src/feature/e_parser.cpp @@ -33,7 +33,7 @@ // Static data members bool EmergencyParser::killed_by_M112, // = false EmergencyParser::quickstop_by_M410, - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) EmergencyParser::rt_stop_by_M496, // = false #endif #if HAS_MEDIA @@ -154,7 +154,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M4: switch (c) { case '1' :state = EP_M41; break; - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) case '9': state = EP_M49; break; #endif default: state = EP_IGNORE; @@ -163,7 +163,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M41: state = (c == '0') ? EP_M410 : EP_IGNORE; break; - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) case EP_M49: state = (c == '6') ? EP_M496 : EP_IGNORE; break; #endif @@ -212,7 +212,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M108: marlin.end_waiting(); break; case EP_M112: killed_by_M112 = true; break; case EP_M410: quickstop_by_M410 = true; break; - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) case EP_M496: rt_stop_by_M496 = true; break; #endif #if ENABLED(EP_BABYSTEPPING) diff --git a/Marlin/src/feature/e_parser.h b/Marlin/src/feature/e_parser.h index a582ec6c3d..1f1ca58c35 100644 --- a/Marlin/src/feature/e_parser.h +++ b/Marlin/src/feature/e_parser.h @@ -43,7 +43,7 @@ public: #if HAS_MEDIA EP_M5, EP_M52, EP_M524, #endif - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) EP_M49, EP_M496, #endif #if ENABLED(EP_BABYSTEPPING) @@ -67,7 +67,7 @@ public: static bool killed_by_M112; static bool quickstop_by_M410; - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) static bool rt_stop_by_M496; #endif diff --git a/Marlin/src/module/ft_motion/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp similarity index 93% rename from Marlin/src/module/ft_motion/resonance_generator.cpp rename to Marlin/src/feature/resonance/resonance_generator.cpp index 1d2bd553fe..01af2a60e5 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -22,20 +22,22 @@ #include "../../inc/MarlinConfigPre.h" -#if ENABLED(FTM_RESONANCE_TEST) +#if ENABLED(RESONANCE_TEST) -#include "../ft_motion.h" +#include "../../module/ft_motion.h" #include "resonance_generator.h" #include -ftm_resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test parameters +resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test parameters bool ResonanceGenerator::active = false; // Resonance test active bool ResonanceGenerator::done = false; // Resonance test done float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer float ResonanceGenerator::timeline = 0.0f; +ResonanceGenerator rtg; + ResonanceGenerator::ResonanceGenerator() {} void ResonanceGenerator::abort() { @@ -44,7 +46,7 @@ void ResonanceGenerator::abort() { } void ResonanceGenerator::reset() { - rt_params = ftm_resonance_test_params_t(); + rt_params = resonance_test_params_t(); rt_time = FTM_TS; active = false; done = false; @@ -109,4 +111,4 @@ void ResonanceGenerator::fill_stepper_plan_buffer() { } } -#endif // FTM_RESONANCE_TEST +#endif // RESONANCE_TEST diff --git a/Marlin/src/module/ft_motion/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h similarity index 94% rename from Marlin/src/module/ft_motion/resonance_generator.h rename to Marlin/src/feature/resonance/resonance_generator.h index a432ee964d..da80642b42 100644 --- a/Marlin/src/module/ft_motion/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -29,7 +29,7 @@ #define M_TAU (2.0f * M_PI) #endif -typedef struct FTMResonanceTestParams { +typedef struct ResonanceTestParams { AxisEnum axis = NO_AXIS_ENUM; // Axis to test float min_freq = 5.0f; // Minimum frequency [Hz] float max_freq = 100.0f; // Maximum frequency [Hz] @@ -37,11 +37,11 @@ typedef struct FTMResonanceTestParams { float accel_per_hz = 60.0f; // Acceleration per Hz [mm/sec/Hz] or [g/Hz] int16_t amplitude_correction = 5; // Amplitude correction factor xyze_pos_t start_pos; // Initial stepper position -} ftm_resonance_test_params_t; +} resonance_test_params_t; class ResonanceGenerator { public: - static ftm_resonance_test_params_t rt_params; // Resonance test parameters + static resonance_test_params_t rt_params; // Resonance test parameters static float timeline; // Timeline Value to calculate resonance frequency ResonanceGenerator(); @@ -83,3 +83,5 @@ class ResonanceGenerator { static bool active; // Resonance test active static bool done; // Resonance test done }; + +extern ResonanceGenerator rtg; diff --git a/Marlin/src/gcode/feature/ft_motion/M495_M496.cpp b/Marlin/src/gcode/feature/resonance/M495_M496.cpp similarity index 92% rename from Marlin/src/gcode/feature/ft_motion/M495_M496.cpp rename to Marlin/src/gcode/feature/resonance/M495_M496.cpp index 18787ba32f..2119dc7d20 100644 --- a/Marlin/src/gcode/feature/ft_motion/M495_M496.cpp +++ b/Marlin/src/gcode/feature/resonance/M495_M496.cpp @@ -22,15 +22,15 @@ #include "../../../inc/MarlinConfig.h" -#if ENABLED(FTM_RESONANCE_TEST) +#if ENABLED(RESONANCE_TEST) #include "../../gcode.h" #include "../../../lcd/marlinui.h" #include "../../../module/ft_motion.h" -#include "../../../module/ft_motion/resonance_generator.h" +#include "../../../feature/resonance/resonance_generator.h" void say_resonance_test() { - const ftm_resonance_test_params_t &p = ftMotion.rtg.rt_params; + const resonance_test_params_t &p = rtg.rt_params; SERIAL_ECHO_START(); SERIAL_ECHOLN(F("M495 "), F("Resonance Test")); SERIAL_ECHOLNPGM(" Axis: ", p.axis == NO_AXIS_ENUM ? C('-') : C(AXIS_CHAR(p.axis))); @@ -64,7 +64,7 @@ void say_resonance_test() { void GcodeSuite::M495() { if (!parser.seen_any()) return say_resonance_test(); - ftm_resonance_test_params_t &p = ftMotion.rtg.rt_params; + resonance_test_params_t &p = rtg.rt_params; const bool seenX = parser.seen_test('X'), seenY = parser.seen_test('Y'), seenZ = parser.seen_test('Z'); @@ -137,8 +137,8 @@ void GcodeSuite::M495() { if (parser.seenval('G')) { const float val = parser.value_float(); if (WITHIN(val, 0, 100)) { - ftMotion.rtg.timeline = val; - SERIAL_ECHOLNPGM("Resonance Frequency set to ", ftMotion.rtg.getFrequencyFromTimeline(), " Hz"); + rtg.timeline = val; + SERIAL_ECHOLNPGM("Resonance Frequency set to ", rtg.getFrequencyFromTimeline(), " Hz"); } else { SERIAL_ECHOLN(F("?Invalid "), F("Timeline value (0..100 s)")); @@ -171,8 +171,8 @@ void GcodeSuite::M495() { * M496: Abort the resonance test (via Emergency Parser) */ void GcodeSuite::M496() { - if (ftMotion.rtg.isActive()) { - ftMotion.rtg.abort(); + if (rtg.isActive()) { + rtg.abort(); EmergencyParser::rt_stop_by_M496 = false; ui.refresh(); #if DISABLED(MARLIN_SMALL_BUILD) @@ -185,4 +185,4 @@ void GcodeSuite::M496() { #endif } -#endif // FTM_RESONANCE_TEST +#endif // RESONANCE_TEST diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 10a6f3d65b..f6ec836ed8 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -924,7 +924,7 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) { #if ANY(FTM_SMOOTHING, FTM_POLYS) case 494: M494(); break; // M494: Fixed-Time Motion extras #endif - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) case 495: M495(); break; // M495: Resonance test for Input Shaping case 496: M496(); break; // M496: Abort resonance test #endif diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 378a1a73f4..773ce55d01 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -252,8 +252,8 @@ * M485 - Send RS485 packets (Requires RS485_SERIAL_PORT) * M486 - Identify and cancel objects. (Requires CANCEL_OBJECTS) * M493 - Set / Report input FT Motion/Shaping parameters. (Requires FT_MOTION) - * M495 - Set / Start resonance test. (Requires FTM_RESONANCE_TEST) - * M496 - Abort resonance test. (Requires FTM_RESONANCE_TEST) + * M495 - Set / Start resonance test. (Requires RESONANCE_TEST) + * M496 - Abort resonance test. (Requires RESONANCE_TEST) * M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS) * M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS) * M502 - Revert to the default "factory settings". ** Does not write them to EEPROM! ** @@ -1124,7 +1124,7 @@ private: static void M493_report(const bool forReplay=true); static void M494(); static void M494_report(const bool forReplay=true); - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) static void M495(); static void M495_report(const bool forReplay=true); static void M496(); diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 761125d790..24151775a2 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -4511,8 +4511,8 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive." static_assert(FTM_SMOOTHING_TIME_Z <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_Z must be <= FTM_MAX_SMOOTHING_TIME."); static_assert(FTM_SMOOTHING_TIME_E <= FTM_MAX_SMOOTHING_TIME, "FTM_SMOOTHING_TIME_E must be <= FTM_MAX_SMOOTHING_TIME."); #endif - #if ENABLED(FTM_RESONANCE_TEST) && DISABLED(EMERGENCY_PARSER) - #error "EMERGENCY_PARSER is required with FTM_RESONANCE_TEST (to cancel the test)." + #if ENABLED(RESONANCE_TEST) && DISABLED(EMERGENCY_PARSER) + #error "EMERGENCY_PARSER is required with RESONANCE_TEST (to cancel the test)." #endif #if !HAS_STANDARD_MOTION #if ENABLED(SMOOTH_LIN_ADVANCE) diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index 59172a4787..2bfad90326 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -405,15 +405,15 @@ void menu_move() { #endif // FTM_POLYS - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) void menu_ftm_resonance_freq() { START_MENU(); BACK_ITEM(MSG_FTM_RESONANCE_TEST); STATIC_ITEM(MSG_FTM_RETRIEVE_FREQ); - EDIT_ITEM(float62, MSG_FTM_TIMELINE_FREQ, &ftMotion.rtg.timeline, 0.0f, 600.0f); - PSTRING_ITEM(MSG_FTM_RESONANCE_FREQ, ftostr53_63(ftMotion.rtg.getFrequencyFromTimeline()), SS_FULL); + EDIT_ITEM(float62, MSG_FTM_TIMELINE_FREQ, &rtg.timeline, 0.0f, 600.0f); + PSTRING_ITEM(MSG_FTM_RESONANCE_FREQ, ftostr53_63(rtg.getFrequencyFromTimeline()), SS_FULL); END_MENU(); } @@ -422,7 +422,7 @@ void menu_move() { START_MENU(); BACK_ITEM(MSG_FIXED_TIME_MOTION); - if (ftMotion.rtg.isActive() && !ftMotion.rtg.isDone()) { + if (rtg.isActive() && !rtg.isDone()) { STATIC_ITEM(MSG_FTM_RT_RUNNING); GCODES_ITEM(MSG_FTM_RT_STOP, F("M496")); } @@ -442,7 +442,7 @@ void menu_move() { END_MENU(); } - #endif // FTM_RESONANCE_TEST + #endif // RESONANCE_TEST #if HAS_DYNAMIC_FREQ @@ -559,7 +559,7 @@ void menu_move() { queue.inject(TS(F("M493"), IAXIS_CHAR(MenuItemBase::itemIndex), 'T', int(editable.state))); }); - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) SUBMENU(MSG_FTM_RESONANCE_TEST, menu_ftm_resonance_test); #endif } diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index 1db5b0ee6b..f380c83c11 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -37,8 +37,8 @@ #include "ft_motion/trajectory_poly5.h" #include "ft_motion/trajectory_poly6.h" #endif -#if ENABLED(FTM_RESONANCE_TEST) - #include "ft_motion/resonance_generator.h" +#if ENABLED(RESONANCE_TEST) + #include "../feature/resonance/resonance_generator.h" #include "../gcode/gcode.h" // for home_all_axes #endif @@ -89,11 +89,6 @@ TrapezoidalTrajectoryGenerator FTMotion::trapezoidalGenerator; TrajectoryGenerator* FTMotion::currentGenerator = &FTMotion::trapezoidalGenerator; #endif -// Resonance Test -#if ENABLED(FTM_RESONANCE_TEST) - ResonanceGenerator FTMotion::rtg; // Resonance trajectory generator instance -#endif - #if FTM_HAS_LIN_ADVANCE bool FTMotion::use_advance_lead; #endif @@ -167,9 +162,9 @@ void FTMotion::loop() { * 4. Signal ready for new block. */ - const bool using_resonance = TERN(FTM_RESONANCE_TEST, rtg.isActive(), false); + const bool using_resonance = TERN(RESONANCE_TEST, rtg.isActive(), false); - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) if (using_resonance) { // Resonance Test has priority over normal ft_motion operation. // Process resonance test if active. When it's done, generate the last data points for a clean ending. @@ -686,13 +681,13 @@ void FTMotion::fill_stepper_plan_buffer() { } } -#if ENABLED(FTM_RESONANCE_TEST) +#if ENABLED(RESONANCE_TEST) // Start Resonance Testing void FTMotion::start_resonance_test() { home_if_needed(); // Ensure known axes first - ftm_resonance_test_params_t &p = rtg.rt_params; + resonance_test_params_t &p = rtg.rt_params; // Safe Acceleration per Hz for Z axis if (p.axis == Z_AXIS && p.accel_per_hz > 15.0f) @@ -705,6 +700,6 @@ void FTMotion::fill_stepper_plan_buffer() { rtg.start(current_position, FTM_TS); } -#endif // FTM_RESONANCE_TEST +#endif // RESONANCE_TEST #endif // FT_MOTION diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h index 15be0154ae..86e9f5615f 100644 --- a/Marlin/src/module/ft_motion.h +++ b/Marlin/src/module/ft_motion.h @@ -30,8 +30,8 @@ #include "ft_motion/trajectory_poly5.h" #include "ft_motion/trajectory_poly6.h" #endif -#if ENABLED(FTM_RESONANCE_TEST) - #include "ft_motion/resonance_generator.h" +#if ENABLED(RESONANCE_TEST) + #include "../feature/resonance/resonance_generator.h" #endif #if HAS_FTM_SHAPING @@ -238,7 +238,7 @@ typedef struct FTConfig { */ class FTMotion { - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) friend void ResonanceGenerator::fill_stepper_plan_buffer(); #endif @@ -268,9 +268,8 @@ class FTMotion { // Public methods static void init(); static void loop(); // Controller main, to be invoked from non-isr task. - #if ENABLED(FTM_RESONANCE_TEST) + #if ENABLED(RESONANCE_TEST) static void start_resonance_test(); // Start a resonance test with given parameters - static ResonanceGenerator rtg; // Resonance trajectory generator instance #endif #if ENABLED(FTM_SMOOTHING) diff --git a/Marlin/src/module/ft_motion/trajectory_generator.h b/Marlin/src/module/ft_motion/trajectory_generator.h index 677249af0d..f39fe4d7ab 100644 --- a/Marlin/src/module/ft_motion/trajectory_generator.h +++ b/Marlin/src/module/ft_motion/trajectory_generator.h @@ -74,5 +74,5 @@ protected: */ enum class TrajectoryType : uint8_t { TRAPEZOIDAL, POLY5, POLY6 - OPTARG(FTM_RESONANCE_TEST, RESONANCE) + OPTARG(RESONANCE_TEST, RESONANCE) }; diff --git a/buildroot/tests/I3DBEEZ9_V1 b/buildroot/tests/I3DBEEZ9_V1 index af6f529289..8e3ea71e4e 100755 --- a/buildroot/tests/I3DBEEZ9_V1 +++ b/buildroot/tests/I3DBEEZ9_V1 @@ -18,7 +18,7 @@ opt_set MOTHERBOARD BOARD_I3DBEEZ9_V1 SERIAL_PORT -1 \ EXTRUDERS 3 TEMP_SENSOR_1 1 TEMP_SENSOR_2 1 \ E0_AUTO_FAN_PIN PC10 E1_AUTO_FAN_PIN PC11 E2_AUTO_FAN_PIN PC12 \ X_DRIVER_TYPE TMC2209 Y_DRIVER_TYPE TMC2130 -opt_enable FT_MOTION FTM_SMOOTHING FTM_HOME_AND_PROBE FTM_RESONANCE_TEST FT_MOTION_MENU \ +opt_enable FT_MOTION FTM_SMOOTHING FTM_HOME_AND_PROBE RESONANCE_TEST FT_MOTION_MENU \ REPRAP_DISCOUNT_SMART_CONTROLLER EMERGENCY_PARSER EEPROM_SETTINGS \ BLTOUCH AUTO_BED_LEVELING_3POINT Z_SAFE_HOMING PINS_DEBUGGING opt_disable FTM_SHAPER_ZVDDD FTM_SHAPER_MZV diff --git a/buildroot/tests/STM32F103RC_btt b/buildroot/tests/STM32F103RC_btt index 9e6019bb91..23bb39ddba 100755 --- a/buildroot/tests/STM32F103RC_btt +++ b/buildroot/tests/STM32F103RC_btt @@ -15,7 +15,7 @@ opt_set MOTHERBOARD BOARD_BTT_SKR_MINI_E3_V1_0 SERIAL_PORT 1 SERIAL_PORT_2 -1 \ X_CURRENT_HOME X_CURRENT/2 Y_CURRENT_HOME Y_CURRENT/2 Z_CURRENT_HOME Y_CURRENT/2 opt_enable CR10_STOCKDISPLAY EMERGENCY_PARSER Z_IDLE_HEIGHT EDITABLE_HOMING_CURRENT \ INPUT_SHAPING_X INPUT_SHAPING_Y \ - FT_MOTION FT_MOTION_MENU FTM_RESONANCE_TEST \ + FT_MOTION FT_MOTION_MENU RESONANCE_TEST \ BIQU_MICROPROBE_V1 PROBE_ENABLE_DISABLE Z_SAFE_HOMING AUTO_BED_LEVELING_BILINEAR \ ADAPTIVE_STEP_SMOOTHING LIN_ADVANCE SMOOTH_LIN_ADVANCE NONLINEAR_EXTRUSION \ PINS_DEBUGGING diff --git a/ini/features.ini b/ini/features.ini index 03a9bdbba8..afc8a5b99d 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -313,7 +313,7 @@ PLATFORM_M997_SUPPORT = build_src_filter=+ FT_MOTION = build_src_filter=+ + - + - - FTM_SMOOTHING = build_src_filter=+ -FTM_RESONANCE_TEST = build_src_filter=+ +RESONANCE_TEST = build_src_filter=+ + FTM_POLYS = build_src_filter=+ HAS_LIN_ADVANCE_K = build_src_filter=+ PHOTO_GCODE = build_src_filter=+ From a72ecce6054f13f311279d6c44d3953d11e8d64b Mon Sep 17 00:00:00 2001 From: narno2202 Date: Thu, 15 Jan 2026 17:46:08 +0100 Subject: [PATCH 02/25] More prep --- .../feature/resonance/resonance_generator.cpp | 40 +++++++++++-------- .../feature/resonance/resonance_generator.h | 20 +++++----- .../src/gcode/feature/resonance/M495_M496.cpp | 2 +- Marlin/src/module/ft_motion.cpp | 25 ------------ Marlin/src/module/ft_motion.h | 3 -- 5 files changed, 34 insertions(+), 56 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 01af2a60e5..8c24cc7d8a 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -26,6 +26,7 @@ #include "../../module/ft_motion.h" #include "resonance_generator.h" +#include "../../gcode/gcode.h" // for home_all_axes #include @@ -40,6 +41,27 @@ ResonanceGenerator rtg; ResonanceGenerator::ResonanceGenerator() {} +void ResonanceGenerator::start() { + home_if_needed(); // Ensure known axes first + + // Safe Acceleration per Hz for Z axis + if (rt_params.axis == Z_AXIS && rt_params.accel_per_hz > 15.0f) + rt_params.accel_per_hz = 15.0f; + + // Always move to the center of the bed + do_blocking_move_to_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING); + + rt_params.start_pos = current_position; + rt_time = FTM_TS; + active = true; + done = false; + // Precompute sine sweep const + amplitude_precalc = (rt_params.amplitude_correction * rt_params.accel_per_hz * 0.25f) / sq(M_PI); + current_freq = rt_params.min_freq; + const float inv_octave_duration = 1.0f / rt_params.octave_duration; + freq_mul = exp2f(FTM_TS * inv_octave_duration); +} + void ResonanceGenerator::abort() { reset(); ftMotion.reset(); @@ -76,10 +98,6 @@ float ResonanceGenerator::fast_sin(float x) { void ResonanceGenerator::fill_stepper_plan_buffer() { xyze_float_t traj_coords = rt_params.start_pos; - const float amplitude_precalc = (rt_params.amplitude_correction * rt_params.accel_per_hz * 0.25f) / sq(M_PI); - - float rt_factor = rt_time * M_TAU; - while (!ftMotion.stepping.is_full()) { // Calculate current frequency current_freq *= freq_mul; @@ -88,23 +106,11 @@ void ResonanceGenerator::fill_stepper_plan_buffer() { return; } - // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) - //const float accel_magnitude = rt_params.accel_per_hz * freq; - //const float amplitude = rt_params.amplitude_correction * accel_magnitude / (4.0f * sq(M_PI) * sq(freq)); - const float amplitude = amplitude_precalc / current_freq; - - // Phase in radians - const float phase = current_freq * rt_factor; - - // Position Offset : between -A and +A - const float pos_offset = amplitude * fast_sin(phase); - // Resonate the axis being tested - traj_coords[rt_params.axis] = rt_params.start_pos[rt_params.axis] + pos_offset; + traj_coords[rt_params.axis] = calc_next_pos(); // Increment for the next point (before calling out) rt_time += FTM_TS; - rt_factor += FTM_TS * M_TAU; // Store in buffer ftMotion.stepping_enqueue(traj_coords); diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index da80642b42..65c810b11a 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -48,16 +48,7 @@ class ResonanceGenerator { void reset(); - void start(const xyze_pos_t &spos, const float t) { - rt_params.start_pos = spos; - rt_time = t; - active = true; - done = false; - // Precompute frequency multiplier - current_freq = rt_params.min_freq; - const float inv_octave_duration = 1.0f / rt_params.octave_duration; - freq_mul = exp2f(FTM_TS * inv_octave_duration); - } + void start(); // Return frequency based on timeline float getFrequencyFromTimeline() { @@ -76,9 +67,18 @@ class ResonanceGenerator { void abort(); // Abort resonance test private: + float calc_next_pos() { + // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) + const float amplitude = amplitude_precalc / current_freq; + // Phase in radians + const float phase = current_freq * M_TAU * rt_time; + // Position Offset : between -A and +A + return (rt_params.start_pos[rt_params.axis] + amplitude * fast_sin(phase)); + } float fast_sin(float x); // Fast sine approximation static float rt_time; // Test timer float freq_mul; // Frequency multiplier for sine sweeping + float amplitude_precalc; // Precalculated part of amplitude formula float current_freq; // Current frequency being generated in sinusoidal motion static bool active; // Resonance test active static bool done; // Resonance test done diff --git a/Marlin/src/gcode/feature/resonance/M495_M496.cpp b/Marlin/src/gcode/feature/resonance/M495_M496.cpp index 2119dc7d20..88b69b2765 100644 --- a/Marlin/src/gcode/feature/resonance/M495_M496.cpp +++ b/Marlin/src/gcode/feature/resonance/M495_M496.cpp @@ -150,7 +150,7 @@ void GcodeSuite::M495() { if (p.axis != NO_AXIS_ENUM) { if (p.max_freq > p.min_freq) { SERIAL_ECHOLN(F("Starting "), F("Resonance Test")); - ftMotion.start_resonance_test(); + rtg.startt(); // The function returns immediately, the test runs in the background. } else { diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp index f380c83c11..94e1cfdab4 100644 --- a/Marlin/src/module/ft_motion.cpp +++ b/Marlin/src/module/ft_motion.cpp @@ -37,10 +37,6 @@ #include "ft_motion/trajectory_poly5.h" #include "ft_motion/trajectory_poly6.h" #endif -#if ENABLED(RESONANCE_TEST) - #include "../feature/resonance/resonance_generator.h" - #include "../gcode/gcode.h" // for home_all_axes -#endif #include "stepper.h" // Access stepper block queue function and abort status. #include "endstops.h" @@ -681,25 +677,4 @@ void FTMotion::fill_stepper_plan_buffer() { } } -#if ENABLED(RESONANCE_TEST) - - // Start Resonance Testing - void FTMotion::start_resonance_test() { - home_if_needed(); // Ensure known axes first - - resonance_test_params_t &p = rtg.rt_params; - - // Safe Acceleration per Hz for Z axis - if (p.axis == Z_AXIS && p.accel_per_hz > 15.0f) - p.accel_per_hz = 15.0f; - - // Always move to the center of the bed - do_blocking_move_to_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING); - - // Start test at the current position with the configured time-step - rtg.start(current_position, FTM_TS); - } - -#endif // RESONANCE_TEST - #endif // FT_MOTION diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h index 86e9f5615f..be2a4dd3ac 100644 --- a/Marlin/src/module/ft_motion.h +++ b/Marlin/src/module/ft_motion.h @@ -268,9 +268,6 @@ class FTMotion { // Public methods static void init(); static void loop(); // Controller main, to be invoked from non-isr task. - #if ENABLED(RESONANCE_TEST) - static void start_resonance_test(); // Start a resonance test with given parameters - #endif #if ENABLED(FTM_SMOOTHING) // Refresh alpha and delay samples used by smoothing functions. From bd424949a6229fee17bcc29c2d12fbb1b46dbb1d Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Thu, 15 Jan 2026 22:59:24 +0100 Subject: [PATCH 03/25] Fix typo --- Marlin/src/gcode/feature/resonance/M495_M496.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/gcode/feature/resonance/M495_M496.cpp b/Marlin/src/gcode/feature/resonance/M495_M496.cpp index 88b69b2765..68979c7ecb 100644 --- a/Marlin/src/gcode/feature/resonance/M495_M496.cpp +++ b/Marlin/src/gcode/feature/resonance/M495_M496.cpp @@ -150,7 +150,7 @@ void GcodeSuite::M495() { if (p.axis != NO_AXIS_ENUM) { if (p.max_freq > p.min_freq) { SERIAL_ECHOLN(F("Starting "), F("Resonance Test")); - rtg.startt(); + rtg.start(); // The function returns immediately, the test runs in the background. } else { From 039b634003e00350265700b2df801ab515cd2f3f Mon Sep 17 00:00:00 2001 From: narno2202 Date: Thu, 15 Jan 2026 23:40:04 +0100 Subject: [PATCH 04/25] More static, adapt menu function names --- .../src/feature/resonance/resonance_generator.cpp | 2 ++ Marlin/src/feature/resonance/resonance_generator.h | 14 +++++++------- Marlin/src/lcd/menu/menu_motion.cpp | 8 ++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 8c24cc7d8a..37d63a9e41 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -36,6 +36,8 @@ bool ResonanceGenerator::active = false; // Resonance test bool ResonanceGenerator::done = false; // Resonance test done float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer float ResonanceGenerator::timeline = 0.0f; +float ResonanceGenerator::amplitude_precalc; +float ResonanceGenerator::freq_mul; ResonanceGenerator rtg; diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index 65c810b11a..0cbd4b366b 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -75,13 +75,13 @@ class ResonanceGenerator { // Position Offset : between -A and +A return (rt_params.start_pos[rt_params.axis] + amplitude * fast_sin(phase)); } - float fast_sin(float x); // Fast sine approximation - static float rt_time; // Test timer - float freq_mul; // Frequency multiplier for sine sweeping - float amplitude_precalc; // Precalculated part of amplitude formula - float current_freq; // Current frequency being generated in sinusoidal motion - static bool active; // Resonance test active - static bool done; // Resonance test done + float fast_sin(float x); // Fast sine approximation + static float rt_time; // Test timer + static float freq_mul; // Frequency multiplier for sine sweeping + static float amplitude_precalc; // Precalculated part of amplitude formula + float current_freq; // Current frequency being generated in sinusoidal motion + static bool active; // Resonance test active + static bool done; // Resonance test done }; extern ResonanceGenerator rtg; diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index 2bfad90326..8abeaeee10 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -407,7 +407,7 @@ void menu_move() { #if ENABLED(RESONANCE_TEST) - void menu_ftm_resonance_freq() { + void menu_resonance_freq() { START_MENU(); BACK_ITEM(MSG_FTM_RESONANCE_TEST); @@ -418,7 +418,7 @@ void menu_move() { END_MENU(); } - void menu_ftm_resonance_test() { + void menu_resonance_test() { START_MENU(); BACK_ITEM(MSG_FIXED_TIME_MOTION); @@ -436,7 +436,7 @@ void menu_move() { #if HAS_Z_AXIS GCODES_ITEM_N(Z_AXIS, MSG_FTM_RT_START_N, F("M495 Z S")); #endif - SUBMENU(MSG_FTM_RETRIEVE_FREQ, menu_ftm_resonance_freq); + SUBMENU(MSG_FTM_RETRIEVE_FREQ, menu_resonance_freq); } END_MENU(); @@ -560,7 +560,7 @@ void menu_move() { }); #if ENABLED(RESONANCE_TEST) - SUBMENU(MSG_FTM_RESONANCE_TEST, menu_ftm_resonance_test); + SUBMENU(MSG_FTM_RESONANCE_TEST, menu_resonance_test); #endif } From 389a4d3a1844aff03dd23e2b81bcb05c4a42657a Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 16 Jan 2026 14:38:49 +0100 Subject: [PATCH 05/25] Change MSG_FTM resonance test text to MSG_ resonance text, minor changes to ResonanceGenerator class, move resonance menu outside FT_MOTION conditional in menu_motion.cpp --- .../feature/resonance/resonance_generator.cpp | 4 +- .../feature/resonance/resonance_generator.h | 1 + Marlin/src/lcd/language/language_en.h | 18 ++--- Marlin/src/lcd/language/language_it.h | 14 ++-- Marlin/src/lcd/menu/menu_motion.cpp | 81 ++++++++++--------- 5 files changed, 61 insertions(+), 57 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 37d63a9e41..6c396bf40d 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -38,6 +38,7 @@ float ResonanceGenerator::rt_time = FTM_TS; // Resonance test float ResonanceGenerator::timeline = 0.0f; float ResonanceGenerator::amplitude_precalc; float ResonanceGenerator::freq_mul; +xyze_float_t ResonanceGenerator:: traj_coords; ResonanceGenerator rtg; @@ -54,6 +55,7 @@ void ResonanceGenerator::start() { do_blocking_move_to_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING); rt_params.start_pos = current_position; + traj_coords = rt_params.start_pos; rt_time = FTM_TS; active = true; done = false; @@ -98,7 +100,7 @@ float ResonanceGenerator::fast_sin(float x) { } void ResonanceGenerator::fill_stepper_plan_buffer() { - xyze_float_t traj_coords = rt_params.start_pos; + //xyze_float_t traj_coords = rt_params.start_pos; while (!ftMotion.stepping.is_full()) { // Calculate current frequency diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index 0cbd4b366b..9bce6944cc 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -80,6 +80,7 @@ class ResonanceGenerator { static float freq_mul; // Frequency multiplier for sine sweeping static float amplitude_precalc; // Precalculated part of amplitude formula float current_freq; // Current frequency being generated in sinusoidal motion + static xyze_float_t traj_coords; static bool active; // Resonance test active static bool done; // Resonance test done }; diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index ba5228a1f1..155fa35f00 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -950,13 +950,13 @@ namespace LanguageNarrow_en { LSTR MSG_FTM_POLY6_OVERSHOOT = _UxGT("@ Poly6 Overshoot"); LSTR MSG_FTM_CONFIGURE_AXIS_N = _UxGT("Configure @ Axis"); - LSTR MSG_FTM_RESONANCE_TEST = _UxGT("Resonance Test"); - LSTR MSG_FTM_RT_RUNNING = _UxGT("Res. Test Running..."); - LSTR MSG_FTM_RT_START_N = _UxGT("Start @ Axis Test"); - LSTR MSG_FTM_RT_STOP = _UxGT("Abort Test"); - LSTR MSG_FTM_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq."); - LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Resonance Freq."); - LSTR MSG_FTM_TIMELINE_FREQ = _UxGT("Timeline (s)"); + LSTR MSG_RESONANCE_TEST = _UxGT("Resonance Test"); + LSTR MSG_RT_RUNNING = _UxGT("Res. Test Running..."); + LSTR MSG_RT_START_N = _UxGT("Start @ Axis Test"); + LSTR MSG_RT_STOP = _UxGT("Abort Test"); + LSTR MSG_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq."); + LSTR MSG_RESONANCE_FREQ = _UxGT("Resonance Freq."); + LSTR MSG_TIMELINE_FREQ = _UxGT("Timeline (s)"); LSTR MSG_LEVEL_X_AXIS = _UxGT("Level X Axis"); LSTR MSG_AUTO_CALIBRATE = _UxGT("Auto Calibrate"); @@ -1173,8 +1173,8 @@ namespace LanguageWide_en { LSTR MSG_HOMING_FEEDRATE_Y = _UxGT("Y Homing Feedrate"); LSTR MSG_HOMING_FEEDRATE_Z = _UxGT("Z Homing Feedrate"); LSTR MSG_EEPROM_INITIALIZED = _UxGT("Default Settings Restored"); - LSTR MSG_FTM_RT_RUNNING = _UxGT("Resonance Test Running..."); - LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Resonance frequency"); + LSTR MSG_RT_RUNNING = _UxGT("Resonance Test Running..."); + LSTR MSG_RESONANCE_FREQ = _UxGT("Resonance frequency"); #endif } diff --git a/Marlin/src/lcd/language/language_it.h b/Marlin/src/lcd/language/language_it.h index 3f45c44156..c4688eed90 100644 --- a/Marlin/src/lcd/language/language_it.h +++ b/Marlin/src/lcd/language/language_it.h @@ -907,13 +907,13 @@ namespace LanguageNarrow_it { LSTR MSG_FTM_SMOOTH_TIME_N = _UxGT("@ Tempo smorzamento"); LSTR MSG_FTM_POLY6_OVERSHOOT = _UxGT("@ Overshoot Poly6"); - LSTR MSG_FTM_RESONANCE_TEST = _UxGT("Test risonanza"); - LSTR MSG_FTM_RT_RUNNING = _UxGT("Test ris.in corso..."); - LSTR MSG_FTM_RT_START_N = _UxGT("Avvia Test Asse @"); - LSTR MSG_FTM_RT_STOP = _UxGT("Annulla Test"); - LSTR MSG_FTM_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq."); - LSTR MSG_FTM_RESONANCE_FREQ = _UxGT("Freq.Risonanza"); - LSTR MSG_FTM_TIMELINE_FREQ = _UxGT("Cronologia (s)"); + LSTR MSG_RESONANCE_TEST = _UxGT("Test risonanza"); + LSTR MSG_RT_RUNNING = _UxGT("Test ris.in corso..."); + LSTR MSG_RT_START_N = _UxGT("Avvia Test Asse @"); + LSTR MSG_RT_STOP = _UxGT("Annulla Test"); + LSTR MSG_RETRIEVE_FREQ = _UxGT("Calc. Res. Freq."); + LSTR MSG_RESONANCE_FREQ = _UxGT("Freq.Risonanza"); + LSTR MSG_TIMELINE_FREQ = _UxGT("Cronologia (s)"); LSTR MSG_LEVEL_X_AXIS = _UxGT("Livello asse X"); LSTR MSG_AUTO_CALIBRATE = _UxGT("Auto Calibra"); diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index 8abeaeee10..464ac21f26 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -308,6 +308,46 @@ void menu_move() { } #endif +#if ENABLED(RESONANCE_TEST) + #include "../../feature/resonance/resonance_generator.h" + + void menu_resonance_freq() { + START_MENU(); + BACK_ITEM(MSG_RESONANCE_TEST); + + STATIC_ITEM(MSG_RETRIEVE_FREQ); + EDIT_ITEM(float62, MSG_TIMELINE_FREQ, &rtg.timeline, 0.0f, 600.0f); + PSTRING_ITEM(MSG_RESONANCE_FREQ, ftostr53_63(rtg.getFrequencyFromTimeline()), SS_FULL); + + END_MENU(); + } + + void menu_resonance_test() { + START_MENU(); + BACK_ITEM(MSG_FIXED_TIME_MOTION); + + if (rtg.isActive() && !rtg.isDone()) { + STATIC_ITEM(MSG_RT_RUNNING); + GCODES_ITEM(MSG_RT_STOP, F("M496")); + } + else { + #if HAS_X_AXIS + GCODES_ITEM_N(X_AXIS, MSG_RT_START_N, F("M495 X S")); + #endif + #if HAS_Y_AXIS + GCODES_ITEM_N(Y_AXIS, MSG_RT_START_N, F("M495 Y S")); + #endif + #if HAS_Z_AXIS + GCODES_ITEM_N(Z_AXIS, MSG_RT_START_N, F("M495 Z S")); + #endif + SUBMENU(MSG_RETRIEVE_FREQ, menu_resonance_freq); + } + + END_MENU(); + } + +#endif // RESONANCE_TEST + #if ENABLED(FT_MOTION_MENU) #include "../../module/ft_motion.h" @@ -405,45 +445,6 @@ void menu_move() { #endif // FTM_POLYS - #if ENABLED(RESONANCE_TEST) - - void menu_resonance_freq() { - START_MENU(); - BACK_ITEM(MSG_FTM_RESONANCE_TEST); - - STATIC_ITEM(MSG_FTM_RETRIEVE_FREQ); - EDIT_ITEM(float62, MSG_FTM_TIMELINE_FREQ, &rtg.timeline, 0.0f, 600.0f); - PSTRING_ITEM(MSG_FTM_RESONANCE_FREQ, ftostr53_63(rtg.getFrequencyFromTimeline()), SS_FULL); - - END_MENU(); - } - - void menu_resonance_test() { - START_MENU(); - BACK_ITEM(MSG_FIXED_TIME_MOTION); - - if (rtg.isActive() && !rtg.isDone()) { - STATIC_ITEM(MSG_FTM_RT_RUNNING); - GCODES_ITEM(MSG_FTM_RT_STOP, F("M496")); - } - else { - #if HAS_X_AXIS - GCODES_ITEM_N(X_AXIS, MSG_FTM_RT_START_N, F("M495 X S")); - #endif - #if HAS_Y_AXIS - GCODES_ITEM_N(Y_AXIS, MSG_FTM_RT_START_N, F("M495 Y S")); - #endif - #if HAS_Z_AXIS - GCODES_ITEM_N(Z_AXIS, MSG_FTM_RT_START_N, F("M495 Z S")); - #endif - SUBMENU(MSG_FTM_RETRIEVE_FREQ, menu_resonance_freq); - } - - END_MENU(); - } - - #endif // RESONANCE_TEST - #if HAS_DYNAMIC_FREQ void menu_ftm_dyn_mode() { @@ -560,7 +561,7 @@ void menu_move() { }); #if ENABLED(RESONANCE_TEST) - SUBMENU(MSG_FTM_RESONANCE_TEST, menu_resonance_test); + SUBMENU(MSG_RESONANCE_TEST, menu_resonance_test); #endif } From 535134b72cbb8a89618ca44e588de0fe56a7234c Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 16 Jan 2026 17:46:32 +0100 Subject: [PATCH 06/25] Update gcode.h --- Marlin/src/gcode/gcode.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 773ce55d01..2a61e67493 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -1124,11 +1124,12 @@ private: static void M493_report(const bool forReplay=true); static void M494(); static void M494_report(const bool forReplay=true); - #if ENABLED(RESONANCE_TEST) + #endif + + #if ENABLED(RESONANCE_TEST) static void M495(); static void M495_report(const bool forReplay=true); static void M496(); - #endif #endif static void M500(); From 027208a0e36bdca1764ee5a8d818745f32f83fc8 Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Sun, 18 Jan 2026 09:35:40 +0100 Subject: [PATCH 07/25] Always home first --- Marlin/src/feature/resonance/resonance_generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 6c396bf40d..ca085b2b51 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -45,7 +45,7 @@ ResonanceGenerator rtg; ResonanceGenerator::ResonanceGenerator() {} void ResonanceGenerator::start() { - home_if_needed(); // Ensure known axes first + gcode.home_all_axes; // For safety and ensure known axes // Safe Acceleration per Hz for Z axis if (rt_params.axis == Z_AXIS && rt_params.accel_per_hz > 15.0f) From 0767f8551001b86ef5b0d0f21e5142406a2f1a75 Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:51:52 +0100 Subject: [PATCH 08/25] Move resonance menu outside FT_MOTION menu --- Marlin/src/lcd/menu/menu_motion.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index 464ac21f26..3237a0b63d 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -324,7 +324,7 @@ void menu_move() { void menu_resonance_test() { START_MENU(); - BACK_ITEM(MSG_FIXED_TIME_MOTION); + BACK_ITEM(MSG_MOTION); if (rtg.isActive() && !rtg.isDone()) { STATIC_ITEM(MSG_RT_RUNNING); @@ -560,9 +560,6 @@ void menu_move() { queue.inject(TS(F("M493"), IAXIS_CHAR(MenuItemBase::itemIndex), 'T', int(editable.state))); }); - #if ENABLED(RESONANCE_TEST) - SUBMENU(MSG_RESONANCE_TEST, menu_resonance_test); - #endif } END_MENU(); @@ -651,6 +648,13 @@ void menu_motion() { SUBMENU(MSG_FIXED_TIME_MOTION, menu_ft_motion); #endif + // + // M495 Resonance Test + // + #if ENABLED(RESONANCE_TEST) + SUBMENU(MSG_RESONANCE_TEST, menu_resonance_test); + #endif + // // Pen up/down menu // From b6951792924f8aeebcc598d34bb77d43772a7896 Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Sun, 18 Jan 2026 15:16:47 +0100 Subject: [PATCH 09/25] Minor fast_sin optimization --- Marlin/src/feature/resonance/resonance_generator.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index ca085b2b51..b399aaa24a 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -80,11 +80,10 @@ void ResonanceGenerator::reset() { // Fast sine approximation float ResonanceGenerator::fast_sin(float x) { - static constexpr float INV_TAU = (1.0f / M_TAU); // Reduce the angle to [-π, π] - const float y = x * INV_TAU; // Multiples of 2π - int k = static_cast(y); // Truncates toward zero + const float y = x * (1.0f / M_TAU); // Multiples of 2π + int k = static_cast(y); // Truncates toward zero // Negative? The truncation is one too high. if (y < 0.0f) --k; // Correct for negatives @@ -95,8 +94,11 @@ float ResonanceGenerator::fast_sin(float x) { else if (r < -M_PI) r += M_TAU; - // Cheap polynomial approximation of sin(r) - return r * (1.27323954f - 0.405284735f * ABS(r)); + // Optimized polynomial approximation + // Using sin(x) ≈ x(1 - (x²/π²) * 0.785398163) where 0.785398163 ≈ 1/π + const float r2 = r * r; + + return r * (1.0f - 0.101321184f * r2); } void ResonanceGenerator::fill_stepper_plan_buffer() { From d18b7c4db5c3cd0a6eca17321ddfb08525301160 Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Mon, 19 Jan 2026 23:55:10 +0100 Subject: [PATCH 10/25] Refactor calc_next_pos method declaration --- Marlin/src/feature/resonance/resonance_generator.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index 9bce6944cc..07b5b70c35 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -67,19 +67,12 @@ class ResonanceGenerator { void abort(); // Abort resonance test private: - float calc_next_pos() { - // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) - const float amplitude = amplitude_precalc / current_freq; - // Phase in radians - const float phase = current_freq * M_TAU * rt_time; - // Position Offset : between -A and +A - return (rt_params.start_pos[rt_params.axis] + amplitude * fast_sin(phase)); - } - float fast_sin(float x); // Fast sine approximation + float calc_next_pos(); // Calculate next position static float rt_time; // Test timer static float freq_mul; // Frequency multiplier for sine sweeping static float amplitude_precalc; // Precalculated part of amplitude formula float current_freq; // Current frequency being generated in sinusoidal motion + static float phase; // Current phase in radians static xyze_float_t traj_coords; static bool active; // Resonance test active static bool done; // Resonance test done From c6014a670d4943a9c0a93cf574fde7c6421b726e Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Mon, 19 Jan 2026 23:59:44 +0100 Subject: [PATCH 11/25] Refactor sine approximation to calc_next_pos Refactor fast_sin to calc_next_pos for improved sine calculation and phase handling. --- .../feature/resonance/resonance_generator.cpp | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index b399aaa24a..b725f747d4 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -37,6 +37,7 @@ bool ResonanceGenerator::done = false; // Resonance test float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer float ResonanceGenerator::timeline = 0.0f; float ResonanceGenerator::amplitude_precalc; +float ResonanceGenerator::phase = 0.0f; float ResonanceGenerator::freq_mul; xyze_float_t ResonanceGenerator:: traj_coords; @@ -78,27 +79,19 @@ void ResonanceGenerator::reset() { done = false; } -// Fast sine approximation -float ResonanceGenerator::fast_sin(float x) { +float ResoanaceGenerator::calc_next_pos() { + // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) + const float amplitude = amplitude_precalc / current_freq; - // Reduce the angle to [-π, π] - const float y = x * (1.0f / M_TAU); // Multiples of 2π - int k = static_cast(y); // Truncates toward zero + // Phase accumulation in radians + phase += current_freq * M_TAU * rt_time; + if (phase >= M_TAU) phase -= M_TAU; - // Negative? The truncation is one too high. - if (y < 0.0f) --k; // Correct for negatives - - float r = x - k * M_TAU; // -π <= r <= π - if (r > M_PI) - r -= M_TAU; - else if (r < -M_PI) - r += M_TAU; - - // Optimized polynomial approximation - // Using sin(x) ≈ x(1 - (x²/π²) * 0.785398163) where 0.785398163 ≈ 1/π + const float r = (phase > M_PI) ? (phase - M_TAU) : phase; const float r2 = r * r; - - return r * (1.0f - 0.101321184f * r2); + + // New postion + return rt_params.start_pos[rt_params.axis] + amplitude * r * (1.0f - 0.101321184f * r2); } void ResonanceGenerator::fill_stepper_plan_buffer() { @@ -115,9 +108,6 @@ void ResonanceGenerator::fill_stepper_plan_buffer() { // Resonate the axis being tested traj_coords[rt_params.axis] = calc_next_pos(); - // Increment for the next point (before calling out) - rt_time += FTM_TS; - // Store in buffer ftMotion.stepping_enqueue(traj_coords); } From c4ce063f2b089ebaea548bebf67e490e8c44478b Mon Sep 17 00:00:00 2001 From: narno2202 <130909513+narno2202@users.noreply.github.com> Date: Tue, 20 Jan 2026 00:05:01 +0100 Subject: [PATCH 12/25] Fix missing () --- Marlin/src/feature/resonance/resonance_generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index b725f747d4..63560f39c9 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -46,7 +46,7 @@ ResonanceGenerator rtg; ResonanceGenerator::ResonanceGenerator() {} void ResonanceGenerator::start() { - gcode.home_all_axes; // For safety and ensure known axes + gcode.home_all_axes(); // For safety and ensure known axes // Safe Acceleration per Hz for Z axis if (rt_params.axis == Z_AXIS && rt_params.accel_per_hz > 15.0f) From 7343fc4aeaa1116a8a9311613ed6b988a39f6510 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Tue, 20 Jan 2026 00:13:29 +0100 Subject: [PATCH 13/25] Typo again --- Marlin/src/feature/resonance/resonance_generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 63560f39c9..0939f6ecd5 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -79,7 +79,7 @@ void ResonanceGenerator::reset() { done = false; } -float ResoanaceGenerator::calc_next_pos() { +float ResonanceGenerator::calc_next_pos() { // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) const float amplitude = amplitude_precalc / current_freq; From 29bee75e692b56d9aff5010f1130119d64c51f56 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Wed, 21 Jan 2026 15:18:11 +0100 Subject: [PATCH 14/25] Make traj_coords local again --- Marlin/src/feature/resonance/resonance_generator.cpp | 4 +--- Marlin/src/feature/resonance/resonance_generator.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 0939f6ecd5..5ce5d217f3 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -39,7 +39,6 @@ float ResonanceGenerator::timeline = 0.0f; float ResonanceGenerator::amplitude_precalc; float ResonanceGenerator::phase = 0.0f; float ResonanceGenerator::freq_mul; -xyze_float_t ResonanceGenerator:: traj_coords; ResonanceGenerator rtg; @@ -56,7 +55,6 @@ void ResonanceGenerator::start() { do_blocking_move_to_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING); rt_params.start_pos = current_position; - traj_coords = rt_params.start_pos; rt_time = FTM_TS; active = true; done = false; @@ -95,7 +93,7 @@ float ResonanceGenerator::calc_next_pos() { } void ResonanceGenerator::fill_stepper_plan_buffer() { - //xyze_float_t traj_coords = rt_params.start_pos; + xyze_float_t traj_coords = rt_params.start_pos; while (!ftMotion.stepping.is_full()) { // Calculate current frequency diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index 07b5b70c35..31bf581b76 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -73,7 +73,6 @@ class ResonanceGenerator { static float amplitude_precalc; // Precalculated part of amplitude formula float current_freq; // Current frequency being generated in sinusoidal motion static float phase; // Current phase in radians - static xyze_float_t traj_coords; static bool active; // Resonance test active static bool done; // Resonance test done }; From 0935de3becb066556aa25f704040a6732fa8f455 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Sat, 24 Jan 2026 23:32:41 +0100 Subject: [PATCH 15/25] Add Resonance Test to standard Motion --- .../feature/resonance/resonance_generator.cpp | 133 ++++++++++++++---- .../feature/resonance/resonance_generator.h | 19 ++- .../src/gcode/feature/resonance/M495_M496.cpp | 20 +-- Marlin/src/lcd/menu/menu_motion.cpp | 1 - Marlin/src/module/stepper.cpp | 115 +++++++++++++++ Marlin/src/module/stepper.h | 5 + 6 files changed, 251 insertions(+), 42 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 5ce5d217f3..1e8414b9b2 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -24,55 +24,102 @@ #if ENABLED(RESONANCE_TEST) -#include "../../module/ft_motion.h" +#if ENABLED(FT_MOTION) + #include "../../module/ft_motion.h" +#endif + +#if HAS_STANDARD_MOTION + #include "../../module/stepper.h" +#endif + #include "resonance_generator.h" #include "../../gcode/gcode.h" // for home_all_axes -#include - resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test parameters bool ResonanceGenerator::active = false; // Resonance test active bool ResonanceGenerator::done = false; // Resonance test done -float ResonanceGenerator::rt_time = FTM_TS; // Resonance test timer +float ResonanceGenerator::rt_time; // Resonance test timer float ResonanceGenerator::timeline = 0.0f; float ResonanceGenerator::amplitude_precalc; -float ResonanceGenerator::phase = 0.0f; float ResonanceGenerator::freq_mul; +float ResonanceGenerator::phase = 0.0f; + +#if HAS_STANDARD_MOTION + block_t ResonanceGenerator::block; +#endif ResonanceGenerator rtg; ResonanceGenerator::ResonanceGenerator() {} void ResonanceGenerator::start() { - gcode.home_all_axes(); // For safety and ensure known axes - - // Safe Acceleration per Hz for Z axis - if (rt_params.axis == Z_AXIS && rt_params.accel_per_hz > 15.0f) - rt_params.accel_per_hz = 15.0f; + gcode.home_all_axes(); // Always home axes first // Always move to the center of the bed do_blocking_move_to_xy(X_CENTER, Y_CENTER, Z_CLEARANCE_FOR_HOMING); rt_params.start_pos = current_position; - rt_time = FTM_TS; active = true; done = false; + + #if ENABLED(FT_MOTION) + if (ftMotion.cfg.active) + rt_time = FTM_TS; + #if HAS_STANDARD_MOTION + else + rt_time = 0.001f; + #endif + #else + rt_time = 0.001f; + #endif + + // Safe Acceleration per Hz for Z axis + if (rt_params.axis == Z_AXIS && rt_params.accel_per_hz > 15.0f) + rt_params.accel_per_hz = 15.0f; + + #if HAS_STANDARD_MOTION + if (TERN1(FT_MOTION, !ftMotion.cfg.active)){ + // Constant speed to be adjusted if necessary + block.reset(); + block.initial_rate = (rt_params.axis == Z_AXIS) ? 2000 : 8000; + } + #endif + // Precompute sine sweep const amplitude_precalc = (rt_params.amplitude_correction * rt_params.accel_per_hz * 0.25f) / sq(M_PI); current_freq = rt_params.min_freq; - const float inv_octave_duration = 1.0f / rt_params.octave_duration; - freq_mul = exp2f(FTM_TS * inv_octave_duration); + freq_mul = exp2f(rt_time / rt_params.octave_duration); } void ResonanceGenerator::abort() { reset(); - ftMotion.reset(); + #if(HAS_STANDARD_MOTION) + if (!TERN0(FT_MOTION, ftMotion.cfg.active)) + return; + #endif + #if ENABLED(FT_MOTION) + ftMotion.reset(); + #endif } void ResonanceGenerator::reset() { rt_params = resonance_test_params_t(); - rt_time = FTM_TS; + + #if ENABLED(FT_MOTION) + if (ftMotion.cfg.active) { + rt_time = FTM_TS; + } + #if HAS_STANDARD_MOTION + else { + rt_time = 0.001f; + block.reset(); + } + #endif + #else + rt_time = 0.001f; + block.reset(); + #endif active = false; done = false; } @@ -92,23 +139,59 @@ float ResonanceGenerator::calc_next_pos() { return rt_params.start_pos[rt_params.axis] + amplitude * r * (1.0f - 0.101321184f * r2); } -void ResonanceGenerator::fill_stepper_plan_buffer() { - xyze_float_t traj_coords = rt_params.start_pos; +#if ENABLED(FT_MOTION) + void ResonanceGenerator::fill_stepper_plan_buffer() { + xyze_pos_t traj_coords = rt_params.start_pos; - while (!ftMotion.stepping.is_full()) { + while (!ftMotion.stepping.is_full()) { + // Calculate current frequency + current_freq *= freq_mul; + if (current_freq > rt_params.max_freq) { + done = true; + return; + } + + // Resonate the axis being tested + traj_coords[rt_params.axis] = calc_next_pos(); + + // Store in buffer + ftMotion.stepping_enqueue(traj_coords); + } + } +#endif + +#if HAS_STANDARD_MOTION + block_t *ResonanceGenerator::generate_resonance_block() { + const float step_mm = planner.settings.axis_steps_per_mm[rt_params.axis]; + static float last_pos = 0.0f; + // Calculate current frequency current_freq *= freq_mul; if (current_freq > rt_params.max_freq) { done = true; - return; + return nullptr; } + + // Calculate next point position + const float target_pos = calc_next_pos(); - // Resonate the axis being tested - traj_coords[rt_params.axis] = calc_next_pos(); + // mm → steps + const float delta_mm = (target_pos - last_pos); + const int32_t delta_steps = abs(lround(delta_mm * step_mm)); + last_pos = target_pos; + // Steps for target axis + block.steps[rt_params.axis] = delta_steps; + // Total event count + block.step_event_count = delta_steps; - // Store in buffer - ftMotion.stepping_enqueue(traj_coords); - } -} + // Direction + if (delta_mm > 0) + block.direction_bits &= ~(1 << rt_params.axis); + else + block.direction_bits |= (1 << rt_params.axis); + + return █ + } +#endif #endif // RESONANCE_TEST diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index 31bf581b76..733b7377d4 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -25,6 +25,10 @@ #include +#if HAS_STANDARD_MOTION +#include "../../module/planner.h" +#endif + #ifndef M_TAU #define M_TAU (2.0f * M_PI) #endif @@ -41,7 +45,7 @@ typedef struct ResonanceTestParams { class ResonanceGenerator { public: - static resonance_test_params_t rt_params; // Resonance test parameters + static resonance_test_params_t rt_params; // Resonance test parameters static float timeline; // Timeline Value to calculate resonance frequency ResonanceGenerator(); @@ -56,7 +60,13 @@ class ResonanceGenerator { return rt_params.min_freq * exp2f(timeline / rt_params.octave_duration); } - void fill_stepper_plan_buffer(); // Fill stepper plan buffer with trajectory points + #if HAS_STANDARD_MOTION + block_t *generate_resonance_block(); // Generate planner block for standard motion + #endif + + #if ENABLED(FT_MOTION) + void fill_stepper_plan_buffer(); // Fill stepper plan buffer with trajectory points + #endif void setActive(const bool state) { active = state; } bool isActive() const { return active; } @@ -67,12 +77,15 @@ class ResonanceGenerator { void abort(); // Abort resonance test private: - float calc_next_pos(); // Calculate next position + float calc_next_pos(); // Calculate next position point based on current frequency static float rt_time; // Test timer static float freq_mul; // Frequency multiplier for sine sweeping static float amplitude_precalc; // Precalculated part of amplitude formula float current_freq; // Current frequency being generated in sinusoidal motion static float phase; // Current phase in radians + #if HAS_STANDARD_MOTION + static block_t block; + #endif static bool active; // Resonance test active static bool done; // Resonance test done }; diff --git a/Marlin/src/gcode/feature/resonance/M495_M496.cpp b/Marlin/src/gcode/feature/resonance/M495_M496.cpp index 68979c7ecb..638c9f3059 100644 --- a/Marlin/src/gcode/feature/resonance/M495_M496.cpp +++ b/Marlin/src/gcode/feature/resonance/M495_M496.cpp @@ -26,7 +26,6 @@ #include "../../gcode.h" #include "../../../lcd/marlinui.h" -#include "../../../module/ft_motion.h" #include "../../../feature/resonance/resonance_generator.h" void say_resonance_test() { @@ -146,23 +145,18 @@ void GcodeSuite::M495() { } if (parser.seen_test('S')) { - if (ftMotion.cfg.active) { - if (p.axis != NO_AXIS_ENUM) { - if (p.max_freq > p.min_freq) { - SERIAL_ECHOLN(F("Starting "), F("Resonance Test")); - rtg.start(); - // The function returns immediately, the test runs in the background. - } - else { - SERIAL_ECHOLNPGM("?End Frequency must be greater than Start Frequency"); - } + if (p.axis != NO_AXIS_ENUM) { + if (p.max_freq > p.min_freq) { + SERIAL_ECHOLN(F("Starting "), F("Resonance Test")); + rtg.start(); + // The function returns immediately, the test runs in the background. } else { - SERIAL_ECHOLN(F("?Specify X, Y, or Z axis"), F(" first")); + SERIAL_ECHOLNPGM("?End Frequency must be greater than Start Frequency"); } } else { - SERIAL_ECHOLN(F("?Activate FT Motion to run the "), F("Resonance Test")); + SERIAL_ECHOLN(F("?Specify X, Y, or Z axis"), F(" first")); } } } diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index 3237a0b63d..386e343002 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -559,7 +559,6 @@ void menu_move() { EDIT_ITEM(bool, MSG_FTM_AXIS_SYNC, &editable.state, []{ queue.inject(TS(F("M493"), IAXIS_CHAR(MenuItemBase::itemIndex), 'T', int(editable.state))); }); - } END_MENU(); diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index cfb2617024..9926f02f6e 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -81,6 +81,10 @@ Stepper stepper; // Singleton #include "ft_motion.h" #endif +#if (ENABLED(RESONANCE_TEST) && HAS_STANDARD_MOTION) + #include "../feature/resonance/resonance_generator.h" +#endif + #include "../lcd/marlinui.h" #include "../gcode/queue.h" #include "../sd/cardreader.h" @@ -618,6 +622,103 @@ bool Stepper::disable_axis(const AxisEnum axis) { return can_disable; } +#if (ENABLED(RESONANCE_TEST) && HAS_STANDARD_MOTION) + hal_timer_t Stepper::resonance_block_phase_isr() { + + const hal_timer_t time_spent = HAL_timer_get_count(MF_TIMER_STEP); + #if MULTISTEPPING_LIMIT > 1 + if (steps_per_isr > 1 && time_spent_out_isr >= time_spent_in_isr + time_spent) + steps_per_isr >>= 1; + #endif + time_spent_in_isr = -time_spent; // Unsigned but guaranteed to be +ve when needed + time_spent_out_isr = 0; + + hal_timer_t interval = 0; + + // If current block is finished, reset pointer and finalize state + if (step_events_completed >= step_event_count) + discard_current_block(); + else + return calc_multistep_timer_interval(current_block->initial_rate); + + // If there is no current block at this point, generate a new block + if (!current_block){ + if ((current_block = rtg.generate_resonance_block())) { + + //Apply direction + DIR_WAIT_BEFORE(); + const uint8_t axis = rtg.rt_params.axis; + const bool fwd = current_block->direction_bits[axis]; + switch (axis) { + case X_AXIS: X_APPLY_DIR(fwd, false); + break; + case Y_AXIS: Y_APPLY_DIR(fwd, false); + break; + case Z_AXIS: Z_APPLY_DIR(fwd, false); + break; + } + + step_event_count = current_block->step_event_count; + + // No step events completed so far + step_events_completed = 0; + interval = calc_multistep_timer_interval(current_block->initial_rate); + } + else + rtg.abort(); + } + // Return the interval to wait + return interval; + } + + void Stepper::resonance_pulse_phase_isr() { + + // If there is no current block, do nothing + if (!current_block || step_events_completed >= step_event_count) return; + + // Count of pending loops and events for this iteration + const uint32_t pending_events = step_event_count - step_events_completed; + uint8_t events_to_do = _MIN(pending_events, steps_per_isr); + + // Just update the value we will get at the end of the loop + step_events_completed += events_to_do; + + USING_TIMED_PULSE(); + + // Take multiple steps per interrupt. For high speed moves. + bool firstStep = true; + const uint8_t axis = rtg.rt_params.axis; + + do { + + if (firstStep) + firstStep = false; + + switch (axis) { + case X_AXIS: + X_APPLY_STEP(STEP_STATE_X, false); + START_TIMED_PULSE(); + AWAIT_HIGH_PULSE(); + X_APPLY_STEP(!STEP_STATE_X, false); + break; + case Y_AXIS: + Y_APPLY_STEP(STEP_STATE_Y, false); + START_TIMED_PULSE(); + AWAIT_HIGH_PULSE(); + Y_APPLY_STEP(!STEP_STATE_Y, false); + break; + case Z_AXIS: + Z_APPLY_STEP(STEP_STATE_Z, false); + START_TIMED_PULSE(); + AWAIT_HIGH_PULSE(); + Z_APPLY_STEP(!STEP_STATE_Z, false); + break; + } + + } while (--events_to_do); + } +#endif + #if HAS_EXTRUDERS void Stepper::enable_extruder(E_TERN_(const uint8_t eindex)) { @@ -1646,6 +1747,19 @@ void Stepper::isr() { #if HAS_STANDARD_MOTION if (!using_ftMotion) { + #if ENABLED(RESONANCE_TEST) + if (rtg.isActive()) { + if (!nextMainISR) resonance_pulse_phase_isr(); + + hal.isr_on(); + if (!nextMainISR) nextMainISR = resonance_block_phase_isr(); + + interval = hal_timer_t(STEPPER_TIMER_RATE * 0.03); // Max wait of 30ms regardless of stepper timer frequency + NOMORE(interval, nextMainISR); // Time until the next Pulse / Block phase + nextMainISR -= interval; + } + else { + #endif TERN_(HAS_ZV_SHAPING, shaping_isr()); // Do Shaper stepping, if needed @@ -1709,6 +1823,7 @@ void Stepper::isr() { TERN_(BABYSTEPPING, if (nextBabystepISR != BABYSTEP_NEVER) nextBabystepISR -= interval); } + TERN_(RESONANCE_TEST, }) #endif // HAS_STANDARD_MOTION diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h index 370c9ffa8e..bbc4610e84 100644 --- a/Marlin/src/module/stepper.h +++ b/Marlin/src/module/stepper.h @@ -579,6 +579,11 @@ class Stepper { static hal_timer_t block_phase_isr(); #endif + #if (ENABLED(RESONANCE_TEST) && HAS_STANDARD_MOTION) + static void resonance_pulse_phase_isr(); + static hal_timer_t resonance_block_phase_isr(); + #endif + #if HAS_ZV_SHAPING static void shaping_isr(); #endif From 972e2d07f92414199081cbd888307c6b6668ecb6 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Sun, 25 Jan 2026 10:33:40 +0100 Subject: [PATCH 16/25] Useless friend void fill_stepper_buffer() in ft_motion.h --- Marlin/src/module/ft_motion.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h index be2a4dd3ac..37e8120416 100644 --- a/Marlin/src/module/ft_motion.h +++ b/Marlin/src/module/ft_motion.h @@ -238,10 +238,6 @@ typedef struct FTConfig { */ class FTMotion { - #if ENABLED(RESONANCE_TEST) - friend void ResonanceGenerator::fill_stepper_plan_buffer(); - #endif - public: // Public variables From 4964e01f89bd434558281c139ecb7d661d5b9c9e Mon Sep 17 00:00:00 2001 From: narno2202 Date: Sun, 25 Jan 2026 10:45:20 +0100 Subject: [PATCH 17/25] Remove useless #include --- Marlin/src/feature/resonance/resonance_generator.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 1e8414b9b2..8e251d38b9 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -28,10 +28,6 @@ #include "../../module/ft_motion.h" #endif -#if HAS_STANDARD_MOTION - #include "../../module/stepper.h" -#endif - #include "resonance_generator.h" #include "../../gcode/gcode.h" // for home_all_axes From a8f16f2df27bfd0e905da75b8eb41a00a4920d3b Mon Sep 17 00:00:00 2001 From: narno2202 Date: Wed, 28 Jan 2026 14:45:56 +0100 Subject: [PATCH 18/25] Fix last_pos init --- Marlin/src/feature/resonance/resonance_generator.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 8e251d38b9..bb5af81ed8 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -159,7 +159,7 @@ float ResonanceGenerator::calc_next_pos() { #if HAS_STANDARD_MOTION block_t *ResonanceGenerator::generate_resonance_block() { const float step_mm = planner.settings.axis_steps_per_mm[rt_params.axis]; - static float last_pos = 0.0f; + static float last_pos = rt_params.start_pos[rt_params.axis]; // Calculate current frequency current_freq *= freq_mul; @@ -173,12 +173,14 @@ float ResonanceGenerator::calc_next_pos() { // mm → steps const float delta_mm = (target_pos - last_pos); - const int32_t delta_steps = abs(lround(delta_mm * step_mm)); + const float prod = delta_mm * step_mm; + const int32_t delta_steps = (int32_t)(prod > 0.0f ? prod + 0.5f : prod - 0.5f); last_pos = target_pos; + // Steps for target axis - block.steps[rt_params.axis] = delta_steps; + block.steps[rt_params.axis] = abs(delta_steps); // Total event count - block.step_event_count = delta_steps; + block.step_event_count = abs(delta_steps); // Direction if (delta_mm > 0) From 55d96c043f020ce98f6eee8085a1a459de6ab6e3 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Thu, 29 Jan 2026 13:04:07 +0100 Subject: [PATCH 19/25] calc_next_pos() returns now offset --- .../feature/resonance/resonance_generator.cpp | 16 ++++++---------- .../src/feature/resonance/resonance_generator.h | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index bb5af81ed8..3c307d8de1 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -36,7 +36,6 @@ resonance_test_params_t ResonanceGenerator::rt_params; // Resonance test par bool ResonanceGenerator::active = false; // Resonance test active bool ResonanceGenerator::done = false; // Resonance test done float ResonanceGenerator::rt_time; // Resonance test timer -float ResonanceGenerator::timeline = 0.0f; float ResonanceGenerator::amplitude_precalc; float ResonanceGenerator::freq_mul; float ResonanceGenerator::phase = 0.0f; @@ -120,6 +119,7 @@ void ResonanceGenerator::reset() { done = false; } +// Returns the next position offset for the current frequency float ResonanceGenerator::calc_next_pos() { // Amplitude based on a sinusoidal wave : A = accel / (4 * PI^2 * f^2) const float amplitude = amplitude_precalc / current_freq; @@ -132,12 +132,13 @@ float ResonanceGenerator::calc_next_pos() { const float r2 = r * r; // New postion - return rt_params.start_pos[rt_params.axis] + amplitude * r * (1.0f - 0.101321184f * r2); + return (amplitude * r * (1.0f - 0.101321184f * r2)); } #if ENABLED(FT_MOTION) void ResonanceGenerator::fill_stepper_plan_buffer() { xyze_pos_t traj_coords = rt_params.start_pos; + const float start_pos = rt_params.start_pos[rt_params.axis]; while (!ftMotion.stepping.is_full()) { // Calculate current frequency @@ -148,7 +149,7 @@ float ResonanceGenerator::calc_next_pos() { } // Resonate the axis being tested - traj_coords[rt_params.axis] = calc_next_pos(); + traj_coords[rt_params.axis] = start_pos + calc_next_pos(); // Store in buffer ftMotion.stepping_enqueue(traj_coords); @@ -159,7 +160,6 @@ float ResonanceGenerator::calc_next_pos() { #if HAS_STANDARD_MOTION block_t *ResonanceGenerator::generate_resonance_block() { const float step_mm = planner.settings.axis_steps_per_mm[rt_params.axis]; - static float last_pos = rt_params.start_pos[rt_params.axis]; // Calculate current frequency current_freq *= freq_mul; @@ -167,15 +167,11 @@ float ResonanceGenerator::calc_next_pos() { done = true; return nullptr; } - - // Calculate next point position - const float target_pos = calc_next_pos(); // mm → steps - const float delta_mm = (target_pos - last_pos); + const float delta_mm = calc_next_pos(); const float prod = delta_mm * step_mm; const int32_t delta_steps = (int32_t)(prod > 0.0f ? prod + 0.5f : prod - 0.5f); - last_pos = target_pos; // Steps for target axis block.steps[rt_params.axis] = abs(delta_steps); @@ -183,7 +179,7 @@ float ResonanceGenerator::calc_next_pos() { block.step_event_count = abs(delta_steps); // Direction - if (delta_mm > 0) + if (delta_steps > 0) block.direction_bits &= ~(1 << rt_params.axis); else block.direction_bits |= (1 << rt_params.axis); diff --git a/Marlin/src/feature/resonance/resonance_generator.h b/Marlin/src/feature/resonance/resonance_generator.h index 733b7377d4..693ae7a511 100644 --- a/Marlin/src/feature/resonance/resonance_generator.h +++ b/Marlin/src/feature/resonance/resonance_generator.h @@ -46,7 +46,7 @@ typedef struct ResonanceTestParams { class ResonanceGenerator { public: static resonance_test_params_t rt_params; // Resonance test parameters - static float timeline; // Timeline Value to calculate resonance frequency + float timeline; // Timeline Value to calculate resonance frequency ResonanceGenerator(); From 649466230c914d40a8fa0131c64206199b00e7fa Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 30 Jan 2026 10:49:34 +0100 Subject: [PATCH 20/25] reset() simplification --- Marlin/src/feature/resonance/resonance_generator.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 3c307d8de1..afadcd5814 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -102,17 +102,9 @@ void ResonanceGenerator::reset() { rt_params = resonance_test_params_t(); #if ENABLED(FT_MOTION) - if (ftMotion.cfg.active) { - rt_time = FTM_TS; - } - #if HAS_STANDARD_MOTION - else { - rt_time = 0.001f; - block.reset(); - } - #endif + if (!ftMotion.cfg.active) + block.reset(); #else - rt_time = 0.001f; block.reset(); #endif active = false; From 27d617e806f377c873234dc7eed20dc57a1863df Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 30 Jan 2026 12:12:47 +0100 Subject: [PATCH 21/25] Fix reset() --- Marlin/src/feature/resonance/resonance_generator.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index afadcd5814..80087f9ed1 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -101,11 +101,9 @@ void ResonanceGenerator::abort() { void ResonanceGenerator::reset() { rt_params = resonance_test_params_t(); - #if ENABLED(FT_MOTION) - if (!ftMotion.cfg.active) + #if HAS_STANDARD_MOTION + if (!TERN0(FT_MOTION, ftMotion.cfg.active)) block.reset(); - #else - block.reset(); #endif active = false; done = false; From 2e3512a24cfd28e564b9e6bb900b5b483395ccb2 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Fri, 30 Jan 2026 23:51:36 +0100 Subject: [PATCH 22/25] Remove intermediate variable --- Marlin/src/feature/resonance/resonance_generator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Marlin/src/feature/resonance/resonance_generator.cpp b/Marlin/src/feature/resonance/resonance_generator.cpp index 80087f9ed1..1488c0f1a2 100644 --- a/Marlin/src/feature/resonance/resonance_generator.cpp +++ b/Marlin/src/feature/resonance/resonance_generator.cpp @@ -70,8 +70,8 @@ void ResonanceGenerator::start() { #endif // Safe Acceleration per Hz for Z axis - if (rt_params.axis == Z_AXIS && rt_params.accel_per_hz > 15.0f) - rt_params.accel_per_hz = 15.0f; + if (rt_params.axis == Z_AXIS) + NOMORE(rt_params.accel_per_hz, 15.0f); #if HAS_STANDARD_MOTION if (TERN1(FT_MOTION, !ftMotion.cfg.active)){ @@ -127,8 +127,7 @@ float ResonanceGenerator::calc_next_pos() { #if ENABLED(FT_MOTION) void ResonanceGenerator::fill_stepper_plan_buffer() { - xyze_pos_t traj_coords = rt_params.start_pos; - const float start_pos = rt_params.start_pos[rt_params.axis]; + static xyze_pos_t traj_coords = rt_params.start_pos; while (!ftMotion.stepping.is_full()) { // Calculate current frequency @@ -139,7 +138,7 @@ float ResonanceGenerator::calc_next_pos() { } // Resonate the axis being tested - traj_coords[rt_params.axis] = start_pos + calc_next_pos(); + traj_coords[rt_params.axis] += calc_next_pos(); // Store in buffer ftMotion.stepping_enqueue(traj_coords); From d3fc33ec74317cd6fb08e7973546e24223f506dc Mon Sep 17 00:00:00 2001 From: narno2202 Date: Sat, 31 Jan 2026 00:08:07 +0100 Subject: [PATCH 23/25] No need to call discard_current_block(), set to nullptr is enough --- Marlin/src/module/stepper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 9926f02f6e..e7b4404498 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -637,7 +637,7 @@ bool Stepper::disable_axis(const AxisEnum axis) { // If current block is finished, reset pointer and finalize state if (step_events_completed >= step_event_count) - discard_current_block(); + current_block = nullptr; else return calc_multistep_timer_interval(current_block->initial_rate); From fc41499a01ee6531e8f06517455036a9539a2fae Mon Sep 17 00:00:00 2001 From: narno2202 Date: Mon, 2 Feb 2026 12:03:13 +0100 Subject: [PATCH 24/25] Cleanup in resonance_block_isr() --- Marlin/src/module/stepper.cpp | 51 +++++++++++++++-------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index e7b4404498..57bbfdf92b 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -635,40 +635,33 @@ bool Stepper::disable_axis(const AxisEnum axis) { hal_timer_t interval = 0; - // If current block is finished, reset pointer and finalize state - if (step_events_completed >= step_event_count) - current_block = nullptr; - else + // If current block is not finished, continue with it + if (step_events_completed < step_event_count) return calc_multistep_timer_interval(current_block->initial_rate); - // If there is no current block at this point, generate a new block - if (!current_block){ - if ((current_block = rtg.generate_resonance_block())) { - - //Apply direction - DIR_WAIT_BEFORE(); - const uint8_t axis = rtg.rt_params.axis; - const bool fwd = current_block->direction_bits[axis]; - switch (axis) { - case X_AXIS: X_APPLY_DIR(fwd, false); - break; - case Y_AXIS: Y_APPLY_DIR(fwd, false); - break; - case Z_AXIS: Z_APPLY_DIR(fwd, false); - break; - } + // Current block is finished, reset pointer + current_block = nullptr; - step_event_count = current_block->step_event_count; - - // No step events completed so far - step_events_completed = 0; - interval = calc_multistep_timer_interval(current_block->initial_rate); + // Generate a new block + if ((current_block = rtg.generate_resonance_block())) { + // Apply direction + DIR_WAIT_BEFORE(); + const uint8_t axis = rtg.rt_params.axis; + const bool fwd = current_block->direction_bits[axis]; + switch (axis) { + case X_AXIS: X_APPLY_DIR(fwd, false); break; + case Y_AXIS: Y_APPLY_DIR(fwd, false); break; + case Z_AXIS: Z_APPLY_DIR(fwd, false); break; } - else - rtg.abort(); + + step_event_count = current_block->step_event_count; + step_events_completed = 0; + interval = calc_multistep_timer_interval(current_block->initial_rate); } - // Return the interval to wait - return interval; + else + rtg.abort(); + + return interval;; } void Stepper::resonance_pulse_phase_isr() { From 8b41d11ecca054be7513074cd7382e469dd51d89 Mon Sep 17 00:00:00 2001 From: narno2202 Date: Wed, 4 Feb 2026 14:21:21 +0100 Subject: [PATCH 25/25] In resonance_pulse_phase_isr(), try to keep switch outside the loop --- Marlin/src/module/stepper.cpp | 73 ++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 57bbfdf92b..2502ef61fe 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -676,39 +676,60 @@ bool Stepper::disable_axis(const AxisEnum axis) { // Just update the value we will get at the end of the loop step_events_completed += events_to_do; + #define RESONANCE_STEP_SEQUENCE(A) do { \ + A##_APPLY_STEP(STEP_STATE_##A, false); \ + START_TIMED_PULSE(); \ + AWAIT_HIGH_PULSE(); \ + A##_APPLY_STEP(!STEP_STATE_##A, false); \ + } while(0) + USING_TIMED_PULSE(); - // Take multiple steps per interrupt. For high speed moves. - bool firstStep = true; const uint8_t axis = rtg.rt_params.axis; - do { - - if (firstStep) - firstStep = false; - - switch (axis) { + switch (axis) { case X_AXIS: - X_APPLY_STEP(STEP_STATE_X, false); - START_TIMED_PULSE(); - AWAIT_HIGH_PULSE(); - X_APPLY_STEP(!STEP_STATE_X, false); + #if ISR_MULTI_STEPS + RESONANCE_STEP_SEQUENCE(X); + while (--events_to_do) { + AWAIT_LOW_PULSE(); + RESONANCE_STEP_SEQUENCE(X); + } + #else + do { + RESONANCE_STEP_SEQUENCE(X); + } while (--events_to_do); + #endif break; - case Y_AXIS: - Y_APPLY_STEP(STEP_STATE_Y, false); - START_TIMED_PULSE(); - AWAIT_HIGH_PULSE(); - Y_APPLY_STEP(!STEP_STATE_Y, false); - break; - case Z_AXIS: - Z_APPLY_STEP(STEP_STATE_Z, false); - START_TIMED_PULSE(); - AWAIT_HIGH_PULSE(); - Z_APPLY_STEP(!STEP_STATE_Z, false); - break; - } - } while (--events_to_do); + case Y_AXIS: + #if ISR_MULTI_STEPS + RESONANCE_STEP_SEQUENCE(Y); + while (--events_to_do) { + AWAIT_LOW_PULSE(); + RESONANCE_STEP_SEQUENCE(Y); + } + #else + do { + RESONANCE_STEP_SEQUENCE(Y); + } while (--events_to_do); + #endif + break; + + case Z_AXIS: + #if ISR_MULTI_STEPS + RESONANCE_STEP_SEQUENCE(Z); + while (--events_to_do) { + AWAIT_LOW_PULSE(); + RESONANCE_STEP_SEQUENCE(Z); + } + #else + do { + RESONANCE_STEP_SEQUENCE(Z); + } while (--events_to_do); + #endif + break; + } } #endif