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