pico_driver/
ps6000a.rs

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