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 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 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 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}