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 = self.quick_send("KelvinCtrl.GainGet", vec![], vec![], vec!["f", "f", "H"])?;
205
206 if result.len() >= 3 {
207 Ok(KelvinGain {
208 p_gain: result[0].as_f32()?,
209 time_constant_s: result[1].as_f32()?,
210 slope: result[2].as_u16()?.try_into()?,
211 })
212 } else {
213 Err(NanonisError::Protocol("Invalid response".to_string()))
214 }
215 }
216
217 pub fn kelvin_ctrl_mod_params_set(
225 &mut self,
226 params: &KelvinModParams,
227 ) -> Result<(), NanonisError> {
228 self.quick_send(
229 "KelvinCtrl.ModParamsSet",
230 vec![
231 NanonisValue::F32(params.frequency_hz),
232 NanonisValue::F32(params.amplitude),
233 NanonisValue::F32(params.phase_deg),
234 ],
235 vec!["f", "f", "f"],
236 vec![],
237 )?;
238 Ok(())
239 }
240
241 pub fn kelvin_ctrl_mod_params_get(&mut self) -> Result<KelvinModParams, NanonisError> {
249 let result = self.quick_send(
250 "KelvinCtrl.ModParamsGet",
251 vec![],
252 vec![],
253 vec!["f", "f", "f"],
254 )?;
255
256 if result.len() >= 3 {
257 Ok(KelvinModParams {
258 frequency_hz: result[0].as_f32()?,
259 amplitude: result[1].as_f32()?,
260 phase_deg: result[2].as_f32()?,
261 })
262 } else {
263 Err(NanonisError::Protocol("Invalid response".to_string()))
264 }
265 }
266
267 pub fn kelvin_ctrl_mod_on_off_set(
276 &mut self,
277 ac_mode: KelvinACMode,
278 modulation: bool,
279 ) -> Result<(), NanonisError> {
280 let mod_flag = if modulation { 1u16 } else { 0u16 };
281 self.quick_send(
282 "KelvinCtrl.ModOnOffSet",
283 vec![
284 NanonisValue::U16(ac_mode.into()),
285 NanonisValue::U16(mod_flag),
286 ],
287 vec!["H", "H"],
288 vec![],
289 )?;
290 Ok(())
291 }
292
293 pub fn kelvin_ctrl_mod_on_off_get(&mut self) -> Result<KelvinModStatus, NanonisError> {
301 let result = self.quick_send("KelvinCtrl.ModOnOffGet", vec![], vec![], vec!["H", "H"])?;
302
303 if result.len() >= 2 {
304 Ok(KelvinModStatus {
305 ac_mode: result[0].as_u16()? != 0,
306 modulation: result[1].as_u16()? != 0,
307 })
308 } else {
309 Err(NanonisError::Protocol("Invalid response".to_string()))
310 }
311 }
312
313 pub fn kelvin_ctrl_signal_set(&mut self, signal_index: i32) -> Result<(), NanonisError> {
321 self.quick_send(
322 "KelvinCtrl.CtrlSignalSet",
323 vec![NanonisValue::I32(signal_index)],
324 vec!["i"],
325 vec![],
326 )?;
327 Ok(())
328 }
329
330 pub fn kelvin_ctrl_signal_get(&mut self) -> Result<i32, NanonisError> {
338 let result = self.quick_send("KelvinCtrl.CtrlSignalGet", vec![], vec![], vec!["i"])?;
339
340 if !result.is_empty() {
341 Ok(result[0].as_i32()?)
342 } else {
343 Err(NanonisError::Protocol("Invalid response".to_string()))
344 }
345 }
346
347 pub fn kelvin_ctrl_amp_get(&mut self) -> Result<f32, NanonisError> {
355 let result = self.quick_send("KelvinCtrl.AmpGet", vec![], vec![], vec!["f"])?;
356
357 if !result.is_empty() {
358 Ok(result[0].as_f32()?)
359 } else {
360 Err(NanonisError::Protocol("Invalid response".to_string()))
361 }
362 }
363
364 pub fn kelvin_ctrl_bias_limits_set(
374 &mut self,
375 limits: &KelvinBiasLimits,
376 ) -> Result<(), NanonisError> {
377 self.quick_send(
378 "KelvinCtrl.BiasLimitsSet",
379 vec![
380 NanonisValue::F32(limits.high_limit_v),
381 NanonisValue::F32(limits.low_limit_v),
382 ],
383 vec!["f", "f"],
384 vec![],
385 )?;
386 Ok(())
387 }
388
389 pub fn kelvin_ctrl_bias_limits_get(&mut self) -> Result<KelvinBiasLimits, NanonisError> {
397 let result = self.quick_send("KelvinCtrl.BiasLimitsGet", vec![], vec![], vec!["f", "f"])?;
398
399 if result.len() >= 2 {
400 Ok(KelvinBiasLimits {
401 high_limit_v: result[0].as_f32()?,
402 low_limit_v: result[1].as_f32()?,
403 })
404 } else {
405 Err(NanonisError::Protocol("Invalid response".to_string()))
406 }
407 }
408}