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![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}