1use super::{CVDeviceConfig, Complex, GaussianState};
7use crate::{DeviceError, DeviceResult};
8use scirs2_core::random::prelude::*;
9use scirs2_core::random::{Distribution, RandNormal};
10type Normal<T> = RandNormal<T>;
12use serde::{Deserialize, Serialize};
13use std::f64::consts::PI;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct HomodyneDetectorConfig {
18 pub lo_power_mw: f64,
20 pub efficiency: f64,
22 pub electronic_noise: f64,
24 pub bandwidth_hz: f64,
26 pub saturation_power_mw: f64,
28 pub pll_config: PLLConfig,
30 pub photodiode_config: PhotodiodeConfig,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct PLLConfig {
37 pub loop_bandwidth_hz: f64,
39 pub phase_noise_density: f64,
41 pub lock_range: f64,
43 pub acquisition_time_ms: f64,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct PhotodiodeConfig {
50 pub responsivity: f64,
52 pub dark_current_na: f64,
54 pub nep: f64,
56 pub active_area_mm2: f64,
58}
59
60impl Default for HomodyneDetectorConfig {
61 fn default() -> Self {
62 Self {
63 lo_power_mw: 10.0,
64 efficiency: 0.95,
65 electronic_noise: 1e-12, bandwidth_hz: 10e6,
67 saturation_power_mw: 100.0,
68 pll_config: PLLConfig::default(),
69 photodiode_config: PhotodiodeConfig::default(),
70 }
71 }
72}
73
74impl Default for PLLConfig {
75 fn default() -> Self {
76 Self {
77 loop_bandwidth_hz: 1000.0,
78 phase_noise_density: 1e-8, lock_range: PI,
80 acquisition_time_ms: 10.0,
81 }
82 }
83}
84
85impl Default for PhotodiodeConfig {
86 fn default() -> Self {
87 Self {
88 responsivity: 0.8, dark_current_na: 10.0,
90 nep: 1e-14, active_area_mm2: 1.0,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct HomodyneResult {
99 pub quadrature_value: f64,
101 pub phase: f64,
103 pub shot_noise_level: f64,
105 pub electronic_noise_level: f64,
107 pub snr_db: f64,
109 pub squeezing_db: f64,
111 pub phase_stability: f64,
113 pub fidelity: f64,
115 pub detector_currents: DetectorCurrents,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct DetectorCurrents {
122 pub current_a: f64,
124 pub current_b: f64,
126 pub difference_current: f64,
128 pub common_mode_current: f64,
130}
131
132pub struct HomodyneDetector {
134 config: HomodyneDetectorConfig,
136 lo_phase: f64,
138 is_phase_locked: bool,
140 calibration: HomodyneCalibration,
142 measurement_history: Vec<HomodyneResult>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct HomodyneCalibration {
149 pub visibility: f64,
151 pub dc_offset: f64,
153 pub gain_imbalance: f64,
155 pub phase_offset: f64,
157 pub cmrr_db: f64,
159}
160
161impl Default for HomodyneCalibration {
162 fn default() -> Self {
163 Self {
164 visibility: 0.99,
165 dc_offset: 0.001, gain_imbalance: 0.005, phase_offset: 0.02, cmrr_db: 60.0,
169 }
170 }
171}
172
173impl HomodyneDetector {
174 pub fn new(config: HomodyneDetectorConfig) -> Self {
176 Self {
177 config,
178 lo_phase: 0.0,
179 is_phase_locked: false,
180 calibration: HomodyneCalibration::default(),
181 measurement_history: Vec::new(),
182 }
183 }
184
185 pub async fn initialize(&mut self) -> DeviceResult<()> {
187 println!("Initializing homodyne detector...");
188
189 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
191
192 self.calibrate().await?;
194
195 self.acquire_phase_lock().await?;
197
198 println!("Homodyne detector initialized successfully");
199 Ok(())
200 }
201
202 async fn calibrate(&mut self) -> DeviceResult<()> {
204 println!("Calibrating homodyne detector...");
205
206 tokio::time::sleep(std::time::Duration::from_millis(200)).await;
208
209 self.calibration.visibility = 0.02f64.mul_add(-thread_rng().gen::<f64>(), 0.99);
211
212 self.calibration.dc_offset = 0.001 * (thread_rng().gen::<f64>() - 0.5);
214
215 self.calibration.gain_imbalance = 0.01 * (thread_rng().gen::<f64>() - 0.5);
217
218 self.calibration.phase_offset = 0.05 * (thread_rng().gen::<f64>() - 0.5);
220
221 println!(
222 "Calibration complete: visibility = {:.3}",
223 self.calibration.visibility
224 );
225 Ok(())
226 }
227
228 async fn acquire_phase_lock(&mut self) -> DeviceResult<()> {
230 println!("Acquiring phase lock...");
231
232 let acquisition_time =
234 std::time::Duration::from_millis(self.config.pll_config.acquisition_time_ms as u64);
235 tokio::time::sleep(acquisition_time).await;
236
237 self.is_phase_locked = true;
238 println!("Phase lock acquired");
239 Ok(())
240 }
241
242 pub async fn set_lo_phase(&mut self, phase: f64) -> DeviceResult<()> {
244 if !self.is_phase_locked {
245 return Err(DeviceError::DeviceNotInitialized(
246 "Phase lock not acquired".to_string(),
247 ));
248 }
249
250 self.lo_phase = phase + self.calibration.phase_offset;
252
253 tokio::time::sleep(std::time::Duration::from_millis(1)).await;
255
256 Ok(())
257 }
258
259 pub async fn measure(
261 &mut self,
262 state: &mut GaussianState,
263 mode: usize,
264 phase: f64,
265 ) -> DeviceResult<HomodyneResult> {
266 if !self.is_phase_locked {
267 return Err(DeviceError::DeviceNotInitialized(
268 "Detector not initialized or phase lock lost".to_string(),
269 ));
270 }
271
272 if mode >= state.num_modes {
273 return Err(DeviceError::InvalidInput(format!(
274 "Mode {mode} exceeds available modes"
275 )));
276 }
277
278 self.set_lo_phase(phase).await?;
280
281 let corrected_phase = phase + self.calibration.phase_offset;
283 let cos_phi = corrected_phase.cos();
284 let sin_phi = corrected_phase.sin();
285
286 let mean_x = state.mean_vector[2 * mode];
287 let mean_p = state.mean_vector[2 * mode + 1];
288 let theoretical_mean = cos_phi.mul_add(mean_x, sin_phi * mean_p);
289
290 let var_x = state.covariancematrix[2 * mode][2 * mode];
291 let var_p = state.covariancematrix[2 * mode + 1][2 * mode + 1];
292 let cov_xp = state.covariancematrix[2 * mode][2 * mode + 1];
293
294 let theoretical_variance = (2.0 * cos_phi * sin_phi).mul_add(
295 cov_xp,
296 cos_phi.powi(2).mul_add(var_x, sin_phi.powi(2) * var_p),
297 );
298
299 let shot_noise = self.calculate_shot_noise_level();
301 let electronic_noise = self.calculate_electronic_noise_level();
302 let phase_noise = self.calculate_phase_noise_contribution(theoretical_mean);
303
304 let total_noise_variance = theoretical_variance / self.config.efficiency
305 + shot_noise
306 + electronic_noise
307 + phase_noise;
308
309 let distribution = Normal::new(theoretical_mean, total_noise_variance.sqrt())
311 .map_err(|e| DeviceError::InvalidInput(format!("Distribution error: {e}")))?;
312
313 let mut rng = StdRng::seed_from_u64(thread_rng().gen::<u64>());
314 let measured_value = distribution.sample(&mut rng);
315
316 let detector_currents = self.calculate_detector_currents(measured_value, theoretical_mean);
318
319 let signal_power = theoretical_mean.powi(2);
321 let noise_power = total_noise_variance;
322 let snr_db = 10.0 * (signal_power / noise_power).log10();
323
324 let squeezing_db = 10.0 * (theoretical_variance / shot_noise).log10();
326
327 let phase_stability = self.estimate_phase_stability();
329
330 let fidelity =
332 self.calculate_measurement_fidelity(signal_power, noise_power, phase_stability);
333
334 let result = HomodyneResult {
335 quadrature_value: measured_value,
336 phase: corrected_phase,
337 shot_noise_level: shot_noise,
338 electronic_noise_level: electronic_noise,
339 snr_db,
340 squeezing_db,
341 phase_stability,
342 fidelity,
343 detector_currents,
344 };
345
346 state.condition_on_homodyne_measurement(mode, corrected_phase, measured_value)?;
348
349 self.measurement_history.push(result.clone());
350 Ok(result)
351 }
352
353 fn calculate_shot_noise_level(&self) -> f64 {
355 1.0 / self.config.efficiency
358 }
359
360 fn calculate_electronic_noise_level(&self) -> f64 {
362 let current_noise = (2.0
364 * 1.602e-19
365 * self.config.photodiode_config.dark_current_na
366 * 1e-9
367 * self.config.bandwidth_hz)
368 .sqrt(); let thermal_noise = (4.0 * 1.381e-23 * 300.0 * self.config.bandwidth_hz / 50.0).sqrt(); let total_electronic_noise = (thermal_noise.mul_add(thermal_noise, current_noise.powi(2))
373 + self.config.electronic_noise)
374 .sqrt();
375
376 total_electronic_noise
378 / (self.config.photodiode_config.responsivity * (self.config.lo_power_mw * 1e-3).sqrt())
379 }
380
381 fn calculate_phase_noise_contribution(&self, signal_amplitude: f64) -> f64 {
383 let phase_noise_variance =
385 self.config.pll_config.phase_noise_density * self.config.bandwidth_hz;
386
387 signal_amplitude.powi(2) * phase_noise_variance
388 }
389
390 fn calculate_detector_currents(
392 &self,
393 measured_value: f64,
394 mean_signal: f64,
395 ) -> DetectorCurrents {
396 let lo_current =
397 self.config.photodiode_config.responsivity * self.config.lo_power_mw * 1e-3; let signal_current = 2.0
401 * (lo_current * self.config.photodiode_config.responsivity * mean_signal.abs() * 1e-3)
402 .sqrt();
403
404 let current_a = self
406 .config
407 .photodiode_config
408 .dark_current_na
409 .mul_add(1e-6, lo_current + signal_current * 0.5);
410 let current_b = self
411 .config
412 .photodiode_config
413 .dark_current_na
414 .mul_add(1e-6, lo_current - signal_current * 0.5);
415
416 let difference_current = current_a - current_b;
417 let common_mode_current = f64::midpoint(current_a, current_b);
418
419 DetectorCurrents {
420 current_a,
421 current_b,
422 difference_current,
423 common_mode_current,
424 }
425 }
426
427 fn estimate_phase_stability(&self) -> f64 {
429 let frequency_noise = self.config.pll_config.phase_noise_density;
431 let loop_bandwidth = self.config.pll_config.loop_bandwidth_hz;
432
433 (frequency_noise * loop_bandwidth).sqrt()
435 }
436
437 fn calculate_measurement_fidelity(
439 &self,
440 signal_power: f64,
441 noise_power: f64,
442 phase_stability: f64,
443 ) -> f64 {
444 let snr = signal_power / noise_power;
445 let phase_penalty = 1.0 / phase_stability.mul_add(phase_stability, 1.0);
446 let efficiency_penalty = self.config.efficiency;
447
448 let fidelity = (snr / (1.0 + snr)) * phase_penalty * efficiency_penalty;
449 fidelity.clamp(0.0, 1.0)
450 }
451
452 pub fn get_measurement_statistics(&self) -> HomodyneStatistics {
454 if self.measurement_history.is_empty() {
455 return HomodyneStatistics::default();
456 }
457
458 let total_measurements = self.measurement_history.len();
459
460 let avg_snr = self
461 .measurement_history
462 .iter()
463 .map(|r| r.snr_db)
464 .sum::<f64>()
465 / total_measurements as f64;
466
467 let avg_squeezing = self
468 .measurement_history
469 .iter()
470 .map(|r| r.squeezing_db)
471 .sum::<f64>()
472 / total_measurements as f64;
473
474 let avg_fidelity = self
475 .measurement_history
476 .iter()
477 .map(|r| r.fidelity)
478 .sum::<f64>()
479 / total_measurements as f64;
480
481 let avg_phase_stability = self
482 .measurement_history
483 .iter()
484 .map(|r| r.phase_stability)
485 .sum::<f64>()
486 / total_measurements as f64;
487
488 HomodyneStatistics {
489 total_measurements,
490 average_snr_db: avg_snr,
491 average_squeezing_db: avg_squeezing,
492 average_fidelity: avg_fidelity,
493 average_phase_stability: avg_phase_stability,
494 is_phase_locked: self.is_phase_locked,
495 detector_efficiency: self.config.efficiency,
496 }
497 }
498
499 pub fn clear_history(&mut self) {
501 self.measurement_history.clear();
502 }
503
504 pub const fn get_calibration(&self) -> &HomodyneCalibration {
506 &self.calibration
507 }
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct HomodyneStatistics {
513 pub total_measurements: usize,
514 pub average_snr_db: f64,
515 pub average_squeezing_db: f64,
516 pub average_fidelity: f64,
517 pub average_phase_stability: f64,
518 pub is_phase_locked: bool,
519 pub detector_efficiency: f64,
520}
521
522impl Default for HomodyneStatistics {
523 fn default() -> Self {
524 Self {
525 total_measurements: 0,
526 average_snr_db: 0.0,
527 average_squeezing_db: 0.0,
528 average_fidelity: 0.0,
529 average_phase_stability: 0.0,
530 is_phase_locked: false,
531 detector_efficiency: 0.0,
532 }
533 }
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 #[tokio::test]
541 async fn test_homodyne_detector_creation() {
542 let config = HomodyneDetectorConfig::default();
543 let detector = HomodyneDetector::new(config);
544 assert!(!detector.is_phase_locked);
545 assert_eq!(detector.measurement_history.len(), 0);
546 }
547
548 #[tokio::test]
549 async fn test_detector_initialization() {
550 let config = HomodyneDetectorConfig::default();
551 let mut detector = HomodyneDetector::new(config);
552
553 detector
554 .initialize()
555 .await
556 .expect("Detector initialization should succeed");
557 assert!(detector.is_phase_locked);
558 }
559
560 #[tokio::test]
561 async fn test_phase_setting() {
562 let config = HomodyneDetectorConfig::default();
563 let mut detector = HomodyneDetector::new(config);
564 detector
565 .initialize()
566 .await
567 .expect("Detector initialization should succeed");
568
569 detector
570 .set_lo_phase(PI / 4.0)
571 .await
572 .expect("Setting LO phase should succeed");
573 assert!((detector.lo_phase - PI / 4.0).abs() < 0.1); }
575
576 #[tokio::test]
577 async fn test_homodyne_measurement() {
578 let config = HomodyneDetectorConfig::default();
579 let mut detector = HomodyneDetector::new(config);
580 detector
581 .initialize()
582 .await
583 .expect("Detector initialization should succeed");
584
585 let mut state = GaussianState::coherent_state(1, vec![Complex::new(2.0, 0.0)])
586 .expect("Coherent state creation should succeed");
587
588 let result = detector
589 .measure(&mut state, 0, 0.0)
590 .await
591 .expect("Homodyne measurement should succeed");
592
593 assert!(result.quadrature_value.is_finite());
594 assert!(result.fidelity > 0.0);
595 assert!(result.snr_db.is_finite());
596 assert_eq!(detector.measurement_history.len(), 1);
597 }
598
599 #[tokio::test]
600 async fn test_squeezing_measurement() {
601 let config = HomodyneDetectorConfig::default();
602 let mut detector = HomodyneDetector::new(config);
603 detector
604 .initialize()
605 .await
606 .expect("Detector initialization should succeed");
607
608 let mut state = GaussianState::squeezed_vacuum_state(1, vec![1.0], vec![0.0])
609 .expect("Squeezed vacuum state creation should succeed");
610
611 let result = detector
612 .measure(&mut state, 0, 0.0)
613 .await
614 .expect("Homodyne measurement should succeed");
615
616 assert!(result.squeezing_db < 0.0); }
619
620 #[test]
621 fn test_noise_calculations() {
622 let config = HomodyneDetectorConfig::default();
623 let detector = HomodyneDetector::new(config);
624
625 let shot_noise = detector.calculate_shot_noise_level();
626 assert!(shot_noise > 0.0);
627
628 let electronic_noise = detector.calculate_electronic_noise_level();
629 assert!(electronic_noise > 0.0);
630 }
631
632 #[test]
633 fn test_detector_current_calculation() {
634 let config = HomodyneDetectorConfig::default();
635 let detector = HomodyneDetector::new(config);
636
637 let currents = detector.calculate_detector_currents(1.0, 0.5);
638 assert!(currents.current_a > 0.0);
639 assert!(currents.current_b > 0.0);
640 assert!(currents.difference_current != 0.0);
641 }
642
643 #[test]
644 fn test_statistics() {
645 let config = HomodyneDetectorConfig::default();
646 let detector = HomodyneDetector::new(config);
647
648 let stats = detector.get_measurement_statistics();
649 assert_eq!(stats.total_measurements, 0);
650 assert!(!stats.is_phase_locked);
651 }
652}