Skip to main content

nanonis_rs/client/bias/
mod.rs

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