Skip to content

Commit 8db2a20

Browse files
committed
test: more realistic multi instance
1 parent a013f12 commit 8db2a20

File tree

1 file changed

+181
-34
lines changed

1 file changed

+181
-34
lines changed

rust/tests/multi_instance_integration_test.rs

Lines changed: 181 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
4747
.with_detection_threshold(0.04);
4848

4949
// Create ConcentrationNodes for each gas species with different polynomials
50+
// NOTE: Adjusted coefficients to work with realistic FFT amplitudes (20-50 range)
51+
// instead of small mV values (0.001-0.020 range)
5052
let mut concentration_co2 = ConcentrationNode::new_with_shared_state(
5153
"concentration_co2".to_string(),
5254
Some(shared_state.clone()),
5355
)
5456
.with_peak_finder_source("peak_finder_co2".to_string())
55-
.with_polynomial_coefficients([0.0, 850.0, -12.5, 0.0, 0.0]) // CO2 calibration curve
57+
.with_polynomial_coefficients([0.0, 25.0, -0.3, 0.0, 0.0]) // CO2 calibration curve for FFT amplitudes
5658
.with_spectral_line_id("CO2_4.26um".to_string())
5759
.with_temperature_compensation(true);
5860

@@ -61,7 +63,7 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
6163
Some(shared_state.clone()),
6264
)
6365
.with_peak_finder_source("peak_finder_ch4".to_string())
64-
.with_polynomial_coefficients([5.0, 1200.0, -8.3, 0.15, 0.0]) // CH4 calibration curve
66+
.with_polynomial_coefficients([5.0, 17.0, -0.25, 0.005, 0.0]) // CH4 calibration curve for FFT amplitudes
6567
.with_spectral_line_id("CH4_3.39um".to_string())
6668
.with_temperature_compensation(true);
6769

@@ -70,7 +72,7 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
7072
Some(shared_state.clone()),
7173
)
7274
.with_peak_finder_source("peak_finder_nh3".to_string())
73-
.with_polynomial_coefficients([2.5, 950.0, -15.2, 0.08, 0.0]) // NH3 calibration curve
75+
.with_polynomial_coefficients([2.5, 40.0, -0.5, 0.003, 0.0]) // NH3 calibration curve for FFT amplitudes (higher sensitivity)
7476
.with_spectral_line_id("NH3_10.4um".to_string())
7577
.with_temperature_compensation(false);
7678

@@ -84,22 +86,22 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
8486
// Generate synthetic photoacoustic signals at different frequencies
8587
for i in 0..frame_size {
8688
let t = i as f32 / sample_rate as f32;
87-
89+
8890
// CO2 signal at 2050 Hz with moderate amplitude (simulates moderate concentration)
8991
let co2_signal = 0.15 * (2.0 * std::f32::consts::PI * 2050.0 * t).sin();
90-
92+
9193
// CH4 signal at 3045 Hz with lower amplitude (simulates low concentration)
9294
let ch4_signal = 0.08 * (2.0 * std::f32::consts::PI * 3045.0 * t).sin();
93-
95+
9496
// NH3 signal at 1550 Hz with higher amplitude (simulates high concentration)
9597
let nh3_signal = 0.25 * (2.0 * std::f32::consts::PI * 1550.0 * t).sin();
96-
98+
9799
// Add some noise to make it more realistic
98100
let noise = 0.01 * ((i as f32 * 123.456).sin() - 0.5);
99-
101+
100102
// Combine all signals
101103
let combined_signal = co2_signal + ch4_signal + nh3_signal + noise;
102-
104+
103105
audio_samples_a[i] = combined_signal;
104106
audio_samples_b[i] = combined_signal; // Same signal on both channels for simplicity
105107
}
@@ -114,7 +116,34 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
114116
frame_number: 1,
115117
});
116118

