timfish / pico-sdk

Continuous streaming from Pico Technology oscilloscopes
MIT License
11 stars 4 forks source link

Support for AWG? #2

Open MGlolenstine opened 3 years ago

MGlolenstine commented 3 years ago

Is there any support for Arbitrary Waveform Generator? All I've seen is Streaming for incoming data, and I only see 2 channels being shown (A, B) on my 2204A.

I've skimmed through most of the crates in this repo but haven't seen a way of using the AWG.

EDIT: More information about the AWG can be seen at 2.11.47 here. People seem to be calling it directly from the dll.

timfish commented 3 years ago

Yes, all that's currently implemented is continuous streaming.

First I need to merge these changes which:

Once thats merged all the SigGen/AWG features will be available like this.

After that, support for SigGen/AWG would be added by:

MGlolenstine commented 3 years ago

Oooh, I see! I did see the improvements branch but didn't really skim through it.

The bindgen will be much better than writing those wrappers by hand for sure.

timfish commented 3 years ago

We can leave this issue open and close it when the AWG is fully supported!

alon commented 2 years ago

Hi,

I've taken a stab at adding AWG, so far I'm getting errors when trying to grab the handle, but those appear to be my misunderstanding about getting a handle from driver and using streaming at the same time.

Is this something that is already being worked on? The PR mentioned above has been merged it seems.

Alon

p.s. A bit preliminary to do a PR, because it still doesn't run (or rather my test code cannot do streaming and call this at the same time since when I do driver.handle.lock().take() it causes the following streaming operation to fail with NOT_FOUND), but any guidelines based on the following would be appreciated.

commit 45a98edc8edf22a08b04cce175aa31999afe4ebc
Author: Alon Levy <alon@pobox.com>
Date:   Thu Apr 7 13:56:14 2022 +0300

    add call to ps2000aSetSigGenArbitrary via PicoDriver::set_sig_gen_arbitrary

diff --git a/common/src/enums.rs b/common/src/enums.rs
index 1250e1e..55c7678 100644
--- a/common/src/enums.rs
+++ b/common/src/enums.rs
@@ -214,3 +214,103 @@ impl From<PicoInfo> for i16 {
         num_traits::ToPrimitive::to_i16(&value).expect("Non-valid info type")
     }
 }
+
+#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
+pub enum PicoSweepType
+{
+        SweepUp = 0,
+        SweepDown = 1,
+        SweepUpDown = 2,
+        SweepDownUp = 3,
+}
+
+#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
+pub enum PicoExtraOperations
+{
+        /// <summary>
+        /// Normal signal generator operation specified by wavetype.
+        /// </summary>
+        ExtraOperationsOff = 0,
+        /// <summary>
+        /// The signal generator produces white noise and ignores all settings except pkToPk and offsetVoltage.
+        /// </summary>
+        ExtraOperationsWhiteNoise = 1,
+        /// <summary>
+        /// produces a pseudorandom random binary sequence with a bit rate
+        /// specified by the start and stop frequency.
+        /// </summary>
+        ExtraOperationsPRBS = 2, // Pseudo-Random Bit Stream
+}
+
+/// <summary>
+/// AWG index modes
+/// </summary>
+#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
+pub enum PicoIndexMode
+{
+        /// <summary>
+        /// The generator outputs the raw contents of the buffer repeatedly .
+        /// </summary>
+        IndexModeSingle = 0,
+        /// <summary>
+        /// The generator outputs the contents of the buffer from beginning to end, and then does a second pass in the reverse
+        /// direction through the buffer
+        /// </summary>
+        IndexModeDual = 1,
+        /// <summary>
+        /// This is similiar to the Dual but passes through the buffer four time inverting, and inverting reversed
+        /// </summary>
+        IndexModeQuad = 2,
+}
+
+/// <summary>
+/// The type of trigger that will be applied to the signal generator
+/// </summary>
+#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
+pub enum PicoSigGenTrigType
+{
+        /// <summary>
+        /// Trigger on rising edge
+        /// </summary>
+        SigGenTrigTypeRising = 0,
+        /// <summary>
+        /// Trigger on falling edge
+        /// </summary>
+        SigGenTripTypeFalling = 1,
+        /// <summary>
+        /// Run while trigger is high
+        /// </summary>
+        SigGenTrigTypeGateHigh = 2,
+        /// <summary>
+        /// Run while trigger is low
+        /// </summary>
+        SigGenTrigTypeGateLow = 3,
+}
+
+/// <summary>
+/// The source that will trigger the signal generator
+/// </summary>
+#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive)]
+pub enum PicoSigGenTrigSource
+{
+        /// <summary>
+        /// Run without waiting for trigger
+        /// </summary>
+        SigGenTrigSourceNone = 0,
+        /// <summary>
+        /// Use scope trigger
+        /// </summary
+        SigGenTrigSourceScopeTrig = 1,
+        /// <summary>
+        /// Use AUXIO input
+        /// </summary>
+        SigGenTrigSourceAuxIn = 2,
+        /// <summary>
+        /// Use external input
+        /// </summary>
+        SigGenTrigSourceExtIn = 3,
+        /// <summary>
+        /// Wait for software trigger
+        /// </summary>
+        SigGenTrigSourceSoftTrig = 4,
+}
\ No newline at end of file
diff --git a/driver/src/lib.rs b/driver/src/lib.rs
index 523851c..c4f3832 100644
--- a/driver/src/lib.rs
+++ b/driver/src/lib.rs
@@ -44,10 +44,8 @@
 #![allow(clippy::upper_case_acronyms)]

 use parking_lot::RwLock;
