nx_bed_mesh_test.nx source
↩ module page · 108 lines · 4776 B
1// nx_bed_mesh_test.nx -- verify bilinear interp + layer taper for
2// slicer-side bed-mesh Z compensation.
3//
4// Closed-form invariants:
5// (a) Flat bed (all deltas 0) -> z_offset returns 0 anywhere
6// (b) set_point updates the right grid cell
7// (c) Grid-corner queries return the exact stored value
8// (d) Midpoint query returns the average of bracketing corners
9// (e) Out-of-bounds queries clamp to nearest edge (no extrapolation)
10// (f) Layer 0 returns full compensation
11// (g) Layer == taper_layers returns 0
12// (h) Layer between linearly interpolates (1/2 taper -> 1/2 offset)
13// (i) Bowl-shaped bed (corners 0, center +100μm) -> center query > 0
14// and edges == 0
15// (j) Bad indices return error verdict
16//
17// expect_exit: 0
18// license_tier: ORIGINAL
19
20import "nx_syscalls.nx"
21import "nx_bed_mesh.nx"
22
23const Q14: i64 = 16384
24
25func tolerance_ok(actual_q14: i64, expected_q14: i64, tol_q14: i64) -> i64 {
26 var diff: i64 = actual_q14 - expected_q14
27 if diff < 0 { diff = 0 - diff }
28 if diff <= tol_q14 { return 1 }
29 return 0
30}
31
32func main() -> i64 {
33 // 3x3 mesh over 0..200 mm in X and Y (Qidi-class bed)
34 let bed_min: i64 = 0
35 let bed_max: i64 = 200 * Q14
36 let m: *NxBedMesh = nx_bed_mesh_new(3, 3, bed_min, bed_max, bed_min, bed_max, 10)
37 if (m as i64) == 0 { return 5 }
38
39 // --- (a) Flat bed: any query returns 0 ---
40 if nx_bed_mesh_z_offset(m, 100 * Q14, 100 * Q14) != 0 { return 10 }
41 if nx_bed_mesh_z_offset(m, 0, 0) != 0 { return 11 }
42 if nx_bed_mesh_z_offset(m, bed_max, bed_max) != 0 { return 12 }
43
44 // --- (b)(c) set_point + corner-exact retrieval ---
45 let dz_100um: i64 = Q14 / 10 // 0.1 mm = 1638 Q14
46 if nx_bed_mesh_set_point(m, 0, 0, dz_100um) != 0 { return 20 }
47 if nx_bed_mesh_z_offset(m, 0, 0) != dz_100um { return 21 }
48 // Corner (2, 2) at bed_max:
49 let dz_neg: i64 = 0 - dz_100um
50 if nx_bed_mesh_set_point(m, 2, 2, dz_neg) != 0 { return 22 }
51 if nx_bed_mesh_z_offset(m, bed_max, bed_max) != dz_neg { return 23 }
52
53 // --- (d) Midpoint between (0,0)=+1638 and (1,0)=0 at X=50mm Y=0 ---
54 // Linear interp: 0.5 × 1638 + 0.5 × 0 = 819 (within ±2 LSB)
55 let mid_x: i64 = 50 * Q14
56 let mid_query: i64 = nx_bed_mesh_z_offset(m, mid_x, 0)
57 let expected_mid: i64 = dz_100um / 2
58 if tolerance_ok(mid_query, expected_mid, 5) != 1 { return 30 }
59
60 // --- (e) Out-of-bounds clamp to nearest edge ---
61 // Query at X = -100mm should clamp to corner (0,0) = +1638
62 let oob_query: i64 = nx_bed_mesh_z_offset(m, 0 - 100 * Q14, 0)
63 if oob_query != dz_100um { return 40 }
64 // Query at Y past bed_max should clamp to edge (0, 2) which is 0
65 let oob_y: i64 = nx_bed_mesh_z_offset(m, 0, bed_max + 50 * Q14)
66 if oob_y != 0 { return 41 }
67
68 // --- (f) Layer 0 returns full compensation ---
69 let l0: i64 = nx_bed_mesh_z_at_layer(m, 0, 0, 0)
70 if l0 != dz_100um { return 50 }
71
72 // --- (g) Layer == taper_layers returns 0 ---
73 let lN: i64 = nx_bed_mesh_z_at_layer(m, 0, 0, 10)
74 if lN != 0 { return 60 }
75
76 // --- (h) Layer 5 (half-taper) returns half compensation ---
77 let l5: i64 = nx_bed_mesh_z_at_layer(m, 0, 0, 5)
78 let expected_half: i64 = dz_100um / 2
79 if tolerance_ok(l5, expected_half, 5) != 1 { return 70 }
80
81 // --- (i) Bowl bed: center >> 0 with corners == 0 ---
82 let bowl: *NxBedMesh = nx_bed_mesh_new(3, 3, bed_min, bed_max, bed_min, bed_max, 10)
83 // Center high (+200 μm)
84 let dz_bowl: i64 = (Q14 / 10) * 2
85 nx_bed_mesh_set_point(bowl, 1, 1, dz_bowl)
86 let center_z: i64 = nx_bed_mesh_z_offset(bowl, 100 * Q14, 100 * Q14)
87 if center_z != dz_bowl { return 80 }
88 // Corner stays 0
89 let corner_z: i64 = nx_bed_mesh_z_offset(bowl, 0, 0)
90 if corner_z != 0 { return 81 }
91 // Midpoint along Y=100 between (0,1)=0 and (1,1)=dz_bowl: true
92 // bilinear gives exactly dz_bowl / 2 (cell has fy=0, fx=0.5).
93 let halfway: i64 = nx_bed_mesh_z_offset(bowl, 50 * Q14, 100 * Q14)
94 let expected_halfway: i64 = dz_bowl / 2
95 if tolerance_ok(halfway, expected_halfway, 10) != 1 { return 82 }
96 // Diagonal midpoint (50, 50): in a cell with ONE non-zero corner,
97 // bilinear gives 1/4 (NOT 1/2 -- surface interp ≠ line interp).
98 let quarter: i64 = nx_bed_mesh_z_offset(bowl, 50 * Q14, 50 * Q14)
99 let expected_quarter: i64 = dz_bowl / 4
100 if tolerance_ok(quarter, expected_quarter, 10) != 1 { return 83 }
101
102 // --- (j) Bad indices return error ---
103 if nx_bed_mesh_set_point(m, -1, 0, 0) == NX_BED_MESH_OK { return 90 }
104 if nx_bed_mesh_set_point(m, 0, 99, 0) == NX_BED_MESH_OK { return 91 }
105 if nx_bed_mesh_set_point(m, 3, 0, 0) == NX_BED_MESH_OK { return 92 } // n_x=3 → max ix=2
106
107 return 0
108}