Skip to main content

nanonis_rs/client/
pll.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// PLL excitation output range.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum PLLExcRange {
8    /// 10V range
9    #[default]
10    V10 = 0,
11    /// 1V range
12    V1 = 1,
13    /// 0.1V range
14    V01 = 2,
15    /// 0.01V range
16    V001 = 3,
17    /// 0.001V range
18    V0001 = 4,
19}
20
21impl From<PLLExcRange> for u16 {
22    fn from(r: PLLExcRange) -> Self {
23        r as u16
24    }
25}
26
27impl TryFrom<u16> for PLLExcRange {
28    type Error = NanonisError;
29
30    fn try_from(value: u16) -> Result<Self, Self::Error> {
31        match value {
32            0 => Ok(PLLExcRange::V10),
33            1 => Ok(PLLExcRange::V1),
34            2 => Ok(PLLExcRange::V01),
35            3 => Ok(PLLExcRange::V001),
36            4 => Ok(PLLExcRange::V0001),
37            _ => Err(NanonisError::Protocol(format!(
38                "Invalid PLLExcRange value: {}",
39                value
40            ))),
41        }
42    }
43}
44
45/// PLL input properties.
46#[derive(Debug, Clone, Copy, Default)]
47pub struct PLLInputProps {
48    /// Differential input enabled
49    pub differential_input: bool,
50    /// 1/10 divider enabled
51    pub divider_1_10: bool,
52}
53
54/// PLL demodulator input configuration.
55#[derive(Debug, Clone, Copy, Default)]
56pub struct PLLDemodInput {
57    /// Input selection
58    pub input: u16,
59    /// Frequency generator selection
60    pub freq_generator: u16,
61}
62
63/// PLL frequency/excitation overwrite configuration.
64#[derive(Debug, Clone, Copy, Default)]
65pub struct PLLOverwrite {
66    /// Excitation overwrite signal index (-1 for none)
67    pub excitation_signal_index: i32,
68    /// Frequency overwrite signal index (-1 for none)
69    pub frequency_signal_index: i32,
70}
71
72/// PLL amplitude controller gain parameters.
73#[derive(Debug, Clone, Copy, Default)]
74pub struct PLLAmpCtrlGain {
75    /// Proportional gain in V/m
76    pub p_gain_v_per_m: f32,
77    /// Time constant in seconds
78    pub time_constant_s: f32,
79    /// Integral gain in V/m/s (read-only, computed)
80    pub integral_gain_v_per_m_s: f32,
81}
82
83/// PLL phase controller gain parameters.
84#[derive(Debug, Clone, Copy, Default)]
85pub struct PLLPhasCtrlGain {
86    /// Proportional gain in Hz/deg
87    pub p_gain_hz_per_deg: f32,
88    /// Time constant in seconds
89    pub time_constant_s: f32,
90}
91
92impl NanonisClient {
93    // ==================== Input Configuration ====================
94
95    /// Set the input calibration of the oscillation control module.
96    ///
97    /// # Arguments
98    /// * `modulator_index` - PLL modulator index (starts from 1)
99    /// * `calibration_m_per_v` - Input calibration in m/V
100    ///
101    /// # Errors
102    /// Returns `NanonisError` if communication fails.
103    pub fn pll_inp_calibr_set(
104        &mut self,
105        modulator_index: i32,
106        calibration_m_per_v: f32,
107    ) -> Result<(), NanonisError> {
108        self.quick_send(
109            "PLL.InpCalibrSet",
110            vec![
111                NanonisValue::I32(modulator_index),
112                NanonisValue::F32(calibration_m_per_v),
113            ],
114            vec!["i", "f"],
115            vec![],
116        )?;
117        Ok(())
118    }
119
120    /// Get the input calibration of the oscillation control module.
121    ///
122    /// # Arguments
123    /// * `modulator_index` - PLL modulator index (starts from 1)
124    ///
125    /// # Returns
126    /// Input calibration in m/V.
127    ///
128    /// # Errors
129    /// Returns `NanonisError` if communication fails.
130    pub fn pll_inp_calibr_get(&mut self, modulator_index: i32) -> Result<f32, NanonisError> {
131        let result = self.quick_send(
132            "PLL.InpCalibrGet",
133            vec![NanonisValue::I32(modulator_index)],
134            vec!["i"],
135            vec!["f"],
136        )?;
137
138        if !result.is_empty() {
139            Ok(result[0].as_f32()?)
140        } else {
141            Err(NanonisError::Protocol("Invalid response".to_string()))
142        }
143    }
144
145    /// Set the input range of the oscillation control module.
146    ///
147    /// # Arguments
148    /// * `modulator_index` - PLL modulator index (starts from 1)
149    /// * `input_range_m` - Input range in meters
150    ///
151    /// # Errors
152    /// Returns `NanonisError` if communication fails.
153    pub fn pll_inp_range_set(
154        &mut self,
155        modulator_index: i32,
156        input_range_m: f32,
157    ) -> Result<(), NanonisError> {
158        self.quick_send(
159            "PLL.InpRangeSet",
160            vec![
161                NanonisValue::I32(modulator_index),
162                NanonisValue::F32(input_range_m),
163            ],
164            vec!["i", "f"],
165            vec![],
166        )?;
167        Ok(())
168    }
169
170    /// Get the input range of the oscillation control module.
171    ///
172    /// # Arguments
173    /// * `modulator_index` - PLL modulator index (starts from 1)
174    ///
175    /// # Returns
176    /// Input range in meters.
177    ///
178    /// # Errors
179    /// Returns `NanonisError` if communication fails.
180    pub fn pll_inp_range_get(&mut self, modulator_index: i32) -> Result<f32, NanonisError> {
181        let result = self.quick_send(
182            "PLL.InpRangeGet",
183            vec![NanonisValue::I32(modulator_index)],
184            vec!["i"],
185            vec!["f"],
186        )?;
187
188        if !result.is_empty() {
189            Ok(result[0].as_f32()?)
190        } else {
191            Err(NanonisError::Protocol("Invalid response".to_string()))
192        }
193    }
194
195    /// Set the input properties of the oscillation control module.
196    ///
197    /// # Arguments
198    /// * `modulator_index` - PLL modulator index (starts from 1)
199    /// * `props` - Input properties
200    ///
201    /// # Errors
202    /// Returns `NanonisError` if communication fails.
203    pub fn pll_inp_props_set(
204        &mut self,
205        modulator_index: i32,
206        props: &PLLInputProps,
207    ) -> Result<(), NanonisError> {
208        let diff_flag = if props.differential_input { 1u16 } else { 0u16 };
209        let div_flag = if props.divider_1_10 { 1u16 } else { 0u16 };
210        self.quick_send(
211            "PLL.InpPropsSet",
212            vec![
213                NanonisValue::I32(modulator_index),
214                NanonisValue::U16(diff_flag),
215                NanonisValue::U16(div_flag),
216            ],
217            vec!["i", "H", "H"],
218            vec![],
219        )?;
220        Ok(())
221    }
222
223    /// Get the input properties of the oscillation control module.
224    ///
225    /// # Arguments
226    /// * `modulator_index` - PLL modulator index (starts from 1)
227    ///
228    /// # Returns
229    /// Input properties.
230    ///
231    /// # Errors
232    /// Returns `NanonisError` if communication fails.
233    pub fn pll_inp_props_get(
234        &mut self,
235        modulator_index: i32,
236    ) -> Result<PLLInputProps, NanonisError> {
237        let result = self.quick_send(
238            "PLL.InpPropsGet",
239            vec![NanonisValue::I32(modulator_index)],
240            vec!["i"],
241            vec!["H", "H"],
242        )?;
243
244        if result.len() >= 2 {
245            Ok(PLLInputProps {
246                differential_input: result[0].as_u16()? != 0,
247                divider_1_10: result[1].as_u16()? != 0,
248            })
249        } else {
250            Err(NanonisError::Protocol("Invalid response".to_string()))
251        }
252    }
253
254    // ==================== Output Configuration ====================
255
256    /// Set the add external signal status.
257    ///
258    /// # Arguments
259    /// * `modulator_index` - PLL modulator index (starts from 1)
260    /// * `enabled` - True to add external signal to output
261    ///
262    /// # Errors
263    /// Returns `NanonisError` if communication fails.
264    pub fn pll_add_on_off_set(
265        &mut self,
266        modulator_index: i32,
267        enabled: bool,
268    ) -> Result<(), NanonisError> {
269        let flag = if enabled { 1u32 } else { 0u32 };
270        self.quick_send(
271            "PLL.AddOnOffSet",
272            vec![NanonisValue::I32(modulator_index), NanonisValue::U32(flag)],
273            vec!["i", "I"],
274            vec![],
275        )?;
276        Ok(())
277    }
278
279    /// Get the add external signal status.
280    ///
281    /// # Arguments
282    /// * `modulator_index` - PLL modulator index (starts from 1)
283    ///
284    /// # Returns
285    /// True if external signal is being added.
286    ///
287    /// # Errors
288    /// Returns `NanonisError` if communication fails.
289    pub fn pll_add_on_off_get(&mut self, modulator_index: i32) -> Result<bool, NanonisError> {
290        let result = self.quick_send(
291            "PLL.AddOnOffGet",
292            vec![NanonisValue::I32(modulator_index)],
293            vec!["i"],
294            vec!["I"],
295        )?;
296
297        if !result.is_empty() {
298            Ok(result[0].as_u32()? != 0)
299        } else {
300            Err(NanonisError::Protocol("Invalid response".to_string()))
301        }
302    }
303
304    /// Set the PLL output on/off status.
305    ///
306    /// # Arguments
307    /// * `modulator_index` - PLL modulator index (starts from 1)
308    /// * `enabled` - True to enable PLL output
309    ///
310    /// # Errors
311    /// Returns `NanonisError` if communication fails.
312    pub fn pll_out_on_off_set(
313        &mut self,
314        modulator_index: i32,
315        enabled: bool,
316    ) -> Result<(), NanonisError> {
317        let flag = if enabled { 1u32 } else { 0u32 };
318        self.quick_send(
319            "PLL.OutOnOffSet",
320            vec![NanonisValue::I32(modulator_index), NanonisValue::U32(flag)],
321            vec!["i", "I"],
322            vec![],
323        )?;
324        Ok(())
325    }
326
327    /// Get the PLL output on/off status.
328    ///
329    /// # Arguments
330    /// * `modulator_index` - PLL modulator index (starts from 1)
331    ///
332    /// # Returns
333    /// True if PLL output is enabled.
334    ///
335    /// # Errors
336    /// Returns `NanonisError` if communication fails.
337    pub fn pll_out_on_off_get(&mut self, modulator_index: i32) -> Result<bool, NanonisError> {
338        let result = self.quick_send(
339            "PLL.OutOnOffGet",
340            vec![NanonisValue::I32(modulator_index)],
341            vec!["i"],
342            vec!["I"],
343        )?;
344
345        if !result.is_empty() {
346            Ok(result[0].as_u32()? != 0)
347        } else {
348            Err(NanonisError::Protocol("Invalid response".to_string()))
349        }
350    }
351
352    // ==================== Excitation ====================
353
354    /// Set the excitation range.
355    ///
356    /// # Arguments
357    /// * `modulator_index` - PLL modulator index (starts from 1)
358    /// * `range` - Excitation output range
359    ///
360    /// # Errors
361    /// Returns `NanonisError` if communication fails.
362    pub fn pll_exc_range_set(
363        &mut self,
364        modulator_index: i32,
365        range: PLLExcRange,
366    ) -> Result<(), NanonisError> {
367        self.quick_send(
368            "PLL.ExcRangeSet",
369            vec![
370                NanonisValue::I32(modulator_index),
371                NanonisValue::U16(range.into()),
372            ],
373            vec!["i", "H"],
374            vec![],
375        )?;
376        Ok(())
377    }
378
379    /// Get the excitation range.
380    ///
381    /// # Arguments
382    /// * `modulator_index` - PLL modulator index (starts from 1)
383    ///
384    /// # Returns
385    /// Excitation output range.
386    ///
387    /// # Errors
388    /// Returns `NanonisError` if communication fails.
389    pub fn pll_exc_range_get(&mut self, modulator_index: i32) -> Result<PLLExcRange, NanonisError> {
390        let result = self.quick_send(
391            "PLL.ExcRangeGet",
392            vec![NanonisValue::I32(modulator_index)],
393            vec!["i"],
394            vec!["H"],
395        )?;
396
397        if !result.is_empty() {
398            result[0].as_u16()?.try_into()
399        } else {
400            Err(NanonisError::Protocol("Invalid response".to_string()))
401        }
402    }
403
404    /// Set the excitation value (drive amplitude).
405    ///
406    /// Only works when amplitude controller is off.
407    ///
408    /// # Arguments
409    /// * `modulator_index` - PLL modulator index (starts from 1)
410    /// * `excitation_v` - Excitation value in volts
411    ///
412    /// # Errors
413    /// Returns `NanonisError` if communication fails.
414    pub fn pll_excitation_set(
415        &mut self,
416        modulator_index: i32,
417        excitation_v: f32,
418    ) -> Result<(), NanonisError> {
419        self.quick_send(
420            "PLL.ExcitationSet",
421            vec![
422                NanonisValue::I32(modulator_index),
423                NanonisValue::F32(excitation_v),
424            ],
425            vec!["i", "f"],
426            vec![],
427        )?;
428        Ok(())
429    }
430
431    /// Get the excitation value (drive amplitude).
432    ///
433    /// # Arguments
434    /// * `modulator_index` - PLL modulator index (starts from 1)
435    ///
436    /// # Returns
437    /// Excitation value in volts.
438    ///
439    /// # Errors
440    /// Returns `NanonisError` if communication fails.
441    pub fn pll_excitation_get(&mut self, modulator_index: i32) -> Result<f32, NanonisError> {
442        let result = self.quick_send(
443            "PLL.ExcitationGet",
444            vec![NanonisValue::I32(modulator_index)],
445            vec!["i"],
446            vec!["f"],
447        )?;
448
449        if !result.is_empty() {
450            Ok(result[0].as_f32()?)
451        } else {
452            Err(NanonisError::Protocol("Invalid response".to_string()))
453        }
454    }
455
456    // ==================== Amplitude Controller ====================
457    /// Set the amplitude controller setpoint for a PLL modulator.
458    ///
459    /// Sets the amplitude controller setpoint value for the specified PLL modulator.
460    /// This controls the target oscillation amplitude for the phase-locked loop.
461    ///
462    /// # Arguments
463    /// * `modulator_index` - PLL modulator index (starts from 1)
464    /// * `setpoint_m` - Amplitude setpoint in meters
465    ///
466    /// # Errors
467    /// Returns `NanonisError` if communication fails or invalid modulator index.
468    ///
469    /// # Examples
470    /// ```no_run
471    /// use nanonis_rs::NanonisClient;
472    ///
473    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
474    ///
475    /// // Set amplitude setpoint for first PLL to 1 nanometer
476    /// client.pll_amp_ctrl_setpnt_set(1, 1e-9)?;
477    ///
478    /// // Set amplitude setpoint for second PLL to 500 picometers
479    /// client.pll_amp_ctrl_setpnt_set(2, 500e-12)?;
480    /// # Ok::<(), Box<dyn std::error::Error>>(())
481    /// ```
482    pub fn pll_amp_ctrl_setpnt_set(
483        &mut self,
484        modulator_index: i32,
485        setpoint_m: f32,
486    ) -> Result<(), NanonisError> {
487        self.quick_send(
488            "PLL.AmpCtrlSetpntSet",
489            vec![
490                NanonisValue::I32(modulator_index),
491                NanonisValue::F32(setpoint_m),
492            ],
493            vec!["i", "f"],
494            vec![],
495        )?;
496        Ok(())
497    }
498
499    /// Get the amplitude controller setpoint for a PLL modulator.
500    ///
501    /// Returns the current amplitude controller setpoint value for the specified
502    /// PLL modulator.
503    ///
504    /// # Arguments
505    /// * `modulator_index` - PLL modulator index (starts from 1)
506    ///
507    /// # Returns
508    /// * `f32` - Current amplitude setpoint in meters
509    ///
510    /// # Errors
511    /// Returns `NanonisError` if communication fails or invalid modulator index.
512    ///
513    /// # Examples
514    /// ```no_run
515    /// use nanonis_rs::NanonisClient;
516    ///
517    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
518    ///
519    /// // Get current amplitude setpoint for first PLL
520    /// let setpoint = client.pll_amp_ctrl_setpnt_get(1)?;
521    /// println!("Current amplitude setpoint: {:.2e} m", setpoint);
522    ///
523    /// // Check if setpoint is within expected range
524    /// if setpoint > 1e-9 {
525    ///     println!("Amplitude setpoint is greater than 1 nm");
526    /// }
527    /// # Ok::<(), Box<dyn std::error::Error>>(())
528    /// ```
529    pub fn pll_amp_ctrl_setpnt_get(&mut self, modulator_index: i32) -> Result<f32, NanonisError> {
530        let response = self.quick_send(
531            "PLL.AmpCtrlSetpntGet",
532            vec![NanonisValue::I32(modulator_index)],
533            vec!["i"],
534            vec!["f"],
535        )?;
536
537        match response.first() {
538            Some(NanonisValue::F32(setpoint)) => Ok(*setpoint),
539            _ => Err(NanonisError::Protocol(
540                "Expected f32 amplitude setpoint".to_string(),
541            )),
542        }
543    }
544
545    /// Set the amplitude controller on/off status for a PLL modulator.
546    ///
547    /// Switches the amplitude controller for the specified PLL modulator on or off.
548    /// When enabled, the amplitude controller actively maintains the oscillation
549    /// amplitude at the setpoint value.
550    ///
551    /// # Arguments
552    /// * `modulator_index` - PLL modulator index (starts from 1)
553    /// * `status` - true to turn on, false to turn off
554    ///
555    /// # Errors
556    /// Returns `NanonisError` if communication fails or invalid modulator index.
557    ///
558    /// # Examples
559    /// ```no_run
560    /// use nanonis_rs::NanonisClient;
561    ///
562    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
563    ///
564    /// // Turn on amplitude controller for first PLL
565    /// client.pll_amp_ctrl_on_off_set(1, true)?;
566    ///
567    /// // Turn off amplitude controller for second PLL
568    /// client.pll_amp_ctrl_on_off_set(2, false)?;
569    /// # Ok::<(), Box<dyn std::error::Error>>(())
570    /// ```
571    pub fn pll_amp_ctrl_on_off_set(
572        &mut self,
573        modulator_index: i32,
574        status: bool,
575    ) -> Result<(), NanonisError> {
576        let status_u32 = if status { 1u32 } else { 0u32 };
577
578        self.quick_send(
579            "PLL.AmpCtrlOnOffSet",
580            vec![
581                NanonisValue::I32(modulator_index),
582                NanonisValue::U32(status_u32),
583            ],
584            vec!["i", "I"],
585            vec![],
586        )?;
587        Ok(())
588    }
589
590    /// Get the amplitude controller on/off status for a PLL modulator.
591    ///
592    /// Returns the current on/off status of the amplitude controller for the
593    /// specified PLL modulator.
594    ///
595    /// # Arguments
596    /// * `modulator_index` - PLL modulator index (starts from 1)
597    ///
598    /// # Returns
599    /// * `bool` - true if controller is on, false if off
600    ///
601    /// # Errors
602    /// Returns `NanonisError` if communication fails or invalid modulator index.
603    ///
604    /// # Examples
605    /// ```no_run
606    /// use nanonis_rs::NanonisClient;
607    ///
608    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
609    ///
610    /// // Check amplitude controller status for first PLL
611    /// let is_on = client.pll_amp_ctrl_on_off_get(1)?;
612    /// if is_on {
613    ///     println!("Amplitude controller is active");
614    /// } else {
615    ///     println!("Amplitude controller is inactive");
616    /// }
617    ///
618    /// // Enable controller if it's off
619    /// if !is_on {
620    ///     client.pll_amp_ctrl_on_off_set(1, true)?;
621    /// }
622    /// # Ok::<(), Box<dyn std::error::Error>>(())
623    /// ```
624    pub fn pll_amp_ctrl_on_off_get(&mut self, modulator_index: i32) -> Result<bool, NanonisError> {
625        let response = self.quick_send(
626            "PLL.AmpCtrlOnOffGet",
627            vec![NanonisValue::I32(modulator_index)],
628            vec!["i"],
629            vec!["I"],
630        )?;
631
632        match response.first() {
633            Some(NanonisValue::U32(status)) => Ok(*status != 0),
634            _ => Err(NanonisError::Protocol(
635                "Expected u32 amplitude controller status".to_string(),
636            )),
637        }
638    }
639
640    /// Set the amplitude controller gain parameters for a PLL modulator.
641    ///
642    /// Sets the proportional gain and time constant for the amplitude controller
643    /// of the specified PLL modulator. These parameters control the response
644    /// characteristics of the amplitude control loop.
645    ///
646    /// # Arguments
647    /// * `modulator_index` - PLL modulator index (starts from 1)
648    /// * `p_gain_v_div_m` - Proportional gain in V/m
649    /// * `time_constant_s` - Time constant in seconds
650    ///
651    /// # Errors
652    /// Returns `NanonisError` if communication fails or invalid modulator index.
653    ///
654    /// # Examples
655    /// ```no_run
656    /// use nanonis_rs::NanonisClient;
657    ///
658    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
659    ///
660    /// // Set moderate gain and fast response for first PLL
661    /// client.pll_amp_ctrl_gain_set(1, 1e6, 0.01)?;
662    ///
663    /// // Set higher gain and slower response for second PLL
664    /// client.pll_amp_ctrl_gain_set(2, 5e6, 0.1)?;
665    /// # Ok::<(), Box<dyn std::error::Error>>(())
666    /// ```
667    pub fn pll_amp_ctrl_gain_set(
668        &mut self,
669        modulator_index: i32,
670        p_gain_v_div_m: f32,
671        time_constant_s: f32,
672    ) -> Result<(), NanonisError> {
673        self.quick_send(
674            "PLL.AmpCtrlGainSet",
675            vec![
676                NanonisValue::I32(modulator_index),
677                NanonisValue::F32(p_gain_v_div_m),
678                NanonisValue::F32(time_constant_s),
679            ],
680            vec!["i", "f", "f"],
681            vec![],
682        )?;
683        Ok(())
684    }
685
686    /// Get the amplitude controller gain parameters for a PLL modulator.
687    ///
688    /// Returns the current proportional gain and time constant settings for the
689    /// amplitude controller of the specified PLL modulator.
690    ///
691    /// # Arguments
692    /// * `modulator_index` - PLL modulator index (starts from 1)
693    ///
694    /// # Returns
695    /// * `(f32, f32)` - Tuple of (proportional gain in V/m, time constant in seconds)
696    ///
697    /// # Errors
698    /// Returns `NanonisError` if communication fails or invalid modulator index.
699    ///
700    /// # Examples
701    /// ```no_run
702    /// use nanonis_rs::NanonisClient;
703    ///
704    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
705    ///
706    /// // Get current gain parameters for first PLL
707    /// let (p_gain, time_const) = client.pll_amp_ctrl_gain_get(1)?;
708    /// println!("P gain: {:.2e} V/m, Time constant: {:.3} s", p_gain, time_const);
709    ///
710    /// // Check if parameters are within acceptable range
711    /// if p_gain < 1e5 {
712    ///     println!("Warning: Low proportional gain");
713    /// }
714    /// if time_const > 1.0 {
715    ///     println!("Warning: Slow time constant");
716    /// }
717    /// # Ok::<(), Box<dyn std::error::Error>>(())
718    /// ```
719    pub fn pll_amp_ctrl_gain_get(
720        &mut self,
721        modulator_index: i32,
722    ) -> Result<(f32, f32), NanonisError> {
723        let response = self.quick_send(
724            "PLL.AmpCtrlGainGet",
725            vec![NanonisValue::I32(modulator_index)],
726            vec!["i"],
727            vec!["f", "f"],
728        )?;
729
730        match (response.first(), response.get(1)) {
731            (Some(NanonisValue::F32(p_gain)), Some(NanonisValue::F32(time_const))) => {
732                Ok((*p_gain, *time_const))
733            }
734            _ => Err(NanonisError::Protocol(
735                "Expected f32 gain parameters (p_gain, time_constant)".to_string(),
736            )),
737        }
738    }
739
740    /// Set the amplitude controller bandwidth.
741    ///
742    /// Uses current Q factor and amplitude to excitation ratio.
743    ///
744    /// # Arguments
745    /// * `modulator_index` - PLL modulator index (starts from 1)
746    /// * `bandwidth_hz` - Bandwidth in Hz
747    ///
748    /// # Errors
749    /// Returns `NanonisError` if communication fails.
750    pub fn pll_amp_ctrl_bandwidth_set(
751        &mut self,
752        modulator_index: i32,
753        bandwidth_hz: f32,
754    ) -> Result<(), NanonisError> {
755        self.quick_send(
756            "PLL.AmpCtrlBandwidthSet",
757            vec![
758                NanonisValue::I32(modulator_index),
759                NanonisValue::F32(bandwidth_hz),
760            ],
761            vec!["i", "f"],
762            vec![],
763        )?;
764        Ok(())
765    }
766
767    /// Get the amplitude controller bandwidth.
768    ///
769    /// # Arguments
770    /// * `modulator_index` - PLL modulator index (starts from 1)
771    ///
772    /// # Returns
773    /// Bandwidth in Hz.
774    ///
775    /// # Errors
776    /// Returns `NanonisError` if communication fails.
777    pub fn pll_amp_ctrl_bandwidth_get(
778        &mut self,
779        modulator_index: i32,
780    ) -> Result<f32, NanonisError> {
781        let result = self.quick_send(
782            "PLL.AmpCtrlBandwidthGet",
783            vec![NanonisValue::I32(modulator_index)],
784            vec!["i"],
785            vec!["f"],
786        )?;
787
788        if !result.is_empty() {
789            Ok(result[0].as_f32()?)
790        } else {
791            Err(NanonisError::Protocol("Invalid response".to_string()))
792        }
793    }
794
795    // ==================== Phase Controller ====================
796
797    /// Set the phase controller on/off status.
798    ///
799    /// # Arguments
800    /// * `modulator_index` - PLL modulator index (starts from 1)
801    /// * `enabled` - True to enable phase controller
802    ///
803    /// # Errors
804    /// Returns `NanonisError` if communication fails.
805    pub fn pll_phas_ctrl_on_off_set(
806        &mut self,
807        modulator_index: i32,
808        enabled: bool,
809    ) -> Result<(), NanonisError> {
810        let flag = if enabled { 1u32 } else { 0u32 };
811        self.quick_send(
812            "PLL.PhasCtrlOnOffSet",
813            vec![NanonisValue::I32(modulator_index), NanonisValue::U32(flag)],
814            vec!["i", "I"],
815            vec![],
816        )?;
817        Ok(())
818    }
819
820    /// Get the phase controller on/off status.
821    ///
822    /// # Arguments
823    /// * `modulator_index` - PLL modulator index (starts from 1)
824    ///
825    /// # Returns
826    /// True if phase controller is enabled.
827    ///
828    /// # Errors
829    /// Returns `NanonisError` if communication fails.
830    pub fn pll_phas_ctrl_on_off_get(&mut self, modulator_index: i32) -> Result<bool, NanonisError> {
831        let result = self.quick_send(
832            "PLL.PhasCtrlOnOffGet",
833            vec![NanonisValue::I32(modulator_index)],
834            vec!["i"],
835            vec!["I"],
836        )?;
837
838        if !result.is_empty() {
839            Ok(result[0].as_u32()? != 0)
840        } else {
841            Err(NanonisError::Protocol("Invalid response".to_string()))
842        }
843    }
844
845    /// Set the phase controller gain parameters.
846    ///
847    /// # Arguments
848    /// * `modulator_index` - PLL modulator index (starts from 1)
849    /// * `p_gain_hz_per_deg` - Proportional gain in Hz/deg
850    /// * `time_constant_s` - Time constant in seconds
851    ///
852    /// # Errors
853    /// Returns `NanonisError` if communication fails.
854    pub fn pll_phas_ctrl_gain_set(
855        &mut self,
856        modulator_index: i32,
857        p_gain_hz_per_deg: f32,
858        time_constant_s: f32,
859    ) -> Result<(), NanonisError> {
860        self.quick_send(
861            "PLL.PhasCtrlGainSet",
862            vec![
863                NanonisValue::I32(modulator_index),
864                NanonisValue::F32(p_gain_hz_per_deg),
865                NanonisValue::F32(time_constant_s),
866            ],
867            vec!["i", "f", "f"],
868            vec![],
869        )?;
870        Ok(())
871    }
872
873    /// Get the phase controller gain parameters.
874    ///
875    /// # Arguments
876    /// * `modulator_index` - PLL modulator index (starts from 1)
877    ///
878    /// # Returns
879    /// Phase controller gain parameters.
880    ///
881    /// # Errors
882    /// Returns `NanonisError` if communication fails.
883    pub fn pll_phas_ctrl_gain_get(
884        &mut self,
885        modulator_index: i32,
886    ) -> Result<PLLPhasCtrlGain, NanonisError> {
887        let result = self.quick_send(
888            "PLL.PhasCtrlGainGet",
889            vec![NanonisValue::I32(modulator_index)],
890            vec!["i"],
891            vec!["f", "f"],
892        )?;
893
894        if result.len() >= 2 {
895            Ok(PLLPhasCtrlGain {
896                p_gain_hz_per_deg: result[0].as_f32()?,
897                time_constant_s: result[1].as_f32()?,
898            })
899        } else {
900            Err(NanonisError::Protocol("Invalid response".to_string()))
901        }
902    }
903
904    /// Set the phase controller bandwidth.
905    ///
906    /// Uses current Q factor.
907    ///
908    /// # Arguments
909    /// * `modulator_index` - PLL modulator index (starts from 1)
910    /// * `bandwidth_hz` - Bandwidth in Hz
911    ///
912    /// # Errors
913    /// Returns `NanonisError` if communication fails.
914    pub fn pll_phas_ctrl_bandwidth_set(
915        &mut self,
916        modulator_index: i32,
917        bandwidth_hz: f32,
918    ) -> Result<(), NanonisError> {
919        self.quick_send(
920            "PLL.PhasCtrlBandwidthSet",
921            vec![
922                NanonisValue::I32(modulator_index),
923                NanonisValue::F32(bandwidth_hz),
924            ],
925            vec!["i", "f"],
926            vec![],
927        )?;
928        Ok(())
929    }
930
931    /// Get the phase controller bandwidth.
932    ///
933    /// # Arguments
934    /// * `modulator_index` - PLL modulator index (starts from 1)
935    ///
936    /// # Returns
937    /// Bandwidth in Hz.
938    ///
939    /// # Errors
940    /// Returns `NanonisError` if communication fails.
941    pub fn pll_phas_ctrl_bandwidth_get(
942        &mut self,
943        modulator_index: i32,
944    ) -> Result<f32, NanonisError> {
945        let result = self.quick_send(
946            "PLL.PhasCtrlBandwidthGet",
947            vec![NanonisValue::I32(modulator_index)],
948            vec!["i"],
949            vec!["f"],
950        )?;
951
952        if !result.is_empty() {
953            Ok(result[0].as_f32()?)
954        } else {
955            Err(NanonisError::Protocol("Invalid response".to_string()))
956        }
957    }
958
959    // ==================== Frequency ====================
960
961    /// Set the frequency range.
962    ///
963    /// # Arguments
964    /// * `modulator_index` - PLL modulator index (starts from 1)
965    /// * `freq_range_hz` - Frequency range in Hz
966    ///
967    /// # Errors
968    /// Returns `NanonisError` if communication fails.
969    pub fn pll_freq_range_set(
970        &mut self,
971        modulator_index: i32,
972        freq_range_hz: f32,
973    ) -> Result<(), NanonisError> {
974        self.quick_send(
975            "PLL.FreqRangeSet",
976            vec![
977                NanonisValue::I32(modulator_index),
978                NanonisValue::F32(freq_range_hz),
979            ],
980            vec!["i", "f"],
981            vec![],
982        )?;
983        Ok(())
984    }
985
986    /// Get the frequency range.
987    ///
988    /// # Arguments
989    /// * `modulator_index` - PLL modulator index (starts from 1)
990    ///
991    /// # Returns
992    /// Frequency range in Hz.
993    ///
994    /// # Errors
995    /// Returns `NanonisError` if communication fails.
996    pub fn pll_freq_range_get(&mut self, modulator_index: i32) -> Result<f32, NanonisError> {
997        let result = self.quick_send(
998            "PLL.FreqRangeGet",
999            vec![NanonisValue::I32(modulator_index)],
1000            vec!["i"],
1001            vec!["f"],
1002        )?;
1003
1004        if !result.is_empty() {
1005            Ok(result[0].as_f32()?)
1006        } else {
1007            Err(NanonisError::Protocol("Invalid response".to_string()))
1008        }
1009    }
1010
1011    /// Set the center frequency.
1012    ///
1013    /// # Arguments
1014    /// * `modulator_index` - PLL modulator index (starts from 1)
1015    /// * `center_freq_hz` - Center frequency in Hz
1016    ///
1017    /// # Errors
1018    /// Returns `NanonisError` if communication fails.
1019    pub fn pll_center_freq_set(
1020        &mut self,
1021        modulator_index: i32,
1022        center_freq_hz: f64,
1023    ) -> Result<(), NanonisError> {
1024        self.quick_send(
1025            "PLL.CenterFreqSet",
1026            vec![
1027                NanonisValue::I32(modulator_index),
1028                NanonisValue::F64(center_freq_hz),
1029            ],
1030            vec!["i", "d"],
1031            vec![],
1032        )?;
1033        Ok(())
1034    }
1035
1036    /// Get the center frequency.
1037    ///
1038    /// # Arguments
1039    /// * `modulator_index` - PLL modulator index (starts from 1)
1040    ///
1041    /// # Returns
1042    /// Center frequency in Hz.
1043    ///
1044    /// # Errors
1045    /// Returns `NanonisError` if communication fails.
1046    pub fn pll_center_freq_get(&mut self, modulator_index: i32) -> Result<f64, NanonisError> {
1047        let result = self.quick_send(
1048            "PLL.CenterFreqGet",
1049            vec![NanonisValue::I32(modulator_index)],
1050            vec!["i"],
1051            vec!["d"],
1052        )?;
1053
1054        if !result.is_empty() {
1055            Ok(result[0].as_f64()?)
1056        } else {
1057            Err(NanonisError::Protocol("Invalid response".to_string()))
1058        }
1059    }
1060
1061    /// Set the frequency shift.
1062    ///
1063    /// # Arguments
1064    /// * `modulator_index` - PLL modulator index (starts from 1)
1065    /// * `freq_shift_hz` - Frequency shift in Hz
1066    ///
1067    /// # Errors
1068    /// Returns `NanonisError` if communication fails.
1069    pub fn pll_freq_shift_set(
1070        &mut self,
1071        modulator_index: i32,
1072        freq_shift_hz: f32,
1073    ) -> Result<(), NanonisError> {
1074        self.quick_send(
1075            "PLL.FreqShiftSet",
1076            vec![
1077                NanonisValue::I32(modulator_index),
1078                NanonisValue::F32(freq_shift_hz),
1079            ],
1080            vec!["i", "f"],
1081            vec![],
1082        )?;
1083        Ok(())
1084    }
1085
1086    /// Get the frequency shift.
1087    ///
1088    /// # Arguments
1089    /// * `modulator_index` - PLL modulator index (starts from 1)
1090    ///
1091    /// # Returns
1092    /// Frequency shift in Hz.
1093    ///
1094    /// # Errors
1095    /// Returns `NanonisError` if communication fails.
1096    pub fn pll_freq_shift_get(&mut self, modulator_index: i32) -> Result<f32, NanonisError> {
1097        let result = self.quick_send(
1098            "PLL.FreqShiftGet",
1099            vec![NanonisValue::I32(modulator_index)],
1100            vec!["i"],
1101            vec!["f"],
1102        )?;
1103
1104        if !result.is_empty() {
1105            Ok(result[0].as_f32()?)
1106        } else {
1107            Err(NanonisError::Protocol("Invalid response".to_string()))
1108        }
1109    }
1110
1111    /// Auto-center frequency shift.
1112    ///
1113    /// Adds current frequency shift to center frequency and sets frequency shift to zero.
1114    ///
1115    /// # Arguments
1116    /// * `modulator_index` - PLL modulator index (starts from 1)
1117    ///
1118    /// # Errors
1119    /// Returns `NanonisError` if communication fails.
1120    pub fn pll_freq_shift_auto_center(&mut self, modulator_index: i32) -> Result<(), NanonisError> {
1121        self.quick_send(
1122            "PLL.FreqShiftAutoCenter",
1123            vec![NanonisValue::I32(modulator_index)],
1124            vec!["i"],
1125            vec![],
1126        )?;
1127        Ok(())
1128    }
1129
1130    /// Set the frequency/excitation overwrite signals.
1131    ///
1132    /// Works when corresponding controller is not active.
1133    /// Use -2 for no change.
1134    ///
1135    /// # Arguments
1136    /// * `modulator_index` - PLL modulator index (starts from 1)
1137    /// * `overwrite` - Overwrite configuration
1138    ///
1139    /// # Errors
1140    /// Returns `NanonisError` if communication fails.
1141    pub fn pll_freq_exc_overwrite_set(
1142        &mut self,
1143        modulator_index: i32,
1144        overwrite: &PLLOverwrite,
1145    ) -> Result<(), NanonisError> {
1146        self.quick_send(
1147            "PLL.FreqExcOverwriteSet",
1148            vec![
1149                NanonisValue::I32(modulator_index),
1150                NanonisValue::I32(overwrite.excitation_signal_index),
1151                NanonisValue::I32(overwrite.frequency_signal_index),
1152            ],
1153            vec!["i", "i", "i"],
1154            vec![],
1155        )?;
1156        Ok(())
1157    }
1158
1159    /// Get the frequency/excitation overwrite signals.
1160    ///
1161    /// # Arguments
1162    /// * `modulator_index` - PLL modulator index (starts from 1)
1163    ///
1164    /// # Returns
1165    /// Overwrite configuration.
1166    ///
1167    /// # Errors
1168    /// Returns `NanonisError` if communication fails.
1169    pub fn pll_freq_exc_overwrite_get(
1170        &mut self,
1171        modulator_index: i32,
1172    ) -> Result<PLLOverwrite, NanonisError> {
1173        let result = self.quick_send(
1174            "PLL.FreqExcOverwriteGet",
1175            vec![NanonisValue::I32(modulator_index)],
1176            vec!["i"],
1177            vec!["i", "i"],
1178        )?;
1179
1180        if result.len() >= 2 {
1181            Ok(PLLOverwrite {
1182                excitation_signal_index: result[0].as_i32()?,
1183                frequency_signal_index: result[1].as_i32()?,
1184            })
1185        } else {
1186            Err(NanonisError::Protocol("Invalid response".to_string()))
1187        }
1188    }
1189
1190    // ==================== Demodulator ====================
1191
1192    /// Set the demodulator input and frequency generator.
1193    ///
1194    /// # Arguments
1195    /// * `demodulator_index` - Demodulator index (starts from 1)
1196    /// * `input` - Demodulator input configuration
1197    ///
1198    /// # Errors
1199    /// Returns `NanonisError` if communication fails.
1200    pub fn pll_demod_input_set(
1201        &mut self,
1202        demodulator_index: u16,
1203        input: &PLLDemodInput,
1204    ) -> Result<(), NanonisError> {
1205        self.quick_send(
1206            "PLL.DemodInputSet",
1207            vec![
1208                NanonisValue::U16(demodulator_index),
1209                NanonisValue::U16(input.input),
1210                NanonisValue::U16(input.freq_generator),
1211            ],
1212            vec!["H", "H", "H"],
1213            vec![],
1214        )?;
1215        Ok(())
1216    }
1217
1218    /// Get the demodulator input and frequency generator.
1219    ///
1220    /// # Arguments
1221    /// * `demodulator_index` - Demodulator index (starts from 1)
1222    ///
1223    /// # Returns
1224    /// Demodulator input configuration.
1225    ///
1226    /// # Errors
1227    /// Returns `NanonisError` if communication fails.
1228    pub fn pll_demod_input_get(
1229        &mut self,
1230        demodulator_index: u16,
1231    ) -> Result<PLLDemodInput, NanonisError> {
1232        let result = self.quick_send(
1233            "PLL.DemodInputGet",
1234            vec![NanonisValue::U16(demodulator_index)],
1235            vec!["H"],
1236            vec!["H", "H"],
1237        )?;
1238
1239        if result.len() >= 2 {
1240            Ok(PLLDemodInput {
1241                input: result[0].as_u16()?,
1242                freq_generator: result[1].as_u16()?,
1243            })
1244        } else {
1245            Err(NanonisError::Protocol("Invalid response".to_string()))
1246        }
1247    }
1248
1249    /// Set the demodulator harmonic.
1250    ///
1251    /// Harmonic 1 corresponds to modulation frequency.
1252    ///
1253    /// # Arguments
1254    /// * `demodulator_index` - Demodulator index (starts from 1)
1255    /// * `harmonic` - Harmonic number
1256    ///
1257    /// # Errors
1258    /// Returns `NanonisError` if communication fails.
1259    pub fn pll_demod_harmonic_set(
1260        &mut self,
1261        demodulator_index: u16,
1262        harmonic: u16,
1263    ) -> Result<(), NanonisError> {
1264        self.quick_send(
1265            "PLL.DemodHarmonicSet",
1266            vec![
1267                NanonisValue::U16(demodulator_index),
1268                NanonisValue::U16(harmonic),
1269            ],
1270            vec!["H", "H"],
1271            vec![],
1272        )?;
1273        Ok(())
1274    }
1275
1276    /// Get the demodulator harmonic.
1277    ///
1278    /// # Arguments
1279    /// * `demodulator_index` - Demodulator index (starts from 1)
1280    ///
1281    /// # Returns
1282    /// Harmonic number.
1283    ///
1284    /// # Errors
1285    /// Returns `NanonisError` if communication fails.
1286    pub fn pll_demod_harmonic_get(&mut self, demodulator_index: u16) -> Result<u16, NanonisError> {
1287        let result = self.quick_send(
1288            "PLL.DemodHarmonicGet",
1289            vec![NanonisValue::U16(demodulator_index)],
1290            vec!["H"],
1291            vec!["H"],
1292        )?;
1293
1294        if !result.is_empty() {
1295            Ok(result[0].as_u16()?)
1296        } else {
1297            Err(NanonisError::Protocol("Invalid response".to_string()))
1298        }
1299    }
1300
1301    /// Set the demodulator phase reference.
1302    ///
1303    /// # Arguments
1304    /// * `demodulator_index` - Demodulator index (starts from 1)
1305    /// * `phase_deg` - Phase reference in degrees
1306    ///
1307    /// # Errors
1308    /// Returns `NanonisError` if communication fails.
1309    pub fn pll_demod_phas_ref_set(
1310        &mut self,
1311        demodulator_index: u16,
1312        phase_deg: f32,
1313    ) -> Result<(), NanonisError> {
1314        self.quick_send(
1315            "PLL.DemodPhasRefSet",
1316            vec![
1317                NanonisValue::U16(demodulator_index),
1318                NanonisValue::F32(phase_deg),
1319            ],
1320            vec!["H", "f"],
1321            vec![],
1322        )?;
1323        Ok(())
1324    }
1325
1326    /// Get the demodulator phase reference.
1327    ///
1328    /// # Arguments
1329    /// * `demodulator_index` - Demodulator index (starts from 1)
1330    ///
1331    /// # Returns
1332    /// Phase reference in degrees.
1333    ///
1334    /// # Errors
1335    /// Returns `NanonisError` if communication fails.
1336    pub fn pll_demod_phas_ref_get(&mut self, demodulator_index: u16) -> Result<f32, NanonisError> {
1337        let result = self.quick_send(
1338            "PLL.DemodPhasRefGet",
1339            vec![NanonisValue::U16(demodulator_index)],
1340            vec!["H"],
1341            vec!["f"],
1342        )?;
1343
1344        if !result.is_empty() {
1345            Ok(result[0].as_f32()?)
1346        } else {
1347            Err(NanonisError::Protocol("Invalid response".to_string()))
1348        }
1349    }
1350
1351    /// Set the demodulator filter order.
1352    ///
1353    /// # Arguments
1354    /// * `demodulator_index` - Demodulator index (starts from 1)
1355    /// * `filter_order` - Low-pass filter order
1356    ///
1357    /// # Errors
1358    /// Returns `NanonisError` if communication fails.
1359    pub fn pll_demod_filter_set(
1360        &mut self,
1361        demodulator_index: u16,
1362        filter_order: u16,
1363    ) -> Result<(), NanonisError> {
1364        self.quick_send(
1365            "PLL.DemodFilterSet",
1366            vec![
1367                NanonisValue::U16(demodulator_index),
1368                NanonisValue::U16(filter_order),
1369            ],
1370            vec!["H", "H"],
1371            vec![],
1372        )?;
1373        Ok(())
1374    }
1375
1376    /// Get the demodulator filter order.
1377    ///
1378    /// # Arguments
1379    /// * `demodulator_index` - Demodulator index (starts from 1)
1380    ///
1381    /// # Returns
1382    /// Low-pass filter order.
1383    ///
1384    /// # Errors
1385    /// Returns `NanonisError` if communication fails.
1386    pub fn pll_demod_filter_get(&mut self, demodulator_index: u16) -> Result<u16, NanonisError> {
1387        let result = self.quick_send(
1388            "PLL.DemodFilterGet",
1389            vec![NanonisValue::U16(demodulator_index)],
1390            vec!["H"],
1391            vec!["H"],
1392        )?;
1393
1394        if !result.is_empty() {
1395            Ok(result[0].as_u16()?)
1396        } else {
1397            Err(NanonisError::Protocol("Invalid response".to_string()))
1398        }
1399    }
1400}