1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum KelvinSlope {
8 #[default]
10 NoChange = 0,
11 Positive = 1,
13 Negative = 2,
15}
16
17impl From<KelvinSlope> for u16 {
18 fn from(slope: KelvinSlope) -> Self {
19 slope as u16
20 }
21}
22
23impl TryFrom<u16> for KelvinSlope {
24 type Error = NanonisError;
25
26 fn try_from(value: u16) -> Result<Self, Self::Error> {
27 match value {
28 0 => Ok(KelvinSlope::Negative),
29 1 => Ok(KelvinSlope::Positive),
30 _ => Err(NanonisError::Protocol(format!(
31 "Invalid KelvinSlope value: {}",
32 value
33 ))),
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
40pub enum KelvinACMode {
41 #[default]
43 NoChange = 0,
44 On = 1,
46 Off = 2,
48}
49
50impl From<KelvinACMode> for u16 {
51 fn from(mode: KelvinACMode) -> Self {
52 mode as u16
53 }
54}
55
56#[derive(Debug, Clone, Copy, Default)]
58pub struct KelvinGain {
59 pub p_gain: f32,
61 pub time_constant_s: f32,
63 pub slope: KelvinSlope,
65}
66
67#[derive(Debug, Clone, Copy, Default)]
69pub struct KelvinModParams {
70 pub frequency_hz: f32,
72 pub amplitude: f32,
74 pub phase_deg: f32,
76}
77
78#[derive(Debug, Clone, Copy, Default)]
80pub struct KelvinModStatus {
81 pub ac_mode: bool,
83 pub modulation: bool,
85}
86
87#[derive(Debug, Clone, Copy, Default)]
89pub struct KelvinBiasLimits {
90 pub high_limit_v: f32,
92 pub low_limit_v: f32,
94}
95
96impl NanonisClient {
97 pub fn kelvin_ctrl_on_off_set(&mut self, enabled: bool) -> Result<(), NanonisError> {
114 let flag = if enabled { 1u32 } else { 0u32 };
115 self.quick_send(
116 "KelvinCtrl.CtrlOnOffSet",
117 vec![NanonisValue::U32(flag)],
118 vec!["I"],
119 vec![],
120 )?;
121 Ok(())
122 }
123
124 pub fn kelvin_ctrl_on_off_get(&mut self) -> Result<bool, NanonisError> {
132 let result = self.quick_send("KelvinCtrl.CtrlOnOffGet", vec![], vec![], vec!["I"])?;
133
134 if !result.is_empty() {
135 Ok(result[0].as_u32()? != 0)
136 } else {
137 Err(NanonisError::Protocol("Invalid response".to_string()))
138 }
139 }
140
141 pub fn kelvin_ctrl_setpnt_set(&mut self, setpoint: f32) -> Result<(), NanonisError> {
149 self.quick_send(
150 "KelvinCtrl.SetpntSet",
151 vec![NanonisValue::F32(setpoint)],
152 vec!["f"],
153 vec![],
154 )?;
155 Ok(())
156 }
157
158 pub fn kelvin_ctrl_setpnt_get(&mut self) -> Result<f32, NanonisError> {
166 let result = self.quick_send("KelvinCtrl.SetpntGet", vec![], vec![], vec!["f"])?;
167
168 if !result.is_empty() {
169 Ok(result[0].as_f32()?)
170 } else {
171 Err(NanonisError::Protocol("Invalid response".to_string()))
172 }
173 }
174
175 pub fn kelvin_ctrl_gain_set(&mut self, gain: &KelvinGain) -> Result<(), NanonisError> {
183 self.quick_send(
184 "KelvinCtrl.GainSet",
185 vec![
186 NanonisValue::F32(gain.p_gain),
187 NanonisValue::F32(gain.time_constant_s),
188 NanonisValue::U16(gain.slope.into()),
189 ],
190 vec!["f", "f", "H"],
191 vec![],
192 )?;
193 Ok(())
194 }
195
196 pub fn kelvin_ctrl_gain_get(&mut self) -> Result<KelvinGain, NanonisError> {
204 let result =
205 self.quick_send("KelvinCtrl.GainGet", vec![], vec![], vec!["f", "f", "H"])?;
206
207 if result.len() >= 3 {
208 Ok(KelvinGain {
209 p_gain: result[0].as_f32()?,
210 time_constant_s: result[1].as_f32()?,
211 slope: result[2].as_u16()?.try_into()?,
212 })
213 } else {
214 Err(NanonisError::Protocol("Invalid response".to_string()))
215 }
216 }
217
218 pub fn kelvin_ctrl_mod_params_set(
226 &mut self,
227 params: &KelvinModParams,
228 ) -> Result<(), NanonisError> {
229 self.quick_send(
230 "KelvinCtrl.ModParamsSet",
231 vec![
232 NanonisValue::F32(params.frequency_hz),
233 NanonisValue::F32(params.amplitude),
234 NanonisValue::F32(params.phase_deg),
235 ],
236 vec!["f", "f", "f"],
237 vec![],
238 )?;
239 Ok(())
240 }
241
242 pub fn kelvin_ctrl_mod_params_get(&mut self) -> Result<KelvinModParams, NanonisError> {
250 let result =
251 self.quick_send("KelvinCtrl.ModParamsGet", vec![], vec![], vec!["f", "f", "f"])?;
252
253 if result.len() >= 3 {
254 Ok(KelvinModParams {
255 frequency_hz: result[0].as_f32()?,
256 amplitude: result[1].as_f32()?,
257 phase_deg: result[2].as_f32()?,
258 })
259 } else {
260 Err(NanonisError::Protocol("Invalid response".to_string()))
261 }
262 }
263
264 pub fn kelvin_ctrl_mod_on_off_set(
273 &mut self,
274 ac_mode: KelvinACMode,
275 modulation: bool,
276 ) -> Result<(), NanonisError> {
277 let mod_flag = if modulation { 1u16 } else { 0u16 };
278 self.quick_send(
279 "KelvinCtrl.ModOnOffSet",
280 vec![
281 NanonisValue::U16(ac_mode.into()),
282 NanonisValue::U16(mod_flag),
283 ],
284 vec!["H", "H"],
285 vec![],
286 )?;
287 Ok(())
288 }
289
290 pub fn kelvin_ctrl_mod_on_off_get(&mut self) -> Result<KelvinModStatus, NanonisError> {
298 let result =
299 self.quick_send("KelvinCtrl.ModOnOffGet", vec![], vec![], vec!["H", "H"])?;
300
301 if result.len() >= 2 {
302 Ok(KelvinModStatus {
303 ac_mode: result[0].as_u16()? != 0,
304 modulation: result[1].as_u16()? != 0,
305 })
306 } else {
307 Err(NanonisError::Protocol("Invalid response".to_string()))
308 }
309 }
310
311 pub fn kelvin_ctrl_signal_set(&mut self, signal_index: i32) -> Result<(), NanonisError> {
319 self.quick_send(
320 "KelvinCtrl.CtrlSignalSet",
321 vec![NanonisValue::I32(signal_index)],
322 vec!["i"],
323 vec![],
324 )?;
325 Ok(())
326 }
327
328 pub fn kelvin_ctrl_signal_get(&mut self) -> Result<i32, NanonisError> {
336 let result = self.quick_send("KelvinCtrl.CtrlSignalGet", vec![], vec![], vec!["i"])?;
337
338 if !result.is_empty() {
339 Ok(result[0].as_i32()?)
340 } else {
341 Err(NanonisError::Protocol("Invalid response".to_string()))
342 }
343 }
344
345 pub fn kelvin_ctrl_amp_get(&mut self) -> Result<f32, NanonisError> {
353 let result = self.quick_send("KelvinCtrl.AmpGet", vec![], vec![], vec!["f"])?;
354
355 if !result.is_empty() {
356 Ok(result[0].as_f32()?)
357 } else {
358 Err(NanonisError::Protocol("Invalid response".to_string()))
359 }
360 }
361
362 pub fn kelvin_ctrl_bias_limits_set(
372 &mut self,
373 limits: &KelvinBiasLimits,
374 ) -> Result<(), NanonisError> {
375 self.quick_send(
376 "KelvinCtrl.BiasLimitsSet",
377 vec![
378 NanonisValue::F32(limits.high_limit_v),
379 NanonisValue::F32(limits.low_limit_v),
380 ],
381 vec!["f", "f"],
382 vec![],
383 )?;
384 Ok(())
385 }
386
387 pub fn kelvin_ctrl_bias_limits_get(&mut self) -> Result<KelvinBiasLimits, NanonisError> {
395 let result =
396 self.quick_send("KelvinCtrl.BiasLimitsGet", vec![], vec![], vec!["f", "f"])?;
397
398 if result.len() >= 2 {
399 Ok(KelvinBiasLimits {
400 high_limit_v: result[0].as_f32()?,
401 low_limit_v: result[1].as_f32()?,
402 })
403 } else {
404 Err(NanonisError::Protocol("Invalid response".to_string()))
405 }
406 }
407}