rusty_tip/nanonis/client/
bias.rs

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