From a893f98a708ef38498df19171c498d2bd8df30c0 Mon Sep 17 00:00:00 2001 From: Dmitry Butyugin Date: Sun, 19 May 2024 22:20:56 +0100 Subject: [PATCH] extruder: Sync extruder motion with input shaping Signed-off-by: Dmitry Butyugin --- klippy/chelper/__init__.py | 7 +- klippy/chelper/kin_extruder.c | 75 +++++++++++++++- klippy/chelper/kin_shaper.c | 26 +++--- klippy/chelper/kin_shaper.h | 17 ++++ klippy/extras/input_shaper.py | 131 ++++++++++++++++++++++++++-- klippy/kinematics/extruder.py | 155 +++++++++++++++++++--------------- 6 files changed, 315 insertions(+), 96 deletions(-) create mode 100644 klippy/chelper/kin_shaper.h diff --git a/klippy/chelper/__init__.py b/klippy/chelper/__init__.py index fe3cfebbe..cf05e4ded 100644 --- a/klippy/chelper/__init__.py +++ b/klippy/chelper/__init__.py @@ -49,6 +49,7 @@ "trapq.h", "pollreactor.h", "msgblock.h", + "kin_shaper.h", ] defs_stepcompress = """ @@ -176,11 +177,13 @@ , double pa_velocity, struct pressure_advance_params *pa_params); void extruder_set_pressure_advance_model_func(struct stepper_kinematics *sk , double (*func)(double, double, struct pressure_advance_params *)); + int extruder_set_shaper_params(struct stepper_kinematics *sk, char axis + , int n, double a[], double t[]); + double extruder_get_step_gen_window(struct stepper_kinematics *sk); """ defs_kin_shaper = """ - double input_shaper_get_step_generation_window( - struct stepper_kinematics *sk); + double input_shaper_get_step_gen_window(struct stepper_kinematics *sk); int input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis , int n, double a[], double t[]); int input_shaper_set_sk(struct stepper_kinematics *sk diff --git a/klippy/chelper/kin_extruder.c b/klippy/chelper/kin_extruder.c index 660946686..b4ccaaf5a 100644 --- a/klippy/chelper/kin_extruder.c +++ b/klippy/chelper/kin_extruder.c @@ -10,6 +10,7 @@ #include // memset #include "compiler.h" // __visible #include "itersolve.h" // struct stepper_kinematics +#include "kin_shaper.h" // struct shaper_pulses #include "pyhelper.h" // errorf #include "trapq.h" // move_get_distance @@ -89,6 +90,14 @@ static void pa_range_integrate(struct move *m, int axis, double move_time, double hst , double *pos_integral, double *pa_velocity_integral) { + while (unlikely(move_time < 0.)) { + m = list_prev_entry(m, node); + move_time += m->move_t; + } + while (unlikely(move_time > m->move_t)) { + move_time -= m->move_t; + m = list_next_entry(m, node); + } // Calculate integral for the current move *pos_integral = *pa_velocity_integral = 0.; double m_pos_int, m_pa_vel_int; @@ -123,6 +132,24 @@ pa_range_integrate(struct move *m, int axis, double move_time, double hst *pos_integral -= m_pos_int; *pa_velocity_integral -= m_pa_vel_int; } + *pos_integral += start_base * hst * hst; +} + +static void +shaper_pa_range_integrate(struct move *m, int axis, double move_time + , double hst, struct shaper_pulses *sp + , double *pos_integral, double *pa_velocity_integral) +{ + *pos_integral = *pa_velocity_integral = 0.; + int num_pulses = sp->num_pulses, i; + for (i = 0; i < num_pulses; ++i) { + double t = sp->pulses[i].t, a = sp->pulses[i].a; + double p_pos_int, p_pa_vel_int; + pa_range_integrate(m, axis, move_time + t, hst, + &p_pos_int, &p_pa_vel_int); + *pos_integral += a * p_pos_int; + *pa_velocity_integral += a * p_pa_vel_int; + } } struct pressure_advance_params { @@ -142,6 +169,7 @@ typedef double (*pressure_advance_func)( struct extruder_stepper { struct stepper_kinematics sk; + struct shaper_pulses sp[3]; struct pressure_advance_params pa_params; pressure_advance_func pa_func; double time_offset, half_smooth_time, inv_half_smooth_time2; @@ -197,16 +225,25 @@ extruder_calc_position(struct stepper_kinematics *sk, struct move *m struct coord e_pos, pa_vel; double move_dist = move_get_distance(m, move_time); for (i = 0; i < 3; ++i) { + int axis = 'x' + i; + struct shaper_pulses* sp = &es->sp[i]; + int num_pulses = sp->num_pulses; if (!hst) { - e_pos.axis[i] = m->axes_r.axis[i] * move_dist; + e_pos.axis[i] = num_pulses + ? shaper_calc_position(m, axis, move_time, sp) + : m->start_pos.axis[i] + m->axes_r.axis[i] * move_dist; pa_vel.axis[i] = 0.; } else { - pa_range_integrate(m, 'x' + i, move_time, hst, - &e_pos.axis[i], &pa_vel.axis[i]); + if (num_pulses) { + shaper_pa_range_integrate(m, axis, move_time, hst, sp, + &e_pos.axis[i], &pa_vel.axis[i]); + } else { + pa_range_integrate(m, axis, move_time, hst, + &e_pos.axis[i], &pa_vel.axis[i]); + } e_pos.axis[i] *= es->inv_half_smooth_time2; pa_vel.axis[i] *= es->inv_half_smooth_time2; } - e_pos.axis[i] += m->start_pos.axis[i]; } double position = e_pos.x + e_pos.y + e_pos.z; if (!hst) @@ -220,6 +257,15 @@ static void extruder_note_generation_time(struct extruder_stepper *es) { double pre_active = 0., post_active = 0.; + int i; + for (i = 0; i < 2; ++i) { + struct shaper_pulses* sp = &es->sp[i]; + if (!es->sp[i].num_pulses) continue; + pre_active = sp->pulses[sp->num_pulses-1].t > pre_active + ? sp->pulses[sp->num_pulses-1].t : pre_active; + post_active = -sp->pulses[0].t > post_active + ? -sp->pulses[0].t : post_active; + } pre_active += es->half_smooth_time + es->time_offset; if (pre_active < 0.) pre_active = 0.; post_active += es->half_smooth_time - es->time_offset; @@ -256,6 +302,27 @@ extruder_set_pressure_advance_model_func(struct stepper_kinematics *sk es->pa_func = func; } +int __visible +extruder_set_shaper_params(struct stepper_kinematics *sk, char axis + , int n, double a[], double t[]) +{ + if (axis != 'x' && axis != 'y') + return -1; + struct extruder_stepper *es = container_of(sk, struct extruder_stepper, sk); + struct shaper_pulses *sp = &es->sp[axis-'x']; + int status = init_shaper(n, a, t, sp); + extruder_note_generation_time(es); + return status; +} + +double __visible +extruder_get_step_gen_window(struct stepper_kinematics *sk) +{ + struct extruder_stepper *es = container_of(sk, struct extruder_stepper, sk); + return es->sk.gen_steps_pre_active > es->sk.gen_steps_post_active + ? es->sk.gen_steps_pre_active : es->sk.gen_steps_post_active; +} + struct stepper_kinematics * __visible extruder_stepper_alloc(void) { diff --git a/klippy/chelper/kin_shaper.c b/klippy/chelper/kin_shaper.c index 1fb0c933f..6a2f9ec71 100644 --- a/klippy/chelper/kin_shaper.c +++ b/klippy/chelper/kin_shaper.c @@ -11,6 +11,7 @@ #include // memset #include "compiler.h" // __visible #include "itersolve.h" // struct stepper_kinematics +#include "kin_shaper.h" // struct shaper_pulses #include "trapq.h" // struct move @@ -18,13 +19,6 @@ * Shaper initialization ****************************************************************/ -struct shaper_pulses { - int num_pulses; - struct { - double t, a; - } pulses[25]; -}; - // Shift pulses around 'mid-point' t=0 so that the input shaper is an identity // transformation for constant-speed motion (i.e. input_shaper(v * T) = v * T) static void @@ -38,7 +32,7 @@ shift_pulses(struct shaper_pulses *sp) sp->pulses[i].t -= ts; } -static int +int init_shaper(int n, double a[], double t[], struct shaper_pulses *sp) { if (n < 0 || n > ARRAY_SIZE(sp->pulses)) { @@ -89,9 +83,9 @@ get_axis_position_across_moves(struct move *m, int axis, double time) } // Calculate the position from the convolution of the shaper with input signal -static inline double -calc_position(struct move *m, int axis, double move_time - , struct shaper_pulses *sp) +inline double +shaper_calc_position(struct move *m, int axis, double move_time + , struct shaper_pulses *sp) { double res = 0.; int num_pulses = sp->num_pulses, i; @@ -124,7 +118,7 @@ shaper_x_calc_position(struct stepper_kinematics *sk, struct move *m struct input_shaper *is = container_of(sk, struct input_shaper, sk); if (!is->sx.num_pulses) return is->orig_sk->calc_position_cb(is->orig_sk, m, move_time); - is->m.start_pos.x = calc_position(m, 'x', move_time, &is->sx); + is->m.start_pos.x = shaper_calc_position(m, 'x', move_time, &is->sx); return is->orig_sk->calc_position_cb(is->orig_sk, &is->m, DUMMY_T); } @@ -136,7 +130,7 @@ shaper_y_calc_position(struct stepper_kinematics *sk, struct move *m struct input_shaper *is = container_of(sk, struct input_shaper, sk); if (!is->sy.num_pulses) return is->orig_sk->calc_position_cb(is->orig_sk, m, move_time); - is->m.start_pos.y = calc_position(m, 'y', move_time, &is->sy); + is->m.start_pos.y = shaper_calc_position(m, 'y', move_time, &is->sy); return is->orig_sk->calc_position_cb(is->orig_sk, &is->m, DUMMY_T); } @@ -150,9 +144,9 @@ shaper_xy_calc_position(struct stepper_kinematics *sk, struct move *m return is->orig_sk->calc_position_cb(is->orig_sk, m, move_time); is->m.start_pos = move_get_coord(m, move_time); if (is->sx.num_pulses) - is->m.start_pos.x = calc_position(m, 'x', move_time, &is->sx); + is->m.start_pos.x = shaper_calc_position(m, 'x', move_time, &is->sx); if (is->sy.num_pulses) - is->m.start_pos.y = calc_position(m, 'y', move_time, &is->sy); + is->m.start_pos.y = shaper_calc_position(m, 'y', move_time, &is->sy); return is->orig_sk->calc_position_cb(is->orig_sk, &is->m, DUMMY_T); } @@ -213,7 +207,7 @@ input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis } double __visible -input_shaper_get_step_generation_window(struct stepper_kinematics *sk) +input_shaper_get_step_gen_window(struct stepper_kinematics *sk) { struct input_shaper *is = container_of(sk, struct input_shaper, sk); return is->sk.gen_steps_pre_active > is->sk.gen_steps_post_active diff --git a/klippy/chelper/kin_shaper.h b/klippy/chelper/kin_shaper.h new file mode 100644 index 000000000..16012c4a4 --- /dev/null +++ b/klippy/chelper/kin_shaper.h @@ -0,0 +1,17 @@ +#ifndef __KIN_SHAPER_H +#define __KIN_SHAPER_H + +struct shaper_pulses { + int num_pulses; + struct { + double t, a; + } pulses[5]; +}; + +struct move; + +int init_shaper(int n, double a[], double t[], struct shaper_pulses *sp); +double shaper_calc_position(struct move *m, int axis, double move_time + , struct shaper_pulses *sp); + +#endif // kin_shaper.h diff --git a/klippy/extras/input_shaper.py b/klippy/extras/input_shaper.py index 99179895e..c24a573aa 100644 --- a/klippy/extras/input_shaper.py +++ b/klippy/extras/input_shaper.py @@ -73,6 +73,9 @@ def __init__(self, axis, config): def get_name(self): return "shaper_" + self.axis + def get_axis(self): + return self.axis + def get_shaper(self): return self.n, self.A, self.T @@ -80,7 +83,7 @@ def update(self, gcmd): self.params.update(gcmd) self.n, self.A, self.T = self.params.get_shaper() - def set_shaper_kinematics(self, sk): + def update_stepper_kinematics(self, sk): ffi_main, ffi_lib = chelper.get_ffi() success = ( ffi_lib.input_shaper_set_shaper_params( @@ -95,18 +98,37 @@ def set_shaper_kinematics(self, sk): ) return success + def update_extruder_kinematics(self, sk): + ffi_main, ffi_lib = chelper.get_ffi() + success = ( + ffi_lib.extruder_set_shaper_params( + sk, self.axis.encode(), self.n, self.A, self.T + ) + == 0 + ) + if not success: + self.disable_shaping() + ffi_lib.extruder_set_shaper_params( + sk, self.axis.encode(), self.n, self.A, self.T + ) + return success + def disable_shaping(self): + was_enabled = False if self.saved is None and self.n: self.saved = (self.n, self.A, self.T) + was_enabled = True A, T = shaper_defs.get_none_shaper() self.n, self.A, self.T = len(A), A, T + return was_enabled def enable_shaping(self): if self.saved is None: # Input shaper was not disabled - return + return False self.n, self.A, self.T = self.saved self.saved = None + return True def report(self, gcmd): info = " ".join( @@ -123,6 +145,8 @@ def __init__(self, config): self.printer = config.get_printer() self.printer.register_event_handler("klippy:connect", self.connect) self.toolhead = None + self.extruders = [] + self.config_extruder_names = config.getlist("enabled_extruders", []) self.shapers = [ AxisInputShaper("x", config), AxisInputShaper("y", config), @@ -136,12 +160,29 @@ def __init__(self, config): self.cmd_SET_INPUT_SHAPER, desc=self.cmd_SET_INPUT_SHAPER_help, ) + gcode.register_command( + "ENABLE_INPUT_SHAPER", + self.cmd_ENABLE_INPUT_SHAPER, + desc=self.cmd_ENABLE_INPUT_SHAPER_help, + ) + gcode.register_command( + "DISABLE_INPUT_SHAPER", + self.cmd_DISABLE_INPUT_SHAPER, + desc=self.cmd_DISABLE_INPUT_SHAPER_help, + ) def get_shapers(self): return self.shapers def connect(self): self.toolhead = self.printer.lookup_object("toolhead") + for en in self.config_extruder_names: + extruder = self.printer.lookup_object(en) + if not hasattr(extruder, "get_extruder_steppers"): + raise self.printer.config_error( + "Invalid extruder '%s' in [input_shaper]" % (en,) + ) + self.extruders.append(extruder) # Configure initial values self._update_input_shaping(error=self.printer.config_error) @@ -175,17 +216,20 @@ def _update_input_shaping(self, error=None): is_sk = self._get_input_shaper_stepper_kinematics(s) if is_sk is None: continue - old_delay = ffi_lib.input_shaper_get_step_generation_window(is_sk) + old_delay = ffi_lib.input_shaper_get_step_gen_window(is_sk) for shaper in self.shapers: if shaper in failed_shapers: continue - if not shaper.set_shaper_kinematics(is_sk): + if not shaper.update_stepper_kinematics(is_sk): failed_shapers.append(shaper) - new_delay = ffi_lib.input_shaper_get_step_generation_window(is_sk) + new_delay = ffi_lib.input_shaper_get_step_gen_window(is_sk) if old_delay != new_delay: self.toolhead.note_step_generation_scan_time( new_delay, old_delay ) + for e in self.extruders: + for es in e.get_extruder_steppers(): + failed_shapers.extend(es.update_input_shaping(self.shapers)) if failed_shapers: error = error or self.printer.command_error raise error( @@ -213,6 +257,83 @@ def cmd_SET_INPUT_SHAPER(self, gcmd): for shaper in self.shapers: shaper.report(gcmd) + cmd_ENABLE_INPUT_SHAPER_help = "Enable input shaper for given objects" + + def cmd_ENABLE_INPUT_SHAPER(self, gcmd): + self.toolhead.flush_step_generation() + axes = gcmd.get("AXIS", "") + msg = "" + for axis_str in axes.split(","): + axis = axis_str.strip().lower() + if not axis: + continue + shapers = [s for s in self.shapers if s.get_axis() == axis] + if not shapers: + raise gcmd.error("Invalid AXIS='%s'" % (axis_str,)) + for s in shapers: + if s.enable_shaping(): + msg += "Enabled input shaper for AXIS='%s'\n" % (axis_str,) + else: + msg += ( + "Cannot enable input shaper for AXIS='%s': " + "was not disabled\n" % (axis_str,) + ) + extruders = gcmd.get("EXTRUDER", "") + for en in extruders.split(","): + extruder_name = en.strip() + if not extruder_name: + continue + extruder = self.printer.lookup_object(extruder_name) + if not hasattr(extruder, "get_extruder_steppers"): + raise gcmd.error("Invalid EXTRUDER='%s'" % (en,)) + if extruder not in self.extruders: + self.extruders.append(extruder) + msg += "Enabled input shaper for '%s'\n" % (en,) + else: + msg += "Input shaper already enabled for '%s'\n" % (en,) + self._update_input_shaping() + gcmd.respond_info(msg) + + cmd_DISABLE_INPUT_SHAPER_help = "Disable input shaper for given objects" + + def cmd_DISABLE_INPUT_SHAPER(self, gcmd): + self.toolhead.flush_step_generation() + axes = gcmd.get("AXIS", "") + msg = "" + for axis_str in axes.split(","): + axis = axis_str.strip().lower() + if not axis: + continue + shapers = [s for s in self.shapers if s.get_axis() == axis] + if not shapers: + raise gcmd.error("Invalid AXIS='%s'" % (axis_str,)) + for s in shapers: + if s.disable_shaping(): + msg += "Disabled input shaper for AXIS='%s'\n" % (axis_str,) + else: + msg += ( + "Cannot disable input shaper for AXIS='%s': not " + "enabled or was already disabled\n" % (axis_str,) + ) + extruders = gcmd.get("EXTRUDER", "") + for en in extruders.split(","): + extruder_name = en.strip() + if not extruder_name: + continue + extruder = self.printer.lookup_object(extruder_name) + if extruder in self.extruders: + to_re_enable = [s for s in self.shapers if s.disable_shaping()] + for es in extruder.get_extruder_steppers(): + es.update_input_shaping(self.shapers) + for shaper in to_re_enable: + shaper.enable_shaping() + self.extruders.remove(extruder) + msg += "Disabled input shaper for '%s'\n" % (en,) + else: + msg += "Input shaper not enabled for '%s'\n" % (en,) + self._update_input_shaping() + gcmd.respond_info(msg) + def load_config(config): return InputShaper(config) diff --git a/klippy/kinematics/extruder.py b/klippy/kinematics/extruder.py index 96e3f310c..55b5d865f 100644 --- a/klippy/kinematics/extruder.py +++ b/klippy/kinematics/extruder.py @@ -10,21 +10,18 @@ class PALinearModel: name = "linear" - def __init__(self, config=None, other=None): - self.pressure_advance = 0.0 + def __init__(self, config=None): if config: self.pressure_advance = config.getfloat( - "pressure_advance", self.pressure_advance, minval=0.0 + "pressure_advance", 0.0, minval=0.0 ) - elif other: - self.pressure_advance = other.pressure_advance + else: + self.pressure_advance = 0.0 - def get_from_gcmd(self, gcmd): - new_model = PALinearModel(config=None, other=self) - new_model.pressure_advance = gcmd.get_float( + def update(self, gcmd): + self.pressure_advance = gcmd.get_float( "ADVANCE", self.pressure_advance, minval=0.0 ) - return new_model def enabled(self): return self.pressure_advance > 0.0 @@ -44,10 +41,7 @@ def get_func(self): class PANonLinearModel: - def __init__(self, config=None, other=None): - self.linear_advance = 0.0 - self.linear_offset = 0.0 - self.linearization_velocity = 0.0 + def __init__(self, config=None): if config: self.linear_advance = config.getfloat( "linear_advance", 0.0, minval=0.0 @@ -63,28 +57,26 @@ def __init__(self, config=None, other=None): self.linearization_velocity = config.getfloat( "linearization_velocity", 0.0, minval=0.0 ) - elif other: - self.linear_advance = other.linear_advance - self.linear_offset = other.linear_offset - self.linearization_velocity = other.linearization_velocity - - def get_from_gcmd(self, gcmd): - new_model = self.__class__(config=None, other=self) - new_model.linear_advance = gcmd.get_float( + else: + self.linear_advance = 0.0 + self.linear_offset = 0.0 + self.linearization_velocity = 0.0 + + def update(self, gcmd): + self.linear_advance = gcmd.get_float( "ADVANCE", self.linear_advance, minval=0.0 ) - new_model.linear_offset = gcmd.get_float( + self.linear_offset = gcmd.get_float( "OFFSET", self.linear_offset, minval=0.0 ) - new_model.linearization_velocity = gcmd.get_float( + self.linearization_velocity = gcmd.get_float( "VELOCITY", self.linearization_velocity ) - if new_model.linear_offset and new_model.linearization_velocity <= 0.0: + if self.linear_offset and self.linearization_velocity <= 0.0: raise gcmd.error( "VELOCITY must be set to a positive value " "when OFFSET is non-zero" ) - return new_model def enabled(self): return self.linear_advance > 0.0 or self.linear_offset > 0.0 @@ -124,8 +116,8 @@ def get_func(self): class PATanhModel(PANonLinearModel): name = "tanh" - def __init__(self, config=None, other=None): - PANonLinearModel.__init__(self, config, other) + def __init__(self, config=None): + PANonLinearModel.__init__(self, config) def get_func(self): ffi_main, ffi_lib = chelper.get_ffi() @@ -135,8 +127,8 @@ def get_func(self): class PAReciprModel(PANonLinearModel): name = "recipr" - def __init__(self, config=None, other=None): - PANonLinearModel.__init__(self, config, other) + def __init__(self, config=None): + PANonLinearModel.__init__(self, config) def get_func(self): ffi_main, ffi_lib = chelper.get_ffi() @@ -156,12 +148,10 @@ def __init__(self, config): self.pa_model = config.getchoice( "pressure_advance_model", self.pa_models, PALinearModel.name )(config) - self.pressure_advance_smooth_time = 0.0 - self.pressure_advance_time_offset = 0.0 - self.config_smooth_time = config.getfloat( + self.pressure_advance_smooth_time = config.getfloat( "pressure_advance_smooth_time", 0.040, above=0.0, maxval=0.200 ) - self.config_time_offset = config.getfloat( + self.pressure_advance_time_offset = config.getfloat( "pressure_advance_time_offset", 0.0, minval=-0.2, maxval=0.2 ) # Setup stepper @@ -175,6 +165,7 @@ def __init__(self, config): self.sk_extruder, self.pa_model.get_func() ) self.motion_queue = None + self.extruder = None # Register commands self.printer.register_event_handler( "klippy:connect", self._handle_connect @@ -211,10 +202,12 @@ def __init__(self, config): ) def _handle_connect(self): - toolhead = self.printer.lookup_object("toolhead") - toolhead.register_step_generator(self.stepper.generate_steps) + self.toolhead = self.printer.lookup_object("toolhead") + self.toolhead.register_step_generator(self.stepper.generate_steps) self._update_pressure_advance( - self.pa_model, self.config_smooth_time, self.config_time_offset + self.pa_model, + self.pressure_advance_smooth_time, + self.pressure_advance_time_offset, ) def get_status(self, eventtime): @@ -232,35 +225,26 @@ def find_past_position(self, print_time): return self.stepper.mcu_to_commanded_position(mcu_pos) def sync_to_extruder(self, extruder_name): - toolhead = self.printer.lookup_object("toolhead") - toolhead.flush_step_generation() + self.toolhead.flush_step_generation() if not extruder_name: - self.stepper.set_trapq(None) + if self.extruder is not None: + self.extruder.unlink_extruder_stepper(self) self.motion_queue = None + self.extruder = None return extruder = self.printer.lookup_object(extruder_name, None) if extruder is None or not isinstance(extruder, PrinterExtruder): raise self.printer.command_error( "'%s' is not a valid extruder." % (extruder_name,) ) - self.stepper.set_position(extruder.last_position) - self.stepper.set_trapq(extruder.get_trapq()) + extruder.link_extruder_stepper(self) self.motion_queue = extruder_name + self.extruder = extruder def _update_pressure_advance(self, pa_model, smooth_time, time_offset): - old_smooth_time = self.pressure_advance_smooth_time - if not self.pa_model.enabled(): - old_smooth_time = 0.0 - old_time_offset = self.pressure_advance_time_offset - new_smooth_time = smooth_time - if not pa_model.enabled(): - new_smooth_time = 0.0 - toolhead = self.printer.lookup_object("toolhead") - toolhead.note_step_generation_scan_time( - new_smooth_time * 0.5 + abs(time_offset), - old_delay=(old_smooth_time * 0.5 + abs(old_time_offset)), - ) + self.toolhead.flush_step_generation() ffi_main, ffi_lib = chelper.get_ffi() + old_delay = ffi_lib.extruder_get_step_gen_window(self.sk_extruder) if self.pa_model.name != pa_model.name: pa_func = pa_model.get_func() ffi_lib.extruder_set_pressure_advance_model_func( @@ -271,32 +255,49 @@ def _update_pressure_advance(self, pa_model, smooth_time, time_offset): self.sk_extruder, len(pa_params), pa_params, - new_smooth_time, + smooth_time, time_offset, ) + new_delay = ffi_lib.extruder_get_step_gen_window(self.sk_extruder) + if old_delay != new_delay: + self.toolhead.note_step_generation_scan_time(new_delay, old_delay) self.pa_model = pa_model self.pressure_advance_smooth_time = smooth_time self.pressure_advance_time_offset = time_offset + def update_input_shaping(self, shapers): + ffi_main, ffi_lib = chelper.get_ffi() + old_delay = ffi_lib.extruder_get_step_gen_window(self.sk_extruder) + failed_shapers = [] + for shaper in self.shapers: + if not shaper.update_extruder_kinematics(self.sk_extruder): + failed_shapers.append(shaper) + new_delay = ffi_lib.extruder_get_step_gen_window(self.sk_extruder) + if old_delay != new_delay: + self.toolhead.note_step_generation_scan_time(new_delay, old_delay) + return failed_shapers + cmd_SET_PRESSURE_ADVANCE_help = "Set pressure advance parameters" def cmd_default_SET_PRESSURE_ADVANCE(self, gcmd): extruder = self.printer.lookup_object("toolhead").get_extruder() - if extruder.extruder_stepper is None: + extruder_steppers = extruder.get_extruder_steppers() + if not extruder_steppers: raise gcmd.error("Active extruder does not have a stepper") - strapq = extruder.extruder_stepper.stepper.get_trapq() - if strapq is not extruder.get_trapq(): - raise gcmd.error("Unable to infer active extruder stepper") - extruder.extruder_stepper.cmd_SET_PRESSURE_ADVANCE(gcmd) + for extruder_stepper in extruder_steppers: + strapq = extruder_stepper.stepper.get_trapq() + if strapq is not extruder.get_trapq(): + raise gcmd.error("Unable to infer active extruder stepper") + extruder_stepper.cmd_SET_PRESSURE_ADVANCE(gcmd) def cmd_SET_PRESSURE_ADVANCE(self, gcmd): pa_model_name = gcmd.get("MODEL", self.pa_model.name) if pa_model_name not in self.pa_models: raise gcmd.error("Invalid MODEL='%s' choice" % (pa_model_name,)) + pa_model = self.pa_model if pa_model_name != self.pa_model.name: - pa_model = self.pa_models[pa_model_name]().get_from_gcmd(gcmd) - else: - pa_model = self.pa_model.get_from_gcmd(gcmd) + pa_model = self.pa_models[pa_model_name]() + pa_model.update(gcmd) smooth_time = gcmd.get_float( "SMOOTH_TIME", self.pressure_advance_smooth_time, @@ -407,14 +408,13 @@ def __init__(self, config, extruder_num): ) # Setup extruder stepper - self.extruder_stepper = None + self.extruder_steppers = [] if ( config.get("step_pin", None) is not None or config.get("dir_pin", None) is not None or config.get("rotation_distance", None) is not None ): - self.extruder_stepper = ExtruderStepper(config) - self.extruder_stepper.stepper.set_trapq(self.trapq) + self.link_extruder_stepper(ExtruderStepper(config)) # Register commands gcode = self.printer.lookup_object("gcode") if self.name == "extruder": @@ -429,14 +429,28 @@ def __init__(self, config, extruder_num): desc=self.cmd_ACTIVATE_EXTRUDER_help, ) + def link_extruder_stepper(self, extruder_stepper): + if extruder_stepper not in self.extruder_steppers: + self.extruder_steppers.append(extruder_stepper) + extruder_stepper.stepper.set_position(self.last_position) + extruder_stepper.stepper.set_trapq(self.trapq) + + def unlink_extruder_stepper(self, extruder_stepper): + if extruder_stepper in self.extruder_steppers: + self.extruder_steppers.remove(extruder_stepper) + extruder_stepper.stepper.set_trapq(None) + + def get_extruder_steppers(self): + return self.extruder_steppers + def update_move_time(self, flush_time, clear_history_time): self.trapq_finalize_moves(self.trapq, flush_time, clear_history_time) def get_status(self, eventtime): sts = self.heater.get_status(eventtime) sts["can_extrude"] = self.heater.can_extrude - if self.extruder_stepper is not None: - sts.update(self.extruder_stepper.get_status(eventtime)) + if self.extruder_steppers: + sts.update(self.extruder_steppers[0].get_status(eventtime)) return sts def get_name(self): @@ -529,9 +543,9 @@ def move(self, print_time, move): self.last_position[i] += extr_d * extr_r[i] def find_past_position(self, print_time): - if self.extruder_stepper is None: + if not self.extruder_steppers: return 0.0 - return self.extruder_stepper.find_past_position(print_time) + return self.extruder_steppers[0].find_past_position(print_time) def cmd_M104(self, gcmd, wait=False): # Set Extruder Temperature @@ -588,6 +602,9 @@ def calc_junction(self, prev_move, move): def get_name(self): return "" + def get_extruder_steppers(self): + return [] + def get_heater(self): raise self.printer.command_error("Extruder not configured")