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(¶ms)?;
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}