pico_driver/
ps3000a.rs

1use crate::{
2    dependencies::{load_dependencies, LoadedDependencies},
3    get_version_string, parse_enum_result,
4    trampoline::split_closure,
5    EnumerationResult, PicoDriver,
6};
7use parking_lot::RwLock;
8use pico_common::{
9    ChannelConfig, DownsampleMode, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo,
10    PicoRange, PicoResult, PicoStatus, SampleConfig, ToPicoStr,
11};
12use pico_sys_dynamic::ps3000a::PS3000ALoader;
13use std::{pin::Pin, sync::Arc};
14
15pub struct PS3000ADriver {
16    _dependencies: LoadedDependencies,
17    bindings: PS3000ALoader,
18}
19
20impl std::fmt::Debug for PS3000ADriver {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.debug_struct("PS3000ADriver").finish()
23    }
24}
25
26impl PS3000ADriver {
27    pub fn new<P>(path: P) -> Result<Self, ::libloading::Error>
28    where
29        P: AsRef<::std::ffi::OsStr>,
30    {
31        let dependencies = load_dependencies(&path.as_ref());
32        let bindings = unsafe { PS3000ALoader::new(path)? };
33        // Disables the splash screen on Windows
34        unsafe { bindings.ps3000aApplyFix(0x1ced9168, 0x11e6) };
35        Ok(PS3000ADriver {
36            bindings,
37            _dependencies: dependencies,
38        })
39    }
40}
41
42impl PicoDriver for PS3000ADriver {
43    fn get_driver(&self) -> Driver {
44        Driver::PS3000A
45    }
46
47    #[tracing::instrument(level = "trace", skip(self))]
48    fn get_version(&self) -> PicoResult<String> {
49        let raw_version = self.get_unit_info(0, PicoInfo::DRIVER_VERSION)?;
50
51        // On non-Windows platforms, the drivers return extra text before the
52        // version string
53        Ok(get_version_string(&raw_version))
54    }
55
56    #[tracing::instrument(level = "trace", skip(self))]
57    fn get_path(&self) -> PicoResult<Option<String>> {
58        Ok(Some(self.get_unit_info(0, PicoInfo::DRIVER_PATH)?))
59    }
60
61    #[tracing::instrument(level = "trace", skip(self))]
62    fn enumerate_units(&self) -> PicoResult<Vec<EnumerationResult>> {
63        let mut device_count = 0;
64        let mut serial_buf = "-v".into_pico_i8_string();
65        serial_buf.extend(vec![0i8; 1000]);
66        let mut serial_buf_len = serial_buf.len() as i16;
67
68        let status = PicoStatus::from(unsafe {
69            self.bindings.ps3000aEnumerateUnits(
70                &mut device_count,
71                serial_buf.as_mut_ptr(),
72                &mut serial_buf_len,
73            )
74        });
75
76        match status {
77            PicoStatus::NOT_FOUND => Ok(Vec::new()),
78            PicoStatus::OK => Ok(parse_enum_result(&serial_buf, serial_buf_len as usize)),
79            x => Err(PicoError::from_status(x, "enumerate_units")),
80        }
81    }
82
83    #[tracing::instrument(level = "trace", skip(self))]
84    fn open_unit(&self, serial: Option<&str>) -> PicoResult<i16> {
85        let serial = serial.map(|s| s.into_pico_i8_string());
86
87        let mut handle = -1i16;
88        let mut status = PicoStatus::from(unsafe {
89            match serial {
90                Some(mut serial) => self
91                    .bindings
92                    .ps3000aOpenUnit(&mut handle, serial.as_mut_ptr()),
93                None => self
94                    .bindings
95                    .ps3000aOpenUnit(&mut handle, std::ptr::null_mut()),
96            }
97        });
98
99        // Handle changing power source...
100        if matches!(
101            status,
102            PicoStatus::POWER_SUPPLY_NOT_CONNECTED | PicoStatus::USB3_0_DEVICE_NON_USB3_0_PORT
103        ) {
104            status = PicoStatus::from(unsafe {
105                self.bindings
106                    .ps3000aChangePowerSource(handle, status.into())
107            })
108        }
109
110        match status {
111            PicoStatus::OK => Ok(handle),
112            x => Err(PicoError::from_status(x, "open_unit")),
113        }
114    }
115
116    #[tracing::instrument(level = "trace", skip(self))]
117    fn ping_unit(&self, handle: i16) -> PicoResult<()> {
118        PicoStatus::from(unsafe { self.bindings.ps3000aPingUnit(handle) })
119            .to_result((), "ping_unit")
120    }
121
122    #[tracing::instrument(level = "trace", skip(self))]
123    fn maximum_value(&self, handle: i16) -> PicoResult<i16> {
124        let mut value = vec![-1i16];
125
126        PicoStatus::from(unsafe {
127            self.bindings
128                .ps3000aMaximumValue(handle, value.as_mut_ptr())
129        })
130        .to_result(value[0], "maximum_value")
131    }
132
133    #[tracing::instrument(level = "trace", skip(self))]
134    fn close(&self, handle: i16) -> PicoResult<()> {
135        PicoStatus::from(unsafe { self.bindings.ps3000aCloseUnit(handle) })
136            .to_result((), "close_unit")
137    }
138
139    #[tracing::instrument(level = "trace", skip(self))]
140    fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String> {
141        let mut string_buf: Vec<i8> = vec![0i8; 256];
142        let mut string_buf_out_len = vec![0i16];
143
144        let status = PicoStatus::from(unsafe {
145            self.bindings.ps3000aGetUnitInfo(
146                handle,
147                string_buf.as_mut_ptr(),
148                string_buf.len() as i16,
149                string_buf_out_len.as_mut_ptr(),
150                info_type.into(),
151            )
152        });
153
154        match status {
155            PicoStatus::OK => Ok(string_buf.from_pico_i8_string(string_buf_out_len[0] as usize)),
156            x => Err(PicoError::from_status(x, "get_unit_info")),
157        }
158    }
159
160    #[tracing::instrument(level = "trace", skip(self))]
161    fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>> {
162        let mut ranges = vec![0i32; 30];
163        let mut len = vec![30i32];
164
165        let status = PicoStatus::from(unsafe {
166            self.bindings.ps3000aGetChannelInformation(
167                handle,
168                0,
169                0,
170                ranges.as_mut_ptr(),
171                len.as_mut_ptr(),
172                channel.into(),
173            )
174        });
175
176        match status {
177            PicoStatus::OK => Ok(ranges[0..len[0] as usize]
178                .to_vec()
179                .iter()
180                .map(|v| PicoRange::from(*v))
181                .collect()),
182            x => Err(PicoError::from_status(x, "get_channel_ranges")),
183        }
184    }
185
186    #[tracing::instrument(level = "trace", skip(self))]
187    fn enable_channel(
188        &self,
189        handle: i16,
190        channel: PicoChannel,
191        config: &ChannelConfig,
192    ) -> PicoResult<()> {
193        PicoStatus::from(unsafe {
194            self.bindings.ps3000aSetChannel(
195                handle,
196                channel.into(),
197                1,
198                config.coupling.into(),
199                config.range.into(),
200                config.offset as f32,
201            )
202        })
203        .to_result((), "set_channel")
204    }
205
206    #[tracing::instrument(level = "trace", skip(self))]
207    fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()> {
208        PicoStatus::from(unsafe {
209            self.bindings
210                .ps3000aSetChannel(handle, channel.into(), 0, 0, 0, 0.0)
211        })
212        .to_result((), "set_channel")
213    }
214
215    #[tracing::instrument(level = "trace", skip(self, buffer))]
216    fn set_data_buffer(
217        &self,
218        handle: i16,
219        channel: PicoChannel,
220        buffer: Arc<RwLock<Pin<Vec<i16>>>>,
221        buffer_len: usize,
222    ) -> PicoResult<()> {
223        let mut buffer = buffer.write();
224
225        PicoStatus::from(unsafe {
226            self.bindings.ps3000aSetDataBuffer(
227                handle,
228                channel.into(),
229                buffer.as_mut_ptr(),
230                buffer_len as i32,
231                0,
232                DownsampleMode::NONE.into(),
233            )
234        })
235        .to_result((), "set_data_buffer")
236    }
237
238    #[tracing::instrument(level = "trace", skip(self))]
239    fn start_streaming(
240        &self,
241        handle: i16,
242        sample_config: &SampleConfig,
243    ) -> PicoResult<SampleConfig> {
244        let mut sample_interval = vec![sample_config.interval];
245
246        PicoStatus::from(unsafe {
247            self.bindings.ps3000aRunStreaming(
248                handle,
249                sample_interval.as_mut_ptr(),
250                sample_config.units.into(),
251                0,
252                0,
253                (false).into(),
254                1,
255                DownsampleMode::NONE.into(),
256                sample_config.samples_per_second(),
257            )
258        })
259        .to_result(
260            sample_config.with_interval(sample_interval[0]),
261            "start_streaming",
262        )
263    }
264
265    #[tracing::instrument(level = "trace", skip(self, callback))]
266    fn get_latest_streaming_values<'a>(
267        &self,
268        handle: i16,
269        _channels: &[PicoChannel],
270        mut callback: Box<dyn FnMut(usize, usize) + 'a>,
271    ) -> PicoResult<()> {
272        let mut simplify_args =
273            |_: i16, sample_count: i32, start_index: u32, _: i16, _: u32, _: i16, _: i16| {
274                callback(start_index as usize, sample_count as usize);
275            };
276
277        let status = PicoStatus::from(unsafe {
278            let (state, callback) = split_closure(&mut simplify_args);
279
280            self.bindings
281                .ps3000aGetStreamingLatestValues(handle, Some(callback), state)
282        });
283
284        match status {
285            PicoStatus::OK | PicoStatus::BUSY => Ok(()),
286            x => Err(PicoError::from_status(x, "get_latest_streaming_values")),
287        }
288    }
289
290    #[tracing::instrument(level = "trace", skip(self))]
291    fn stop(&self, handle: i16) -> PicoResult<()> {
292        PicoStatus::from(unsafe { self.bindings.ps3000aStop(handle) }).to_result((), "stop")
293    }
294}