pico_driver/
ps5000a.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::ps5000a::{enPicoDeviceResolution_PICO_DR_14BIT, PS5000ALoader};
13use std::{pin::Pin, sync::Arc};
14
15pub struct PS5000ADriver {
16    _dependencies: LoadedDependencies,
17    bindings: PS5000ALoader,
18}
19
20impl std::fmt::Debug for PS5000ADriver {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.debug_struct("PS5000ADriver").finish()
23    }
24}
25
26impl PS5000ADriver {
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 { PS5000ALoader::new(path)? };
33        // Disables the splash screen on Windows
34        unsafe { bindings.ps5000aApplyFix(0x1ced9168, 0x11e6) };
35        Ok(PS5000ADriver {
36            bindings,
37            _dependencies: dependencies,
38        })
39    }
40}
41
42impl PicoDriver for PS5000ADriver {
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.ps5000aEnumerateUnits(
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.bindings.ps5000aOpenUnit(
91                    &mut handle,
92                    serial.as_mut_ptr(),
93                    enPicoDeviceResolution_PICO_DR_14BIT,
94                ),
95                None => self.bindings.ps5000aOpenUnit(
96                    &mut handle,
97                    std::ptr::null_mut(),
98                    enPicoDeviceResolution_PICO_DR_14BIT,
99                ),
100            }
101        });
102
103        // Handle changing power source...
104        if matches!(
105            status,
106            PicoStatus::POWER_SUPPLY_NOT_CONNECTED | PicoStatus::USB3_0_DEVICE_NON_USB3_0_PORT
107        ) {
108            status = PicoStatus::from(unsafe {
109                self.bindings
110                    .ps5000aChangePowerSource(handle, status.into())
111            })
112        }
113
114        match status {
115            PicoStatus::OK => Ok(handle),
116            x => Err(PicoError::from_status(x, "open_unit")),
117        }
118    }
119
120    #[tracing::instrument(level = "trace", skip(self))]
121    fn ping_unit(&self, handle: i16) -> PicoResult<()> {
122        PicoStatus::from(unsafe { self.bindings.ps5000aPingUnit(handle) })
123            .to_result((), "ping_unit")
124    }
125
126    #[tracing::instrument(level = "trace", skip(self))]
127    fn maximum_value(&self, handle: i16) -> PicoResult<i16> {
128        let mut value = 0;
129
130        PicoStatus::from(unsafe { self.bindings.ps5000aMaximumValue(handle, &mut value) })
131            .to_result(value, "maximum_value")
132    }
133
134    #[tracing::instrument(level = "trace", skip(self))]
135    fn close(&self, handle: i16) -> PicoResult<()> {
136        PicoStatus::from(unsafe { self.bindings.ps5000aCloseUnit(handle) })
137            .to_result((), "close_unit")
138    }
139
140    #[tracing::instrument(level = "trace", skip(self))]
141    fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String> {
142        let mut string_buf: Vec<i8> = vec![0i8; 256];
143        let mut string_buf_out_len = 0i16;
144
145        let status = PicoStatus::from(unsafe {
146            self.bindings.ps5000aGetUnitInfo(
147                handle,
148                string_buf.as_mut_ptr(),
149                string_buf.len() as i16,
150                &mut string_buf_out_len,
151                info_type.into(),
152            )
153        });
154
155        match status {
156            PicoStatus::OK => Ok(string_buf.from_pico_i8_string(string_buf_out_len as usize)),
157            x => Err(PicoError::from_status(x, "get_unit_info")),
158        }
159    }
160
161    #[tracing::instrument(level = "trace", skip(self))]
162    fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>> {
163        let mut ranges = vec![0i32; 30];
164        let mut len = 30;
165
166        let status = PicoStatus::from(unsafe {
167            self.bindings.ps5000aGetChannelInformation(
168                handle,
169                0,
170                0,
171                ranges.as_mut_ptr(),
172                &mut len,
173                channel.into(),
174            )
175        });
176
177        match status {
178            PicoStatus::OK => Ok(ranges[0..len as usize]
179                .to_vec()
180                .iter()
181                .map(|v| PicoRange::from(*v))
182                .collect()),
183            x => Err(PicoError::from_status(x, "get_channel_ranges")),
184        }
185    }
186
187    #[tracing::instrument(level = "trace", skip(self))]
188    fn enable_channel(
189        &self,
190        handle: i16,
191        channel: PicoChannel,
192        config: &ChannelConfig,
193    ) -> PicoResult<()> {
194        PicoStatus::from(unsafe {
195            self.bindings.ps5000aSetChannel(
196                handle,
197                channel.into(),
198                1,
199                config.coupling.into(),
200                config.range.into(),
201                config.offset as f32,
202            )
203        })
204        .to_result((), "set_channel")
205    }
206
207    #[tracing::instrument(level = "trace", skip(self))]
208    fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()> {
209        PicoStatus::from(unsafe {
210            self.bindings
211                .ps5000aSetChannel(handle, channel.into(), 0, 0, 0, 0.0)
212        })
213        .to_result((), "set_channel")
214    }
215
216    #[tracing::instrument(level = "trace", skip(self, buffer))]
217    fn set_data_buffer(
218        &self,
219        handle: i16,
220        channel: PicoChannel,
221        buffer: Arc<RwLock<Pin<Vec<i16>>>>,
222        buffer_len: usize,
223    ) -> PicoResult<()> {
224        let mut buffer = buffer.write();
225
226        PicoStatus::from(unsafe {
227            self.bindings.ps5000aSetDataBuffer(
228                handle,
229                channel.into(),
230                buffer.as_mut_ptr(),
231                buffer_len as i32,
232                0,
233                DownsampleMode::NONE.into(),
234            )
235        })
236        .to_result((), "set_data_buffer")
237    }
238
239    #[tracing::instrument(level = "trace", skip(self))]
240    fn start_streaming(
241        &self,
242        handle: i16,
243        sample_config: &SampleConfig,
244    ) -> PicoResult<SampleConfig> {
245        let mut sample_interval = sample_config.interval;
246
247        PicoStatus::from(unsafe {
248            self.bindings.ps5000aRunStreaming(
249                handle,
250                &mut sample_interval,
251                sample_config.units.into(),
252                0,
253                0,
254                (false).into(),
255                1,
256                DownsampleMode::NONE.into(),
257                sample_config.samples_per_second(),
258            )
259        })
260        .to_result(
261            sample_config.with_interval(sample_interval),
262            "start_streaming",
263        )
264    }
265
266    #[tracing::instrument(level = "trace", skip(self, callback))]
267    fn get_latest_streaming_values<'a>(
268        &self,
269        handle: i16,
270        _channels: &[PicoChannel],
271        mut callback: Box<dyn FnMut(usize, usize) + 'a>,
272    ) -> PicoResult<()> {
273        let mut simplify_args =
274            |_: i16, sample_count: i32, start_index: u32, _: i16, _: u32, _: i16, _: i16| {
275                callback(start_index as usize, sample_count as usize);
276            };
277
278        let status = PicoStatus::from(unsafe {
279            let (state, callback) = split_closure(&mut simplify_args);
280
281            self.bindings
282                .ps5000aGetStreamingLatestValues(handle, Some(callback), state)
283        });
284
285        match status {
286            PicoStatus::OK | PicoStatus::BUSY => Ok(()),
287            x => Err(PicoError::from_status(x, "get_latest_streaming_values")),
288        }
289    }
290
291    #[tracing::instrument(level = "trace", skip(self))]
292    fn stop(&self, handle: i16) -> PicoResult<()> {
293        PicoStatus::from(unsafe { self.bindings.ps5000aStop(handle) }).to_result((), "stop")
294    }
295}