code wiki / (root) / nx_rot_health.nx

nx_rot_health.nx source

↩ module page · 148 lines · 5468 B

1// nx_rot_health.nx -- rotating-machine health kernel (#2 of the 4 physics 2// kernels). Independent vibration + thermal signature -> health verdict for 3// motors, pumps, fans, compressors, bearings. 4// 5// Operator (2026-06-23): the maintenance platform's computed top-build to cross 6// into market LEADER (beats Augury, the strongest incumbent) -- and the 2nd 7// physics kernel, which unlocks the whole rotating-equipment domain. 8// 9// FFT-FREE first cut = real entry-level predictive-maintenance practice, 10// integer/no-float: 11// - RMS overall severity (ISO-10816-style zones) -> imbalance/misalign/loose 12// - CREST factor (peak/RMS) -> bearing impacts; RISES BEFORE RMS does, so it 13// catches an incipient bearing fault an overall-amplitude reading MISSES 14// (the measured exceed) 15// - bearing temp-rise -> friction/overheat (safety) 16// (Frequency-resolved diagnosis -- 1x imbalance vs 2x misalignment vs bearing 17// pass-frequencies -- is a later FFT rung; this overall+crest+thermal cut is 18// what a handheld vibration pen / ISO-10816 overall measurement already uses.) 19// 20// NEVER-BRICK (#26): reads samples, emits a verdict; writes nothing to any 21// machine. Read-only by construction; no firmware path exists here. 22// 23// genealogy_id: project-maintenance-platform-sclass-2026-06-23 (kernel #2; CIQ top-build) 24// license_tier: ORIGINAL 25// 26// nx_capability_claims: 27// needs: [pointer_arithmetic] 28// provides: [rotating_machine_health_verdict, iso10816_overall_severity, 29// crest_factor_bearing_early_warning, bearing_overheat_detect] 30// safety: [no_floating_point, no_syscall, bounded_iteration, 31// read_only_no_device_write, no_firmware_write_by_construction, 32// sealed_enum_verdict] 33// verdict: [sealed_enum_rot, no_silent_failure] 34// license: ORIGINAL 35// kind: maintenance_runtime_primitive 36 37// ---- Sealed verdict enum ------------------------------------------- 38const NX_ROT_INSUFFICIENT_DATA: i64 = 0 39const NX_ROT_HEALTHY: i64 = 1 40const NX_ROT_ROUGH_WARNING: i64 = 2 // ISO zone B/C -- elevated vibration 41const NX_ROT_ROUGH_ALARM: i64 = 3 // ISO zone C/D -- unacceptable (imbalance/misalign/loose) 42const NX_ROT_BEARING_WEAR: i64 = 4 // high crest factor -- bearing impacts (early fault) 43const NX_ROT_OVERHEAT: i64 = 5 // bearing temp-rise over ceiling 44const NX_ROT_IDLE: i64 = 6 // not running (valid, not a fault) 45const NX_ROT_BAD_ARG: i64 = 7 46const NX_ROT_N: i64 = 8 47 48func nx_rot_verdict_is_valid(v: i64) -> i64 { 49 if v < 0 { return 0 } 50 if v >= NX_ROT_N { return 0 } 51 return 1 52} 53 54// ---- Data-driven spec = the healthy envelope (per machine class, #11) ---- 55struct RotSpec { 56 iso_warn_um_s: i64, // ISO zone B/C boundary (RMS velocity, micro-mm/s) 57 iso_alarm_um_s: i64, // ISO zone C/D boundary 58 crest_warn_x100: i64, // crest ceiling x100 (sine ~141; bearing faults rise past ~300) 59 temp_rise_warn_mC: i64, // bearing temp-rise ceiling (milli-degC) 60} 61 62struct RotEff { 63 rms_um_s: i64, 64 peak_um_s: i64, 65 crest_x100: i64, 66 temp_rise_mC: i64, 67 verdict: i64, 68} 69 70// Integer square root (Newton). Pure; no float. 71func nx_rot_isqrt(n: i64) -> i64 { 72 if n <= 0 { return 0 } 73 var x: i64 = n 74 var y: i64 = (x + 1) / 2 75 while y < x { 76 x = y 77 y = (x + n / x) / 2 78 } 79 return x 80} 81 82// ---- The analysis -------------------------------------------------- 83// vib[i] = instantaneous vibration velocity samples (signed, micro-mm/s). 84// bearing_mC / ambient_mC = bearing + ambient temperature (milli-degC). 85// running = 1 if the machine ran during the window, 0 if stopped. 86func nx_rot_analyze(vib: *i64, n: i64, bearing_mC: i64, ambient_mC: i64, 87 running: i64, spec: *RotSpec, out: *RotEff) -> i64 { 88 out.rms_um_s = 0 89 out.peak_um_s = 0 90 out.crest_x100 = 0 91 out.temp_rise_mC = 0 92 out.verdict = NX_ROT_INSUFFICIENT_DATA 93 94 if n < 1 { 95 out.verdict = NX_ROT_INSUFFICIENT_DATA 96 return out.verdict 97 } 98 if running != 0 { 99 if running != 1 { 100 out.verdict = NX_ROT_BAD_ARG 101 return out.verdict 102 } 103 } 104 105 var ss: i64 = 0 106 var peak: i64 = 0 107 var i: i64 = 0 108 while i < n { 109 var v: i64 = vib[i] 110 if v < 0 { v = 0 - v } 111 if v > peak { peak = v } 112 ss = ss + v * v 113 i = i + 1 114 } 115 let meansq: i64 = ss / n 116 let rms: i64 = nx_rot_isqrt(meansq) 117 out.rms_um_s = rms 118 out.peak_um_s = peak 119 var crest: i64 = 100 120 if rms > 0 { crest = (peak * 100) / rms } 121 out.crest_x100 = crest 122 out.temp_rise_mC = bearing_mC - ambient_mC 123 124 if running == 0 { 125 out.verdict = NX_ROT_IDLE 126 return out.verdict 127 } 128 // severity-ordered: safety (overheat) > specific bearing fault (crest) > 129 // overall roughness (RMS zones). 130 if out.temp_rise_mC > spec.temp_rise_warn_mC { 131 out.verdict = NX_ROT_OVERHEAT 132 return out.verdict 133 } 134 if out.crest_x100 > spec.crest_warn_x100 { 135 out.verdict = NX_ROT_BEARING_WEAR 136 return out.verdict 137 } 138 if out.rms_um_s >= spec.iso_alarm_um_s { 139 out.verdict = NX_ROT_ROUGH_ALARM 140 return out.verdict 141 } 142 if out.rms_um_s >= spec.iso_warn_um_s { 143 out.verdict = NX_ROT_ROUGH_WARNING 144 return out.verdict 145 } 146 out.verdict = NX_ROT_HEALTHY 147 return out.verdict 148}