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}