From d1a3939884114a5acc37f6e691f9b38817ce2467 Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:07:09 +0200 Subject: [PATCH 1/7] Added ps3000a support, tested with Picoscope 3205MSO --- sds1004x_bode/awgdrivers/ps3000a.py | 181 ++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 sds1004x_bode/awgdrivers/ps3000a.py diff --git a/sds1004x_bode/awgdrivers/ps3000a.py b/sds1004x_bode/awgdrivers/ps3000a.py new file mode 100644 index 0000000..6d0498a --- /dev/null +++ b/sds1004x_bode/awgdrivers/ps3000a.py @@ -0,0 +1,181 @@ +''' +Created on Jun 7, 2020 + +@author: Mark Watson + +Driver for Picoscope 3205MSO (3000a series) +''' + +import ctypes +from picosdk.ps3000a import ps3000a as ps +import time +from picosdk.functions import assert_pico_ok + +from base_awg import BaseAWG +import constants +from exceptions import UnknownChannelError + +CHANNELS = (0, 1) +CHANNELS_ERROR = "ps3000a has only one channel." +WAVEFORM_COMMANDS = { + constants.SINE: ctypes.c_int16(0), + constants.SQUARE: ctypes.c_int16(1), + constants.PULSE: ctypes.c_int16(8), #TODO: what should this be? + constants.TRIANGLE: ctypes.c_int16(2) + } +# Output impedance of the AWG +R_IN = 600.0 +#typedef enum enPS3000AWaveType +#{ +# PS3000A_SINE, +# PS3000A_SQUARE, +# PS3000A_TRIANGLE, +# PS3000A_RAMP_UP, +# PS3000A_RAMP_DOWN, +# PS3000A_SINC, +# PS3000A_GAUSSIAN, +# PS3000A_HALF_SINE, +# PS3000A_DC_VOLTAGE, +# PS3000A_MAX_WAVE_TYPES +#} PS3000A_WAVE_TYPE; + +class ps3000a(BaseAWG): + ''' + Picoscope 3205MSO AWG driver + ''' + SHORT_NAME = "ps3000a" + + def __init__(self, *args): + self.connect() + self.initialize() + + def connect(self): + # Gives the device a handle + self.chandle = ctypes.c_int16() + + # Opens the device/s + status = {} + status["openunit"] = ps.ps3000aOpenUnit(ctypes.byref(self.chandle), None) + + try: + assert_pico_ok(status["openunit"]) + except: + + # powerstate becomes the status number of openunit + powerstate = status["openunit"] + + # If powerstate is the same as 282 then it will run this if statement + if powerstate == 282: + # Changes the power input to "PICO_POWER_SUPPLY_NOT_CONNECTED" + status["ChangePowerSource"] = ps.ps3000aChangePowerSource(self.chandle, 282) + # If the powerstate is the same as 286 then it will run this if statement + elif powerstate == 286: + # Changes the power input to "PICO_USB3_0_DEVICE_NON_USB3_0_PORT" + status["ChangePowerSource"] = ps.ps3000aChangePowerSource(self.chandle, 286) + else: + raise + + assert_pico_ok(status["ChangePowerSource"]) + print ("Connected") + + def disconnect(self): + status = {} + status["close"] = ps.ps3000aCloseUnit(self.chandle) + assert_pico_ok(status["close"]) + + def initialize(self): + self.wavetype = ctypes.c_int16(0) #sine + self.frequency = 1; #1Hz + self.amplitude = 0.001; #0.001v + self.offsetVoltage = 0; + z = 50 + self.v_out_coeff = z / (z + R_IN) + self.v_out_coeff = 1.0 + self.onoff = False + + def get_id(self): + return "ps3000a" + + def enable_output(self, channel, on): + if channel is not None and channel not in CHANNELS: + raise UnknownChannelError(CHANNELS_ERROR) +# handle = chandle +# offsetVoltage = 0 +# pkToPk = 2000000 +# waveType = ctypes.c_int16(0) = PS3000A_SINE +# startFrequency = 10000 Hz +# stopFrequency = 10000 Hz +# increment = 0 +# dwellTime = 1 +# sweepType = ctypes.c_int16(1) = PS3000A_UP +# operation = 0 +# shots = 0 +# sweeps = 0 +# triggerType = ctypes.c_int16(0) = PS3000A_SIGGEN_RISING +# triggerSource = ctypes.c_int16(0) = P3000A_SIGGEN_NONE +# extInThreshold = 1 + sweepType = ctypes.c_int32(0) + triggertype = ctypes.c_int32(0) + triggerSource = ctypes.c_int32(0) + status = {} + +# print("offset,amplitude,wavetype,frequency\n") +# print(self.offsetVoltage) +# print(self.amplitude) +# print(self.wavetype) +# print(self.frequency) + print("amplitude:" + str(self.amplitude)) + print("offset:" + str(self.offsetVoltage)) + print("v_out_coeff:" + str(self.v_out_coeff)) + amplitude = ctypes.c_uint32(int(round(self.amplitude*1000000/self.v_out_coeff))) + offsetVoltage = ctypes.c_int32(int(round(self.offsetVoltage*1000000/self.v_out_coeff))) + if on: + status["SetSigGenBuiltIn"] = ps.ps3000aSetSigGenBuiltIn(self.chandle, offsetVoltage, amplitude, self.wavetype, self.frequency, self.frequency, 0, 1, sweepType, 0, 0, 0, triggertype, triggerSource, 1) + assert_pico_ok(status["SetSigGenBuiltIn"]) + self.onoff = True + else: + status["SetSigGenBuiltIn"] = ps.ps3000aSetSigGenBuiltIn(self.chandle, ctypes.c_int32(0), ctypes.c_uint32(0), self.wavetype, self.frequency, self.frequency, 0, 1, sweepType, 0, 0, 0, triggertype, triggerSource, 1) + assert_pico_ok(status["SetSigGenBuiltIn"]) + self.onoff = False + + def set_frequency(self, channel, freq): + if channel is not None and channel not in CHANNELS: + raise UnknownChannelError(CHANNELS_ERROR) + self.frequency = ctypes.c_float(freq) + self.enable_output(channel,self.onoff) + + def set_phase(self, phase): + # if channel is not None and channel not in CHANNELS: + # raise UnknownChannelError(CHANNELS_ERROR) + pass + + def set_wave_type(self, channel, wave_type): + if channel is not None and channel not in CHANNELS: + raise UnknownChannelError(CHANNELS_ERROR) + self.wavetype = WAVEFORM_COMMANDS[wave_type] + self.enable_output(channel,self.onoff) + + def set_amplitue(self, channel, amplitude): #BSWV specifies this in pk-pk + if channel is not None and channel not in CHANNELS: + raise UnknownChannelError(CHANNELS_ERROR) + self.amplitude = amplitude + self.enable_output(channel,self.onoff) + + def set_offset(self, channel, offset): + if channel is not None and channel not in CHANNELS: + raise UnknownChannelError(CHANNELS_ERROR) + self.offsetVoltage = offset + self.enable_output(channel,self.onoff) + + def set_load_impedance(self, channel, z): + if channel is not None and channel not in CHANNELS: + raise UnknownChannelError(CHANNELS_ERROR) + if z == constants.HI_Z: + v_out_coeff = 1 + else: + v_out_coeff = z / (z + R_IN) + print ("Z:" + str(z)) + self.v_out_coeff = v_out_coeff + self.enable_output(channel,self.onoff) + pass + From 62e290da7b3a4263864744011591f06416b4cb11 Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:07:56 +0200 Subject: [PATCH 2/7] Added ps3000a support, tested with Picoscope 3205MSO --- sds1004x_bode/awg_factory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sds1004x_bode/awg_factory.py b/sds1004x_bode/awg_factory.py index f2c1008..f31f601 100644 --- a/sds1004x_bode/awg_factory.py +++ b/sds1004x_bode/awg_factory.py @@ -4,12 +4,14 @@ @author: 4x1md Update of original file on Nov. 17 2018 by Dundarave to add entries needed for FY6600 support. +Update of original file on Jun. 7 2020 by Mark Watson to add entries needed for ps3000a support. ''' from awgdrivers.dummy_awg import DummyAWG from awgdrivers.jds6600 import JDS6600 from awgdrivers.bk4075 import BK4075 from awgdrivers.fy6600 import FY6600 +from awgdrivers.ps3000a import ps3000a class AwgFactory(object): @@ -28,4 +30,5 @@ def get_class_by_name(self, short_name): awg_factory.add_awg(JDS6600.SHORT_NAME, JDS6600) awg_factory.add_awg(BK4075.SHORT_NAME, BK4075) awg_factory.add_awg(FY6600.SHORT_NAME, FY6600) +awg_factory.add_awg(ps3000a.SHORT_NAME, ps3000a) From 414bbd028526b5f71ceb20c75758351b1727f2e8 Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:12:01 +0200 Subject: [PATCH 3/7] Updated readme to add picoscope --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c182c87..6197a52 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Current version of the program was tested under Linux only. Later, I'll test it ## Supported AWG Models -As of January 30, 2019 the program supports the following models: +As of June 7, 2020 the program supports the following models: * **BK Precision BK4075** One channel 25MHz AWG. Requires a RS-232 serial port for the connection to a PC. It is compatible with the SCPI 1992.0 standard. @@ -20,6 +20,8 @@ As of January 30, 2019 the program supports the following models: * **Feeltech FY6600** Another Chinese generator which is widely sold on eBay and AliExpress. It also connects to the PC as a USB serial port. +* **Picoscope 3000a series** It connects to the PC usb support and their is an API that needs downloading from Picotech. Tested with Picoscope 3205 MSO only. + ## Program Structure TBD From 04c3506f1351033266108ecf5cbae81cbd069922 Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:15:52 +0200 Subject: [PATCH 4/7] Improve the picoscope notes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6197a52..19cc359 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ As of June 7, 2020 the program supports the following models: * **Feeltech FY6600** Another Chinese generator which is widely sold on eBay and AliExpress. It also connects to the PC as a USB serial port. -* **Picoscope 3000a series** It connects to the PC usb support and their is an API that needs downloading from Picotech. Tested with Picoscope 3205 MSO only. +* **Picoscope 3000a series** A UK made USB scope that includes a basic function generator/AWG in some models. There is an API that needs downloading from Picotech, also their python wrappers are needed. Tested with the Picoscope 3205 MSO only but should work with other models. ## Program Structure TBD From 8833dd9569340f0b7e7a456d54176ba375ce6f88 Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:17:27 +0200 Subject: [PATCH 5/7] Improve the picoscope notes --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 19cc359..27e12a3 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,10 @@ VXI-11 DESTROY_LINK, SCPI command: None ## Changelog +### 2020-06-07 + +* Support for the Picoscope 3205 MSO + ### 2019-01-30 * The program supports Feeltech FY6600 AWG. From 631e2912ad37d4f27fa602f4a2260fe7fad7242d Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:18:23 +0200 Subject: [PATCH 6/7] Improve the picoscope notes --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 27e12a3..78ec154 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,8 @@ I'd like to add here more AWGs but it's impossible to have them all at the home * **Nick Bryant (Dundarave on EEVblog Forum)** - Driver for Feeltech FY6600 AWG. +* **Mark Watson** - Driver for Picoscope 3205 MSO. + ## Links 1. [Siglent SDS1104X-E and SDS1204X-E: Bode plot with non-Siglent AWG](http://www.eevblog.com/forum/testgear/siglent-sds1104x-e-and-sds1204x-e-bode-plot-with-non-siglent-awg/) on EEVblog Forum. From 45e0c05e7cca5eada9ca7d5787ff7b241c0f4835 Mon Sep 17 00:00:00 2001 From: scrameta Date: Sun, 7 Jun 2020 21:21:31 +0200 Subject: [PATCH 7/7] Tidied up some comments from when writing it and removed some debug prints --- sds1004x_bode/awgdrivers/ps3000a.py | 40 ----------------------------- 1 file changed, 40 deletions(-) diff --git a/sds1004x_bode/awgdrivers/ps3000a.py b/sds1004x_bode/awgdrivers/ps3000a.py index 6d0498a..1ee09f5 100644 --- a/sds1004x_bode/awgdrivers/ps3000a.py +++ b/sds1004x_bode/awgdrivers/ps3000a.py @@ -25,19 +25,6 @@ } # Output impedance of the AWG R_IN = 600.0 -#typedef enum enPS3000AWaveType -#{ -# PS3000A_SINE, -# PS3000A_SQUARE, -# PS3000A_TRIANGLE, -# PS3000A_RAMP_UP, -# PS3000A_RAMP_DOWN, -# PS3000A_SINC, -# PS3000A_GAUSSIAN, -# PS3000A_HALF_SINE, -# PS3000A_DC_VOLTAGE, -# PS3000A_MAX_WAVE_TYPES -#} PS3000A_WAVE_TYPE; class ps3000a(BaseAWG): ''' @@ -76,7 +63,6 @@ def connect(self): raise assert_pico_ok(status["ChangePowerSource"]) - print ("Connected") def disconnect(self): status = {} @@ -99,34 +85,11 @@ def get_id(self): def enable_output(self, channel, on): if channel is not None and channel not in CHANNELS: raise UnknownChannelError(CHANNELS_ERROR) -# handle = chandle -# offsetVoltage = 0 -# pkToPk = 2000000 -# waveType = ctypes.c_int16(0) = PS3000A_SINE -# startFrequency = 10000 Hz -# stopFrequency = 10000 Hz -# increment = 0 -# dwellTime = 1 -# sweepType = ctypes.c_int16(1) = PS3000A_UP -# operation = 0 -# shots = 0 -# sweeps = 0 -# triggerType = ctypes.c_int16(0) = PS3000A_SIGGEN_RISING -# triggerSource = ctypes.c_int16(0) = P3000A_SIGGEN_NONE -# extInThreshold = 1 sweepType = ctypes.c_int32(0) triggertype = ctypes.c_int32(0) triggerSource = ctypes.c_int32(0) status = {} -# print("offset,amplitude,wavetype,frequency\n") -# print(self.offsetVoltage) -# print(self.amplitude) -# print(self.wavetype) -# print(self.frequency) - print("amplitude:" + str(self.amplitude)) - print("offset:" + str(self.offsetVoltage)) - print("v_out_coeff:" + str(self.v_out_coeff)) amplitude = ctypes.c_uint32(int(round(self.amplitude*1000000/self.v_out_coeff))) offsetVoltage = ctypes.c_int32(int(round(self.offsetVoltage*1000000/self.v_out_coeff))) if on: @@ -145,8 +108,6 @@ def set_frequency(self, channel, freq): self.enable_output(channel,self.onoff) def set_phase(self, phase): - # if channel is not None and channel not in CHANNELS: - # raise UnknownChannelError(CHANNELS_ERROR) pass def set_wave_type(self, channel, wave_type): @@ -174,7 +135,6 @@ def set_load_impedance(self, channel, z): v_out_coeff = 1 else: v_out_coeff = z / (z + R_IN) - print ("Z:" + str(z)) self.v_out_coeff = v_out_coeff self.enable_output(channel,self.onoff) pass