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}