Skip to main content

nanonis_rs/client/
cpd_comp.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// CPD compensation parameters.
6#[derive(Debug, Clone, Copy, Default)]
7pub struct CPDCompParams {
8    /// Sweep speed in Hz
9    pub speed_hz: f32,
10    /// Voltage range in volts
11    pub range_v: f32,
12    /// Number of averaging cycles
13    pub averaging: i32,
14}
15
16/// CPD compensation fit coefficients.
17///
18/// The fit model is: df = a(U-Uo)^2 + b(U-Uo) + c
19/// where Uo is the bias voltage.
20#[derive(Debug, Clone, Copy, Default)]
21pub struct CPDFitCoefficients {
22    /// Quadratic coefficient 'a'
23    pub a: f64,
24    /// Linear coefficient 'b'
25    pub b: f64,
26}
27
28/// CPD compensation sweep data for one direction.
29#[derive(Debug, Clone, Default)]
30pub struct CPDSweepData {
31    /// Bias voltage data (X axis)
32    pub bias_v: Vec<f32>,
33    /// Frequency shift data
34    pub freq_shift: Vec<f32>,
35    /// Frequency shift fit data
36    pub freq_shift_fit: Vec<f32>,
37}
38
39/// Complete CPD compensation data.
40#[derive(Debug, Clone, Default)]
41pub struct CPDCompData {
42    /// Forward sweep data
43    pub forward: CPDSweepData,
44    /// Backward sweep data
45    pub backward: CPDSweepData,
46    /// CPD estimate in volts
47    pub cpd_estimate_v: f32,
48    /// Fit coefficients
49    pub fit_coefficients: CPDFitCoefficients,
50}
51
52impl NanonisClient {
53    /// Open the CPD compensation module.
54    ///
55    /// This opens the Contact Potential Difference compensation interface.
56    ///
57    /// # Errors
58    /// Returns `NanonisError` if communication fails.
59    ///
60    /// # Examples
61    /// ```no_run
62    /// use nanonis_rs::NanonisClient;
63    ///
64    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
65    /// client.cpd_comp_open()?;
66    /// # Ok::<(), Box<dyn std::error::Error>>(())
67    /// ```
68    pub fn cpd_comp_open(&mut self) -> Result<(), NanonisError> {
69        self.quick_send("CPDComp.Open", vec![], vec![], vec![])?;
70        Ok(())
71    }
72
73    /// Close the CPD compensation module.
74    ///
75    /// # Errors
76    /// Returns `NanonisError` if communication fails.
77    ///
78    /// # Examples
79    /// ```no_run
80    /// use nanonis_rs::NanonisClient;
81    ///
82    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
83    /// client.cpd_comp_close()?;
84    /// # Ok::<(), Box<dyn std::error::Error>>(())
85    /// ```
86    pub fn cpd_comp_close(&mut self) -> Result<(), NanonisError> {
87        self.quick_send("CPDComp.Close", vec![], vec![], vec![])?;
88        Ok(())
89    }
90
91    /// Set the CPD compensation parameters.
92    ///
93    /// # Arguments
94    /// * `params` - A [`CPDCompParams`] struct with compensation parameters
95    ///
96    /// # Errors
97    /// Returns `NanonisError` if communication fails.
98    ///
99    /// # Examples
100    /// ```no_run
101    /// use nanonis_rs::NanonisClient;
102    /// use nanonis_rs::cpd_comp::CPDCompParams;
103    ///
104    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
105    /// let params = CPDCompParams {
106    ///     speed_hz: 10.0,
107    ///     range_v: 2.0,
108    ///     averaging: 5,
109    /// };
110    /// client.cpd_comp_params_set(&params)?;
111    /// # Ok::<(), Box<dyn std::error::Error>>(())
112    /// ```
113    pub fn cpd_comp_params_set(&mut self, params: &CPDCompParams) -> Result<(), NanonisError> {
114        self.quick_send(
115            "CPDComp.ParamsSet",
116            vec![
117                NanonisValue::F32(params.speed_hz),
118                NanonisValue::F32(params.range_v),
119                NanonisValue::I32(params.averaging),
120            ],
121            vec!["f", "f", "i"],
122            vec![],
123        )?;
124        Ok(())
125    }
126
127    /// Get the CPD compensation parameters.
128    ///
129    /// # Returns
130    /// A [`CPDCompParams`] struct with current parameters.
131    ///
132    /// # Errors
133    /// Returns `NanonisError` if communication fails.
134    ///
135    /// # Examples
136    /// ```no_run
137    /// use nanonis_rs::NanonisClient;
138    ///
139    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
140    /// let params = client.cpd_comp_params_get()?;
141    /// println!("Speed: {} Hz, Range: {} V", params.speed_hz, params.range_v);
142    /// # Ok::<(), Box<dyn std::error::Error>>(())
143    /// ```
144    pub fn cpd_comp_params_get(&mut self) -> Result<CPDCompParams, NanonisError> {
145        let result = self.quick_send("CPDComp.ParamsGet", vec![], vec![], vec!["f", "f", "i"])?;
146
147        if result.len() >= 3 {
148            Ok(CPDCompParams {
149                speed_hz: result[0].as_f32()?,
150                range_v: result[1].as_f32()?,
151                averaging: result[2].as_i32()?,
152            })
153        } else {
154            Err(NanonisError::Protocol("Invalid response".to_string()))
155        }
156    }
157
158    /// Get the CPD compensation data.
159    ///
160    /// Returns the graph data, CPD estimate, and fit coefficients from the
161    /// CPD compensation module.
162    ///
163    /// # Returns
164    /// A [`CPDCompData`] struct with sweep data and fit results.
165    ///
166    /// # Errors
167    /// Returns `NanonisError` if communication fails.
168    ///
169    /// # Examples
170    /// ```no_run
171    /// use nanonis_rs::NanonisClient;
172    ///
173    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
174    /// let data = client.cpd_comp_data_get()?;
175    /// println!("CPD estimate: {} V", data.cpd_estimate_v);
176    /// println!("Fit: a={}, b={}", data.fit_coefficients.a, data.fit_coefficients.b);
177    /// # Ok::<(), Box<dyn std::error::Error>>(())
178    /// ```
179    pub fn cpd_comp_data_get(&mut self) -> Result<CPDCompData, NanonisError> {
180        let result = self.quick_send(
181            "CPDComp.DataGet",
182            vec![],
183            vec![],
184            vec![
185                "i", "*f", "*f", "*f", // Forward: size, bias, freq_shift, fit
186                "i", "*f", "*f", "*f", // Backward: size, bias, freq_shift, fit
187                "f", "d", "d", // CPD estimate, a, b coefficients
188            ],
189        )?;
190
191        if result.len() >= 11 {
192            let forward_bias = result[1].as_f32_array()?.to_vec();
193            let forward_freq = result[2].as_f32_array()?.to_vec();
194            let forward_fit = result[3].as_f32_array()?.to_vec();
195
196            let backward_bias = result[5].as_f32_array()?.to_vec();
197            let backward_freq = result[6].as_f32_array()?.to_vec();
198            let backward_fit = result[7].as_f32_array()?.to_vec();
199
200            Ok(CPDCompData {
201                forward: CPDSweepData {
202                    bias_v: forward_bias,
203                    freq_shift: forward_freq,
204                    freq_shift_fit: forward_fit,
205                },
206                backward: CPDSweepData {
207                    bias_v: backward_bias,
208                    freq_shift: backward_freq,
209                    freq_shift_fit: backward_fit,
210                },
211                cpd_estimate_v: result[8].as_f32()?,
212                fit_coefficients: CPDFitCoefficients {
213                    a: result[9].as_f64()?,
214                    b: result[10].as_f64()?,
215                },
216            })
217        } else {
218            Err(NanonisError::Protocol("Invalid response".to_string()))
219        }
220    }
221}