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![NanonisValue::U16(control.into()), NanonisValue::U16(status)],
82 vec!["H", "H"],
83 vec![],
84 )?;
85 Ok(())
86 }
87
88 /// Get the status of the selected Atom Tracking control.
89 ///
90 /// # Arguments
91 /// * `control` - Which control to query
92 ///
93 /// # Returns
94 /// True if the control is enabled.
95 ///
96 /// # Errors
97 /// Returns `NanonisError` if communication fails.
98 pub fn atom_track_status_get(&mut self, control: ATControl) -> Result<bool, NanonisError> {
99 let result = self.quick_send(
100 "AtomTrack.StatusGet",
101 vec![NanonisValue::U16(control.into())],
102 vec!["H"],
103 vec!["H"],
104 )?;
105
106 if !result.is_empty() {
107 Ok(result[0].as_u16()? != 0)
108 } else {
109 Err(NanonisError::Protocol("Invalid response".to_string()))
110 }
111 }
112
113 /// Set the Atom Tracking parameters.
114 ///
115 /// # Arguments
116 /// * `props` - An [`AtomTrackProps`] struct with tracking parameters
117 ///
118 /// # Errors
119 /// Returns `NanonisError` if communication fails.
120 ///
121 /// # Examples
122 /// ```no_run
123 /// use nanonis_rs::NanonisClient;
124 /// use nanonis_rs::atom_track::AtomTrackProps;
125 ///
126 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
127 /// let props = AtomTrackProps {
128 /// integral_gain: 10.0,
129 /// frequency_hz: 200.0,
130 /// amplitude_m: 1e-10,
131 /// phase_deg: 0.0,
132 /// switch_off_delay_s: 0.1,
133 /// };
134 /// client.atom_track_props_set(&props)?;
135 /// # Ok::<(), Box<dyn std::error::Error>>(())
136 /// ```
137 pub fn atom_track_props_set(&mut self, props: &AtomTrackProps) -> Result<(), NanonisError> {
138 self.quick_send(
139 "AtomTrack.PropsSet",
140 vec![
141 NanonisValue::F32(props.integral_gain),
142 NanonisValue::F32(props.frequency_hz),
143 NanonisValue::F32(props.amplitude_m),
144 NanonisValue::F32(props.phase_deg),
145 NanonisValue::F32(props.switch_off_delay_s),
146 ],
147 vec!["f", "f", "f", "f", "f"],
148 vec![],
149 )?;
150 Ok(())
151 }
152
153 /// Get the Atom Tracking parameters.
154 ///
155 /// # Returns
156 /// An [`AtomTrackProps`] struct with current tracking parameters.
157 ///
158 /// # Errors
159 /// Returns `NanonisError` if communication fails.
160 pub fn atom_track_props_get(&mut self) -> Result<AtomTrackProps, NanonisError> {
161 let result = self.quick_send(
162 "AtomTrack.PropsGet",
163 vec![],
164 vec![],
165 vec!["f", "f", "f", "f", "f"],
166 )?;
167
168 if result.len() >= 5 {
169 Ok(AtomTrackProps {
170 integral_gain: result[0].as_f32()?,
171 frequency_hz: result[1].as_f32()?,
172 amplitude_m: result[2].as_f32()?,
173 phase_deg: result[3].as_f32()?,
174 switch_off_delay_s: result[4].as_f32()?,
175 })
176 } else {
177 Err(NanonisError::Protocol("Invalid response".to_string()))
178 }
179 }
180
181 /// Start the Tilt or Drift compensation.
182 ///
183 /// # Arguments
184 /// * `comp_type` - Which compensation to start (tilt or drift)
185 ///
186 /// # Errors
187 /// Returns `NanonisError` if communication fails.
188 pub fn atom_track_quick_comp_start(
189 &mut self,
190 comp_type: QuickCompType,
191 ) -> Result<(), NanonisError> {
192 self.quick_send(
193 "AtomTrack.QuickCompStart",
194 vec![NanonisValue::U16(comp_type.into())],
195 vec!["H"],
196 vec![],
197 )?;
198 Ok(())
199 }
200
201 /// Apply the Drift measurement to the Drift compensation and turn on compensation.
202 ///
203 /// # Errors
204 /// Returns `NanonisError` if communication fails.
205 pub fn atom_track_drift_comp(&mut self) -> Result<(), NanonisError> {
206 self.quick_send("AtomTrack.DriftComp", vec![], vec![], vec![])?;
207 Ok(())
208 }
209}