code wiki / _hdl_build / nx_motion_plan.nx
nx_motion_plan.nx source
↩ module page · 62 lines · 2837 B
1// nx_motion_plan.nx -- SOVEREIGN PRINTER ARC rung 1a: trapezoidal motion planning (operator is
2// building our OWN printer; the QIDI is inferior). Every motion controller (Klipper/Marlin/GRBL
3// class) reduces a G-code move to an accel/cruise/decel velocity profile and then step pulses.
4// This is that math, integer-exact: distances um, velocities um/s, accel um/s^2, times ms.
5// d_acc = v^2 / (2a). If 2*d_acc <= dist -> TRAPEZOID (accel, cruise, decel).
6// Else -> TRIANGLE: v_peak = isqrt(a * dist) (from v^2 = 2a*(dist/2)).
7// Steps: steps = dist_um * steps_per_mm / 1000 (integer; fractional step carry = next rung).
8// HONEST SCOPE: single-move planning. Junction velocity (lookahead between moves), jerk limits,
9// and the step-pulse interval table for the MCU ISR are the flagged next rungs.
10// LAWS: struct-free, integer-only. license_tier: ORIGINAL
11import "nx_syscalls.nx"
12import "nx_vecmath.nx"
13
14const MP_TRAPEZOID: i64 = 0
15const MP_TRIANGLE: i64 = 1
16const MP_BAD_INPUT: i64 = 2 // defensive boundary: zero/negative dist, vmax, or accel
17
18// integer sqrt (Newton, floor)
19func mp_isqrt(x: i64) -> i64 { return vm_isqrt(x) }
20
21// plan one move. out[0]=shape out[1]=v_peak(um/s) out[2]=d_acc(um) out[3]=d_cruise(um)
22// out[4]=t_acc(ms) out[5]=t_cruise(ms) out[6]=t_total(ms). returns shape / MP_BAD_INPUT.
23func mp_plan(dist_um: i64, vmax: i64, acc: i64, out: *i64) -> i64 {
24 if dist_um <= 0 { out[0] = MP_BAD_INPUT; return MP_BAD_INPUT }
25 if vmax <= 0 { out[0] = MP_BAD_INPUT; return MP_BAD_INPUT }
26 if acc <= 0 { out[0] = MP_BAD_INPUT; return MP_BAD_INPUT }
27 let d_acc_full: i64 = (vmax * vmax) / (2 * acc)
28 if 2 * d_acc_full <= dist_um {
29 // trapezoid
30 out[0] = MP_TRAPEZOID
31 out[1] = vmax
32 out[2] = d_acc_full
33 out[3] = dist_um - 2 * d_acc_full
34 out[4] = (vmax * 1000) / acc // t_acc ms
35 out[5] = (out[3] * 1000) / vmax // t_cruise ms
36 out[6] = 2 * out[4] + out[5]
37 return MP_TRAPEZOID
38 }
39 // triangle: accelerate to v_peak over half the distance, decelerate
40 out[0] = MP_TRIANGLE
41 let vp: i64 = mp_isqrt(acc * dist_um)
42 out[1] = vp
43 out[2] = dist_um / 2
44 out[3] = 0
45 out[4] = (vp * 1000) / acc
46 out[5] = 0
47 out[6] = 2 * out[4]
48 return MP_TRIANGLE
49}
50
51// whole steps for a distance at steps_per_mm (fractional carry = flagged next rung)
52func mp_steps(dist_um: i64, steps_per_mm: i64) -> i64 {
53 return (dist_um * steps_per_mm) / 1000
54}
55
56// average step interval in MICROSECONDS during cruise at v (um/s), steps_per_mm:
57// steps/s = v * steps_per_mm / 1000 ; interval_us = 1e6 / steps_per_s
58func mp_cruise_interval_us(v_um_s: i64, steps_per_mm: i64) -> i64 {
59 let sps: i64 = (v_um_s * steps_per_mm) / 1000
60 if sps <= 0 { return 0 }
61 return 1000000 / sps
62}