Skip to main content

nanonis_rs/client/piezo/
mod.rs

1pub mod types;
2pub use types::*;
3
4use super::NanonisClient;
5use crate::error::NanonisError;
6use crate::types::NanonisValue;
7
8impl NanonisClient {
9    /// Set the piezo tilt correction parameters.
10    ///
11    /// # Arguments
12    /// * `tilt_x_deg` - Tilt angle correction in X direction (degrees)
13    /// * `tilt_y_deg` - Tilt angle correction in Y direction (degrees)
14    ///
15    /// # Errors
16    /// Returns `NanonisError` if communication fails.
17    ///
18    /// # Examples
19    /// ```no_run
20    /// use nanonis_rs::NanonisClient;
21    ///
22    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
23    /// client.piezo_tilt_set(0.5, -0.3)?;
24    /// # Ok::<(), Box<dyn std::error::Error>>(())
25    /// ```
26    pub fn piezo_tilt_set(&mut self, tilt_x_deg: f32, tilt_y_deg: f32) -> Result<(), NanonisError> {
27        self.quick_send(
28            "Piezo.TiltSet",
29            vec![NanonisValue::F32(tilt_x_deg), NanonisValue::F32(tilt_y_deg)],
30            vec!["f", "f"],
31            vec![],
32        )?;
33        Ok(())
34    }
35
36    /// Get the piezo tilt correction parameters.
37    ///
38    /// # Returns
39    /// A [`TiltCorrection`] struct with current tilt angles.
40    ///
41    /// # Errors
42    /// Returns `NanonisError` if communication fails.
43    pub fn piezo_tilt_get(&mut self) -> Result<TiltCorrection, NanonisError> {
44        let result = self.quick_send("Piezo.TiltGet", vec![], vec![], vec!["f", "f"])?;
45
46        if result.len() >= 2 {
47            Ok(TiltCorrection {
48                tilt_x_deg: result[0].as_f32()?,
49                tilt_y_deg: result[1].as_f32()?,
50            })
51        } else {
52            Err(NanonisError::Protocol("Invalid response".to_string()))
53        }
54    }
55
56    /// Set the piezo range values for all 3 axes.
57    ///
58    /// Changing the range will also change the sensitivity
59    /// (HV gain will remain unchanged).
60    ///
61    /// # Arguments
62    /// * `range` - A [`PiezoRange`] struct with X, Y, Z range values in meters
63    ///
64    /// # Errors
65    /// Returns `NanonisError` if communication fails.
66    pub fn piezo_range_set(&mut self, range: &PiezoRange) -> Result<(), NanonisError> {
67        self.quick_send(
68            "Piezo.RangeSet",
69            vec![
70                NanonisValue::F32(range.range_x_m),
71                NanonisValue::F32(range.range_y_m),
72                NanonisValue::F32(range.range_z_m),
73            ],
74            vec!["f", "f", "f"],
75            vec![],
76        )?;
77        Ok(())
78    }
79
80    /// Get the piezo range values for all 3 axes.
81    ///
82    /// # Returns
83    /// A [`PiezoRange`] struct with current range values in meters.
84    ///
85    /// # Errors
86    /// Returns `NanonisError` if communication fails.
87    pub fn piezo_range_get(&mut self) -> Result<PiezoRange, NanonisError> {
88        let result = self.quick_send("Piezo.RangeGet", vec![], vec![], vec!["f", "f", "f"])?;
89
90        if result.len() >= 3 {
91            Ok(PiezoRange {
92                range_x_m: result[0].as_f32()?,
93                range_y_m: result[1].as_f32()?,
94                range_z_m: result[2].as_f32()?,
95            })
96        } else {
97            Err(NanonisError::Protocol("Invalid response".to_string()))
98        }
99    }
100
101    /// Set the piezo sensitivity values for all 3 axes.
102    ///
103    /// Changing the sensitivity will also change the range
104    /// (HV gain will remain unchanged).
105    ///
106    /// # Arguments
107    /// * `sensitivity` - A [`PiezoSensitivity`] struct with X, Y, Z values in m/V
108    ///
109    /// # Errors
110    /// Returns `NanonisError` if communication fails.
111    pub fn piezo_sens_set(&mut self, sensitivity: &PiezoSensitivity) -> Result<(), NanonisError> {
112        self.quick_send(
113            "Piezo.SensSet",
114            vec![
115                NanonisValue::F32(sensitivity.sens_x_m_per_v),
116                NanonisValue::F32(sensitivity.sens_y_m_per_v),
117                NanonisValue::F32(sensitivity.sens_z_m_per_v),
118            ],
119            vec!["f", "f", "f"],
120            vec![],
121        )?;
122        Ok(())
123    }
124
125    /// Get the piezo sensitivity values for all 3 axes.
126    ///
127    /// # Returns
128    /// A [`PiezoSensitivity`] struct with current sensitivity values in m/V.
129    ///
130    /// # Errors
131    /// Returns `NanonisError` if communication fails.
132    pub fn piezo_sens_get(&mut self) -> Result<PiezoSensitivity, NanonisError> {
133        let result = self.quick_send("Piezo.SensGet", vec![], vec![], vec!["f", "f", "f"])?;
134
135        if result.len() >= 3 {
136            Ok(PiezoSensitivity {
137                sens_x_m_per_v: result[0].as_f32()?,
138                sens_y_m_per_v: result[1].as_f32()?,
139                sens_z_m_per_v: result[2].as_f32()?,
140            })
141        } else {
142            Err(NanonisError::Protocol("Invalid response".to_string()))
143        }
144    }
145
146    /// Set the drift compensation parameters.
147    ///
148    /// # Arguments
149    /// * `config` - A [`DriftCompConfig`] struct with compensation settings
150    ///
151    /// # Errors
152    /// Returns `NanonisError` if communication fails.
153    ///
154    /// # Examples
155    /// ```no_run
156    /// use nanonis_rs::NanonisClient;
157    /// use nanonis_rs::piezo::{DriftCompConfig, PiezoToggle};
158    ///
159    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
160    /// let config = DriftCompConfig {
161    ///     enabled: PiezoToggle::On,
162    ///     vx_m_s: 1e-12,
163    ///     vy_m_s: 0.5e-12,
164    ///     vz_m_s: 0.0,
165    ///     saturation_limit: 0.1,
166    /// };
167    /// client.piezo_drift_comp_set(&config)?;
168    /// # Ok::<(), Box<dyn std::error::Error>>(())
169    /// ```
170    pub fn piezo_drift_comp_set(&mut self, config: &DriftCompConfig) -> Result<(), NanonisError> {
171        self.quick_send(
172            "Piezo.DriftCompSet",
173            vec![
174                NanonisValue::U32(config.enabled.into()),
175                NanonisValue::F32(config.vx_m_s),
176                NanonisValue::F32(config.vy_m_s),
177                NanonisValue::F32(config.vz_m_s),
178                NanonisValue::F32(config.saturation_limit),
179            ],
180            vec!["I", "f", "f", "f", "f"],
181            vec![],
182        )?;
183        Ok(())
184    }
185
186    /// Get the drift compensation settings and status.
187    ///
188    /// # Returns
189    /// A [`DriftCompStatus`] struct with current settings and saturation status.
190    ///
191    /// # Errors
192    /// Returns `NanonisError` if communication fails.
193    pub fn piezo_drift_comp_get(&mut self) -> Result<DriftCompStatus, NanonisError> {
194        let result = self.quick_send(
195            "Piezo.DriftCompGet",
196            vec![],
197            vec![],
198            vec!["I", "f", "f", "f", "I", "I", "I", "f"],
199        )?;
200
201        if result.len() >= 8 {
202            Ok(DriftCompStatus {
203                enabled: result[0].as_u32()? != 0,
204                vx_m_s: result[1].as_f32()?,
205                vy_m_s: result[2].as_f32()?,
206                vz_m_s: result[3].as_f32()?,
207                x_saturated: result[4].as_u32()? != 0,
208                y_saturated: result[5].as_u32()? != 0,
209                z_saturated: result[6].as_u32()? != 0,
210                saturation_limit: result[7].as_f32()?,
211            })
212        } else {
213            Err(NanonisError::Protocol("Invalid response".to_string()))
214        }
215    }
216
217    /// Get the piezo calibration values for all 3 axes.
218    ///
219    /// The calibration returned is for the low voltage signals (±10V)
220    /// before the HV amplifier.
221    ///
222    /// # Returns
223    /// A [`PiezoSensitivity`] struct with calibration values in m/V.
224    ///
225    /// # Errors
226    /// Returns `NanonisError` if communication fails.
227    pub fn piezo_calibr_get(&mut self) -> Result<PiezoSensitivity, NanonisError> {
228        let result = self.quick_send("Piezo.CalibrGet", vec![], vec![], vec!["f", "f", "f"])?;
229
230        if result.len() >= 3 {
231            Ok(PiezoSensitivity {
232                sens_x_m_per_v: result[0].as_f32()?,
233                sens_y_m_per_v: result[1].as_f32()?,
234                sens_z_m_per_v: result[2].as_f32()?,
235            })
236        } else {
237            Err(NanonisError::Protocol("Invalid response".to_string()))
238        }
239    }
240
241    /// Get the HVA (High Voltage Amplifier) gain information.
242    ///
243    /// If HVA gain readout is not enabled, this function returns a warning.
244    ///
245    /// # Returns
246    /// A [`HVAInfo`] struct with gain values and enabled status.
247    ///
248    /// # Errors
249    /// Returns `NanonisError` if communication fails.
250    pub fn piezo_hva_info_get(&mut self) -> Result<HVAInfo, NanonisError> {
251        let result = self.quick_send(
252            "Piezo.HVAInfoGet",
253            vec![],
254            vec![],
255            vec!["f", "f", "f", "f", "I", "I", "I"],
256        )?;
257
258        if result.len() >= 7 {
259            Ok(HVAInfo {
260                gain_aux: result[0].as_f32()?,
261                gain_x: result[1].as_f32()?,
262                gain_y: result[2].as_f32()?,
263                gain_z: result[3].as_f32()?,
264                xy_enabled: result[4].as_u32()? != 0,
265                z_enabled: result[5].as_u32()? != 0,
266                aux_enabled: result[6].as_u32()? != 0,
267            })
268        } else {
269            Err(NanonisError::Protocol("Invalid response".to_string()))
270        }
271    }
272
273    /// Get the HVA status LED indicators.
274    ///
275    /// # Returns
276    /// A [`HVAStatusLED`] struct with LED status indicators.
277    ///
278    /// # Errors
279    /// Returns `NanonisError` if communication fails.
280    pub fn piezo_hva_status_led_get(&mut self) -> Result<HVAStatusLED, NanonisError> {
281        let result = self.quick_send(
282            "Piezo.HVAStatusLEDGet",
283            vec![],
284            vec![],
285            vec!["I", "I", "I", "I"],
286        )?;
287
288        if result.len() >= 4 {
289            Ok(HVAStatusLED {
290                overheated: result[0].as_u32()? != 0,
291                hv_supply: result[1].as_u32()? != 0,
292                high_temperature: result[2].as_u32()? != 0,
293                output_connector: result[3].as_u32()? != 0,
294            })
295        } else {
296            Err(NanonisError::Protocol("Invalid response".to_string()))
297        }
298    }
299
300    /// Set the XYZ voltage limits.
301    ///
302    /// # Arguments
303    /// * `enable` - Enable/disable limits (use [`PiezoToggle`])
304    /// * `limits` - A [`XYZLimits`] struct with voltage limits
305    ///
306    /// # Errors
307    /// Returns `NanonisError` if communication fails.
308    pub fn piezo_xyz_limits_set(
309        &mut self,
310        enable: PiezoToggle,
311        limits: &XYZLimits,
312    ) -> Result<(), NanonisError> {
313        self.quick_send(
314            "Piezo.XYZLimitsSet",
315            vec![
316                NanonisValue::U16(enable.into()),
317                NanonisValue::F32(limits.x_low_v),
318                NanonisValue::F32(limits.x_high_v),
319                NanonisValue::F32(limits.y_low_v),
320                NanonisValue::F32(limits.y_high_v),
321                NanonisValue::F32(limits.z_low_v),
322                NanonisValue::F32(limits.z_high_v),
323            ],
324            vec!["H", "f", "f", "f", "f", "f", "f"],
325            vec![],
326        )?;
327        Ok(())
328    }
329
330    /// Get the XYZ voltage limits.
331    ///
332    /// # Returns
333    /// A [`XYZLimits`] struct with current voltage limits.
334    ///
335    /// # Errors
336    /// Returns `NanonisError` if communication fails.
337    pub fn piezo_xyz_limits_get(&mut self) -> Result<XYZLimits, NanonisError> {
338        let result = self.quick_send(
339            "Piezo.XYZLimitsGet",
340            vec![],
341            vec![],
342            vec!["H", "f", "f", "f", "f", "f", "f"],
343        )?;
344
345        if result.len() >= 7 {
346            Ok(XYZLimits {
347                enabled: result[0].as_u16()? != 0,
348                x_low_v: result[1].as_f32()?,
349                x_high_v: result[2].as_f32()?,
350                y_low_v: result[3].as_f32()?,
351                y_high_v: result[4].as_f32()?,
352                z_low_v: result[5].as_f32()?,
353                z_high_v: result[6].as_f32()?,
354            })
355        } else {
356            Err(NanonisError::Protocol("Invalid response".to_string()))
357        }
358    }
359
360    /// Enable or disable hysteresis compensation.
361    ///
362    /// # Arguments
363    /// * `enabled` - True to enable, false to disable
364    ///
365    /// # Errors
366    /// Returns `NanonisError` if communication fails.
367    pub fn piezo_hyst_on_off_set(&mut self, enabled: bool) -> Result<(), NanonisError> {
368        let flag = if enabled { 1u32 } else { 0u32 };
369        self.quick_send(
370            "Piezo.HystOnOffSet",
371            vec![NanonisValue::U32(flag)],
372            vec!["I"],
373            vec![],
374        )?;
375        Ok(())
376    }
377
378    /// Get hysteresis compensation enabled status.
379    ///
380    /// # Returns
381    /// True if hysteresis compensation is enabled.
382    ///
383    /// # Errors
384    /// Returns `NanonisError` if communication fails.
385    pub fn piezo_hyst_on_off_get(&mut self) -> Result<bool, NanonisError> {
386        let result = self.quick_send("Piezo.HystOnOffGet", vec![], vec![], vec!["I"])?;
387
388        if !result.is_empty() {
389            Ok(result[0].as_u32()? != 0)
390        } else {
391            Err(NanonisError::Protocol("Invalid response".to_string()))
392        }
393    }
394
395    /// Set and apply the hysteresis compensation values.
396    ///
397    /// # Arguments
398    /// * `values` - A [`HysteresisValues`] struct with hysteresis points
399    ///
400    /// # Errors
401    /// Returns `NanonisError` if communication fails.
402    pub fn piezo_hyst_vals_set(&mut self, values: &HysteresisValues) -> Result<(), NanonisError> {
403        self.quick_send(
404            "Piezo.HystValsSet",
405            vec![
406                NanonisValue::I32(values.fast_axis.x_points.len() as i32),
407                NanonisValue::ArrayF32(values.fast_axis.x_points.clone()),
408                NanonisValue::I32(values.fast_axis.y_points.len() as i32),
409                NanonisValue::ArrayF32(values.fast_axis.y_points.clone()),
410                NanonisValue::I32(values.slow_axis.x_points.len() as i32),
411                NanonisValue::ArrayF32(values.slow_axis.x_points.clone()),
412                NanonisValue::I32(values.slow_axis.y_points.len() as i32),
413                NanonisValue::ArrayF32(values.slow_axis.y_points.clone()),
414            ],
415            vec!["i", "*f", "i", "*f", "i", "*f", "i", "*f"],
416            vec![],
417        )?;
418        Ok(())
419    }
420
421    /// Get the hysteresis compensation values.
422    ///
423    /// # Returns
424    /// A [`HysteresisValues`] struct with current hysteresis points.
425    ///
426    /// # Errors
427    /// Returns `NanonisError` if communication fails.
428    pub fn piezo_hyst_vals_get(&mut self) -> Result<HysteresisValues, NanonisError> {
429        let result = self.quick_send(
430            "Piezo.HystValsGet",
431            vec![],
432            vec![],
433            vec!["i", "*f", "i", "*f", "i", "*f", "i", "*f"],
434        )?;
435
436        if result.len() >= 8 {
437            Ok(HysteresisValues {
438                fast_axis: HysteresisAxisPoints {
439                    x_points: result[1].as_f32_array()?.to_vec(),
440                    y_points: result[3].as_f32_array()?.to_vec(),
441                },
442                slow_axis: HysteresisAxisPoints {
443                    x_points: result[5].as_f32_array()?.to_vec(),
444                    y_points: result[7].as_f32_array()?.to_vec(),
445                },
446            })
447        } else {
448            Err(NanonisError::Protocol("Invalid response".to_string()))
449        }
450    }
451
452    /// Load hysteresis compensation values from a CSV file.
453    ///
454    /// # Arguments
455    /// * `file_path` - Path to the CSV file to load
456    ///
457    /// # Errors
458    /// Returns `NanonisError` if communication fails or file cannot be loaded.
459    pub fn piezo_hyst_file_load(&mut self, file_path: &str) -> Result<(), NanonisError> {
460        self.quick_send(
461            "Piezo.HystFileLoad",
462            vec![NanonisValue::String(file_path.to_string())],
463            vec!["+*c"],
464            vec![],
465        )?;
466        Ok(())
467    }
468
469    /// Save hysteresis compensation values to a CSV file.
470    ///
471    /// # Arguments
472    /// * `file_path` - Path to the CSV file to save
473    ///
474    /// # Errors
475    /// Returns `NanonisError` if communication fails or file cannot be saved.
476    pub fn piezo_hyst_file_save(&mut self, file_path: &str) -> Result<(), NanonisError> {
477        self.quick_send(
478            "Piezo.HystFileSave",
479            vec![NanonisValue::String(file_path.to_string())],
480            vec!["+*c"],
481            vec![],
482        )?;
483        Ok(())
484    }
485}