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