code wiki / (root) / nx_pcb_cam_gate.nx

nx_pcb_cam_gate.nx source

↩ module page · 164 lines · 7568 B

1// nx_pcb_cam_gate.nx -- R2 GATE of the Omniforge PCB/EDA leg: isolation-milling CAM (in-house loop). 2// Builds the demo board, computes isolation toolpaths via nx_pcb_cam, writes the real .gcode, then 3// VERIFIES: the offset coordinates are reconstructed EXACTLY from the integer model (axis-aligned 4// offsets are exact: hw+tool_r added/subtracted to a coordinate), pass count == 2*traces + pads, 5// retract count is consistent, the G21/G90/spindle/M2 headers are present, and a NEGATIVE CONTROL 6// (an offset wrong by 1um) is ABSENT. Proves design->in-house-millable G-code with no external fab, 7// integer-only/no-float. 100% sovereign. license_tier: ORIGINAL expect_exit: 0 8import "nx_syscalls.nx" 9import "nx_pcb_cam.nx" 10 11func sw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 12func sn(v: i64) -> i64 { 13 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 } 14 var m: i64 = v 15 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 16 let d: *u8 = sys_mmap(24); var k: i64 = 0 17 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 18 let o: *u8 = sys_mmap(24); var i: i64 = 0 19 while i < k { o[i] = d[k - 1 - i]; i = i + 1 } 20 sys_write(1, o, k); return 0 21} 22 23func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 24 25func contains(hay: *u8, haylen: i64, needle: *u8) -> i64 { 26 let nl: i64 = slen(needle) 27 if nl == 0 { return 1 } 28 var i: i64 = 0 29 while i + nl <= haylen { 30 var j: i64 = 0 31 var ok: i64 = 1 32 while j < nl { if hay[i + j] != needle[j] { ok = 0; j = nl } else { j = j + 1 } } 33 if ok == 1 { return 1 } 34 i = i + 1 35 } 36 return 0 37} 38 39func count_occur(hay: *u8, haylen: i64, needle: *u8) -> i64 { 40 let nl: i64 = slen(needle) 41 if nl == 0 { return 0 } 42 var c: i64 = 0 43 var i: i64 = 0 44 while i + nl <= haylen { 45 var j: i64 = 0 46 var ok: i64 = 1 47 while j < nl { if hay[i + j] != needle[j] { ok = 0; j = nl } else { j = j + 1 } } 48 if ok == 1 { c = c + 1; i = i + nl } else { i = i + 1 } 49 } 50 return c 51} 52 53// build expected "X<mm> Y<mm>" (NUL-terminated) into nb, matching cam's exact formatting 54func expect_xy(nb: *u8, x_um: i64, y_um: i64) -> i64 { 55 let len: *i64 = sys_mmap(8) as *i64 56 len[0] = 0 57 g_puts(nb, len, "X" as *u8); g_mm6(nb, len, x_um) 58 g_puts(nb, len, " Y" as *u8); g_mm6(nb, len, y_um) 59 nb[len[0]] = 0 as u8 60 return 0 61} 62 63func check(name: *u8, cond: i64, tot: *i64) -> i64 { 64 if cond == 1 { sw(" ok " as *u8); tot[0] = tot[0] + 1 } 65 else { sw(" FAIL " as *u8); tot[1] = tot[1] + 1 } 66 sw(name); sw("\n" as *u8) 67 return 0 68} 69 70func main() -> i64 { 71 let tot: *i64 = sys_mmap(16) as *i64 72 tot[0] = 0; tot[1] = 0 73 74 sw("=== nx_pcb_cam_gate R2 -- PCB isolation-milling CAM (in-house G-code, no external fab) ===\n" as *u8) 75 76 // demo board: 2 axis-aligned traces (hw=125um), 3 pads (r=300um) -- same geometry as R1 77 let tr_hw: *i64 = sys_mmap(64) as *i64 78 let tr_x1: *i64 = sys_mmap(64) as *i64 79 let tr_y1: *i64 = sys_mmap(64) as *i64 80 let tr_x2: *i64 = sys_mmap(64) as *i64 81 let tr_y2: *i64 = sys_mmap(64) as *i64 82 tr_hw[0] = 125; tr_x1[0] = 1000; tr_y1[0] = 1000; tr_x2[0] = 9000; tr_y2[0] = 1000 83 tr_hw[1] = 125; tr_x1[1] = 9000; tr_y1[1] = 1000; tr_x2[1] = 9000; tr_y2[1] = 6000 84 let n_tr: i64 = 2 85 86 let pad_r: *i64 = sys_mmap(64) as *i64 87 let pad_x: *i64 = sys_mmap(64) as *i64 88 let pad_y: *i64 = sys_mmap(64) as *i64 89 pad_r[0] = 300; pad_x[0] = 1000; pad_y[0] = 1000 90 pad_r[1] = 300; pad_x[1] = 9000; pad_y[1] = 1000 91 pad_r[2] = 300; pad_x[2] = 9000; pad_y[2] = 6000 92 let n_pad: i64 = 3 93 94 let tool_dia: i64 = 200 // 0.2mm isolation bit -> tool_radius 100um 95 let cut_depth: i64 = 100 // 0.1mm into copper 96 let safe_z: i64 = 2000 // 2mm retract 97 let feed: i64 = 60 98 let plunge: i64 = 30 99 100 let cap: i64 = 131072 101 let out: *u8 = sys_mmap(cap) 102 let glen: i64 = cam_emit(tr_hw, tr_x1, tr_y1, tr_x2, tr_y2, n_tr, 103 pad_r, pad_x, pad_y, n_pad, 104 tool_dia, cut_depth, safe_z, feed, plunge, out) 105 sw(" emitted G-code bytes=" as *u8); sn(glen); sw("\n" as *u8) 106 107 let fd: i64 = sys_openat_wr("knowledge/nx_omniforge_demo.gcode" as *u8, 0x1a4) 108 if fd >= 0 { sys_write(fd, out, glen); sys_close(fd); sw(" wrote knowledge/nx_omniforge_demo.gcode\n" as *u8) } 109 else { sw(" (artifact write skipped: open failed)\n" as *u8) } 110 111 // headers 112 check("G21 (mm) present" as *u8, contains(out, glen, "G21" as *u8), tot) 113 check("G90 (absolute) present" as *u8, contains(out, glen, "G90" as *u8), tot) 114 check("M3 spindle-on present" as *u8, contains(out, glen, "M3 S" as *u8), tot) 115 check("M5 spindle-off present" as *u8, contains(out, glen, "M5" as *u8), tot) 116 check("M2 program-end present" as *u8, contains(out, glen, "M2" as *u8), tot) 117 118 // offset round-trip exact (tool_radius=100 + hw=125 => offset 225um; axis-aligned => exact) 119 let nb: *u8 = sys_mmap(64) 120 var ok_off: i64 = 1 121 expect_xy(nb, 1000, 1225); if contains(out, glen, nb) == 0 { ok_off = 0 } // T0 +offset start 122 expect_xy(nb, 9000, 1225); if contains(out, glen, nb) == 0 { ok_off = 0 } // T0 +offset end 123 expect_xy(nb, 1000, 775); if contains(out, glen, nb) == 0 { ok_off = 0 } // T0 -offset start 124 expect_xy(nb, 8775, 1000); if contains(out, glen, nb) == 0 { ok_off = 0 } // T1 +offset start 125 expect_xy(nb, 8775, 6000); if contains(out, glen, nb) == 0 { ok_off = 0 } // T1 +offset end 126 expect_xy(nb, 9225, 1000); if contains(out, glen, nb) == 0 { ok_off = 0 } // T1 -offset start 127 check("trace isolation offsets (+/- 0.225mm) round-trip exact" as *u8, ok_off, tot) 128 129 // pad isolation ring corners (r=300 + tool_r=100 => 400um box half-extent) 130 var ok_pad: i64 = 1 131 expect_xy(nb, 600, 600); if contains(out, glen, nb) == 0 { ok_pad = 0 } // pad0 corner 132 expect_xy(nb, 1400, 1400); if contains(out, glen, nb) == 0 { ok_pad = 0 } // pad0 corner 133 expect_xy(nb, 9400, 6400); if contains(out, glen, nb) == 0 { ok_pad = 0 } // pad2 corner 134 check("pad isolation ring corners (+/- 0.400mm) round-trip exact" as *u8, ok_pad, tot) 135 136 // pass count: each trace -> 2 passes, each pad -> 1 ring = 2*n_tr + n_pad plunges 137 var nplunge: i64 = count_occur(out, glen, "G1 Z-" as *u8) 138 var exp_pass: i64 = 2 * n_tr + n_pad 139 var ok_pc: i64 = 0 140 if nplunge == exp_pass { ok_pc = 1 } 141 sw(" plunges=" as *u8); sn(nplunge); sw(" expected=" as *u8); sn(exp_pass); sw("\n" as *u8) 142 check("isolation pass count == 2*traces + pads (7)" as *u8, ok_pc, tot) 143 144 // retract count: 1 initial + one per pass + 1 final 145 var nret: i64 = count_occur(out, glen, "G0 Z2.000000" as *u8) 146 var exp_ret: i64 = exp_pass + 2 147 var ok_rc: i64 = 0 148 if nret == exp_ret { ok_rc = 1 } 149 check("retract count == passes + 2 (9)" as *u8, ok_rc, tot) 150 151 // plunge depth correct (0.1mm) 152 check("plunge depth Z-0.100000 present" as *u8, contains(out, glen, "G1 Z-0.100000" as *u8), tot) 153 154 // NEGATIVE CONTROL: an offset wrong by 1um must be ABSENT 155 expect_xy(nb, 1000, 1224) 156 var neg_absent: i64 = 0 157 if contains(out, glen, nb) == 0 { neg_absent = 1 } 158 check("NEG-CONTROL: off-by-1um toolpath is ABSENT (offsets have teeth)" as *u8, neg_absent, tot) 159 160 sw("=== VERDICT pass=" as *u8); sn(tot[0]); sw(" fail=" as *u8); sn(tot[1]); sw(" ===\n" as *u8) 161 if tot[1] == 0 { sys_exit(0) } 162 sys_exit(1) 163 return 1 164}