Skip to main content

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}