pico_driver/
ps6000.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::ps6000::PS6000Loader;
13use std::{pin::Pin, sync::Arc};
14
15pub struct PS6000Driver {
16    _dependencies: LoadedDependencies,
17    bindings: PS6000Loader,
18}
19
20impl std::fmt::Debug for PS6000Driver {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.debug_struct("PS6000Driver").finish()
23    }
24}
25
26impl PS6000Driver {
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 { PS6000Loader::new(path)? };
33        // Disables the splash screen on Windows
34        unsafe { bindings.ps6000ApplyFix(0x1ced9168, 0x11e6) };
35        Ok(PS6000Driver {
36            bindings,
37            _dependencies: dependencies,
38        })
39    }
40}
41
42impl PicoDriver for PS6000Driver {
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.ps6000EnumerateUnits(
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 status = PicoStatus::from(unsafe {
89            match serial {
90                Some(mut serial) => self
91                    .bindings
92                    .ps6000OpenUnit(&mut handle, serial.as_mut_ptr()),
93                None => self
94                    .bindings
95                    .ps6000OpenUnit(&mut handle, std::ptr::null_mut()),
96            }
97        });
98
99        match status {
100            PicoStatus::OK => Ok(handle),
101            x => Err(PicoError::from_status(x, "open_unit")),
102        }
103    }
104
105    #[tracing::instrument(level = "trace", skip(self))]
106    fn ping_unit(&self, handle: i16) -> PicoResult<()> {
107        PicoStatus::from(unsafe { self.bindings.ps6000PingUnit(handle) }).to_result((), "ping_unit")
108    }
109
110    #[tracing::instrument(level = "trace", skip(self))]
111    fn maximum_value(&self, handle: i16) -> PicoResult<i16> {
112        Ok(32_512)
113    }
114
115    #[tracing::instrument(level = "trace", skip(self))]
116    fn close(&self, handle: i16) -> PicoResult<()> {
117        PicoStatus::from(unsafe { self.bindings.ps6000CloseUnit(handle) })
118            .to_result((), "close_unit")
119    }
120
121    #[tracing::instrument(level = "trace", skip(self))]
122    fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String> {
123        let mut string_buf: Vec<i8> = vec![0i8; 256];
124        let mut string_buf_out_len = vec![0i16];
125
126        let status = PicoStatus::from(unsafe {
127            self.bindings.ps6000GetUnitInfo(
128                handle,
129                string_buf.as_mut_ptr(),
130                string_buf.len() as i16,
131                string_buf_out_len.as_mut_ptr(),
132                info_type.into(),
133            )
134        });
135
136        match status {
137            PicoStatus::OK => Ok(string_buf.from_pico_i8_string(string_buf_out_len[0] as usize)),
138            x => Err(PicoError::from_status(x, "get_unit_info")),
139        }
140    }
141
142    #[tracing::instrument(level = "trace", skip(self))]
143    fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>> {
144        // The 6000 doesn't support querying of supported channel ranges but
145        // fortunately they (mostly) use the same 50mV to 20v ranges for all devices
146        Ok((2..=10).map(|r| r.into()).collect())
147    }
148
149    #[tracing::instrument(level = "trace", skip(self))]
150    fn enable_channel(
151        &self,
152        handle: i16,
153        channel: PicoChannel,
154        config: &ChannelConfig,
155    ) -> PicoResult<()> {
156        PicoStatus::from(unsafe {
157            self.bindings.ps6000SetChannel(
158                handle,
159                channel.into(),
160                1,
161                config.coupling.into(),
162                config.range.into(),
163                config.offset as f32,
164                0,
165            )
166        })
167        .to_result((), "set_channel")
168    }
169
170    #[tracing::instrument(level = "trace", skip(self))]
171    fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()> {
172        PicoStatus::from(unsafe {
173            self.bindings
174                .ps6000SetChannel(handle, channel.into(), 0, 0, 0, 0.0, 0)
175        })
176        .to_result((), "set_channel")
177    }
178
179    #[tracing::instrument(level = "trace", skip(self, buffer))]
180    fn set_data_buffer(
181        &self,
182        handle: i16,
183        channel: PicoChannel,
184        buffer: Arc<RwLock<Pin<Vec<i16>>>>,
185        buffer_len: usize,
186    ) -> PicoResult<()> {
187        let mut buffer = buffer.write();
188
189        PicoStatus::from(unsafe {
190            self.bindings.ps6000SetDataBuffer(
191                handle,
192                channel.into(),
193                buffer.as_mut_ptr(),
194                buffer_len as u32,
195                DownsampleMode::NONE.into(),
196            )
197        })
198        .to_result((), "set_data_buffer")
199    }
200
201    #[tracing::instrument(level = "trace", skip(self))]
202    fn start_streaming(
203        &self,
204        handle: i16,
205        sample_config: &SampleConfig,
206    ) -> PicoResult<SampleConfig> {
207        let mut sample_interval = vec![sample_config.interval];
208
209        PicoStatus::from(unsafe {
210            self.bindings.ps6000RunStreaming(
211                handle,
212                sample_interval.as_mut_ptr(),
213                sample_config.units.into(),
214                0,
215                0,
216                (false).into(),
217                1,
218                DownsampleMode::NONE.into(),
219                sample_config.samples_per_second(),
220            )
221        })
222        .to_result(
223            sample_config.with_interval(sample_interval[0]),
224            "start_streaming",
225        )
226    }
227
228    #[tracing::instrument(level = "trace", skip(self, callback))]
229    fn get_latest_streaming_values<'a>(
230        &self,
231        handle: i16,
232        _channels: &[PicoChannel],
233        mut callback: Box<dyn FnMut(usize, usize) + 'a>,
234    ) -> PicoResult<()> {
235        let mut simplify_args =
236            |_: i16, sample_count: u32, start_index: u32, _: i16, _: u32, _: i16, _: i16| {
237                callback(start_index as usize, sample_count as usize);
238            };
239
240        let status = PicoStatus::from(unsafe {
241            let (state, callback) = split_closure(&mut simplify_args);
242
243            self.bindings
244                .ps6000GetStreamingLatestValues(handle, Some(callback), state)
245        });
246
247        match status {
248            PicoStatus::OK | PicoStatus::BUSY => Ok(()),
249            x => Err(PicoError::from_status(x, "get_latest_streaming_values")),
250        }
251    }
252
253    #[tracing::instrument(level = "trace", skip(self))]
254    fn stop(&self, handle: i16) -> PicoResult<()> {
255        PicoStatus::from(unsafe { self.bindings.ps6000Stop(handle) }).to_result((), "stop")
256    }
257}