-use pico_common::{
-    ChannelConfig, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo, PicoRange, PicoResult,
-    SampleConfig,
-};
+use pico_common::{ChannelConfig, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo, PicoRange, PicoResult, SampleConfig,
+                  PicoSweepType, PicoExtraOperations, PicoIndexMode, PicoSigGenTrigType, PicoSigGenTrigSource};
 pub use resolution::Resolution;
 use std::{fmt, pin::Pin, sync::Arc};
 use thiserror::Error;
@@ -159,6 +157,28 @@ pub trait PicoDriver: fmt::Debug + Send + Sync {
             Ok(())
         }
     }
+
+     fn set_sig_gen_arbitrary(
+        &self,
+        _handle: i16,
+        _offset_voltage: i32,
+        _pk_to_pk: u32,
+        _start_delta_phase: u32,
+        _stop_delta_phase: u32,
+        _delta_phase_increment: u32,
+        _dwell_count: u32,
+        _arbitrary_waveform: &Vec<i16>,
+        _sweep_type: PicoSweepType,
+        _operation: PicoExtraOperations,
+        _index_mode: PicoIndexMode,
+        _shots: u32,
+        _sweeps: u32,
+        _trigger_type: PicoSigGenTrigType,
+        _trigger_source: PicoSigGenTrigSource,
+        _ext_in_threshold: i16,
+     ) ->  PicoResult<()> {
+         unimplemented!()
+     }
 }

 pub type ArcDriver = Arc<dyn PicoDriver>;
diff --git a/driver/src/ps2000a.rs b/driver/src/ps2000a.rs
index 252ff67..328a841 100644
--- a/driver/src/ps2000a.rs
+++ b/driver/src/ps2000a.rs
@@ -5,11 +5,9 @@ use crate::{
     EnumerationResult, PicoDriver,
 };
 use parking_lot::RwLock;
-use pico_common::{
-    ChannelConfig, DownsampleMode, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo,
-    PicoRange, PicoResult, PicoStatus, SampleConfig, ToPicoStr,
-};
-use pico_sys_dynamic::ps2000a::PS2000ALoader;
+use pico_common::{ChannelConfig, DownsampleMode, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo, PicoRange, PicoResult, PicoStatus, SampleConfig, ToPicoStr,
+                  PicoSweepType, PicoExtraOperations, PicoIndexMode, PicoSigGenTrigType, PicoSigGenTrigSource};
+use pico_sys_dynamic::ps2000a::{PS2000A_EXTRA_OPERATIONS, PS2000A_INDEX_MODE, PS2000A_SIGGEN_TRIG_SOURCE, PS2000A_SIGGEN_TRIG_TYPE, PS2000A_SWEEP_TYPE, PS2000ALoader};
 use std::{pin::Pin, sync::Arc};

 pub struct PS2000ADriver {
@@ -280,4 +278,51 @@ impl PicoDriver for PS2000ADriver {
     fn stop(&self, handle: i16) -> PicoResult<()> {
         PicoStatus::from(unsafe { self.bindings.ps2000aStop(handle) }).to_result((), "stop")
     }
+
+    #[tracing::instrument(level = "trace", skip(self))]
+    fn set_sig_gen_arbitrary(
+        &self,
+        handle: i16,
+        offset_voltage: i32,
+        pk_to_pk: u32,
+        start_delta_phase: u32,
+        stop_delta_phase: u32,
+        delta_phase_increment: u32,
+        dwell_count: u32,
+        arbitrary_waveform: &Vec<i16>,
+        sweep_type: PicoSweepType,
+        operation: PicoExtraOperations,
+        index_mode: PicoIndexMode,
+        shots: u32,
+        sweeps: u32,
+        trigger_type: PicoSigGenTrigType,
+        trigger_source: PicoSigGenTrigSource,
+        ext_in_threshold: i16,
+     ) ->  PicoResult<()> {
+        // TODO: no idea how to do this better
+        // to avoid the data being taken away, store a copy here?
+        // go read the SDK to see if the memory is caller responsibility or
+        // copied by the library.
+        let mut arbitrary_waveform = arbitrary_waveform.clone();
+         PicoStatus::from(unsafe {
+             self.bindings.ps2000aSetSigGenArbitrary(
+                 handle,
+                 offset_voltage,
+                 pk_to_pk,
+                 start_delta_phase,
+                 stop_delta_phase,
+                 delta_phase_increment,
+                 dwell_count,
+                 arbitrary_waveform.as_mut_ptr(),
+                 arbitrary_waveform.len() as i32,
+                 sweep_type as PS2000A_SWEEP_TYPE,
+                 operation as PS2000A_EXTRA_OPERATIONS,
+                 index_mode as PS2000A_INDEX_MODE,
+                 shots,
+                 sweeps,
+                 trigger_type as PS2000A_SIGGEN_TRIG_TYPE,
+                 trigger_source as PS2000A_SIGGEN_TRIG_SOURCE,
+                 ext_in_threshold)
+         }).to_result((), "set_sig_gen_arbitrary")
+     }
 }
timfish commented 2 years ago

Hi @alon,

No, I have started looking at the AWG yet. Even though I have experience with every Pico driver, I have never implemented AWG features!

My understanding of how the Pico devices work, and observing them in PicoScope is that you cannot modify the AWG settings while streaming is in progress. You need to configure before streaming starts and if you want to modify the settings later, you need to stop capture, modify AWG settings and then start streaming again.

timfish commented 2 years ago

I have access to most Pico devices so if you get stuck, link to a repository and I'll be happy to take a look!

alon commented 2 years ago

Ok. Incidentally, I tried that meanwhile with the built in v2 variant, and it worked! Bummer about the required work flow, but it should be still usable. I will need ro figure out the relation between transmit start (i.e. AWG trigger?) And the recieve (i.e. streaming). I will post my test code as PR for review.