nanonis_rs/client/z_spectr.rs
1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Return type for Z spectroscopy start operation (channel names, data, bias values)
6pub type ZSpectroscopyResult = (Vec<String>, Vec<Vec<f32>>, Vec<f32>);
7
8impl NanonisClient {
9 /// Open the Z Spectroscopy module.
10 ///
11 /// Opens and initializes the Z Spectroscopy module for distance-dependent
12 /// measurements. This must be called before performing spectroscopy operations.
13 ///
14 /// # Errors
15 /// Returns `NanonisError` if communication fails or module cannot be opened.
16 ///
17 /// # Examples
18 /// ```no_run
19 /// use nanonis_rs::NanonisClient;
20 ///
21 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
22 ///
23 /// // Open Z spectroscopy module
24 /// client.z_spectr_open()?;
25 /// println!("Z Spectroscopy module opened");
26 /// # Ok::<(), Box<dyn std::error::Error>>(())
27 /// ```
28 pub fn z_spectr_open(&mut self) -> Result<(), NanonisError> {
29 self.quick_send("ZSpectr.Open", vec![], vec![], vec![])?;
30 Ok(())
31 }
32
33 /// Start a Z spectroscopy measurement.
34 ///
35 /// Initiates a Z spectroscopy measurement with the configured parameters.
36 /// The tip is moved through a range of Z positions while recording selected channels.
37 ///
38 /// # Arguments
39 /// * `get_data` - If `true`, returns measurement data; if `false`, only starts measurement
40 /// * `save_base_name` - Base filename for saving data (empty for no change)
41 ///
42 /// # Returns
43 /// If `get_data` is true, returns a tuple containing:
44 /// - `Vec<String>` - Channel names
45 /// - `Vec<Vec<f32>>` - 2D measurement data \[rows\]\[columns\]
46 /// - `Vec<f32>` - Fixed parameters and settings
47 ///
48 /// # Errors
49 /// Returns `NanonisError` if communication fails or measurement cannot start.
50 ///
51 /// # Examples
52 /// ```no_run
53 /// use nanonis_rs::NanonisClient;
54 ///
55 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
56 ///
57 /// // Start measurement and get data
58 /// let (channels, data, params) = client.z_spectr_start(true, "approach_001")?;
59 /// println!("Recorded {} channels with {} points", channels.len(), data.len());
60 ///
61 /// // Just start measurement without getting data
62 /// let (_, _, _) = client.z_spectr_start(false, "")?;
63 /// # Ok::<(), Box<dyn std::error::Error>>(())
64 /// ```
65 pub fn z_spectr_start(
66 &mut self,
67 get_data: bool,
68 save_base_name: &str,
69 ) -> Result<ZSpectroscopyResult, NanonisError> {
70 let get_data_flag = if get_data { 1u32 } else { 0u32 };
71
72 let result = self.quick_send(
73 "ZSpectr.Start",
74 vec![
75 NanonisValue::U32(get_data_flag),
76 NanonisValue::String(save_base_name.to_string()),
77 ],
78 vec!["I", "+*c"],
79 vec!["i", "i", "*+c", "i", "i", "2f", "i", "*f"],
80 )?;
81
82 if result.len() >= 8 {
83 let channel_names = result[2].as_string_array()?.to_vec();
84 let rows = result[3].as_i32()? as usize;
85 let cols = result[4].as_i32()? as usize;
86
87 // Parse 2D data array
88 let flat_data = result[5].as_f32_array()?;
89 let mut data_2d = Vec::with_capacity(rows);
90 for row in 0..rows {
91 let start_idx = row * cols;
92 let end_idx = start_idx + cols;
93 if end_idx <= flat_data.len() {
94 data_2d.push(flat_data[start_idx..end_idx].to_vec());
95 } else {
96 return Err(NanonisError::Protocol(
97 "Truncated Z-spectroscopy data".to_string(),
98 ));
99 }
100 }
101
102 let parameters = result[7].as_f32_array()?.to_vec();
103 Ok((channel_names, data_2d, parameters))
104 } else {
105 Err(NanonisError::Protocol(
106 "Invalid Z spectroscopy start response".to_string(),
107 ))
108 }
109 }
110
111 /// Stop the current Z spectroscopy measurement.
112 ///
113 /// Immediately stops any running Z spectroscopy measurement and returns
114 /// the tip to its original position.
115 ///
116 /// # Errors
117 /// Returns `NanonisError` if communication fails.
118 ///
119 /// # Examples
120 /// ```no_run
121 /// use nanonis_rs::NanonisClient;
122 ///
123 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
124 ///
125 /// // Start a measurement
126 /// let (_, _, _) = client.z_spectr_start(false, "")?;
127 ///
128 /// // Stop it after some condition
129 /// client.z_spectr_stop()?;
130 /// println!("Z spectroscopy stopped");
131 /// # Ok::<(), Box<dyn std::error::Error>>(())
132 /// ```
133 pub fn z_spectr_stop(&mut self) -> Result<(), NanonisError> {
134 self.quick_send("ZSpectr.Stop", vec![], vec![], vec![])?;
135 Ok(())
136 }
137
138 /// Get the status of Z spectroscopy measurement.
139 ///
140 /// Returns whether a Z spectroscopy measurement is currently running.
141 ///
142 /// # Returns
143 /// `true` if measurement is running, `false` if stopped.
144 ///
145 /// # Errors
146 /// Returns `NanonisError` if communication fails.
147 ///
148 /// # Examples
149 /// ```no_run
150 /// use nanonis_rs::NanonisClient;
151 ///
152 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
153 ///
154 /// if client.z_spectr_status_get()? {
155 /// println!("Z spectroscopy is running");
156 /// } else {
157 /// println!("Z spectroscopy is stopped");
158 /// }
159 /// # Ok::<(), Box<dyn std::error::Error>>(())
160 /// ```
161 pub fn z_spectr_status_get(&mut self) -> Result<bool, NanonisError> {
162 let result = self.quick_send("ZSpectr.StatusGet", vec![], vec![], vec!["I"])?;
163
164 match result.first() {
165 Some(value) => Ok(value.as_u32()? == 1),
166 None => Err(NanonisError::Protocol(
167 "No Z spectroscopy status returned".to_string(),
168 )),
169 }
170 }
171
172 /// Set the channels to record during Z spectroscopy.
173 ///
174 /// Configures which signals will be recorded during the Z spectroscopy measurement.
175 /// Channel indexes correspond to the 24 signals assigned in the Signals Manager (0-23).
176 ///
177 /// # Arguments
178 /// * `channel_indexes` - Vector of channel indexes to record (0-23)
179 ///
180 /// # Errors
181 /// Returns `NanonisError` if communication fails or invalid channel indexes provided.
182 ///
183 /// # Examples
184 /// ```no_run
185 /// use nanonis_rs::NanonisClient;
186 ///
187 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
188 ///
189 /// // Record current (0), Z position (1), and bias voltage (2)
190 /// client.z_spectr_chs_set(vec![0, 1, 2])?;
191 ///
192 /// // Record more comprehensive dataset
193 /// client.z_spectr_chs_set(vec![0, 1, 2, 3, 4, 5])?;
194 /// # Ok::<(), Box<dyn std::error::Error>>(())
195 /// ```
196 pub fn z_spectr_chs_set(&mut self, channel_indexes: Vec<i32>) -> Result<(), NanonisError> {
197 self.quick_send(
198 "ZSpectr.ChsSet",
199 vec![NanonisValue::ArrayI32(channel_indexes)],
200 vec!["+*i"],
201 vec![],
202 )?;
203 Ok(())
204 }
205
206 /// Get the channels configured for Z spectroscopy recording.
207 ///
208 /// Returns the channel indexes and names that will be recorded during measurements.
209 ///
210 /// # Returns
211 /// A tuple containing:
212 /// - `Vec<i32>` - Channel indexes (0-23 for Signals Manager slots)
213 /// - `Vec<String>` - Channel names corresponding to the indexes
214 ///
215 /// # Errors
216 /// Returns `NanonisError` if communication fails.
217 ///
218 /// # Examples
219 /// ```no_run
220 /// use nanonis_rs::NanonisClient;
221 ///
222 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
223 ///
224 /// let (indexes, names) = client.z_spectr_chs_get()?;
225 /// println!("Recording {} channels:", indexes.len());
226 /// for (idx, name) in indexes.iter().zip(names.iter()) {
227 /// println!(" Channel {}: {}", idx, name);
228 /// }
229 /// # Ok::<(), Box<dyn std::error::Error>>(())
230 /// ```
231 pub fn z_spectr_chs_get(&mut self) -> Result<(Vec<i32>, Vec<String>), NanonisError> {
232 let result = self.quick_send(
233 "ZSpectr.ChsGet",
234 vec![],
235 vec![],
236 vec!["i", "*i", "i", "i", "*+c"],
237 )?;
238
239 if result.len() >= 5 {
240 let channel_indexes = result[1].as_i32_array()?.to_vec();
241 let channel_names = result[4].as_string_array()?.to_vec();
242 Ok((channel_indexes, channel_names))
243 } else {
244 Err(NanonisError::Protocol(
245 "Invalid Z spectroscopy channels response".to_string(),
246 ))
247 }
248 }
249
250 /// Set the Z range for spectroscopy measurements.
251 ///
252 /// Configures the Z offset and sweep distance for the spectroscopy measurement.
253 /// The tip will move from (offset - distance/2) to (offset + distance/2).
254 ///
255 /// # Arguments
256 /// * `z_offset_m` - Z offset position in meters (center of sweep)
257 /// * `z_sweep_distance_m` - Total sweep distance in meters
258 ///
259 /// # Errors
260 /// Returns `NanonisError` if communication fails or invalid range parameters.
261 ///
262 /// # Examples
263 /// ```no_run
264 /// use nanonis_rs::NanonisClient;
265 ///
266 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
267 ///
268 /// // Sweep ±5 nm around current position
269 /// client.z_spectr_range_set(0.0, 10e-9)?;
270 ///
271 /// // Sweep from current position up to +20 nm
272 /// client.z_spectr_range_set(10e-9, 20e-9)?;
273 /// # Ok::<(), Box<dyn std::error::Error>>(())
274 /// ```
275 pub fn z_spectr_range_set(
276 &mut self,
277 z_offset_m: f32,
278 z_sweep_distance_m: f32,
279 ) -> Result<(), NanonisError> {
280 self.quick_send(
281 "ZSpectr.RangeSet",
282 vec![
283 NanonisValue::F32(z_offset_m),
284 NanonisValue::F32(z_sweep_distance_m),
285 ],
286 vec!["f", "f"],
287 vec![],
288 )?;
289 Ok(())
290 }
291
292 /// Get the current Z range configuration for spectroscopy.
293 ///
294 /// Returns the configured Z offset and sweep distance.
295 ///
296 /// # Returns
297 /// A tuple containing:
298 /// - `f32` - Z offset in meters (center position)
299 /// - `f32` - Z sweep distance in meters (total range)
300 ///
301 /// # Errors
302 /// Returns `NanonisError` if communication fails.
303 ///
304 /// # Examples
305 /// ```no_run
306 /// use nanonis_rs::NanonisClient;
307 ///
308 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
309 ///
310 /// let (offset, distance) = client.z_spectr_range_get()?;
311 /// println!("Z sweep: {:.1} nm ± {:.1} nm", offset * 1e9, distance * 1e9 / 2.0);
312 /// # Ok::<(), Box<dyn std::error::Error>>(())
313 /// ```
314 pub fn z_spectr_range_get(&mut self) -> Result<(f32, f32), NanonisError> {
315 let result = self.quick_send("ZSpectr.RangeGet", vec![], vec![], vec!["f", "f"])?;
316
317 if result.len() >= 2 {
318 Ok((result[0].as_f32()?, result[1].as_f32()?))
319 } else {
320 Err(NanonisError::Protocol(
321 "Invalid Z spectroscopy range response".to_string(),
322 ))
323 }
324 }
325
326 /// Set the timing parameters for Z spectroscopy.
327 ///
328 /// Configures timing-related parameters that control the speed and quality
329 /// of the Z spectroscopy measurement.
330 ///
331 /// # Arguments
332 /// * `z_averaging_time_s` - Time to average signals at each Z position
333 /// * `initial_settling_time_s` - Initial settling time before measurement
334 /// * `maximum_slew_rate_vdivs` - Maximum slew rate in V/s
335 /// * `settling_time_s` - Settling time between measurement points
336 /// * `integration_time_s` - Integration time for each measurement point
337 /// * `end_settling_time_s` - Settling time at the end of sweep
338 ///
339 /// # Errors
340 /// Returns `NanonisError` if communication fails or invalid timing parameters.
341 ///
342 /// # Examples
343 /// ```no_run
344 /// use nanonis_rs::NanonisClient;
345 ///
346 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
347 ///
348 /// // Fast spectroscopy settings
349 /// client.z_spectr_timing_set(0.01, 0.1, 1000.0, 0.01, 0.01, 0.1)?;
350 ///
351 /// // High-quality slow spectroscopy
352 /// client.z_spectr_timing_set(0.1, 0.5, 100.0, 0.05, 0.05, 0.2)?;
353 /// # Ok::<(), Box<dyn std::error::Error>>(())
354 /// ```
355 pub fn z_spectr_timing_set(
356 &mut self,
357 z_averaging_time_s: f32,
358 initial_settling_time_s: f32,
359 maximum_slew_rate_vdivs: f32,
360 settling_time_s: f32,
361 integration_time_s: f32,
362 end_settling_time_s: f32,
363 ) -> Result<(), NanonisError> {
364 self.quick_send(
365 "ZSpectr.TimingSet",
366 vec![
367 NanonisValue::F32(z_averaging_time_s),
368 NanonisValue::F32(initial_settling_time_s),
369 NanonisValue::F32(maximum_slew_rate_vdivs),
370 NanonisValue::F32(settling_time_s),
371 NanonisValue::F32(integration_time_s),
372 NanonisValue::F32(end_settling_time_s),
373 ],
374 vec!["f", "f", "f", "f", "f", "f"],
375 vec![],
376 )?;
377 Ok(())
378 }
379
380 /// Get the current timing parameters for Z spectroscopy.
381 ///
382 /// Returns all timing-related configuration parameters.
383 ///
384 /// # Returns
385 /// A tuple containing:
386 /// - `f32` - Z averaging time (s)
387 /// - `f32` - Initial settling time (s)
388 /// - `f32` - Maximum slew rate (V/s)
389 /// - `f32` - Settling time (s)
390 /// - `f32` - Integration time (s)
391 /// - `f32` - End settling time (s)
392 ///
393 /// # Errors
394 /// Returns `NanonisError` if communication fails.
395 ///
396 /// # Examples
397 /// ```no_run
398 /// use nanonis_rs::NanonisClient;
399 ///
400 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
401 ///
402 /// let (z_avg, init_settle, slew_rate, settle, integrate, end_settle) =
403 /// client.z_spectr_timing_get()?;
404 /// println!("Integration time: {:.3} s, settling: {:.3} s", integrate, settle);
405 /// # Ok::<(), Box<dyn std::error::Error>>(())
406 /// ```
407 pub fn z_spectr_timing_get(&mut self) -> Result<(f32, f32, f32, f32, f32, f32), NanonisError> {
408 let result = self.quick_send(
409 "ZSpectr.TimingGet",
410 vec![],
411 vec![],
412 vec!["f", "f", "f", "f", "f", "f"],
413 )?;
414
415 if result.len() >= 6 {
416 Ok((
417 result[0].as_f32()?,
418 result[1].as_f32()?,
419 result[2].as_f32()?,
420 result[3].as_f32()?,
421 result[4].as_f32()?,
422 result[5].as_f32()?,
423 ))
424 } else {
425 Err(NanonisError::Protocol(
426 "Invalid Z spectroscopy timing response".to_string(),
427 ))
428 }
429 }
430
431 /// Set the retraction parameters for tip protection during Z spectroscopy.
432 ///
433 /// Configures automatic tip retraction based on signal thresholds to prevent
434 /// tip crashes during approach spectroscopy.
435 ///
436 /// # Arguments
437 /// * `enable` - Enable/disable automatic retraction
438 /// * `threshold` - Signal threshold value for retraction trigger
439 /// * `signal_index` - Index of signal to monitor (0-23)
440 /// * `comparison` - Comparison type: 0=greater than, 1=less than
441 ///
442 /// # Errors
443 /// Returns `NanonisError` if communication fails or invalid parameters.
444 ///
445 /// # Examples
446 /// ```no_run
447 /// use nanonis_rs::NanonisClient;
448 ///
449 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
450 ///
451 /// // Enable retraction when current exceeds 1 nA (signal 0, greater than)
452 /// client.z_spectr_retract_set(true, 1e-9, 0, 0)?;
453 ///
454 /// // Disable retraction
455 /// client.z_spectr_retract_set(false, 0.0, 0, 0)?;
456 /// # Ok::<(), Box<dyn std::error::Error>>(())
457 /// ```
458 pub fn z_spectr_retract_set(
459 &mut self,
460 enable: bool,
461 threshold: f32,
462 signal_index: i32,
463 comparison: u16,
464 ) -> Result<(), NanonisError> {
465 let enable_flag = if enable { 1u16 } else { 0u16 };
466
467 self.quick_send(
468 "ZSpectr.RetractSet",
469 vec![
470 NanonisValue::U16(enable_flag),
471 NanonisValue::F32(threshold),
472 NanonisValue::I32(signal_index),
473 NanonisValue::U16(comparison),
474 ],
475 vec!["H", "f", "i", "H"],
476 vec![],
477 )?;
478 Ok(())
479 }
480
481 /// Get the current retraction configuration for Z spectroscopy.
482 ///
483 /// Returns the tip protection settings that prevent crashes during measurements.
484 ///
485 /// # Returns
486 /// A tuple containing:
487 /// - `bool` - Retraction enabled/disabled
488 /// - `f32` - Threshold value for retraction
489 /// - `i32` - Signal index being monitored
490 /// - `u16` - Comparison type (0=greater, 1=less than)
491 ///
492 /// # Errors
493 /// Returns `NanonisError` if communication fails.
494 ///
495 /// # Examples
496 /// ```no_run
497 /// use nanonis_rs::NanonisClient;
498 ///
499 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
500 ///
501 /// let (enabled, threshold, signal_idx, comparison) = client.z_spectr_retract_get()?;
502 /// if enabled {
503 /// let comp_str = if comparison == 0 { ">" } else { "<" };
504 /// println!("Retraction: signal[{}] {} {:.3e}", signal_idx, comp_str, threshold);
505 /// }
506 /// # Ok::<(), Box<dyn std::error::Error>>(())
507 /// ```
508 pub fn z_spectr_retract_get(&mut self) -> Result<(bool, f32, i32, u16), NanonisError> {
509 let result = self.quick_send(
510 "ZSpectr.RetractGet",
511 vec![],
512 vec![],
513 vec!["H", "f", "i", "H"],
514 )?;
515
516 if result.len() >= 4 {
517 let enabled = result[0].as_u16()? == 1;
518 let threshold = result[1].as_f32()?;
519 let signal_index = result[2].as_i32()?;
520 let comparison = result[3].as_u16()?;
521 Ok((enabled, threshold, signal_index, comparison))
522 } else {
523 Err(NanonisError::Protocol(
524 "Invalid Z spectroscopy retract response".to_string(),
525 ))
526 }
527 }
528
529 /// Set the Z spectroscopy properties.
530 ///
531 /// # Arguments
532 /// * `backward_sweep` - 0=no change, 1=enable backward sweep, 2=disable
533 /// * `num_points` - Number of points (0=no change)
534 /// * `num_sweeps` - Number of sweeps to average (0=no change)
535 /// * `autosave` - 0=no change, 1=enable autosave, 2=disable
536 /// * `show_save_dialog` - 0=no change, 1=show dialog, 2=don't show
537 /// * `save_all` - 0=no change, 1=save individual sweeps, 2=don't save
538 ///
539 /// # Errors
540 /// Returns `NanonisError` if communication fails.
541 pub fn z_spectr_props_set(
542 &mut self,
543 backward_sweep: u16,
544 num_points: i32,
545 num_sweeps: u16,
546 autosave: u16,
547 show_save_dialog: u16,
548 save_all: u16,
549 ) -> Result<(), NanonisError> {
550 self.quick_send(
551 "ZSpectr.PropsSet",
552 vec![
553 NanonisValue::U16(backward_sweep),
554 NanonisValue::I32(num_points),
555 NanonisValue::U16(num_sweeps),
556 NanonisValue::U16(autosave),
557 NanonisValue::U16(show_save_dialog),
558 NanonisValue::U16(save_all),
559 ],
560 vec!["H", "i", "H", "H", "H", "H"],
561 vec![],
562 )?;
563 Ok(())
564 }
565
566 /// Get the Z spectroscopy properties.
567 ///
568 /// Returns the current property configuration.
569 ///
570 /// # Returns
571 /// A tuple containing:
572 /// - `bool` - Backward sweep enabled
573 /// - `i32` - Number of points
574 /// - `u16` - Number of sweeps to average
575 /// - `bool` - Autosave enabled
576 /// - `bool` - Show save dialog
577 /// - `bool` - Save all individual sweeps
578 ///
579 /// # Errors
580 /// Returns `NanonisError` if communication fails.
581 ///
582 /// # Examples
583 /// ```no_run
584 /// use nanonis_rs::NanonisClient;
585 ///
586 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
587 ///
588 /// let (backward, points, sweeps, autosave, dialog, save_all) =
589 /// client.z_spectr_props_get()?;
590 /// println!("Points: {}, Sweeps: {}, Backward: {}", points, sweeps, backward);
591 /// # Ok::<(), Box<dyn std::error::Error>>(())
592 /// ```
593 pub fn z_spectr_props_get(
594 &mut self,
595 ) -> Result<(bool, i32, u16, bool, bool, bool), NanonisError> {
596 let result = self.quick_send(
597 "ZSpectr.PropsGet",
598 vec![],
599 vec![],
600 vec!["H", "i", "H", "I", "I", "I"],
601 )?;
602
603 if result.len() >= 6 {
604 Ok((
605 result[0].as_u16()? != 0,
606 result[1].as_i32()?,
607 result[2].as_u16()?,
608 result[3].as_u32()? != 0,
609 result[4].as_u32()? != 0,
610 result[5].as_u32()? != 0,
611 ))
612 } else {
613 Err(NanonisError::Protocol(
614 "Invalid Z spectroscopy props response".to_string(),
615 ))
616 }
617 }
618
619 /// Set the advanced Z spectroscopy properties.
620 ///
621 /// # Arguments
622 /// * `time_between_sweeps_s` - Time between forward and backward sweep
623 /// * `record_final_z` - 0=no change, 1=on, 2=off
624 /// * `lockin_run` - 0=no change, 1=on, 2=off
625 /// * `reset_z` - 0=no change, 1=on, 2=off
626 ///
627 /// # Errors
628 /// Returns `NanonisError` if communication fails.
629 pub fn z_spectr_adv_props_set(
630 &mut self,
631 time_between_sweeps_s: f32,
632 record_final_z: u16,
633 lockin_run: u16,
634 reset_z: u16,
635 ) -> Result<(), NanonisError> {
636 self.quick_send(
637 "ZSpectr.AdvPropsSet",
638 vec![
639 NanonisValue::F32(time_between_sweeps_s),
640 NanonisValue::U16(record_final_z),
641 NanonisValue::U16(lockin_run),
642 NanonisValue::U16(reset_z),
643 ],
644 vec!["f", "H", "H", "H"],
645 vec![],
646 )?;
647 Ok(())
648 }
649
650 /// Get the advanced Z spectroscopy properties.
651 ///
652 /// # Returns
653 /// Tuple of (time_between_sweeps, record_final_z, lockin_run, reset_z).
654 ///
655 /// # Errors
656 /// Returns `NanonisError` if communication fails.
657 pub fn z_spectr_adv_props_get(&mut self) -> Result<(f32, bool, bool, bool), NanonisError> {
658 let result = self.quick_send(
659 "ZSpectr.AdvPropsGet",
660 vec![],
661 vec![],
662 vec!["f", "H", "H", "H"],
663 )?;
664
665 Ok((
666 result[0].as_f32()?,
667 result[1].as_u16()? != 0,
668 result[2].as_u16()? != 0,
669 result[3].as_u16()? != 0,
670 ))
671 }
672
673 /// Set the retract delay.
674 ///
675 /// # Arguments
676 /// * `delay_s` - Delay in seconds between forward and backward sweep
677 ///
678 /// # Errors
679 /// Returns `NanonisError` if communication fails.
680 pub fn z_spectr_retract_delay_set(&mut self, delay_s: f32) -> Result<(), NanonisError> {
681 self.quick_send(
682 "ZSpectr.RetractDelaySet",
683 vec![NanonisValue::F32(delay_s)],
684 vec!["f"],
685 vec![],
686 )?;
687 Ok(())
688 }
689
690 /// Get the retract delay.
691 ///
692 /// # Returns
693 /// Delay in seconds.
694 ///
695 /// # Errors
696 /// Returns `NanonisError` if communication fails.
697 pub fn z_spectr_retract_delay_get(&mut self) -> Result<f32, NanonisError> {
698 let result = self.quick_send("ZSpectr.RetractDelayGet", vec![], vec![], vec!["f"])?;
699 if result.is_empty() {
700 return Err(NanonisError::Protocol(
701 "Empty response from ZSpectr.RetractDelayGet".to_string(),
702 ));
703 }
704 result[0].as_f32()
705 }
706
707 /// Set the second retraction condition.
708 ///
709 /// # Arguments
710 /// * `condition` - 0=no change, 1=disabled, 2=OR, 3=AND, 4=THEN
711 /// * `threshold` - Threshold value
712 /// * `signal_index` - Signal index (0-127, -1 for no change)
713 /// * `comparison` - 0=greater than, 1=less than, 2=no change
714 ///
715 /// # Errors
716 /// Returns `NanonisError` if communication fails.
717 pub fn z_spectr_retract_second_set(
718 &mut self,
719 condition: i32,
720 threshold: f32,
721 signal_index: i32,
722 comparison: u16,
723 ) -> Result<(), NanonisError> {
724 self.quick_send(
725 "ZSpectr.RetractSecondSet",
726 vec![
727 NanonisValue::I32(condition),
728 NanonisValue::F32(threshold),
729 NanonisValue::I32(signal_index),
730 NanonisValue::U16(comparison),
731 ],
732 vec!["i", "f", "i", "H"],
733 vec![],
734 )?;
735 Ok(())
736 }
737
738 /// Get the second retraction condition.
739 ///
740 /// # Returns
741 /// Tuple of (condition, threshold, signal_index, comparison).
742 ///
743 /// # Errors
744 /// Returns `NanonisError` if communication fails.
745 pub fn z_spectr_retract_second_get(&mut self) -> Result<(i32, f32, i32, u16), NanonisError> {
746 let result = self.quick_send(
747 "ZSpectr.RetractSecondGet",
748 vec![],
749 vec![],
750 vec!["i", "f", "i", "H"],
751 )?;
752 if result.len() < 4 {
753 return Err(NanonisError::Protocol(
754 "Truncated response from ZSpectr.RetractSecondGet".to_string(),
755 ));
756 }
757 Ok((
758 result[0].as_i32()?,
759 result[1].as_f32()?,
760 result[2].as_i32()?,
761 result[3].as_u16()?,
762 ))
763 }
764
765 /// Set the digital synchronization mode.
766 ///
767 /// # Arguments
768 /// * `dig_sync` - 0=no change, 1=off, 2=TTL sync, 3=pulse sequence
769 ///
770 /// # Errors
771 /// Returns `NanonisError` if communication fails.
772 pub fn z_spectr_dig_sync_set(&mut self, dig_sync: u16) -> Result<(), NanonisError> {
773 self.quick_send(
774 "ZSpectr.DigSyncSet",
775 vec![NanonisValue::U16(dig_sync)],
776 vec!["H"],
777 vec![],
778 )?;
779 Ok(())
780 }
781
782 /// Get the digital synchronization mode.
783 ///
784 /// # Returns
785 /// Sync mode (0=off, 1=TTL sync, 2=pulse sequence).
786 ///
787 /// # Errors
788 /// Returns `NanonisError` if communication fails.
789 pub fn z_spectr_dig_sync_get(&mut self) -> Result<u16, NanonisError> {
790 let result = self.quick_send("ZSpectr.DigSyncGet", vec![], vec![], vec!["H"])?;
791 if result.is_empty() {
792 return Err(NanonisError::Protocol(
793 "Empty response from ZSpectr.DigSyncGet".to_string(),
794 ));
795 }
796 result[0].as_u16()
797 }
798
799 /// Set the TTL synchronization parameters.
800 ///
801 /// # Arguments
802 /// * `ttl_line` - 0=no change, 1-4=HS line number
803 /// * `polarity` - 0=no change, 1=low active, 2=high active
804 /// * `time_to_on_s` - Time to wait before activation
805 /// * `on_duration_s` - Duration of activation
806 ///
807 /// # Errors
808 /// Returns `NanonisError` if communication fails.
809 pub fn z_spectr_ttl_sync_set(
810 &mut self,
811 ttl_line: u16,
812 polarity: u16,
813 time_to_on_s: f32,
814 on_duration_s: f32,
815 ) -> Result<(), NanonisError> {
816 self.quick_send(
817 "ZSpectr.TTLSyncSet",
818 vec![
819 NanonisValue::U16(ttl_line),
820 NanonisValue::U16(polarity),
821 NanonisValue::F32(time_to_on_s),
822 NanonisValue::F32(on_duration_s),
823 ],
824 vec!["H", "H", "f", "f"],
825 vec![],
826 )?;
827 Ok(())
828 }
829
830 /// Get the TTL synchronization parameters.
831 ///
832 /// # Returns
833 /// Tuple of (ttl_line, polarity, time_to_on_s, on_duration_s).
834 ///
835 /// # Errors
836 /// Returns `NanonisError` if communication fails.
837 pub fn z_spectr_ttl_sync_get(&mut self) -> Result<(u16, u16, f32, f32), NanonisError> {
838 let result = self.quick_send(
839 "ZSpectr.TTLSyncGet",
840 vec![],
841 vec![],
842 vec!["H", "H", "f", "f"],
843 )?;
844 if result.len() < 4 {
845 return Err(NanonisError::Protocol(
846 "Truncated response from ZSpectr.TTLSyncGet".to_string(),
847 ));
848 }
849 Ok((
850 result[0].as_u16()?,
851 result[1].as_u16()?,
852 result[2].as_f32()?,
853 result[3].as_f32()?,
854 ))
855 }
856
857 /// Set the pulse sequence synchronization.
858 ///
859 /// # Arguments
860 /// * `pulse_seq_nr` - Pulse sequence number (0=no change)
861 /// * `num_periods` - Number of periods
862 ///
863 /// # Errors
864 /// Returns `NanonisError` if communication fails.
865 pub fn z_spectr_pulse_seq_sync_set(
866 &mut self,
867 pulse_seq_nr: u16,
868 num_periods: u32,
869 ) -> Result<(), NanonisError> {
870 self.quick_send(
871 "ZSpectr.PulseSeqSyncSet",
872 vec![
873 NanonisValue::U16(pulse_seq_nr),
874 NanonisValue::U32(num_periods),
875 ],
876 vec!["H", "I"],
877 vec![],
878 )?;
879 Ok(())
880 }
881
882 /// Get the pulse sequence synchronization.
883 ///
884 /// # Returns
885 /// Tuple of (pulse_seq_nr, num_periods).
886 ///
887 /// # Errors
888 /// Returns `NanonisError` if communication fails.
889 pub fn z_spectr_pulse_seq_sync_get(&mut self) -> Result<(u16, u32), NanonisError> {
890 let result = self.quick_send("ZSpectr.PulseSeqSyncGet", vec![], vec![], vec!["H", "I"])?;
891 if result.len() < 2 {
892 return Err(NanonisError::Protocol(
893 "Truncated response from ZSpectr.PulseSeqSyncGet".to_string(),
894 ));
895 }
896 Ok((result[0].as_u16()?, result[1].as_u32()?))
897 }
898}