PicoScope 7 Software
Available on Windows, Mac and Linux
Code: Select all
#Pico4000 scope setup and data transfer
#Patrick Carle, August 2012
#Adapted from http://www.picotech.com/support/topic4926.html
#Example code from command line (assuming file is named ps4000.py):
#C:\> python
#>>> import ps4000
#>>> ps4000.runScope()
import datetime
from ctypes import * # shouldn't import *, but there are so many items to use...
import time
from numpy import *
#Picoscope library
LIBNAME = 'ps4000.dll'
#Picoscope channel max/min ADC count value
PS4000_MAX_VALUE = 32764
PS4000_MIN_VALUE = -32764
PS4000_EXT_MAX_VALUE = 32767
PS4000_EXT_MIN_VALUE = -32767
PS4000_EXT_RANGE_VOLTS = 20
VOLTS2ADC_EXT = PS4000_EXT_MAX_VALUE / PS4000_EXT_RANGE_VOLTS
#Sampling frequencies
SAMPLE_250MHZ = 0
SAMPLE_125MHZ = 1
SAMPLE_62MHZ = 2 #actually 62.5MHz
SAMPLE_31MHZ = 3 #actually 31.25MHz
#Channel range codes
RANGE_20MV = 1 # 20 mV
RANGE_50MV = 2 # 50 mV
RANGE_100MV = 3 # 100 mV
RANGE_200MV = 4 # 200 mV
RANGE_500MV = 5 # 500 mV
RANGE_1V = 6 # 1 V
RANGE_2V = 7 # 2 V
RANGE_5V = 8 # 5 V
RANGE_10V = 9 # 10 V
RANGE_20V = 10 # 20 V
VOLTAGE_RANGES = { 1:.02, 2:.05, 3:.1, 4:.2, 5:.5, 6:1, 7:2, 8:5, 9:10, 10:20 }
#Channels
CHANNEL_A = 0
CHANNEL_B = 1
CHANNEL_EXT = 4
#Coupling
DC_COUPLING = 1
AC_COUPLING = 0
#Enabled/disabled
ENABLED = 1
DISABLED = 0
#Trigger direction
DIR_FALLING = 1
DIR_RISING = 0
#Downsample mode
RATIO_MODE_NONE = 0
RATIO_MODE_AGGREGATE = 1
#=============================================================================================
#=============================================================================================
class PS4000(object):
#Initialize
def __init__(self):
self.lib = windll.LoadLibrary(LIBNAME)
self.handle = None
self.serial = None
self.chRange = [ None, None ]
self.coupling = [ None, None ]
self.chEnabled = [ None, None ]
self.chName = [ None, None ]
self.timeInterval = None
#Open scope by serial number
def openScope(self):
handlePointer = c_short()
serialNullTermStr = c_char_p( bytes(self.serial,'ascii') )
message = self.lib.ps4000OpenUnitEx( byref(handlePointer), serialNullTermStr )
self.handle = handlePointer.value
if message != 0: print('Error openScope:', message)
#Setup channel
def setChannel( self ):
message = self.lib.ps4000SetChannel( self.handle, CHANNEL_A, self.chEnabled[CHANNEL_A], self.coupling[CHANNEL_A], self.chRange[CHANNEL_A] )
if message != 0: print('Error setChannel A:', message)
message = self.lib.ps4000SetChannel( self.handle, CHANNEL_B, self.chEnabled[CHANNEL_B], self.coupling[CHANNEL_B], self.chRange[CHANNEL_B] )
if message != 0: print('Error setChannel B:', message)
#Setup trigger
def setTrigger( self, threshold, delay, autoTrigger_ms ):
message = self.lib.ps4000SetSimpleTrigger( self.handle, ENABLED, CHANNEL_EXT, threshold, DIR_RISING, delay, autoTrigger_ms )
if message != 0: print('Error setTrigger:', message)
#Setup timebase
def getTimebase( self, timebase, numSamples, oversample, segmentIndex ):
timeInterval_ns = c_long()
maxSamples = c_long()
message = self.lib.ps4000GetTimebase( self.handle, timebase, numSamples, byref(timeInterval_ns), oversample, byref(maxSamples), segmentIndex )
if message != 0: print('Error getTimebase:', message)
else: self.timeInterval = timeInterval_ns.value / 1e9
#Run block (record data on scope)
def runBlock( self, numPreTrigSamples, numPostTrigSamples, timebase, oversample, segmentIndex ):
timeIndisposedMs = c_long()
pParameter = c_void_p()
message = self.lib.ps4000RunBlock( self.handle, numPreTrigSamples, numPostTrigSamples, timebase, oversample, byref(timeIndisposedMs), segmentIndex, None, byref(pParameter) )
if message != 0: print('Error runBlock:', message)
#Determine if scope is ready to transfer data
def isReady( self ):
ready = c_short()
message = self.lib.ps4000IsReady( self.handle, byref(ready) )
if ready.value: print('Triggered')
if message != 0: print('Error isReady:', message)
return ready.value
#Transfers data to computer
def getValues( self, channel, numSamples, startIndex, downSampleRatio, downSampleRatioMode, segmentIndex ):
#Set channel from which to get data
dataPointer = ( c_short * numSamples )()
message = self.lib.ps4000SetDataBuffer( self.handle, channel, byref(dataPointer), numSamples )
if message != 0: print('Error setDataBuffer:', message)
#Get data
numSamplesReturned = c_long()
numSamplesReturned.value = numSamples
overflow = c_short()
message = self.lib.ps4000GetValues( self.handle, startIndex, byref(numSamplesReturned), downSampleRatio, downSampleRatioMode, segmentIndex, byref(overflow) )
print('Scope', self.serial, 'channel', channel,':',numSamplesReturned.value, 'samples returned')
if message != 0:
print('Error getValues:', message)
return 0
#ADC count to voltage conversion factor
newData = asarray(list(dataPointer))
a2v = VOLTAGE_RANGES[self.chRange[channel]] / PS4000_MAX_VALUE
newData = newData * a2v
return newData
#Close scope
def closeScope(self):
message = self.lib.ps4000CloseUnit(self.handle)
self.handle = None
if message != 0: print('Error closeScope:', message)
else: print('Scope', self.serial, 'closed')
#==================================================================================
#==================================================================================
def runScope(shotnum=654321,filedir='C:/'):
#Sets up scope, waits for trigger, collects data and saves to file
#Inputs:
# shotnum Integer identifying the shot number (ie. to give the data an ID)
# filtedir Directory in which to save the file
#Sampling parameters
timebase = SAMPLE_125MHZ
numSamples = int(10e6) #number of samples to record from each channel
#Create scope objects
scopes = [ PS4000() ]
# scopes = [ PS4000(), PS4000(), PS4000() ]
#Serial numbers
scopes[0].serial = 'AT630/060'
# scopes[1].serial = 'AT630/090'
# scopes[2].serial = 'XXXXX/XXX'
#Channel names
scopes[0].chName = [ 'ch1a', 'ch1b' ]
# scopes[1].chName = [ 'ch2a', 'ch2b' ]
# scopes[2].chName = [ 'ch3a', 'ch3b' ]
#Channel coupling
scopes[0].coupling = [ AC_COUPLING, AC_COUPLING ]
# scopes[1].coupling = [ AC_COUPLING, AC_COUPLING ]
# scopes[2].coupling = [ AC_COUPLING, AC_COUPLING ]
#Channel enabled
scopes[0].chEnabled = [ ENABLED, ENABLED ]
# scopes[1].chEnabled = [ DISABLED, DISABLED ]
# scopes[2].chEnabled = [ DISABLED, DISABLED ]
#Channel range
scopes[0].chRange = [ RANGE_200MV, RANGE_200MV ]
# scopes[1].chRange = [ RANGE_200MV, RANGE_200MV ]
# scopes[2].chRange = [ RANGE_200MV, RANGE_200MV ]
#Other parameters
#Sampling
oversample = 1 #1 means no oversampling
segmentIndex = 0
#Triggering
threshold_volts = 10 #[V]
threshold_adc = int( threshold_volts * VOLTS2ADC_EXT ) #trigger threshold in ADC counts
delay = 0 #[s]
autoTrigger_ms = 0 #[ms] time before automatic trigger... 0 means wait indefinitely for trigger
maxTriggerSeconds = 60 #[s] maximum number of seconds to wait for trigger
#Data collection
numPreTrigSamples = 0
numPostTrigSamples = numSamples
startIndex = 1
downSampleRatio = 1
downSampleRatioMode = RATIO_MODE_NONE
#Setup scopes
for scopeNum in range(0,len(scopes)):
#Open scope
scopes[scopeNum].openScope()
#If scope exists, set it up
if scopes[scopeNum].handle != 0:
print('Configuring scope', scopes[scopeNum].serial, '...'),
#Channels
scopes[scopeNum].setChannel()
scopes[scopeNum].getTimebase( timebase, numSamples, oversample, segmentIndex )
#Trigger
scopes[scopeNum].setTrigger( threshold_adc, delay, autoTrigger_ms )
#Activate scope to save data to memory
scopes[scopeNum].runBlock( numPreTrigSamples, numPostTrigSamples, timebase, oversample, segmentIndex )
#Wait for trigger
print('Waiting for trigger ...')
secondsElapsed = 0
while secondsElapsed < maxTriggerSeconds and not scopes[0].isReady():
time.sleep(1)
secondsElapsed += 1
#Trigger time out
if secondsElapsed == maxTriggerSeconds:
print('Trigger timed out')
closeAllScopes(scopes)
return
#Download data from scope
data = []
dataNames = []
for scopeNum in range(len(scopes)):
#If scope exists and channel enabled, download data
#Channel A
if scopes[scopeNum].handle != 0 and scopes[scopeNum].chEnabled[CHANNEL_A]:
newData = scopes[scopeNum].getValues( CHANNEL_A, numSamples, startIndex, downSampleRatio, downSampleRatioMode, segmentIndex )
data.append(newData)
dataNames = dataNames + [scopes[scopeNum].chName[CHANNEL_A]]
#Channel B
if scopes[scopeNum].handle != 0 and scopes[scopeNum].chEnabled[CHANNEL_B]:
newData = scopes[scopeNum].getValues( CHANNEL_B, numSamples, startIndex, downSampleRatio, downSampleRatioMode, segmentIndex )
data.append(newData)
dataNames = dataNames + [scopes[scopeNum].chName[CHANNEL_B]]
#Exit if no data
if len(data)==0:
closeAllScopes(scopes)
return
#Write data to file
#Filepath
filepath = filedir + 'pol' + str(shotnum).zfill(6) + '.csv'
print('Writing',filepath,'...')
#Open file
f = open(filepath, 'w', newline='') # open for 'w'riting
#Date and time
f.write('Date and time:\n')
f.write('%s\n' % datetime.datetime.now())
#Shot number
f.write('Shot number:\n')
f.write('%06d\n' % shotnum)
#Sampling time interval
f.write('Sampling time interval in seconds (dt):\n')
f.write('%s\n' % scopes[0].timeInterval)
#Initial sample time
f.write('Initial sample time in seconds (t0):\n')
f.write('0\n')
#Column names
for name in dataNames[:-1]:
f.write(name+',')
f.write(dataNames[-1]+'\n')
#Data columns
for row in range(len(data[0])):
for col in range(len(data[:-1])):
f.write('%.5f,' % data[col][row])
f.write('%.5f\n' % data[-1][row])
#Close file
f.close() # close the file
print('File written')
#Close scopes
closeAllScopes(scopes)
def closeAllScopes(scopes):
#Close all scopes
for scopeNum in range(0,len(scopes)):
scopes[scopeNum].closeScope()
#Error Codes
# 00 PICO_OK. The PicoScope 4000 is functioning correctly.
# 01 PICO_MAX_UNITS_OPENED. An attempt has been made to open more than PS4000_MAX_UNITS. (Reserved)
# 02 PICO_MEMORY_FAIL. Not enough memory could be allocated on the host machine.
# 03 PICO_NOT_FOUND. No PicoScope 4000 could be found.
# 04 PICO_FW_FAIL. Unable to download firmware.
# 05 PICO_OPEN_OPERATION_IN_PROGRESS. The driver is busy opening a device.
# 06 PICO_OPERATION_FAILED. An unspecified error occurred.
# 07 PICO_NOT_RESPONDING. The PicoScope 4000 is not responding to commands from the PC.
# 08 PICO_CONFIG_FAIL. The configuration information in the PicoScope 4000 has become corrupt or is missing.
# 09 PICO_KERNEL_DRIVER_TOO_OLD. The picopp.sys file is too old to be used with the device driver.
# 0A PICO_EEPROM_CORRUPT. The EEPROM has become corrupt, so the device will use a default setting.
# 0B PICO_OS_NOT_SUPPORTED. The operating system on the PC is not supported by this driver.
# 0C PICO_INVALID_HANDLE. There is no device with the handle value passed.
# 0D PICO_INVALID_PARAMETER. A parameter value is not valid.
# 0E PICO_INVALID_TIMEBASE. The time base is not supported or is invalid.
# 0F PICO_INVALID_VOLTAGE_RANGE. The voltage range is not supported or is invalid.
# 10 PICO_INVALID_CHANNEL. The channel number is not valid on this device or no channels have been set.
# 11 PICO_INVALID_TRIGGER_CHANNEL. The channel set for a trigger is not available on this device.
# 12 PICO_INVALID_CONDITION_CHANNEL. The channel set for a condition is not available on this device.
# 13 PICO_NO_SIGNAL_GENERATOR. The device does not have a signal generator.
# 14 PICO_STREAMING_FAILED. Streaming has failed to start or has stopped without user request.
# 15 PICO_BLOCK_MODE_FAILED. Block failed to start - a parameter may have been set wrongly.
# 16 PICO_NULL_PARAMETER. A parameter that was required is NULL.
# 17 PICO_ETS_MODE_SET. The function call failed because ETS mode is being used.
# 18 PICO_DATA_NOT_AVAILABLE. No data is available from a run block call.
# 19 PICO_STRING_BUFFER_TOO_SMALL. The buffer passed was too small for the string to be returned.
# 1A PICO_ETS_NOT_SUPPORTED. ETS is not supported on this device variant.
# 1B PICO_AUTO_TRIGGER_TIME_TOO_SHORT. The auto trigger time is less than the time it will take to collect the data.
# 1C PICO_BUFFER_STALL. The collection of data has stalled as unread data would be overwritten.
# 1D PICO_TOO_MANY_SAMPLES. Number of samples requested is more than available in the current memory segment.
# 1E PICO_TOO_MANY_SEGMENTS. Not possible to create number of segments requested.
# 1F PICO_PULSE_WIDTH_QUALIFIER. A null pointer has been passed in the trigger function or one of the parameters is out of range.
# 20 PICO_DELAY. One or more of the hold-off parameters are out of range.
# 21 PICO_SOURCE_DETAILS. One or more of the source details are incorrect.
# 22 PICO_CONDITIONS. One or more of the conditions are incorrect.
# 23 PICO_USER_CALLBACK. The driver's thread is currently in the ps4000...Ready callback function and therefore the action cannot be carried out.
# 24 PICO_DEVICE_SAMPLING. An attempt is being made to get stored data while streaming. Either stop streaming by calling ps4000Stop, or use ps4000GetStreamingLatestValues.
# 25 PICO_NO_SAMPLES_AVAILABLE. ...because a run has not been completed.
# 26 PICO_SEGMENT_OUT_OF_RANGE. The memory index is out of range.
# 27 PICO_BUSY. The driver cannot return data yet.
# 28 PICO_STARTINDEX_INVALID. The start time to get stored data is out of range.
# 29 PICO_INVALID_INFO. The information number requested is not a valid number.
# 2A PICO_INFO_UNAVAILABLE. The handle is invalid so no information is available about the device. Only PICO_DRIVER_VERSION is available.
# 2B PICO_INVALID_SAMPLE_INTERVAL. The sample interval selected for streaming is out of range.
# 2C PICO_TRIGGER_ERROR. ETS is set but no trigger has been set. A trigger setting is required for ETS.
# 2D PICO_MEMORY. Driver cannot allocate memory
# 2E PICO_SIG_GEN_PARAM. Error in signal generator parameter
# 2F PICO_SHOTS_SWEEPS_WARNING. The signal generator will output the signal required but sweeps and shots will be ignored. Only one parameter can be non-zero.
# 30 PICO_SIGGEN_TRIGGER_SOURCE. A software trigger has been sent but the trigger source is not a software trigger.
# 31 PICO_AUX_OUTPUT_CONFLICT. A ps4000SetTrigger... call has found a conflict between the trigger source and the AUX output enable.
# 32 PICO_AUX_OUTPUT_ETS_CONFLICT. ETS mode is being used and AUX is set as an input.
# 33 PICO_WARNING_EXT_THRESHOLD_CONFLICT. The EXT threshold is being set in both a ps4000SetTrigger... function and in the signal generator but the threshold values differ. The last value set will be used.
# 34 PICO_WARNING_AUX_OUTPUT_CONFLICT. A ps4000SetTrigger... function has set AUX as an output and the signal generator is using it as a trigger.
# 35 PICO_SIGGEN_OUTPUT_OVER_VOLTAGE. The requested voltage and offset levels combine to give an overvoltage.
# 36 PICO_DELAY_NULL. NULL pointer passed as delay parameter.
# 37 PICO_INVALID_BUFFER. The buffers for overview data have not been set while streaming.
# 38 PICO_SIGGEN_OFFSET_VOLTAGE. The signal generator offset voltage is higher than allowed.
# 39 PICO_SIGGEN_PK_TO_PK. The signal generator peak-to-peak voltage is higher than allowed.
# 3A PICO_CANCELLED. A block collection has been cancelled.
# 3B PICO_SEGMENT_NOT_USED. The specified segment index is not in use.
# 3C PICO_INVALID_CALL. The wrong GetValues function has been called for the collection mode in use.
# 3D PICO_GET_VALUES_INTERRUPTED
# 3F PICO_NOT_USED. The function is not available.
# 40 PICO_INVALID_SAMPLERATIO. The aggregation ratio requested is out of range.
# 41 PICO_INVALID_STATE. Device is in an invalid state.
# 42 PICO_NOT_ENOUGH_SEGMENTS. The number of segments allocated is fewer than the number of captures requested.
# 43 PICO_DRIVER_FUNCTION. You called a driver function while another driver function was still being processed.
# 44 PICO_RESERVED
# 45 PICO_INVALID_COUPLING. The dc argument passed to ps4000SetChannel was invalid.
# 46 PICO_BUFFERS_NOT_SET. Memory buffers were not set up before calling one of the ps4000Run... functions.
# 47 PICO_RATIO_MODE_NOT_SUPPORTED. downSampleRatioMode is not valid for the connected device.
# 48 PICO_RAPID_NOT_SUPPORT_AGGREGATION. Aggregation was requested in rapid block mode.
# 49 PICO_INVALID_TRIGGER_PROPERTY. An incorrect value was passed to ps4000SetTriggerChannelProperties.
Code: Select all
#Open scope by serial number
def openScope(self):
handlePointer = c_short()
serialNullTermStr = c_char_p( bytes(self.serial,'ascii') ) <<<<<<<<<<<<
message = self.lib.ps4000OpenUnitEx( byref(handlePointer), serialNullTermStr )
self.handle = handlePointer.value
if message != 0: print('Error openScope:', message)
Code: Select all
self.lib.ps4000OpenUnit(byref(handlePointer))