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}