117-
// Process data through all concentration nodes
119+
// STEP 1: Process audio data through PeakFinderNodes to detect peaks
120+
// This simulates real acquisition where audio signals are analyzed for spectral peaks
121+
let audio_output_co2 = peak_finder_co2.process(test_audio.clone())?;
122+
let audio_output_ch4 = peak_finder_ch4.process(test_audio.clone())?;
123+
let audio_output_nh3 = peak_finder_nh3.process(test_audio.clone())?;
124+
125+
// Verify audio data passes through unchanged
126+
assert_eq!(test_audio, audio_output_co2);
127+
assert_eq!(test_audio, audio_output_ch4);
128+
assert_eq!(test_audio, audio_output_nh3);
129+
130+
// Allow some time for peak detection to stabilize (simulate multiple frames)
131+
// In real operation, peak detection needs several frames to establish coherence
132+
for frame_num in 2..=5 {
133+
let mut audio_frame = match test_audio {
134+
ProcessingData::AudioFrame(ref frame) => frame.clone(),
135+
_ => unreachable!(),
136+
};
137+
audio_frame.frame_number = frame_num;
138+
let frame_data = ProcessingData::AudioFrame(audio_frame);
139+
140+
peak_finder_co2.process(frame_data.clone())?;
141+
peak_finder_ch4.process(frame_data.clone())?;
142+
peak_finder_nh3.process(frame_data)?;
143+
}
144+
145+
// STEP 2: Process the same audio data through ConcentrationNodes
146+
// They will use the peak detection results from the PeakFinderNodes
118147
let output_co2 = concentration_co2.process(test_audio.clone())?;
119148
let output_ch4 = concentration_ch4.process(test_audio.clone())?;
120149
let output_nh3 = concentration_nh3.process(test_audio.clone())?;
@@ -124,57 +153,165 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
124153
assert_eq!(test_audio, output_ch4);
125154
assert_eq!(test_audio, output_nh3);
126155

