Skip to main content

nanonis_rs/client/bias/
mod.rs

1mod types;
2pub use types::*;
3
4use super::NanonisClient;
5use crate::error::NanonisError;
6use crate::types::NanonisValue;
7
8impl NanonisClient {
9    /// Set the bias voltage applied to the scanning probe tip.
10    ///
11    /// This corresponds to the Nanonis `Bias.Set` command and is fundamental
12    /// for tip-sample interaction control.
13    ///
14    /// # Arguments
15    /// * `voltage` - The bias voltage to apply (in volts)
16    ///
17    /// # Errors
18    /// Returns `NanonisError` if:
19    /// - The command fails or communication times out
20    /// - The voltage is outside the instrument's safe operating range
21    ///
22    /// # Examples
23    /// ```no_run
24    /// use nanonis_rs::NanonisClient;
25    ///
26    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
27    ///
28    /// // Set bias to 1.5V
29    /// client.bias_set(1.5)?;
30    ///
31    /// // Set bias to -0.5V
32    /// client.bias_set(-0.5)?;
33    /// # Ok::<(), Box<dyn std::error::Error>>(())
34    /// ```
35    pub fn bias_set(&mut self, voltage: f32) -> Result<(), NanonisError> {
36        self.quick_send(
37            "Bias.Set",
38            vec![NanonisValue::F32(voltage)],
39            vec!["f"],
40            vec![],
41        )?;
42        Ok(())
43    }
44
45    /// Get the current bias voltage applied to the scanning probe tip.
46    ///
47    /// This corresponds to the Nanonis `Bias.Get` command.
48    ///
49    /// # Returns
50    /// The current bias voltage in volts.
51    ///
52    /// # Errors
53    /// Returns `NanonisError` if:
54    /// - The command fails or communication times out
55    /// - The server returns invalid or missing data
56    ///
57    /// # Examples
58    /// ```no_run
59    /// use nanonis_rs::NanonisClient;
60    ///
61    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
62    ///
63    /// let current_bias = client.bias_get()?;
64    /// println!("Current bias voltage: {:.3}V", current_bias);
65    /// # Ok::<(), Box<dyn std::error::Error>>(())
66    /// ```
67    pub fn bias_get(&mut self) -> Result<f32, NanonisError> {
68        let result = self.quick_send("Bias.Get", vec![], vec![], vec!["f"])?;
69        match result.first() {
70            Some(value) => Ok(value.as_f32()?),
71            None => {
72                Err(NanonisError::Protocol("No bias value returned".to_string()))
73            }
74        }
75    }
76
77    /// Set the range of the bias voltage, if different ranges are available.
78    ///
79    /// Sets the bias voltage range by selecting from available ranges.
80    /// Use `bias_range_get()` first to retrieve the list of available ranges.
81    ///
82    /// # Arguments
83    /// * `bias_range_index` - Index from the list of ranges (0-based)
84    ///
85    /// # Errors
86    /// Returns `NanonisError` if:
87    /// - Invalid range index is provided
88    /// - Communication timeout or protocol error
89    ///
90    /// # Examples
91    /// ```no_run
92    /// use nanonis_rs::NanonisClient;
93    ///
94    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
95    ///
96    /// // First get available ranges
97    /// let (ranges, current_index) = client.bias_range_get()?;
98    /// println!("Available ranges: {:?}", ranges);
99    ///
100    /// // Set to range index 1
101    /// client.bias_range_set(1)?;
102    /// # Ok::<(), Box<dyn std::error::Error>>(())
103    /// ```
104    pub fn bias_range_set(
105        &mut self,
106        bias_range_index: u16,
107    ) -> Result<(), NanonisError> {
108        self.quick_send(
109            "Bias.RangeSet",
110            vec![NanonisValue::U16(bias_range_index)],
111            vec!["H"],
112            vec![],
113        )?;
114        Ok(())
115    }
116
117    /// Get the selectable ranges of bias voltage and the index of the selected one.
118    ///
119    /// Returns all available bias voltage ranges and which one is currently selected.
120    /// This information is needed for `bias_range_set()` and `bias_calibr_set/get()`.
121    ///
122    /// # Returns
123    /// A tuple containing:
124    /// - `Vec<String>` - Array of available bias range descriptions
125    /// - `u16` - Index of currently selected range
126    ///
127    /// # Errors
128    /// Returns `NanonisError` if communication fails or protocol error occurs.
129    ///
130    /// # Examples
131    /// ```no_run
132    /// use nanonis_rs::NanonisClient;
133    ///
134    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
135    ///
136    /// let (ranges, current_index) = client.bias_range_get()?;
137    /// println!("Current range: {} (index {})", ranges[current_index as usize], current_index);
138    ///
139    /// for (i, range) in ranges.iter().enumerate() {
140    ///     println!("Range {}: {}", i, range);
141    /// }
142    /// # Ok::<(), Box<dyn std::error::Error>>(())
143    /// ```
144    pub fn bias_range_get(&mut self) -> Result<(Vec<String>, u16), NanonisError> {
145        let result = self.quick_send(
146            "Bias.RangeGet",
147            vec![],
148            vec![],
149            vec!["i", "i", "*+c", "H"],
150        )?;
151        if result.len() >= 4 {
152            let ranges = result[2].as_string_array()?.to_vec();
153            let current_index = result[3].as_u16()?;
154            Ok((ranges, current_index))
155        } else {
156            Err(NanonisError::Protocol(
157                "Invalid bias range response".to_string(),
158            ))
159        }
160    }
161
162    /// Set the calibration and offset of bias voltage.
163    ///
164    /// Sets the calibration parameters for the currently selected bias range.
165    /// If multiple ranges are available, this affects only the selected range.
166    ///
167    /// # Arguments
168    /// * `calibration` - Calibration factor (typically in V/V or similar units)
169    /// * `offset` - Offset value in the same units as calibration
170    ///
171    /// # Errors
172    /// Returns `NanonisError` if communication fails or invalid parameters provided.
173    ///
174    /// # Examples
175    /// ```no_run
176    /// use nanonis_rs::NanonisClient;
177    ///
178    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
179    ///
180    /// // Set calibration factor and offset for current range
181    /// client.bias_calibr_set(1.0, 0.0)?;
182    ///
183    /// // Apply a small offset correction
184    /// client.bias_calibr_set(0.998, 0.005)?;
185    /// Ok::<(), Box<dyn std::error::Error>>(())
186    /// ```
187    pub fn bias_calibr_set(
188        &mut self,
189        calibration: f32,
190        offset: f32,
191    ) -> Result<(), NanonisError> {
192        self.quick_send(
193            "Bias.CalibrSet",
194            vec![NanonisValue::F32(calibration), NanonisValue::F32(offset)],
195            vec!["f", "f"],
196            vec![],
197        )?;
198        Ok(())
199    }
200
201    /// Get the calibration and offset of bias voltage.
202    ///
203    /// Returns the calibration parameters for the currently selected bias range.
204    /// If multiple ranges are available, this returns values for the selected range.
205    ///
206    /// # Returns
207    /// A tuple containing:
208    /// - `f32` - Calibration factor
209    /// - `f32` - Offset value
210    ///
211    /// # Errors
212    /// Returns `NanonisError` if communication fails or protocol error occurs.
213    ///
214    /// # Examples
215    /// ```no_run
216    /// use nanonis_rs::NanonisClient;
217    ///
218    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
219    ///
220    /// let (calibration, offset) = client.bias_calibr_get()?;
221    /// println!("Bias calibration: {:.6}, offset: {:.6}", calibration, offset);
222    /// Ok::<(), Box<dyn std::error::Error>>(())
223    /// ```
224    pub fn bias_calibr_get(&mut self) -> Result<(f32, f32), NanonisError> {
225        let result =
226            self.quick_send("Bias.CalibrGet", vec![], vec![], vec!["f", "f"])?;
227        if result.len() >= 2 {
228            let calibration = result[0].as_f32()?;
229            let offset = result[1].as_f32()?;
230            Ok((calibration, offset))
231        } else {
232            Err(NanonisError::Protocol(
233                "Invalid bias calibration response".to_string(),
234            ))
235        }
236    }
237
238    /// Generate one bias pulse.
239    ///
240    /// Applies a bias voltage pulse for a specified duration. This is useful for
241    /// tunneling spectroscopy, tip conditioning, or sample manipulation experiments.
242    ///
243    /// # Arguments
244    /// * `wait_until_done` - If true, function waits until pulse completes
245    /// * `pulse_width_s` - Pulse duration in seconds
246    /// * `bias_value_v` - Bias voltage during pulse (in volts)
247    /// * `z_controller_hold` - Z-controller behavior: 0=no change, 1=hold, 2=don't hold
248    /// * `pulse_mode` - Pulse mode: 0=no change, 1=relative to current, 2=absolute value
249    ///
250    /// # Errors
251    /// Returns `NanonisError` if:
252    /// - Invalid pulse parameters (negative duration, etc.)
253    /// - Bias voltage exceeds safety limits
254    /// - Communication timeout or protocol error
255    ///
256    /// # Examples
257    /// ```no_run
258    /// use nanonis_rs::NanonisClient;
259    ///
260    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
261    ///
262    /// // Apply a 100ms pulse at +2V, holding Z-controller, absolute voltage
263    /// client.bias_pulse(true, 0.1, 2.0, 1, 2)?;
264    ///
265    /// // Quick +0.5V pulse relative to current bias, don't wait
266    /// client.bias_pulse(false, 0.01, 0.5, 0, 1)?;
267    ///
268    /// // Long conditioning pulse at -3V absolute, hold Z-controller
269    /// client.bias_pulse(true, 1.0, -3.0, 1, 2)?;
270    /// # Ok::<(), Box<dyn std::error::Error>>(())
271    /// ```
272    pub fn bias_pulse(
273        &mut self,
274        wait_until_done: bool,
275        pulse_width_s: f32,
276        bias_value_v: f32,
277        z_controller_hold: u16,
278        pulse_mode: u16,
279    ) -> Result<(), NanonisError> {
280        let wait_flag = if wait_until_done { 1u32 } else { 0u32 };
281
282        self.quick_send(
283            "Bias.Pulse",
284            vec![
285                NanonisValue::U32(wait_flag),
286                NanonisValue::F32(pulse_width_s),
287                NanonisValue::F32(bias_value_v),
288                NanonisValue::U16(z_controller_hold),
289                NanonisValue::U16(pulse_mode),
290            ],
291            vec!["I", "f", "f", "H", "H"],
292            vec![],
293        )?;
294        Ok(())
295    }
296}