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