127-
// Verify that all concentrations were calculated correctly
156+
// Verify that peak detection and concentration calculations work correctly
128157
{
129158
let state = shared_state.read().await;
130159

131-
// Check CO2 concentration: 0 + 850 * 0.008 - 12.5 * 0.008^2 = 6.8 - 0.0008 ≈ 6.8 ppm
132-
assert!(state
133-
.concentration_results
134-
.contains_key("concentration_co2"));
160+
// Verify that peaks were detected by each PeakFinderNode
161+
assert!(
162+
state.peak_results.contains_key("peak_finder_co2"),
163+
"CO2 peak finder should have detected a peak"
164+
);
165+
assert!(
166+
state.peak_results.contains_key("peak_finder_ch4"),
167+
"CH4 peak finder should have detected a peak"
168+
);
169+
assert!(
170+
state.peak_results.contains_key("peak_finder_nh3"),
171+
"NH3 peak finder should have detected a peak"
172+
);
173+
174+
// Verify peak frequencies are within expected ranges
175+
let co2_peak = &state.peak_results["peak_finder_co2"];
176+
assert!(
177+
co2_peak.frequency >= 2000.0 && co2_peak.frequency <= 2100.0,
178+
"CO2 peak frequency should be in expected range (2000-2100 Hz), got {}",
179+
co2_peak.frequency
180+
);
181+
182+
let ch4_peak = &state.peak_results["peak_finder_ch4"];
183+
assert!(
184+
ch4_peak.frequency >= 3000.0 && ch4_peak.frequency <= 3100.0,
185+
"CH4 peak frequency should be in expected range (3000-3100 Hz), got {}",
186+
ch4_peak.frequency
187+
);
188+
189+
let nh3_peak = &state.peak_results["peak_finder_nh3"];
190+
assert!(
191+
nh3_peak.frequency >= 1500.0 && nh3_peak.frequency <= 1600.0,
192+
"NH3 peak frequency should be in expected range (1500-1600 Hz), got {}",
193+
nh3_peak.frequency
194+
);
195+
196+
// Verify that concentrations were calculated
197+
assert!(
198+
state
199+
.concentration_results
200+
.contains_key("concentration_co2"),
201+
"CO2 concentration should have been calculated"
202+
);
203+
assert!(
204+
state
205+
.concentration_results
206+
.contains_key("concentration_ch4"),
207+
"CH4 concentration should have been calculated"
208+
);
209+
assert!(
210+
state
211+
.concentration_results
212+
.contains_key("concentration_nh3"),
213+
"NH3 concentration should have been calculated"
214+
);
215+
216+
// Check CO2 concentration calculation
135217
let co2_result = &state.concentration_results["concentration_co2"];
136-
assert!((co2_result.concentration_ppm - 6.7992).abs() < 0.001);
218+
assert!(
219+
co2_result.concentration_ppm > 0.0,
220+
"CO2 concentration should be positive, got {}",
221+
co2_result.concentration_ppm
222+
);
137223
assert_eq!(co2_result.source_peak_finder_id, "peak_finder_co2");
138224
assert_eq!(co2_result.spectral_line_id.as_ref().unwrap(), "CO2_4.26um");
139225
assert!(co2_result.temperature_compensated);
140226

141-
// Check CH4 concentration: 5 + 1200 * 0.003 - 8.3 * 0.003^2 + 0.15 * 0.003^3
142-
// = 5 + 3.6 - 0.0000747 + 0.000004 ≈ 8.6 ppm
143-
assert!(state
144-
.concentration_results
145-
.contains_key("concentration_ch4"));
227+
// Check CH4 concentration calculation
146228
let ch4_result = &state.concentration_results["concentration_ch4"];
147-
assert!((ch4_result.concentration_ppm - 8.59993).abs() < 0.001);
229+
assert!(
230+
ch4_result.concentration_ppm > 0.0,
231+
"CH4 concentration should be positive, got {}",
232+
ch4_result.concentration_ppm
233+
);
148234
assert_eq!(ch4_result.source_peak_finder_id, "peak_finder_ch4");
149235
assert_eq!(ch4_result.spectral_line_id.as_ref().unwrap(), "CH4_3.39um");
150236
assert!(ch4_result.temperature_compensated);
151237

152-
// Check NH3 concentration: 2.5 + 950 * 0.012 - 15.2 * 0.012^2 + 0.08 * 0.012^3
153-
// = 2.5 + 11.4 - 0.021888 + 0.00013824 ≈ 13.88 ppm
154-
assert!(state
155-
.concentration_results
156-
.contains_key("concentration_nh3"));
238+
// Check NH3 concentration calculation
157239
let nh3_result = &state.concentration_results["concentration_nh3"];
158-
assert!((nh3_result.concentration_ppm - 13.8978).abs() < 0.01); // Use actual calculated value
240+
assert!(
241+
nh3_result.concentration_ppm > 0.0,
242+
"NH3 concentration should be positive, got {}",
243+
nh3_result.concentration_ppm
244+
);
159245
assert_eq!(nh3_result.source_peak_finder_id, "peak_finder_nh3");
160246
assert_eq!(nh3_result.spectral_line_id.as_ref().unwrap(), "NH3_10.4um");
161247
assert!(!nh3_result.temperature_compensated);
162248

249+
// Log actual values for debugging BEFORE assertions
250+
println!("Peak detection results:");
251+
println!(
252+
" CO2: {:.2} Hz, amplitude {:.4}",
253+
co2_peak.frequency, co2_peak.amplitude
254+
);
255+
println!(
256+
" CH4: {:.2} Hz, amplitude {:.4}",
257+
ch4_peak.frequency, ch4_peak.amplitude
258+
);
259+
println!(
260+
" NH3: {:.2} Hz, amplitude {:.4}",
261+
nh3_peak.frequency, nh3_peak.amplitude
262+
);
263+
println!("Concentration results:");
264+
println!(" CO2: {:.2} ppm", co2_result.concentration_ppm);
265+
println!(" CH4: {:.2} ppm", ch4_result.concentration_ppm);
266+
println!(" NH3: {:.2} ppm", nh3_result.concentration_ppm);
267+
268+
// Verify that NH3 has the highest amplitude signal and thus highest concentration
269+
// since we generated it with the highest amplitude (0.25 vs 0.15 for CO2 and 0.08 for CH4)
270+
assert!(
271+
nh3_result.concentration_ppm > co2_result.concentration_ppm,
272+
"NH3 concentration ({}) should be higher than CO2 ({})",
273+
nh3_result.concentration_ppm,
274+
co2_result.concentration_ppm
275+
);
276+
assert!(
277+
nh3_result.concentration_ppm > ch4_result.concentration_ppm,
278+
"NH3 concentration ({}) should be higher than CH4 ({})",
279+
nh3_result.concentration_ppm,
280+
ch4_result.concentration_ppm
281+
);
282+
283+
// Verify that CO2 has higher concentration than CH4 (0.15 vs 0.08 amplitude)
284+
assert!(
285+
co2_result.concentration_ppm > ch4_result.concentration_ppm,
286+
"CO2 concentration ({}) should be higher than CH4 ({})",
287+
co2_result.concentration_ppm,
288+
ch4_result.concentration_ppm
289+
);
290+
163291
// Verify that legacy fields contain the last calculated concentration (NH3 in this case)
164292
assert!(state.concentration_ppm.is_some());
165293
let legacy_concentration = state.concentration_ppm.unwrap() as f64;
166-
assert!((legacy_concentration - 13.8978).abs() < 0.01); // Use actual calculated value
294+
assert!(
295+
(legacy_concentration - nh3_result.concentration_ppm).abs() < 0.01,
296+
"Legacy concentration field should match NH3 result"
297+
);
167298
}
168299

