code wiki / (root) / nx_rot_health.nx

nx_rot_health.nx source

↩ module page · 140 lines · 5356 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 ------------------------------------------- 38import "nx_vecmath.nx" 39const NX_ROT_INSUFFICIENT_DATA: i64 = 0 40const NX_ROT_HEALTHY: i64 = 1 41const NX_ROT_ROUGH_WARNING: i64 = 2 // ISO zone B/C -- elevated vibration 42const NX_ROT_ROUGH_ALARM: i64 = 3 // ISO zone C/D -- unacceptable (imbalance/misalign/loose) 43const NX_ROT_BEARING_WEAR: i64 = 4 // high crest factor -- bearing impacts (early fault) 44const NX_ROT_OVERHEAT: i64 = 5 // bearing temp-rise over ceiling 45const NX_ROT_IDLE: i64 = 6 // not running (valid, not a fault) 46const NX_ROT_BAD_ARG: i64 = 7 47const NX_ROT_N: i64 = 8 48 49func nx_rot_verdict_is_valid(v: i64) -> i64 { 50 if v < 0 { return 0 } 51 if v >= NX_ROT_N { return 0 } 52 return 1 53} 54 55// ---- Data-driven spec = the healthy envelope (per machine class, #11) ---- 56struct RotSpec { 57 iso_warn_um_s: i64, // ISO zone B/C boundary (RMS velocity, micro-mm/s) 58 iso_alarm_um_s: i64, // ISO zone C/D boundary 59 crest_warn_x100: i64, // crest ceiling x100 (sine ~141; bearing faults rise past ~300) 60 temp_rise_warn_mC: i64, // bearing temp-rise ceiling (milli-degC) 61} 62 63struct RotEff { 64 rms_um_s: i64, 65 peak_um_s: i64, 66 crest_x100: i64, 67 temp_rise_mC: i64, 68 verdict: i64, 69} 70 71// Integer square root (Newton). Pure; no float. 72func nx_rot_isqrt(n: i64) -> i64 { return vm_isqrt(n) } 73 74// ---- The analysis -------------------------------------------------- 75// vib[i] = instantaneous vibration velocity samples (signed, micro-mm/s). 76// bearing_mC / ambient_mC = bearing + ambient temperature (milli-degC). 77// running = 1 if the machine ran during the window, 0 if stopped. 78func nx_rot_analyze(vib: *i64, n: i64, bearing_mC: i64, ambient_mC: i64, 79 running: i64, spec: *RotSpec, out: *RotEff) -> i64 { 80 out.rms_um_s = 0 81 out.peak_um_s = 0 82 out.crest_x100 = 0 83 out.temp_rise_mC = 0 84 out.verdict = NX_ROT_INSUFFICIENT_DATA 85 86 if n < 1 { 87 out.verdict = NX_ROT_INSUFFICIENT_DATA 88 return out.verdict 89 } 90 if running != 0 { 91 if running != 1 { 92 out.verdict = NX_ROT_BAD_ARG 93 return out.verdict 94 } 95 } 96 97 var ss: i64 = 0 98 var peak: i64 = 0 99 var i: i64 = 0 100 while i < n { 101 var v: i64 = vib[i] 102 if v < 0 { v = 0 - v } 103 if v > peak { peak = v } 104 ss = ss + v * v 105 i = i + 1 106 } 107 let meansq: i64 = ss / n 108 let rms: i64 = nx_rot_isqrt(meansq) 109 out.rms_um_s = rms 110 out.peak_um_s = peak 111 var crest: i64 = 100 112 if rms > 0 { crest = (peak * 100) / rms } 113 out.crest_x100 = crest 114 out.temp_rise_mC = bearing_mC - ambient_mC 115 116 if running == 0 { 117 out.verdict = NX_ROT_IDLE 118 return out.verdict 119 } 120 // severity-ordered: safety (overheat) > specific bearing fault (crest) > 121 // overall roughness (RMS zones). 122 if out.temp_rise_mC > spec.temp_rise_warn_mC { 123 out.verdict = NX_ROT_OVERHEAT 124 return out.verdict 125 } 126 if out.crest_x100 > spec.crest_warn_x100 { 127 out.verdict = NX_ROT_BEARING_WEAR 128 return out.verdict 129 } 130 if out.rms_um_s >= spec.iso_alarm_um_s { 131 out.verdict = NX_ROT_ROUGH_ALARM 132 return out.verdict 133 } 134 if out.rms_um_s >= spec.iso_warn_um_s { 135 out.verdict = NX_ROT_ROUGH_WARNING 136 return out.verdict 137 } 138 out.verdict = NX_ROT_HEALTHY 139 return out.verdict 140}