nx_signal_compensator.nx source
↩ module page · 182 lines · 10318 B
1// nx_signal_compensator.nx -- meta-level inverse-response DSP correction.
2//
3// module: nishi-core.perception.signal_compensator
4// depends: nishi-core.perception.profile + nishi-core.perception.transducer_characterizer +
5// nishi-core.io.syscalls
6// disk_kb: 5
7// capability: PERCEPTION
8// wired_status: PARTIAL_WIRED
9//
10// MISSING_CAPABILITIES:
11// - RESPONSE_CURVE_LOAD (deserialize stored response from provenance store;
12// queued, depends on nx_provenance_curve_store)
13// - INVERSE_RESPONSE_COMPUTE (regularized inverse, handles unstable nulls
14// in driver response per Kirkeby & Nelson 1999; queued, depends on
15// nx_fft + Tikhonov regularization)
16// - TEMPORAL_FILTER_DESIGN (FIR or minimum-phase IIR from inverse curve)
17// - SPATIAL_FILTER_DESIGN (gamma LUT / color matrix for visual transducers)
18// - REAL_TIME_APPLY_TEMPORAL (convolution at render time for audio + vibration)
19// - REAL_TIME_APPLY_SPATIAL (LUT lookup at render time for visual)
20//
21// license_tier: PUBLIC_NISHI_SUBSTRATE
22// genealogy_id: feedback-substrate-primitives-meta-not-one-off_2026 +
23// feedback-substrate-does-heavy-lifting-user-is-partner-not-gate_2026 +
24// feedback-multi-species-perceptual-substrate-not-anthropocentric_2026 +
25// kirkeby_nelson_1999_inverse_filter_regularization +
26// mourjopoulos_1994_digital_equalization_room_response +
27// poynton_2003_gamma_correction +
28// feedback-build-intelligence-never-strip-features
29//
30// Per cardinal [[feedback-substrate-primitives-meta-not-one-off]]:
31// THIS primitive applies inverse-response compensation to ANY signal chain —
32// not just audio rigs. Originally scoped as "rig_compensator" during dog
33// perceptual test bring-up, promoted to meta-level when reuse across
34// products surfaced.
35//
36// Reuse set served by this single primitive:
37// - Audio pre-correction (dog perceptual test, dog toy, vet stethoscope,
38// working-dog trainer, conservation recorder, beekeeping sensor)
39// - Display gamma + gamut correction (Nishi /video output to species-aware
40// displays)
41// - Vibration linearization (haptic motor in dog toy + other haptic outputs)
42// - Hydrophone pressure-response correction (aquaculture)
43// - Camera + photodiode response linearization (visual input)
44// - Thermal sensor calibration correction (vet diagnostics)
45// - Composite chain compensation when multiple transducers in series
46//
47// User invocation: implicit -- nx_audio_render / nx_video_render /
48// nx_haptic_render automatically invoke nx_signal_compensator_apply if a
49// valid response curve exists for the configured transducer. No user knob.
50//
51// THIS IS A PARTIAL_WIRED STUB. Sealed enums + function signatures + the
52// honest-verdict gate are wired now. Downstream callers can compose against
53// the stable interface; substrate returns DEPENDENCY_MISSING until the
54// FFT + regularizer + filter-design layer lands.
55
56import "nx_syscalls.nx"
57import "nx_perceptual_profile.nx"
58import "nx_transducer_characterizer.nx"
59
60// ===== Signal domain sealed enum (meta — covers full reuse set) ===
61
62const NX_SIG_DOMAIN_TEMPORAL_AUDIO: i64 = 1 // time-domain audio
63const NX_SIG_DOMAIN_TEMPORAL_VIBRATION: i64 = 2 // haptic, accel
64const NX_SIG_DOMAIN_TEMPORAL_SCALAR: i64 = 3 // single-channel scalar (thermal, pressure)
65const NX_SIG_DOMAIN_TEMPORAL_MULTICHANNEL: i64 = 4 // N-channel sensor array
66const NX_SIG_DOMAIN_SPATIAL_VISUAL: i64 = 5 // image, display pixels
67const NX_SIG_DOMAIN_SPATIO_TEMPORAL_VIDEO: i64 = 6 // frame sequence
68
69func nx_sig_domain_name(d: i64) -> *u8 {
70 if d == NX_SIG_DOMAIN_TEMPORAL_AUDIO { return "TEMPORAL_AUDIO" }
71 if d == NX_SIG_DOMAIN_TEMPORAL_VIBRATION { return "TEMPORAL_VIBRATION" }
72 if d == NX_SIG_DOMAIN_TEMPORAL_SCALAR { return "TEMPORAL_SCALAR" }
73 if d == NX_SIG_DOMAIN_TEMPORAL_MULTICHANNEL { return "TEMPORAL_MULTICHANNEL" }
74 if d == NX_SIG_DOMAIN_SPATIAL_VISUAL { return "SPATIAL_VISUAL" }
75 if d == NX_SIG_DOMAIN_SPATIO_TEMPORAL_VIDEO { return "SPATIO_TEMPORAL_VIDEO" }
76 return "UNKNOWN_DOMAIN"
77}
78
79// ===== Compensation strategy sealed enum ==========================
80//
81// Different strategies trade latency, artifacts, stability per domain.
82// Substrate picks domain-appropriate default; caller can override.
83
84const NX_SIG_COMP_NONE: i64 = 0 // disabled; pass-through
85const NX_SIG_COMP_MINIMUM_PHASE_IIR: i64 = 1 // audio default, low-latency
86const NX_SIG_COMP_LINEAR_PHASE_FIR: i64 = 2 // audio phase-preserving
87const NX_SIG_COMP_REGULARIZED_INVERSE: i64 = 3 // Kirkeby-Nelson 1999 for
88 // noisy / peaky response
89const NX_SIG_COMP_PARAMETRIC_EQ: i64 = 4 // biquad chain, lowest CPU
90const NX_SIG_COMP_GAMMA_LUT_1D: i64 = 5 // visual: per-channel gamma
91const NX_SIG_COMP_COLOR_MATRIX_3X3: i64 = 6 // visual: cross-channel gamut
92const NX_SIG_COMP_3D_LUT: i64 = 7 // visual: full color transform
93const NX_SIG_COMP_TIME_DELAY: i64 = 8 // scalar: response-time
94 // correction
95const NX_SIG_COMP_POLYNOMIAL_LINEARIZE: i64 = 9 // scalar: nonlinear sensor
96 // correction
97
98func nx_sig_comp_strategy_name(s: i64) -> *u8 {
99 if s == NX_SIG_COMP_NONE { return "NONE" }
100 if s == NX_SIG_COMP_MINIMUM_PHASE_IIR { return "MINIMUM_PHASE_IIR" }
101 if s == NX_SIG_COMP_LINEAR_PHASE_FIR { return "LINEAR_PHASE_FIR" }
102 if s == NX_SIG_COMP_REGULARIZED_INVERSE { return "REGULARIZED_INVERSE" }
103 if s == NX_SIG_COMP_PARAMETRIC_EQ { return "PARAMETRIC_EQ" }
104 if s == NX_SIG_COMP_GAMMA_LUT_1D { return "GAMMA_LUT_1D" }
105 if s == NX_SIG_COMP_COLOR_MATRIX_3X3 { return "COLOR_MATRIX_3X3" }
106 if s == NX_SIG_COMP_3D_LUT { return "3D_LUT" }
107 if s == NX_SIG_COMP_TIME_DELAY { return "TIME_DELAY" }
108 if s == NX_SIG_COMP_POLYNOMIAL_LINEARIZE { return "POLYNOMIAL_LINEARIZE" }
109 return "UNKNOWN_STRATEGY"
110}
111
112// ===== Application verdicts =======================================
113
114const NX_SIG_COMP_APPLIED: i64 = 0
115const NX_SIG_COMP_NOT_APPLIED_NO_CURVE: i64 = 1 // no response curve loaded
116const NX_SIG_COMP_NOT_APPLIED_STALE_CURVE: i64 = 2 // curve > 90 days old
117const NX_SIG_COMP_NOT_APPLIED_INSTABILITY: i64 = 3 // inverse would exceed headroom
118const NX_SIG_COMP_NOT_APPLIED_DOMAIN_MISMATCH: i64 = 4 // wrong strategy for domain
119const NX_SIG_COMP_NOT_APPLIED_DEPENDENCY_MISSING: i64 = 5 // PARTIAL_WIRED default
120
121func nx_sig_comp_verdict_name(v: i64) -> *u8 {
122 if v == NX_SIG_COMP_APPLIED { return "APPLIED" }
123 if v == NX_SIG_COMP_NOT_APPLIED_NO_CURVE { return "NOT_APPLIED_NO_CURVE" }
124 if v == NX_SIG_COMP_NOT_APPLIED_STALE_CURVE { return "NOT_APPLIED_STALE_CURVE" }
125 if v == NX_SIG_COMP_NOT_APPLIED_INSTABILITY { return "NOT_APPLIED_INSTABILITY" }
126 if v == NX_SIG_COMP_NOT_APPLIED_DOMAIN_MISMATCH { return "NOT_APPLIED_DOMAIN_MISMATCH" }
127 if v == NX_SIG_COMP_NOT_APPLIED_DEPENDENCY_MISSING { return "NOT_APPLIED_DEPENDENCY_MISSING" }
128 return "UNKNOWN_COMP_VERDICT"
129}
130
131// ===== Top-level entry stubs ======================================
132
133// nx_signal_compensator_apply -- called by nx_audio_render / nx_video_render
134// / nx_haptic_render automatically. Given samples + transducer name +
135// signal domain + strategy, applies inverse-response correction in place.
136
137func nx_signal_compensator_apply(samples_ptr: *u8, samples_len: i64,
138 txr_name_ptr: *u8, txr_name_len: i64,
139 domain: i64, strategy: i64) -> i64 {
140 if samples_len <= 0 { return NX_SIG_COMP_NOT_APPLIED_NO_CURVE }
141 if txr_name_len <= 0 { return NX_SIG_COMP_NOT_APPLIED_NO_CURVE }
142 if domain < NX_SIG_DOMAIN_TEMPORAL_AUDIO { return NX_SIG_COMP_NOT_APPLIED_DOMAIN_MISMATCH }
143 if domain > NX_SIG_DOMAIN_SPATIO_TEMPORAL_VIDEO { return NX_SIG_COMP_NOT_APPLIED_DOMAIN_MISMATCH }
144 if strategy < NX_SIG_COMP_NONE { return NX_SIG_COMP_NOT_APPLIED_DEPENDENCY_MISSING }
145 if strategy > NX_SIG_COMP_POLYNOMIAL_LINEARIZE { return NX_SIG_COMP_NOT_APPLIED_DEPENDENCY_MISSING }
146 if strategy == NX_SIG_COMP_NONE { return NX_SIG_COMP_APPLIED } // honest no-op
147 // PARTIAL_WIRED: dependencies not yet landed.
148 return NX_SIG_COMP_NOT_APPLIED_DEPENDENCY_MISSING
149}
150
151// nx_signal_compensator_default_strategy_for_domain -- substrate picks
152// domain-appropriate default; caller can override.
153
154func nx_signal_compensator_default_strategy_for_domain(domain: i64) -> i64 {
155 if domain == NX_SIG_DOMAIN_TEMPORAL_AUDIO { return NX_SIG_COMP_MINIMUM_PHASE_IIR }
156 if domain == NX_SIG_DOMAIN_TEMPORAL_VIBRATION { return NX_SIG_COMP_PARAMETRIC_EQ }
157 if domain == NX_SIG_DOMAIN_TEMPORAL_SCALAR { return NX_SIG_COMP_POLYNOMIAL_LINEARIZE }
158 if domain == NX_SIG_DOMAIN_TEMPORAL_MULTICHANNEL { return NX_SIG_COMP_MINIMUM_PHASE_IIR }
159 if domain == NX_SIG_DOMAIN_SPATIAL_VISUAL { return NX_SIG_COMP_GAMMA_LUT_1D }
160 if domain == NX_SIG_DOMAIN_SPATIO_TEMPORAL_VIDEO { return NX_SIG_COMP_GAMMA_LUT_1D }
161 return NX_SIG_COMP_NONE
162}
163
164// nx_signal_compensator_disable -- explicitly turn off compensation for
165// diagnostic purposes (compare compensated vs uncompensated rendering).
166
167func nx_signal_compensator_disable(txr_name_ptr: *u8, txr_name_len: i64) -> i64 {
168 if txr_name_len <= 0 { return 0 }
169 return 1
170}
171
172// nx_signal_compensator_get_post_correction_flatness_dB10 -- inspector
173// that reports predicted post-correction flatness in dB×10 (e.g., 18
174// means ± 1.8 dB). Audio + vibration only; visual domain reports
175// delta-E instead, separate inspector.
176
177func nx_signal_compensator_get_post_correction_flatness_dB10(txr_name_ptr: *u8,
178 txr_name_len: i64) -> i64 {
179 if txr_name_len <= 0 { return -1 }
180 // PARTIAL_WIRED: depends on response load + inverse compute.
181 return -1
182}