169300
// Test hot-reload configuration update for one of the nodes
170301
let new_ch4_config = serde_json::json!({
171-
"polynomial_coefficients": [10.0, 1100.0, -6.0, 0.1, 0.0],
302+
"polynomial_coefficients": [10.0, 30.0, -0.2, 0.003, 0.0],
172303
"temperature_compensation": false
173304
});
174305

175306
let updated = concentration_ch4.update_config(&new_ch4_config)?;
176307
assert!(updated);
177308

309+
// Store the original CH4 concentration for comparison
310+
let original_ch4_concentration = {
311+
let state = shared_state.read().await;
312+
state.concentration_results["concentration_ch4"].concentration_ppm
313+
};
314+
178315
// Process again with updated configuration
179316
concentration_ch4.process(test_audio)?;
180317

@@ -183,10 +320,20 @@ async fn test_multi_spectral_analysis_pipeline() -> Result<()> {
183320
let state = shared_state.read().await;
184321
let ch4_result = &state.concentration_results["concentration_ch4"];
185322

186-
// New calculation: 10 + 1100 * 0.003 - 6.0 * 0.003^2 + 0.1 * 0.003^3
187-
// = 10 + 3.3 - 0.000054 + 0.0000027 ≈ 13.3 ppm
188-
assert!((ch4_result.concentration_ppm - 13.29995).abs() < 0.001);
189-
assert!(!ch4_result.temperature_compensated); // Should be updated
323+
// The new polynomial should produce a different result
324+
assert_ne!(
325+
ch4_result.concentration_ppm, original_ch4_concentration,
326+
"Updated polynomial should produce different concentration"
327+
);
328+
assert!(
329+
!ch4_result.temperature_compensated,
330+
"Temperature compensation should be disabled"
331+
);
332+
333+
println!(
334+
"CH4 concentration after config update: {:.2} ppm (was {:.2} ppm)",
335+
ch4_result.concentration_ppm, original_ch4_concentration
336+
);
190337
}
191338

192339
Ok(())

0 commit comments

Comments
 (0)