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}