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