code wiki / _hdl_build / nx_motion_plan.nx
nx_motion_plan.nx source
↩ module page · 73 lines · 3034 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"
12
13const MP_TRAPEZOID: i64 = 0
14const MP_TRIANGLE: i64 = 1
15const MP_BAD_INPUT: i64 = 2 // defensive boundary: zero/negative dist, vmax, or accel
16
17// integer sqrt (Newton, floor)
18func mp_isqrt(x: i64) -> i64 {
19 if x <= 0 { return 0 }
20 var r: i64 = x
21 if r > 1 { r = x / 2 }
22 var p: i64 = 0
23 var n: i64 = 0
24 while n < 64 {
25 p = r
26 r = (r + x / r) / 2
27 if r >= p { n = 64 } else { n = n + 1 }
28 }
29 return p
30}
31
32// plan one move. out[0]=shape out[1]=v_peak(um/s) out[2]=d_acc(um) out[3]=d_cruise(um)
33// out[4]=t_acc(ms) out[5]=t_cruise(ms) out[6]=t_total(ms). returns shape / MP_BAD_INPUT.
34func mp_plan(dist_um: i64, vmax: i64, acc: i64, out: *i64) -> i64 {
35 if dist_um <= 0 { out[0] = MP_BAD_INPUT; return MP_BAD_INPUT }
36 if vmax <= 0 { out[0] = MP_BAD_INPUT; return MP_BAD_INPUT }
37 if acc <= 0 { out[0] = MP_BAD_INPUT; return MP_BAD_INPUT }
38 let d_acc_full: i64 = (vmax * vmax) / (2 * acc)
39 if 2 * d_acc_full <= dist_um {
40 // trapezoid
41 out[0] = MP_TRAPEZOID
42 out[1] = vmax
43 out[2] = d_acc_full
44 out[3] = dist_um - 2 * d_acc_full
45 out[4] = (vmax * 1000) / acc // t_acc ms
46 out[5] = (out[3] * 1000) / vmax // t_cruise ms
47 out[6] = 2 * out[4] + out[5]
48 return MP_TRAPEZOID
49 }
50 // triangle: accelerate to v_peak over half the distance, decelerate
51 out[0] = MP_TRIANGLE
52 let vp: i64 = mp_isqrt(acc * dist_um)
53 out[1] = vp
54 out[2] = dist_um / 2
55 out[3] = 0
56 out[4] = (vp * 1000) / acc
57 out[5] = 0
58 out[6] = 2 * out[4]
59 return MP_TRIANGLE
60}
61
62// whole steps for a distance at steps_per_mm (fractional carry = flagged next rung)
63func mp_steps(dist_um: i64, steps_per_mm: i64) -> i64 {
64 return (dist_um * steps_per_mm) / 1000
65}
66
67// average step interval in MICROSECONDS during cruise at v (um/s), steps_per_mm:
68// steps/s = v * steps_per_mm / 1000 ; interval_us = 1e6 / steps_per_s
69func mp_cruise_interval_us(v_um_s: i64, steps_per_mm: i64) -> i64 {
70 let sps: i64 = (v_um_s * steps_per_mm) / 1000
71 if sps <= 0 { return 0 }
72 return 1000000 / sps
73}