code wiki / _hdl_build / nx_contactfriction_gate.nx

nx_contactfriction_gate.nx source

↩ module page · 138 lines · 6745 B

1// nx_contactfriction_gate.nx -- FRICTION IS REAL AND ITS ABSENCE IS HONEST (softbody SB3, first leg). 2// 3// WHAT THIS PROVES. Press a probe into the tissue, then DRAG it sideways, and measure how far the tissue 4// follows. With a high coefficient the surface is carried along; with mu 0 it slides and the surface stays. 5// That difference IS friction -- a normal-only contact cannot produce it, and neither can a blur. 6// 7// THE THIRD STATE IS THE POINT. mu has three answers, not two: a number, ZERO, and UNSET. Zero is a 8// measured frictionless surface. UNSET means no citation exists and the solver declines to invent one -- 9// and the estate has NO cited macroscopic skin-on-skin Coulomb mu whose bytes it has read (the two papers 10// fetched 2026-08-25 give a Frictiometer device index and microasperity values of 0.004/0.001; the quoted 11// 0.2-0.5 band is from a review nobody here has read). So UNSET is today's honest production state, and 12// this gate proves it BEHAVES like frictionless rather than silently defaulting to some plausible number. 13// The mu used below is a FIXTURE VALUE for proving the mechanism, never a claim about skin. 14// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 15import "nx_syscalls.nx" 16import "nx_softtissue.nx" 17import "nx_gate_verdict.nx" 18 19const CFR_G: i64 = 9800 20const CFR_ITERS: i64 = 8 21const CFR_SETTLE: i64 = 24 22const CFR_RAMP: i64 = 24 23const CFR_PROBE_R: i64 = 2500 24const CFR_DEEP: i64 = 2000 25const CFR_H_MM: i64 = 10 26const CFR_DRAG: i64 = 1500 // lateral sweep after the press, cmm 27const CFR_DRAGSTEPS: i64 = 24 28const CFR_MU_STICK: i64 = 230 // FIXTURE ONLY (~0.9). Proves the mechanism; asserts nothing about skin. 29const CFR_MU_MID: i64 = 64 // FIXTURE ONLY (~0.25). Exists to prove the cone is a CONTINUUM, not a switch. 30 31// mean x of the cage's SURFACE particles -- the thing a sideways drag moves 32func cfr_surface_x(W: *i64) -> i64 { 33 let px: *i64 = W[ST_W_PX] as *i64 34 let srf: *i64 = W[ST_W_SRF] as *i64 35 let np: i64 = W[ST_W_NP] 36 var sum: i64 = 0 37 var n: i64 = 0 38 var i: i64 = 0 39 while i < np { 40 if srf[i] == 1 { sum = sum + px[i]; n = n + 1 } 41 i = i + 1 42 } 43 if n == 0 { return 0 } 44 return sum/n 45} 46 47// Press, set mu, drag sideways, and report how far the surface followed. out[0]=follow out[1]=surface_n 48func cfr_drag(mu: i64, out: *i64) -> i64 { 49 let W: *i64 = st_new(ST_PROF_SMALL_FIRM, CFR_H_MM) 50 st_run(W, 0, 0 - CFR_G, 0, CFR_SETTLE, ST_DT_REF_US, CFR_ITERS) 51 let m: *i64 = sys_mmap(64*8) as *i64 52 st_measure(W, m) 53 let z_touch: i64 = m[ST_M_PROJ] + CFR_PROBE_R 54 let z_in: i64 = z_touch - CFR_DEEP 55 var p: i64 = 0 56 while p <= CFR_RAMP { 57 st_set_touch(W, 1, 0, 0, z_touch - CFR_DEEP*p/CFR_RAMP, CFR_PROBE_R) 58 st_run(W, 0, 0 - CFR_G, 0, 1, ST_DT_REF_US, CFR_ITERS) 59 p = p + 1 60 } 61 // friction is armed AFTER the press so the normal approach is identical in every arm -- otherwise a 62 // difference in the final surface could have come from the way it went in, not from the drag. 63 st_set_friction(W, mu) 64 let x0: i64 = cfr_surface_x(W) 65 var s: i64 = 1 66 while s <= CFR_DRAGSTEPS { 67 st_set_touch(W, 1, CFR_DRAG*s/CFR_DRAGSTEPS, 0, z_in, CFR_PROBE_R) 68 st_run(W, 0, 0 - CFR_G, 0, 1, ST_DT_REF_US, CFR_ITERS) 69 s = s + 1 70 } 71 let x1: i64 = cfr_surface_x(W) 72 out[0] = x1 - x0 73 out[1] = W[ST_W_NP] 74 return 0 75} 76 77func main() -> i64 { 78 let ctr: *i64 = gv_ctr() 79 gv_head("nx_contactfriction_gate -- a dragged probe carries the surface, and UNSET declines to" as *u8) 80 81 let a: *i64 = sys_mmap(8*8) as *i64 82 let b: *i64 = sys_mmap(8*8) as *i64 83 let c: *i64 = sys_mmap(8*8) as *i64 84 let d: *i64 = sys_mmap(8*8) as *i64 85 cfr_drag(ST_MU_UNSET, a) 86 cfr_drag(0, b) 87 cfr_drag(CFR_MU_STICK, c) 88 cfr_drag(CFR_MU_MID, d) 89 90 gv_puts(" surface follow over a "); gv_num(CFR_DRAG); gv_puts(" cmm drag: unset=" as *u8); gv_num(a[0]) 91 gv_puts(" mu0=" as *u8); gv_num(b[0]) 92 gv_puts(" mu" as *u8); gv_num(CFR_MU_MID); gv_puts("=" as *u8); gv_num(d[0]) 93 gv_puts(" mu" as *u8); gv_num(CFR_MU_STICK); gv_puts("=" as *u8); gv_num(c[0]); gv_puts("\n" as *u8) 94 95 // T1 the mechanism FIRES: a high coefficient carries the surface further than a frictionless one. 96 // A normal-only contact makes these two identical, so this is the tooth that separates them. 97 var t1: i64 = 0 98 if c[0] > b[0] { t1 = 1 } 99 gv_check("T1 FRICTION CARRIES: a high coefficient drags the surface further than mu=0 does" as *u8, t1, ctr) 100 101 // T2 UNSET behaves exactly like frictionless -- the honest production state, asserted rather than 102 // assumed. If an absent citation silently became a sticky default, this tooth is what catches it. 103 var t2: i64 = 0 104 if a[0] == b[0] { t2 = 1 } 105 gv_check("T2 UNSET is FRICTIONLESS, not a plausible default (unset follow == mu=0 follow exactly)" as *u8, t2, ctr) 106 107 // T3 the two are DISTINGUISHABLE states, not one value wearing two names. 108 var t3: i64 = 0 109 let Wq: *i64 = st_new(ST_PROF_SMALL_FIRM, CFR_H_MM) 110 if st_friction(Wq) == ST_MU_UNSET { t3 = 1 } 111 gv_check("T3 a FRESH cage reports mu UNSET, so no caller inherits an invented coefficient" as *u8, t3, ctr) 112 113 // neg-control: setting then clearing must return to the unset state, or the third state is one-way 114 // and a caller could never put the solver back into "no citation". 115 st_set_friction(Wq, CFR_MU_STICK) 116 var t4: i64 = 0 117 if st_friction(Wq) == CFR_MU_STICK { 118 st_set_friction(Wq, ST_MU_UNSET) 119 if st_friction(Wq) == ST_MU_UNSET { t4 = 1 } 120 } 121 gv_check("neg-control-friction-is-CLEARABLE-back-to-unset-not-a-one-way-latch" as *u8, t4, ctr) 122 123 // neg-control: the surface set must be non-empty, or every follow above was the mean of nothing 124 // and all three arms would read 0 while the teeth still passed. 125 var t5: i64 = 0 126 if a[1] > 0 { t5 = 1 } 127 gv_check("neg-control-the-cage-HAS-particles-so-the-follow-figures-are-not-means-of-an-empty-set" as *u8, t5, ctr) 128 129 // T6 the cone is a CONTINUUM, not a switch. T1 alone passes just as happily for an implementation 130 // that sticks above some threshold and slides below it -- which is not Coulomb friction, and it 131 // would hand a therapeutic or haptic consumer a coefficient dial with two positions on it. 132 var t6: i64 = 0 133 if d[0] > b[0] { if c[0] > d[0] { t6 = 1 } } 134 gv_check("T6 MONOTONIC IN MU: a middling coefficient carries MORE than frictionless and LESS than sticky" as *u8, t6, ctr) 135 136 return gv_verdict("CONTACT-FRICTION" as *u8, ctr, 137 "positional Coulomb cone ported from nx_phys3d; mu is CITED-OR-UNSET and unset is proven frictionless" as *u8) 138}