nanonis_rs/client/bias_spectr/mod.rs
1mod types;
2pub use types::*;
3
4use super::NanonisClient;
5use crate::error::NanonisError;
6use crate::types::NanonisValue;
7use std::time::Duration;
8
9impl NanonisClient {
10 /// Open the Bias Spectroscopy module.
11 ///
12 /// Opens and initializes the Bias Spectroscopy module for STS measurements.
13 /// This must be called before performing spectroscopy operations.
14 ///
15 /// # Errors
16 /// Returns `NanonisError` if communication fails or module cannot be opened.
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.bias_spectr_open()?;
24 /// # Ok::<(), Box<dyn std::error::Error>>(())
25 /// ```
26 pub fn bias_spectr_open(&mut self) -> Result<(), NanonisError> {
27 self.quick_send("BiasSpectr.Open", vec![], vec![], vec![])?;
28 Ok(())
29 }
30
31 /// Start a bias spectroscopy measurement.
32 ///
33 /// Starts a bias spectroscopy (STS) measurement with configured parameters.
34 /// Select channels to record before calling this function.
35 ///
36 /// # Arguments
37 /// * `get_data` - If true, returns measurement data
38 /// * `save_base_name` - Base filename for saving (empty for no change)
39 ///
40 /// # Returns
41 /// A [`BiasSpectrResult`] containing channel names, 2D data, and parameters.
42 ///
43 /// # Errors
44 /// Returns `NanonisError` if communication fails or measurement cannot start.
45 ///
46 /// # Examples
47 /// ```no_run
48 /// use nanonis_rs::NanonisClient;
49 ///
50 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
51 ///
52 /// // Start spectroscopy and get data
53 /// let result = client.bias_spectr_start(true, "sts_001")?;
54 /// println!("Recorded {} channels", result.channel_names.len());
55 /// println!("Data shape: {} x {}", result.data.len(),
56 /// result.data.first().map(|r| r.len()).unwrap_or(0));
57 /// # Ok::<(), Box<dyn std::error::Error>>(())
58 /// ```
59 pub fn bias_spectr_start(
60 &mut self,
61 get_data: bool,
62 save_base_name: &str,
63 ) -> Result<BiasSpectrResult, NanonisError> {
64 let get_data_flag = if get_data { 1u32 } else { 0u32 };
65
66 let result = self.quick_send(
67 "BiasSpectr.Start",
68 vec![
69 NanonisValue::U32(get_data_flag),
70 NanonisValue::String(save_base_name.to_string()),
71 ],
72 vec!["I", "+*c"],
73 vec!["i", "i", "*+c", "i", "i", "2f", "i", "*f"],
74 )?;
75
76 if result.len() >= 8 {
77 let channel_names = result[2].as_string_array()?.to_vec();
78 let rows = result[3].as_i32()? as usize;
79 let cols = result[4].as_i32()? as usize;
80
81 // Parse 2D data array
82 let flat_data = result[5].as_f32_array()?;
83 let mut data_2d = Vec::with_capacity(rows);
84 for row in 0..rows {
85 let start_idx = row * cols;
86 let end_idx = start_idx + cols;
87 if end_idx <= flat_data.len() {
88 data_2d.push(flat_data[start_idx..end_idx].to_vec());
89 }
90 }
91
92 let parameters = result[7].as_f32_array()?.to_vec();
93
94 Ok(BiasSpectrResult {
95 channel_names,
96 data: data_2d,
97 parameters,
98 })
99 } else {
100 Ok(BiasSpectrResult {
101 channel_names: vec![],
102 data: vec![],
103 parameters: vec![],
104 })
105 }
106 }
107
108 /// Stop the current bias spectroscopy measurement.
109 ///
110 /// # Errors
111 /// Returns `NanonisError` if communication fails.
112 ///
113 /// # Examples
114 /// ```no_run
115 /// use nanonis_rs::NanonisClient;
116 ///
117 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
118 /// client.bias_spectr_stop()?;
119 /// # Ok::<(), Box<dyn std::error::Error>>(())
120 /// ```
121 pub fn bias_spectr_stop(&mut self) -> Result<(), NanonisError> {
122 self.quick_send("BiasSpectr.Stop", vec![], vec![], vec![])?;
123 Ok(())
124 }
125
126 /// Get the status of the bias spectroscopy measurement.
127 ///
128 /// # Returns
129 /// `true` if measurement is running, `false` otherwise.
130 ///
131 /// # Errors
132 /// Returns `NanonisError` if communication fails.
133 ///
134 /// # Examples
135 /// ```no_run
136 /// use nanonis_rs::NanonisClient;
137 ///
138 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
139 /// if client.bias_spectr_status_get()? {
140 /// println!("Spectroscopy is running");
141 /// }
142 /// # Ok::<(), Box<dyn std::error::Error>>(())
143 /// ```
144 pub fn bias_spectr_status_get(&mut self) -> Result<bool, NanonisError> {
145 let result = self.quick_send("BiasSpectr.StatusGet", vec![], vec![], vec!["I"])?;
146 if let Some(val) = result.first() {
147 Ok(val.as_u32()? != 0)
148 } else {
149 Err(NanonisError::Protocol(
150 "Invalid status response".to_string(),
151 ))
152 }
153 }
154
155 /// Set the list of recorded channels in bias spectroscopy.
156 ///
157 /// # Arguments
158 /// * `channel_indexes` - Indexes of channels to record (0-23 for signals in Signals Manager)
159 ///
160 /// # Errors
161 /// Returns `NanonisError` if communication fails or invalid indexes provided.
162 ///
163 /// # Examples
164 /// ```no_run
165 /// use nanonis_rs::NanonisClient;
166 ///
167 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
168 /// // Record channels 0, 1, and 5
169 /// client.bias_spectr_chs_set(&[0, 1, 5])?;
170 /// # Ok::<(), Box<dyn std::error::Error>>(())
171 /// ```
172 pub fn bias_spectr_chs_set(&mut self, channel_indexes: &[i32]) -> Result<(), NanonisError> {
173 self.quick_send(
174 "BiasSpectr.ChsSet",
175 vec![NanonisValue::ArrayI32(channel_indexes.to_vec())],
176 vec!["+*i"],
177 vec![],
178 )?;
179 Ok(())
180 }
181
182 /// Get the list of recorded channels in bias spectroscopy.
183 ///
184 /// # Returns
185 /// A tuple containing:
186 /// - `Vec<i32>` - Indexes of recorded channels
187 /// - `Vec<String>` - Names of recorded channels
188 ///
189 /// # Errors
190 /// Returns `NanonisError` if communication fails.
191 ///
192 /// # Examples
193 /// ```no_run
194 /// use nanonis_rs::NanonisClient;
195 ///
196 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
197 /// let (indexes, names) = client.bias_spectr_chs_get()?;
198 /// for (idx, name) in indexes.iter().zip(names.iter()) {
199 /// println!("Channel {}: {}", idx, name);
200 /// }
201 /// # Ok::<(), Box<dyn std::error::Error>>(())
202 /// ```
203 pub fn bias_spectr_chs_get(&mut self) -> Result<(Vec<i32>, Vec<String>), NanonisError> {
204 let result = self.quick_send(
205 "BiasSpectr.ChsGet",
206 vec![],
207 vec![],
208 vec!["i", "*i", "i", "i", "*+c"],
209 )?;
210
211 if result.len() >= 5 {
212 let indexes = result[1].as_i32_array()?.to_vec();
213 let names = result[4].as_string_array()?.to_vec();
214 Ok((indexes, names))
215 } else {
216 Err(NanonisError::Protocol(
217 "Invalid channels response".to_string(),
218 ))
219 }
220 }
221
222 /// Set the bias spectroscopy properties.
223 ///
224 /// Uses a builder pattern to configure only the properties you want to change.
225 /// Properties not set in the builder will remain unchanged on the instrument.
226 ///
227 /// # Arguments
228 /// * `config` - Configuration builder with properties to set
229 ///
230 /// # Errors
231 /// Returns `NanonisError` if communication fails.
232 ///
233 /// # Examples
234 /// ```no_run
235 /// use nanonis_rs::NanonisClient;
236 /// use nanonis_rs::bias_spectr::{BiasSpectrPropsBuilder, OptionalFlag};
237 ///
238 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
239 ///
240 /// // Configure using builder pattern - only set what you need
241 /// let config = BiasSpectrPropsBuilder::new()
242 /// .num_sweeps(10)
243 /// .num_points(200)
244 /// .backward_sweep(OptionalFlag::On)
245 /// .autosave(OptionalFlag::On);
246 ///
247 /// client.bias_spectr_props_set(config)?;
248 /// # Ok::<(), Box<dyn std::error::Error>>(())
249 /// ```
250 pub fn bias_spectr_props_set(
251 &mut self,
252 config: BiasSpectrPropsBuilder,
253 ) -> Result<(), NanonisError> {
254 self.quick_send(
255 "BiasSpectr.PropsSet",
256 vec![
257 NanonisValue::U16(config.save_all.into()),
258 NanonisValue::I32(config.num_sweeps),
259 NanonisValue::U16(config.backward_sweep.into()),
260 NanonisValue::I32(config.num_points),
261 NanonisValue::F32(config.z_offset_m),
262 NanonisValue::U16(config.autosave.into()),
263 NanonisValue::U16(config.show_save_dialog.into()),
264 ],
265 vec!["H", "i", "H", "i", "f", "H", "H"],
266 vec![],
267 )?;
268 Ok(())
269 }
270
271 /// Get the bias spectroscopy properties.
272 ///
273 /// # Returns
274 /// A [`BiasSpectrProps`] struct with current configuration.
275 ///
276 /// # Errors
277 /// Returns `NanonisError` if communication fails.
278 ///
279 /// # Examples
280 /// ```no_run
281 /// use nanonis_rs::NanonisClient;
282 ///
283 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
284 /// let props = client.bias_spectr_props_get()?;
285 /// println!("Sweeps: {}, Points: {}", props.num_sweeps, props.num_points);
286 /// # Ok::<(), Box<dyn std::error::Error>>(())
287 /// ```
288 pub fn bias_spectr_props_get(&mut self) -> Result<BiasSpectrProps, NanonisError> {
289 let result = self.quick_send(
290 "BiasSpectr.PropsGet",
291 vec![],
292 vec![],
293 vec![
294 "H", "i", "H", "i", "i", "i", "*+c", "i", "i", "*+c", "i", "i", "*+c",
295 ],
296 )?;
297
298 if result.len() >= 13 {
299 Ok(BiasSpectrProps {
300 save_all: result[0].as_u16()? != 0,
301 num_sweeps: result[1].as_i32()?,
302 backward_sweep: result[2].as_u16()? != 0,
303 num_points: result[3].as_i32()?,
304 channels: result[6].as_string_array()?.to_vec(),
305 parameters: result[9].as_string_array()?.to_vec(),
306 fixed_parameters: result[12].as_string_array()?.to_vec(),
307 })
308 } else {
309 Err(NanonisError::Protocol(
310 "Invalid props response".to_string(),
311 ))
312 }
313 }
314
315 /// Set the advanced bias spectroscopy properties.
316 ///
317 /// # Arguments
318 /// * `reset_bias` - Reset bias to initial value after sweep: NoChange/On/Off
319 /// * `z_controller_hold` - Hold Z-controller during sweep: NoChange/On/Off
320 /// * `record_final_z` - Record final Z position: NoChange/On/Off
321 /// * `lockin_run` - Run lock-in during measurement: NoChange/On/Off
322 ///
323 /// # Errors
324 /// Returns `NanonisError` if communication fails.
325 ///
326 /// # Examples
327 /// ```no_run
328 /// use nanonis_rs::NanonisClient;
329 /// use nanonis_rs::bias_spectr::OptionalFlag;
330 ///
331 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
332 /// client.bias_spectr_adv_props_set(
333 /// OptionalFlag::On, // reset_bias
334 /// OptionalFlag::On, // z_controller_hold
335 /// OptionalFlag::Off, // record_final_z
336 /// OptionalFlag::Off, // lockin_run
337 /// )?;
338 /// # Ok::<(), Box<dyn std::error::Error>>(())
339 /// ```
340 pub fn bias_spectr_adv_props_set(
341 &mut self,
342 reset_bias: OptionalFlag,
343 z_controller_hold: OptionalFlag,
344 record_final_z: OptionalFlag,
345 lockin_run: OptionalFlag,
346 ) -> Result<(), NanonisError> {
347 self.quick_send(
348 "BiasSpectr.AdvPropsSet",
349 vec![
350 NanonisValue::U16(reset_bias.into()),
351 NanonisValue::U16(z_controller_hold.into()),
352 NanonisValue::U16(record_final_z.into()),
353 NanonisValue::U16(lockin_run.into()),
354 ],
355 vec!["H", "H", "H", "H"],
356 vec![],
357 )?;
358 Ok(())
359 }
360
361 /// Get the advanced bias spectroscopy properties.
362 ///
363 /// # Returns
364 /// A [`BiasSpectrAdvProps`] struct with current advanced settings.
365 ///
366 /// # Errors
367 /// Returns `NanonisError` if communication fails.
368 ///
369 /// # Examples
370 /// ```no_run
371 /// use nanonis_rs::NanonisClient;
372 ///
373 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
374 /// let adv = client.bias_spectr_adv_props_get()?;
375 /// println!("Z-controller hold: {}", adv.z_controller_hold);
376 /// # Ok::<(), Box<dyn std::error::Error>>(())
377 /// ```
378 pub fn bias_spectr_adv_props_get(&mut self) -> Result<BiasSpectrAdvProps, NanonisError> {
379 let result = self.quick_send(
380 "BiasSpectr.AdvPropsGet",
381 vec![],
382 vec![],
383 vec!["H", "H", "H", "H"],
384 )?;
385
386 if result.len() >= 4 {
387 Ok(BiasSpectrAdvProps {
388 reset_bias: result[0].as_u16()? != 0,
389 z_controller_hold: result[1].as_u16()? != 0,
390 record_final_z: result[2].as_u16()? != 0,
391 lockin_run: result[3].as_u16()? != 0,
392 })
393 } else {
394 Err(NanonisError::Protocol(
395 "Invalid adv props response".to_string(),
396 ))
397 }
398 }
399
400 /// Set the bias spectroscopy sweep limits.
401 ///
402 /// # Arguments
403 /// * `start_value_v` - Starting bias voltage in volts
404 /// * `end_value_v` - Ending bias voltage in volts
405 ///
406 /// # Errors
407 /// Returns `NanonisError` if communication fails.
408 ///
409 /// # Examples
410 /// ```no_run
411 /// use nanonis_rs::NanonisClient;
412 ///
413 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
414 /// // Sweep from -2V to +2V
415 /// client.bias_spectr_limits_set(-2.0, 2.0)?;
416 /// # Ok::<(), Box<dyn std::error::Error>>(())
417 /// ```
418 pub fn bias_spectr_limits_set(
419 &mut self,
420 start_value_v: f32,
421 end_value_v: f32,
422 ) -> Result<(), NanonisError> {
423 self.quick_send(
424 "BiasSpectr.LimitsSet",
425 vec![
426 NanonisValue::F32(start_value_v),
427 NanonisValue::F32(end_value_v),
428 ],
429 vec!["f", "f"],
430 vec![],
431 )?;
432 Ok(())
433 }
434
435 /// Get the bias spectroscopy sweep limits.
436 ///
437 /// # Returns
438 /// A tuple `(start_v, end_v)` with the voltage limits.
439 ///
440 /// # Errors
441 /// Returns `NanonisError` if communication fails.
442 ///
443 /// # Examples
444 /// ```no_run
445 /// use nanonis_rs::NanonisClient;
446 ///
447 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
448 /// let (start, end) = client.bias_spectr_limits_get()?;
449 /// println!("Sweep range: {:.2}V to {:.2}V", start, end);
450 /// # Ok::<(), Box<dyn std::error::Error>>(())
451 /// ```
452 pub fn bias_spectr_limits_get(&mut self) -> Result<(f32, f32), NanonisError> {
453 let result = self.quick_send("BiasSpectr.LimitsGet", vec![], vec![], vec!["f", "f"])?;
454
455 if result.len() >= 2 {
456 Ok((result[0].as_f32()?, result[1].as_f32()?))
457 } else {
458 Err(NanonisError::Protocol(
459 "Invalid limits response".to_string(),
460 ))
461 }
462 }
463
464 /// Set the bias spectroscopy timing parameters.
465 ///
466 /// # Arguments
467 /// * `timing` - A [`BiasSpectrTiming`] struct with timing configuration
468 ///
469 /// # Errors
470 /// Returns `NanonisError` if communication fails.
471 ///
472 /// # Examples
473 /// ```no_run
474 /// use nanonis_rs::NanonisClient;
475 /// use nanonis_rs::bias_spectr::BiasSpectrTiming;
476 /// use std::time::Duration;
477 ///
478 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
479 /// let timing = BiasSpectrTiming {
480 /// settling_time: Duration::from_millis(20),
481 /// integration_time: Duration::from_millis(50),
482 /// ..Default::default()
483 /// };
484 /// client.bias_spectr_timing_set(&timing)?;
485 /// # Ok::<(), Box<dyn std::error::Error>>(())
486 /// ```
487 pub fn bias_spectr_timing_set(&mut self, timing: &BiasSpectrTiming) -> Result<(), NanonisError> {
488 self.quick_send(
489 "BiasSpectr.TimingSet",
490 vec![
491 NanonisValue::F32(timing.z_averaging_time.as_secs_f32()),
492 NanonisValue::F32(timing.z_offset_m),
493 NanonisValue::F32(timing.initial_settling_time.as_secs_f32()),
494 NanonisValue::F32(timing.max_slew_rate),
495 NanonisValue::F32(timing.settling_time.as_secs_f32()),
496 NanonisValue::F32(timing.integration_time.as_secs_f32()),
497 NanonisValue::F32(timing.end_settling_time.as_secs_f32()),
498 NanonisValue::F32(timing.z_control_time.as_secs_f32()),
499 ],
500 vec!["f", "f", "f", "f", "f", "f", "f", "f"],
501 vec![],
502 )?;
503 Ok(())
504 }
505
506 /// Get the bias spectroscopy timing parameters.
507 ///
508 /// # Returns
509 /// A [`BiasSpectrTiming`] struct with current timing settings.
510 ///
511 /// # Errors
512 /// Returns `NanonisError` if communication fails.
513 ///
514 /// # Examples
515 /// ```no_run
516 /// use nanonis_rs::NanonisClient;
517 ///
518 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
519 /// let timing = client.bias_spectr_timing_get()?;
520 /// println!("Integration time: {:?}", timing.integration_time);
521 /// # Ok::<(), Box<dyn std::error::Error>>(())
522 /// ```
523 pub fn bias_spectr_timing_get(&mut self) -> Result<BiasSpectrTiming, NanonisError> {
524 let result = self.quick_send(
525 "BiasSpectr.TimingGet",
526 vec![],
527 vec![],
528 vec!["f", "f", "f", "f", "f", "f", "f", "f"],
529 )?;
530
531 if result.len() >= 8 {
532 Ok(BiasSpectrTiming {
533 z_averaging_time: Duration::from_secs_f32(result[0].as_f32()?),
534 z_offset_m: result[1].as_f32()?,
535 initial_settling_time: Duration::from_secs_f32(result[2].as_f32()?),
536 max_slew_rate: result[3].as_f32()?,
537 settling_time: Duration::from_secs_f32(result[4].as_f32()?),
538 integration_time: Duration::from_secs_f32(result[5].as_f32()?),
539 end_settling_time: Duration::from_secs_f32(result[6].as_f32()?),
540 z_control_time: Duration::from_secs_f32(result[7].as_f32()?),
541 })
542 } else {
543 Err(NanonisError::Protocol(
544 "Invalid timing response".to_string(),
545 ))
546 }
547 }
548
549 /// Set the digital synchronization mode.
550 ///
551 /// # Arguments
552 /// * `mode` - The [`DigitalSync`] mode to set
553 ///
554 /// # Errors
555 /// Returns `NanonisError` if communication fails.
556 ///
557 /// # Examples
558 /// ```no_run
559 /// use nanonis_rs::NanonisClient;
560 /// use nanonis_rs::bias_spectr::DigitalSync;
561 ///
562 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
563 /// client.bias_spectr_dig_sync_set(DigitalSync::TTLSync)?;
564 /// # Ok::<(), Box<dyn std::error::Error>>(())
565 /// ```
566 pub fn bias_spectr_dig_sync_set(&mut self, mode: DigitalSync) -> Result<(), NanonisError> {
567 self.quick_send(
568 "BiasSpectr.DigSyncSet",
569 vec![NanonisValue::U16(mode.into())],
570 vec!["H"],
571 vec![],
572 )?;
573 Ok(())
574 }
575
576 /// Get the digital synchronization mode.
577 ///
578 /// # Returns
579 /// The current [`DigitalSync`] mode.
580 ///
581 /// # Errors
582 /// Returns `NanonisError` if communication fails.
583 ///
584 /// # Examples
585 /// ```no_run
586 /// use nanonis_rs::NanonisClient;
587 ///
588 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
589 /// let mode = client.bias_spectr_dig_sync_get()?;
590 /// println!("Digital sync mode: {:?}", mode);
591 /// # Ok::<(), Box<dyn std::error::Error>>(())
592 /// ```
593 pub fn bias_spectr_dig_sync_get(&mut self) -> Result<DigitalSync, NanonisError> {
594 let result = self.quick_send("BiasSpectr.DigSyncGet", vec![], vec![], vec!["H"])?;
595
596 if let Some(val) = result.first() {
597 DigitalSync::try_from(val.as_u16()?)
598 } else {
599 Err(NanonisError::Protocol(
600 "Invalid dig sync response".to_string(),
601 ))
602 }
603 }
604
605 /// Set the TTL synchronization configuration.
606 ///
607 /// # Arguments
608 /// * `config` - A [`TTLSyncConfig`] struct with TTL settings
609 ///
610 /// # Errors
611 /// Returns `NanonisError` if communication fails.
612 ///
613 /// # Examples
614 /// ```no_run
615 /// use nanonis_rs::NanonisClient;
616 /// use nanonis_rs::bias_spectr::{TTLSyncConfig, TTLLine, TTLPolarity};
617 /// use std::time::Duration;
618 ///
619 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
620 /// let config = TTLSyncConfig {
621 /// line: TTLLine::HSLine1,
622 /// polarity: TTLPolarity::HighActive,
623 /// time_to_on: Duration::from_millis(10),
624 /// on_duration: Duration::from_millis(100),
625 /// };
626 /// client.bias_spectr_ttl_sync_set(&config)?;
627 /// # Ok::<(), Box<dyn std::error::Error>>(())
628 /// ```
629 pub fn bias_spectr_ttl_sync_set(&mut self, config: &TTLSyncConfig) -> Result<(), NanonisError> {
630 self.quick_send(
631 "BiasSpectr.TTLSyncSet",
632 vec![
633 NanonisValue::U16(config.line.into()),
634 NanonisValue::U16(config.polarity.into()),
635 NanonisValue::F32(config.time_to_on.as_secs_f32()),
636 NanonisValue::F32(config.on_duration.as_secs_f32()),
637 ],
638 vec!["H", "H", "f", "f"],
639 vec![],
640 )?;
641 Ok(())
642 }
643
644 /// Get the TTL synchronization configuration.
645 ///
646 /// # Returns
647 /// A [`TTLSyncConfig`] struct with current TTL settings.
648 ///
649 /// # Errors
650 /// Returns `NanonisError` if communication fails.
651 ///
652 /// # Examples
653 /// ```no_run
654 /// use nanonis_rs::NanonisClient;
655 ///
656 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
657 /// let config = client.bias_spectr_ttl_sync_get()?;
658 /// println!("TTL line: {:?}, on duration: {:?}", config.line, config.on_duration);
659 /// # Ok::<(), Box<dyn std::error::Error>>(())
660 /// ```
661 pub fn bias_spectr_ttl_sync_get(&mut self) -> Result<TTLSyncConfig, NanonisError> {
662 let result = self.quick_send(
663 "BiasSpectr.TTLSyncGet",
664 vec![],
665 vec![],
666 vec!["H", "H", "f", "f"],
667 )?;
668
669 if result.len() >= 4 {
670 Ok(TTLSyncConfig {
671 line: TTLLine::try_from(result[0].as_u16()?)?,
672 polarity: TTLPolarity::try_from(result[1].as_u16()?)?,
673 time_to_on: Duration::from_secs_f32(result[2].as_f32()?),
674 on_duration: Duration::from_secs_f32(result[3].as_f32()?),
675 })
676 } else {
677 Err(NanonisError::Protocol(
678 "Invalid TTL sync response".to_string(),
679 ))
680 }
681 }
682
683 /// Set the pulse sequence synchronization configuration.
684 ///
685 /// # Arguments
686 /// * `config` - A [`PulseSeqSyncConfig`] struct with pulse sequence settings
687 ///
688 /// # Errors
689 /// Returns `NanonisError` if communication fails.
690 ///
691 /// # Examples
692 /// ```no_run
693 /// use nanonis_rs::NanonisClient;
694 /// use nanonis_rs::bias_spectr::PulseSeqSyncConfig;
695 ///
696 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
697 /// let config = PulseSeqSyncConfig {
698 /// sequence_nr: 1,
699 /// num_periods: 10,
700 /// };
701 /// client.bias_spectr_pulse_seq_sync_set(&config)?;
702 /// # Ok::<(), Box<dyn std::error::Error>>(())
703 /// ```
704 pub fn bias_spectr_pulse_seq_sync_set(
705 &mut self,
706 config: &PulseSeqSyncConfig,
707 ) -> Result<(), NanonisError> {
708 self.quick_send(
709 "BiasSpectr.PulseSeqSyncSet",
710 vec![
711 NanonisValue::U16(config.sequence_nr),
712 NanonisValue::U32(config.num_periods),
713 ],
714 vec!["H", "I"],
715 vec![],
716 )?;
717 Ok(())
718 }
719
720 /// Get the pulse sequence synchronization configuration.
721 ///
722 /// # Returns
723 /// A [`PulseSeqSyncConfig`] struct with current settings.
724 ///
725 /// # Errors
726 /// Returns `NanonisError` if communication fails.
727 ///
728 /// # Examples
729 /// ```no_run
730 /// use nanonis_rs::NanonisClient;
731 ///
732 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
733 /// let config = client.bias_spectr_pulse_seq_sync_get()?;
734 /// println!("Sequence #{}, {} periods", config.sequence_nr, config.num_periods);
735 /// # Ok::<(), Box<dyn std::error::Error>>(())
736 /// ```
737 pub fn bias_spectr_pulse_seq_sync_get(&mut self) -> Result<PulseSeqSyncConfig, NanonisError> {
738 let result =
739 self.quick_send("BiasSpectr.PulseSeqSyncGet", vec![], vec![], vec!["H", "I"])?;
740
741 if result.len() >= 2 {
742 Ok(PulseSeqSyncConfig {
743 sequence_nr: result[0].as_u16()?,
744 num_periods: result[1].as_u32()?,
745 })
746 } else {
747 Err(NanonisError::Protocol(
748 "Invalid pulse seq sync response".to_string(),
749 ))
750 }
751 }
752
753 /// Set the alternate Z-controller setpoint configuration.
754 ///
755 /// # Arguments
756 /// * `config` - An [`AltZCtrlConfig`] struct with alternate setpoint settings
757 ///
758 /// # Errors
759 /// Returns `NanonisError` if communication fails.
760 ///
761 /// # Examples
762 /// ```no_run
763 /// use nanonis_rs::NanonisClient;
764 /// use nanonis_rs::bias_spectr::AltZCtrlConfig;
765 /// use std::time::Duration;
766 ///
767 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
768 /// let config = AltZCtrlConfig {
769 /// enabled: true,
770 /// setpoint: 1e-9, // 1 nA
771 /// settling_time: Duration::from_millis(200),
772 /// };
773 /// client.bias_spectr_alt_z_ctrl_set(&config)?;
774 /// # Ok::<(), Box<dyn std::error::Error>>(())
775 /// ```
776 pub fn bias_spectr_alt_z_ctrl_set(&mut self, config: &AltZCtrlConfig) -> Result<(), NanonisError> {
777 let enabled_flag = if config.enabled {
778 OptionalFlag::On
779 } else {
780 OptionalFlag::Off
781 };
782
783 self.quick_send(
784 "BiasSpectr.AltZCtrlSet",
785 vec![
786 NanonisValue::U16(enabled_flag.into()),
787 NanonisValue::F32(config.setpoint),
788 NanonisValue::F32(config.settling_time.as_secs_f32()),
789 ],
790 vec!["H", "f", "f"],
791 vec![],
792 )?;
793 Ok(())
794 }
795
796 /// Get the alternate Z-controller setpoint configuration.
797 ///
798 /// # Returns
799 /// An [`AltZCtrlConfig`] struct with current settings.
800 ///
801 /// # Errors
802 /// Returns `NanonisError` if communication fails.
803 ///
804 /// # Examples
805 /// ```no_run
806 /// use nanonis_rs::NanonisClient;
807 ///
808 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
809 /// let config = client.bias_spectr_alt_z_ctrl_get()?;
810 /// println!("Alt Z-ctrl enabled: {}, setpoint: {}", config.enabled, config.setpoint);
811 /// # Ok::<(), Box<dyn std::error::Error>>(())
812 /// ```
813 pub fn bias_spectr_alt_z_ctrl_get(&mut self) -> Result<AltZCtrlConfig, NanonisError> {
814 let result =
815 self.quick_send("BiasSpectr.AltZCtrlGet", vec![], vec![], vec!["H", "f", "f"])?;
816
817 if result.len() >= 3 {
818 Ok(AltZCtrlConfig {
819 enabled: result[0].as_u16()? != 0,
820 setpoint: result[1].as_f32()?,
821 settling_time: Duration::from_secs_f32(result[2].as_f32()?),
822 })
823 } else {
824 Err(NanonisError::Protocol(
825 "Invalid alt z ctrl response".to_string(),
826 ))
827 }
828 }
829
830 /// Set the Z offset revert flag.
831 ///
832 /// When enabled, the Z offset applied at the beginning is reverted at the end.
833 ///
834 /// # Arguments
835 /// * `revert` - Whether to revert Z offset: NoChange/On/Off
836 ///
837 /// # Errors
838 /// Returns `NanonisError` if communication fails.
839 ///
840 /// # Examples
841 /// ```no_run
842 /// use nanonis_rs::NanonisClient;
843 /// use nanonis_rs::bias_spectr::OptionalFlag;
844 ///
845 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
846 /// client.bias_spectr_z_off_revert_set(OptionalFlag::On)?;
847 /// # Ok::<(), Box<dyn std::error::Error>>(())
848 /// ```
849 pub fn bias_spectr_z_off_revert_set(&mut self, revert: OptionalFlag) -> Result<(), NanonisError> {
850 self.quick_send(
851 "BiasSpectr.ZOffRevertSet",
852 vec![NanonisValue::U16(revert.into())],
853 vec!["H"],
854 vec![],
855 )?;
856 Ok(())
857 }
858
859 /// Get the Z offset revert flag.
860 ///
861 /// # Returns
862 /// `true` if Z offset revert is enabled.
863 ///
864 /// # Errors
865 /// Returns `NanonisError` if communication fails.
866 ///
867 /// # Examples
868 /// ```no_run
869 /// use nanonis_rs::NanonisClient;
870 ///
871 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
872 /// let revert = client.bias_spectr_z_off_revert_get()?;
873 /// println!("Z offset revert: {}", revert);
874 /// # Ok::<(), Box<dyn std::error::Error>>(())
875 /// ```
876 pub fn bias_spectr_z_off_revert_get(&mut self) -> Result<bool, NanonisError> {
877 let result = self.quick_send("BiasSpectr.ZOffRevertGet", vec![], vec![], vec!["H"])?;
878
879 if let Some(val) = result.first() {
880 Ok(val.as_u16()? != 0)
881 } else {
882 Err(NanonisError::Protocol(
883 "Invalid z off revert response".to_string(),
884 ))
885 }
886 }
887
888 /// Set the MLS lock-in per segment flag.
889 ///
890 /// When enabled, lock-in can be configured per segment in the MLS editor.
891 ///
892 /// # Arguments
893 /// * `enabled` - Whether to enable per-segment lock-in configuration
894 ///
895 /// # Errors
896 /// Returns `NanonisError` if communication fails.
897 ///
898 /// # Examples
899 /// ```no_run
900 /// use nanonis_rs::NanonisClient;
901 ///
902 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
903 /// client.bias_spectr_mls_lockin_per_seg_set(true)?;
904 /// # Ok::<(), Box<dyn std::error::Error>>(())
905 /// ```
906 pub fn bias_spectr_mls_lockin_per_seg_set(&mut self, enabled: bool) -> Result<(), NanonisError> {
907 let flag = if enabled { 1u32 } else { 0u32 };
908 self.quick_send(
909 "BiasSpectr.MLSLockinPerSegSet",
910 vec![NanonisValue::U32(flag)],
911 vec!["I"],
912 vec![],
913 )?;
914 Ok(())
915 }
916
917 /// Get the MLS lock-in per segment flag.
918 ///
919 /// # Returns
920 /// `true` if per-segment lock-in configuration is enabled.
921 ///
922 /// # Errors
923 /// Returns `NanonisError` if communication fails.
924 ///
925 /// # Examples
926 /// ```no_run
927 /// use nanonis_rs::NanonisClient;
928 ///
929 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
930 /// let enabled = client.bias_spectr_mls_lockin_per_seg_get()?;
931 /// println!("MLS lock-in per segment: {}", enabled);
932 /// # Ok::<(), Box<dyn std::error::Error>>(())
933 /// ```
934 pub fn bias_spectr_mls_lockin_per_seg_get(&mut self) -> Result<bool, NanonisError> {
935 let result =
936 self.quick_send("BiasSpectr.MLSLockinPerSegGet", vec![], vec![], vec!["I"])?;
937
938 if let Some(val) = result.first() {
939 Ok(val.as_u32()? != 0)
940 } else {
941 Err(NanonisError::Protocol(
942 "Invalid MLS lockin per seg response".to_string(),
943 ))
944 }
945 }
946
947 /// Set the MLS sweep mode.
948 ///
949 /// # Arguments
950 /// * `mode` - The [`SweepMode`] to set
951 ///
952 /// # Errors
953 /// Returns `NanonisError` if communication fails.
954 ///
955 /// # Examples
956 /// ```no_run
957 /// use nanonis_rs::NanonisClient;
958 /// use nanonis_rs::bias_spectr::SweepMode;
959 ///
960 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
961 /// client.bias_spectr_mls_mode_set(SweepMode::MLS)?;
962 /// # Ok::<(), Box<dyn std::error::Error>>(())
963 /// ```
964 pub fn bias_spectr_mls_mode_set(&mut self, mode: SweepMode) -> Result<(), NanonisError> {
965 let mode_str: &str = mode.into();
966 self.quick_send(
967 "BiasSpectr.MLSModeSet",
968 vec![NanonisValue::String(mode_str.to_string())],
969 vec!["+*c"],
970 vec![],
971 )?;
972 Ok(())
973 }
974
975 /// Get the current MLS sweep mode.
976 ///
977 /// # Returns
978 /// The current [`SweepMode`].
979 ///
980 /// # Errors
981 /// Returns `NanonisError` if communication fails.
982 ///
983 /// # Examples
984 /// ```no_run
985 /// use nanonis_rs::NanonisClient;
986 ///
987 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
988 /// let mode = client.bias_spectr_mls_mode_get()?;
989 /// println!("Sweep mode: {:?}", mode);
990 /// # Ok::<(), Box<dyn std::error::Error>>(())
991 /// ```
992 pub fn bias_spectr_mls_mode_get(&mut self) -> Result<SweepMode, NanonisError> {
993 let result = self.quick_send(
994 "BiasSpectr.MLSModeGet",
995 vec![],
996 vec![],
997 vec!["i", "i", "*+c"],
998 )?;
999
1000 if result.len() >= 3 {
1001 let modes = result[2].as_string_array()?;
1002 if let Some(mode_str) = modes.first() {
1003 SweepMode::try_from(mode_str.as_str())
1004 } else {
1005 Ok(SweepMode::Linear)
1006 }
1007 } else {
1008 Err(NanonisError::Protocol(
1009 "Invalid MLS mode response".to_string(),
1010 ))
1011 }
1012 }
1013
1014 /// Set the MLS segment values.
1015 ///
1016 /// # Arguments
1017 /// * `segments` - Vector of [`MLSSegment`] configurations
1018 ///
1019 /// # Errors
1020 /// Returns `NanonisError` if communication fails.
1021 ///
1022 /// # Examples
1023 /// ```no_run
1024 /// use nanonis_rs::NanonisClient;
1025 /// use nanonis_rs::bias_spectr::MLSSegment;
1026 /// use std::time::Duration;
1027 ///
1028 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
1029 /// let segments = vec![
1030 /// MLSSegment {
1031 /// bias_start: -2.0,
1032 /// bias_end: 0.0,
1033 /// steps: 100,
1034 /// ..Default::default()
1035 /// },
1036 /// MLSSegment {
1037 /// bias_start: 0.0,
1038 /// bias_end: 2.0,
1039 /// steps: 100,
1040 /// ..Default::default()
1041 /// },
1042 /// ];
1043 /// client.bias_spectr_mls_vals_set(&segments)?;
1044 /// # Ok::<(), Box<dyn std::error::Error>>(())
1045 /// ```
1046 pub fn bias_spectr_mls_vals_set(&mut self, segments: &[MLSSegment]) -> Result<(), NanonisError> {
1047 let num_segments = segments.len() as i32;
1048 let bias_start: Vec<f32> = segments.iter().map(|s| s.bias_start).collect();
1049 let bias_end: Vec<f32> = segments.iter().map(|s| s.bias_end).collect();
1050 let initial_settling: Vec<f32> = segments
1051 .iter()
1052 .map(|s| s.initial_settling_time.as_secs_f32())
1053 .collect();
1054 let settling: Vec<f32> = segments
1055 .iter()
1056 .map(|s| s.settling_time.as_secs_f32())
1057 .collect();
1058 let integration: Vec<f32> = segments
1059 .iter()
1060 .map(|s| s.integration_time.as_secs_f32())
1061 .collect();
1062 let slew_rate: Vec<f32> = segments.iter().map(|s| s.max_slew_rate).collect();
1063 let steps: Vec<i32> = segments.iter().map(|s| s.steps).collect();
1064
1065 self.quick_send(
1066 "BiasSpectr.MLSValsSet",
1067 vec![
1068 NanonisValue::I32(num_segments),
1069 NanonisValue::ArrayF32(bias_start),
1070 NanonisValue::ArrayF32(bias_end),
1071 NanonisValue::ArrayF32(initial_settling),
1072 NanonisValue::ArrayF32(settling),
1073 NanonisValue::ArrayF32(integration),
1074 NanonisValue::ArrayF32(slew_rate),
1075 NanonisValue::ArrayI32(steps),
1076 ],
1077 vec!["i", "*f", "*f", "*f", "*f", "*f", "*f", "*i"],
1078 vec![],
1079 )?;
1080 Ok(())
1081 }
1082
1083 /// Get the MLS segment values.
1084 ///
1085 /// # Returns
1086 /// A vector of [`MLSSegment`] configurations.
1087 ///
1088 /// # Errors
1089 /// Returns `NanonisError` if communication fails.
1090 ///
1091 /// # Examples
1092 /// ```no_run
1093 /// use nanonis_rs::NanonisClient;
1094 ///
1095 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
1096 /// let segments = client.bias_spectr_mls_vals_get()?;
1097 /// for (i, seg) in segments.iter().enumerate() {
1098 /// println!("Segment {}: {:.2}V to {:.2}V, {} steps",
1099 /// i, seg.bias_start, seg.bias_end, seg.steps);
1100 /// }
1101 /// # Ok::<(), Box<dyn std::error::Error>>(())
1102 /// ```
1103 pub fn bias_spectr_mls_vals_get(&mut self) -> Result<Vec<MLSSegment>, NanonisError> {
1104 let result = self.quick_send(
1105 "BiasSpectr.MLSValsGet",
1106 vec![],
1107 vec![],
1108 vec!["i", "*f", "*f", "*f", "*f", "*f", "*f", "*i"],
1109 )?;
1110
1111 if result.len() >= 8 {
1112 let num_segments = result[0].as_i32()? as usize;
1113 let bias_start = result[1].as_f32_array()?;
1114 let bias_end = result[2].as_f32_array()?;
1115 let initial_settling = result[3].as_f32_array()?;
1116 let settling = result[4].as_f32_array()?;
1117 let integration = result[5].as_f32_array()?;
1118 let slew_rate = result[6].as_f32_array()?;
1119 let steps = result[7].as_i32_array()?;
1120
1121 let mut segments = Vec::with_capacity(num_segments);
1122 for i in 0..num_segments {
1123 segments.push(MLSSegment {
1124 bias_start: *bias_start.get(i).unwrap_or(&0.0),
1125 bias_end: *bias_end.get(i).unwrap_or(&0.0),
1126 initial_settling_time: Duration::from_secs_f32(
1127 *initial_settling.get(i).unwrap_or(&0.0),
1128 ),
1129 settling_time: Duration::from_secs_f32(*settling.get(i).unwrap_or(&0.0)),
1130 integration_time: Duration::from_secs_f32(*integration.get(i).unwrap_or(&0.0)),
1131 max_slew_rate: *slew_rate.get(i).unwrap_or(&1.0),
1132 steps: *steps.get(i).unwrap_or(&100),
1133 });
1134 }
1135
1136 Ok(segments)
1137 } else {
1138 Err(NanonisError::Protocol(
1139 "Invalid MLS vals response".to_string(),
1140 ))
1141 }
1142 }
1143}