code wiki / (root) / nx_bed_mesh_test.nx

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}