nanonis_rs/client/atom_track.rs
1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Atom Tracking control type.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum ATControl {
8 /// Modulation control
9 #[default]
10 Modulation = 0,
11 /// Controller
12 Controller = 1,
13 /// Drift measurement
14 DriftMeasurement = 2,
15}
16
17impl From<ATControl> for u16 {
18 fn from(ctrl: ATControl) -> Self {
19 ctrl as u16
20 }
21}
22
23/// Quick compensation type.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25pub enum QuickCompType {
26 /// Tilt compensation
27 #[default]
28 Tilt = 0,
29 /// Drift compensation
30 Drift = 1,
31}
32
33impl From<QuickCompType> for u16 {
34 fn from(comp: QuickCompType) -> Self {
35 comp as u16
36 }
37}
38
39/// Atom Tracking properties.
40#[derive(Debug, Clone, Copy, Default)]
41pub struct AtomTrackProps {
42 /// Integral gain of the controller
43 pub integral_gain: f32,
44 /// Modulation frequency in Hz
45 pub frequency_hz: f32,
46 /// Modulation amplitude in meters
47 pub amplitude_m: f32,
48 /// Modulation phase in degrees
49 pub phase_deg: f32,
50 /// Switch off delay in seconds
51 pub switch_off_delay_s: f32,
52}
53
54impl NanonisClient {
55 /// Turn the selected Atom Tracking control on or off.
56 ///
57 /// # Arguments
58 /// * `control` - Which control to switch (modulation, controller, or drift measurement)
59 /// * `enabled` - True to enable, false to disable
60 ///
61 /// # Errors
62 /// Returns `NanonisError` if communication fails.
63 ///
64 /// # Examples
65 /// ```no_run
66 /// use nanonis_rs::NanonisClient;
67 /// use nanonis_rs::atom_track::ATControl;
68 ///
69 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
70 /// client.atom_track_ctrl_set(ATControl::Controller, true)?;
71 /// # Ok::<(), Box<dyn std::error::Error>>(())
72 /// ```
73 pub fn atom_track_ctrl_set(
74 &mut self,
75 control: ATControl,
76 enabled: bool,
77 ) -> Result<(), NanonisError> {
78 let status = if enabled { 1u16 } else { 0u16 };
79 self.quick_send(
80 "AtomTrack.CtrlSet",
81 vec![
82 NanonisValue::U16(control.into()),
83 NanonisValue::U16(status),
84 ],
85 vec!["H", "H"],
86 vec![],
87 )?;
88 Ok(())
89 }
90
91 /// Get the status of the selected Atom Tracking control.
92 ///
93 /// # Arguments
94 /// * `control` - Which control to query
95 ///
96 /// # Returns
97 /// True if the control is enabled.
98 ///
99 /// # Errors
100 /// Returns `NanonisError` if communication fails.
101 pub fn atom_track_status_get(&mut self, control: ATControl) -> Result<bool, NanonisError> {
102 let result = self.quick_send(
103 "AtomTrack.StatusGet",
104 vec![NanonisValue::U16(control.into())],
105 vec!["H"],
106 vec!["H"],
107 )?;
108
109 if !result.is_empty() {
110 Ok(result[0].as_u16()? != 0)
111 } else {
112 Err(NanonisError::Protocol("Invalid response".to_string()))
113 }
114 }
115
116 /// Set the Atom Tracking parameters.
117 ///
118 /// # Arguments
119 /// * `props` - An [`AtomTrackProps`] struct with tracking parameters
120 ///
121 /// # Errors
122 /// Returns `NanonisError` if communication fails.
123 ///
124 /// # Examples
125 /// ```no_run
126 /// use nanonis_rs::NanonisClient;
127 /// use nanonis_rs::atom_track::AtomTrackProps;
128 ///
129 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
130 /// let props = AtomTrackProps {
131 /// integral_gain: 10.0,
132 /// frequency_hz: 200.0,
133 /// amplitude_m: 1e-10,
134 /// phase_deg: 0.0,
135 /// switch_off_delay_s: 0.1,
136 /// };
137 /// client.atom_track_props_set(&props)?;
138 /// # Ok::<(), Box<dyn std::error::Error>>(())
139 /// ```
140 pub fn atom_track_props_set(&mut self, props: &AtomTrackProps) -> Result<(), NanonisError> {
141 self.quick_send(
142 "AtomTrack.PropsSet",
143 vec![
144 NanonisValue::F32(props.integral_gain),
145 NanonisValue::F32(props.frequency_hz),
146 NanonisValue::F32(props.amplitude_m),
147 NanonisValue::F32(props.phase_deg),
148 NanonisValue::F32(props.switch_off_delay_s),
149 ],
150 vec!["f", "f", "f", "f", "f"],
151 vec![],
152 )?;
153 Ok(())
154 }
155
156 /// Get the Atom Tracking parameters.
157 ///
158 /// # Returns
159 /// An [`AtomTrackProps`] struct with current tracking parameters.
160 ///
161 /// # Errors
162 /// Returns `NanonisError` if communication fails.
163 pub fn atom_track_props_get(&mut self) -> Result<AtomTrackProps, NanonisError> {
164 let result = self.quick_send(
165 "AtomTrack.PropsGet",
166 vec![],
167 vec![],
168 vec!["f", "f", "f", "f", "f"],
169 )?;
170
171 if result.len() >= 5 {
172 Ok(AtomTrackProps {
173 integral_gain: result[0].as_f32()?,
174 frequency_hz: result[1].as_f32()?,
175 amplitude_m: result[2].as_f32()?,
176 phase_deg: result[3].as_f32()?,
177 switch_off_delay_s: result[4].as_f32()?,
178 })
179 } else {
180 Err(NanonisError::Protocol("Invalid response".to_string()))
181 }
182 }
183
184 /// Start the Tilt or Drift compensation.
185 ///
186 /// # Arguments
187 /// * `comp_type` - Which compensation to start (tilt or drift)
188 ///
189 /// # Errors
190 /// Returns `NanonisError` if communication fails.
191 pub fn atom_track_quick_comp_start(
192 &mut self,
193 comp_type: QuickCompType,
194 ) -> Result<(), NanonisError> {
195 self.quick_send(
196 "AtomTrack.QuickCompStart",
197 vec![NanonisValue::U16(comp_type.into())],
198 vec!["H"],
199 vec![],
200 )?;
201 Ok(())
202 }
203
204 /// Apply the Drift measurement to the Drift compensation and turn on compensation.
205 ///
206 /// # Errors
207 /// Returns `NanonisError` if communication fails.
208 pub fn atom_track_drift_comp(&mut self) -> Result<(), NanonisError> {
209 self.quick_send("AtomTrack.DriftComp", vec![], vec![], vec![])?;
210 Ok(())
211 }
212}