Skip to content

Commit 30bdafb

Browse files
authored
Merge pull request #174 from bmoneke/callback
Add callback functions to runBlock
2 parents 2071cd6 + d32a6b4 commit 30bdafb

File tree

11 files changed

+146
-17
lines changed

11 files changed

+146
-17
lines changed

examples/runBlock_callback.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from picoscope import ps4000a
2+
import matplotlib.pyplot as plt
3+
import time
4+
5+
ps = ps4000a.PS4000a()
6+
7+
8+
def callbackFunction(handle, status, pParameter=None):
9+
"""This function is executed once the block is ready."""
10+
if status == 0:
11+
print("Block is ready and can be read.")
12+
data = ps.getDataV('A')
13+
14+
print("data transferred")
15+
plt.plot(data)
16+
plt.show()
17+
else:
18+
print("An error occurred.")
19+
20+
21+
# Picoscope setup
22+
ps.setChannel(channel="A", coupling="DC", VRange=1)
23+
ps.setChannel(channel="B", enabled=False)
24+
ps.setChannel(channel="C", enabled=False)
25+
ps.setChannel(channel="D", enabled=False)
26+
27+
sample_interval = 100e-9 # 100 ns
28+
sample_duration = 2e-3 # 1 ms
29+
ps.setSamplingInterval(sample_interval, sample_duration)
30+
ps.setSimpleTrigger("A", threshold_V=0.1, timeout_ms=1)
31+
32+
# Run a block
33+
print("Run a block with a callback function")
34+
ps.runBlock(callback=callbackFunction)
35+
36+
time.sleep(10)
37+
ps.close()
38+
print("end")

picoscope/picobase.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def setChannel(self, channel='A', coupling="AC", VRange=2.0,
305305

306306
return VRange
307307

308-
def runBlock(self, pretrig=0.0, segmentIndex=0):
308+
def runBlock(self, pretrig=0.0, segmentIndex=0, callback=None):
309309
"""Run a single block.
310310
311311
Must have already called setSampling for proper setup.
@@ -316,6 +316,8 @@ def runBlock(self, pretrig=0.0, segmentIndex=0):
316316
Fraction of samples before the trigger.
317317
segmentIndex
318318
Index of scope memory segment to save data to.
319+
callback
320+
Function to call once the data acquisition finishes.
319321
"""
320322
# getting max samples is riddiculous.
321323
# 1GS buffer means it will take so long
@@ -325,7 +327,8 @@ def runBlock(self, pretrig=0.0, segmentIndex=0):
325327
nSamples_pretrig = int(round(nSamples * pretrig))
326328
self._lowLevelRunBlock(nSamples_pretrig,
327329
nSamples - nSamples_pretrig,
328-
self.timebase, self.oversample, segmentIndex)
330+
self.timebase, self.oversample, segmentIndex,
331+
callback, None)
329332

330333
def isReady(self):
331334
"""Return whether scope is ready to transfer data."""

picoscope/ps2000.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,10 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
204204
self.checkResult(m)
205205

206206
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
207-
timebase, oversample, segmentIndex):
207+
timebase, oversample, segmentIndex, callback,
208+
pParameter):
208209
# NOT: Oversample is NOT used!
210+
# Note: callback and pParameter are not available.
209211

210212
# TODO: Fix 'delay' which is where trigger occurs in block
211213
if numPreTrigSamples > 0:

picoscope/ps2000a.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ def _lowLevelGetMaxSegments(self):
269269
return maxSegments.value
270270

271271
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
272-
timebase, oversample, segmentIndex):
272+
timebase, oversample, segmentIndex, callback,
273+
pParameter):
273274
# NOT: Oversample is NOT used!
274275
timeIndisposedMs = c_int32()
275276
m = self.lib.ps2000aRunBlock(
@@ -278,6 +279,8 @@ def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
278279
c_int16(oversample), byref(timeIndisposedMs),
279280
c_uint32(segmentIndex),
280281
c_void_p(), c_void_p())
282+
# According to the documentation, 'callback, pParameter' should work
283+
# instead of the last two c_void_p parameters.
281284
self.checkResult(m)
282285
return timeIndisposedMs.value
283286

picoscope/ps3000.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,10 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
191191
self.checkResult(m)
192192

193193
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
194-
timebase, oversample, segmentIndex):
194+
timebase, oversample, segmentIndex, callback,
195+
pParameter):
195196
# NOT: Oversample is NOT used!
197+
# Note: callback and pParameter are not available.
196198

197199
# TODO: Fix 'delay' which is where trigger occurs in block
198200
if numPreTrigSamples > 0:

picoscope/ps3000a.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ def _lowLevelGetMaxSegments(self):
267267
return maxSegments.value
268268

269269
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
270-
timebase, oversample, segmentIndex):
270+
timebase, oversample, segmentIndex, callback,
271+
pParameter):
271272
# NOT: Oversample is NOT used!
272273
timeIndisposedMs = c_int32()
273274
m = self.lib.ps3000aRunBlock(
@@ -276,6 +277,8 @@ def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
276277
c_int16(oversample), byref(timeIndisposedMs),
277278
c_uint32(segmentIndex),
278279
c_void_p(), c_void_p())
280+
# According to the documentation, 'callback, pParameter' should work
281+
# instead of the last two c_void_p parameters.
279282
self.checkResult(m)
280283
return timeIndisposedMs.value
281284

picoscope/ps4000.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,16 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
269269
self.checkResult(m)
270270

271271
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
272-
timebase, oversample, segmentIndex):
272+
timebase, oversample, segmentIndex, callback,
273+
pParameter):
273274
timeIndisposedMs = c_int32()
274275
m = self.lib.ps4000RunBlock(
275276
c_int16(self.handle), c_uint32(numPreTrigSamples),
276277
c_uint32(numPostTrigSamples), c_uint32(timebase),
277278
c_int16(oversample), byref(timeIndisposedMs),
278279
c_uint16(segmentIndex), c_void_p(), c_void_p())
280+
# According to the documentation, 'callback, pParameter' should work
281+
# instead of the last two c_void_p parameters.
279282
self.checkResult(m)
280283
return timeIndisposedMs.value
281284

picoscope/ps4000a.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,61 @@
5959
# float is always defined as 32 bits
6060
# double is defined as 64 bits
6161
from ctypes import byref, POINTER, create_string_buffer, c_float, \
62-
c_int16, c_uint16, c_int32, c_uint32, c_uint64, c_void_p, c_int8
62+
c_int16, c_uint16, c_int32, c_uint32, c_uint64, c_void_p, c_int8, \
63+
CFUNCTYPE
6364
from ctypes import c_int32 as c_enum
6465

6566
import warnings
6667

6768
from picoscope.picobase import _PicoscopeBase
6869

6970

71+
# Decorators for callback functions. PICO_STATUS is uint32_t.
72+
def blockReady(function):
73+
"""typedef void (*ps4000aBlockReady)
74+
(
75+
int16_t handle,
76+
PICO_STATUS status,
77+
void * pParameter
78+
)
79+
"""
80+
callback = CFUNCTYPE(c_void_p, c_int16, c_uint32, c_void_p)
81+
return callback(function)
82+
83+
84+
def dataReady(function):
85+
"""typedef void (*ps4000aDataReady)
86+
(
87+
int16_t handle,
88+
PICO_STATUS status,
89+
uint32_t noOfSamples,
90+
int16_t overflow,
91+
void * pParameter
92+
)
93+
"""
94+
callback = CFUNCTYPE(c_void_p,
95+
c_int16, c_uint32, c_uint32, c_int16, c_void_p)
96+
return callback(function)
97+
98+
99+
def streamingReady(function):
100+
"""typedef void (*ps4000aStreamingReady)
101+
(
102+
int16_t handle,
103+
int32_t noOfSamples,
104+
uint32_t startIndex,
105+
int16_t overflow,
106+
uint32_t triggerAt,
107+
int16_t triggered,
108+
int16_t autoStop,
109+
void * pParameter
110+
)
111+
"""
112+
callback = CFUNCTYPE(c_void_p, c_int16, c_int32, c_uint32, c_int16,
113+
c_uint32, c_int16, c_int16, c_void_p)
114+
return callback(function)
115+
116+
70117
class PS4000a(_PicoscopeBase):
71118
"""The following are low-level functions for the PS4000A."""
72119

@@ -281,13 +328,17 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
281328
self.checkResult(m)
282329

283330
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
284-
timebase, oversample, segmentIndex):
331+
timebase, oversample, segmentIndex, callback,
332+
pParameter):
333+
# Hold a reference to the callback so that the Python
334+
# function pointer doesn't get free'd
335+
self._c_runBlock_callback = blockReady(callback)
285336
timeIndisposedMs = c_int32()
286337
m = self.lib.ps4000aRunBlock(
287338
c_int16(self.handle), c_int32(numPreTrigSamples),
288339
c_int32(numPostTrigSamples), c_uint32(timebase),
289-
byref(timeIndisposedMs),
290-
c_uint32(segmentIndex), c_void_p(), c_void_p())
340+
byref(timeIndisposedMs), c_uint32(segmentIndex),
341+
self._c_runBlock_callback, c_void_p())
291342
self.checkResult(m)
292343
return timeIndisposedMs.value
293344

picoscope/ps5000.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,18 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
264264
self.checkResult(m)
265265

266266
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
267-
timebase, oversample, segmentIndex):
267+
timebase, oversample, segmentIndex, callback,
268+
pParameter):
268269
timeIndisposedMs = c_int32()
269270
m = self.lib.ps5000RunBlock(
270271
c_int16(self.handle), c_uint32(numPreTrigSamples),
271272
c_uint32(numPostTrigSamples), c_uint32(timebase),
272273
c_int16(oversample), byref(timeIndisposedMs),
273274
c_uint32(segmentIndex), c_void_p(), c_void_p())
275+
# According to the documentation, 'callback, pParameter' should work
276+
# instead of the last two c_void_p parameters.
277+
# However to avoid the potential for serious crashes, we decided to
278+
# not include them in this function call.
274279
self.checkResult(m)
275280
return timeIndisposedMs.value
276281

picoscope/ps5000a.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,25 @@
5656
# float is always defined as 32 bits
5757
# double is defined as 64 bits
5858
from ctypes import byref, POINTER, create_string_buffer, c_float, \
59-
c_int16, c_int32, c_uint32, c_void_p, c_int64
59+
c_int16, c_int32, c_uint32, c_void_p, c_int64, CFUNCTYPE
6060
from ctypes import c_int32 as c_enum
6161

6262
from picoscope.picobase import _PicoscopeBase
6363

6464

65+
# Decorators for callback functions. PICO_STATUS is uint32_t.
66+
def blockReady(function):
67+
"""typedef void (*ps5000aBlockReady)
68+
(
69+
int16_t handle,
70+
PICO_STATUS status,
71+
void * pParameter
72+
)
73+
"""
74+
callback = CFUNCTYPE(c_void_p, c_int16, c_uint32, c_void_p)
75+
return callback(function)
76+
77+
6578
class PS5000a(_PicoscopeBase):
6679
"""The following are low-level functions for the PS5000."""
6780

@@ -274,14 +287,17 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
274287
self.checkResult(m)
275288

276289
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
277-
timebase, oversample, segmentIndex):
278-
# Oversample is NOT used!
290+
timebase, oversample, segmentIndex, callback,
291+
pParameter):
292+
# Hold a reference to the callback so that the Python
293+
# function pointer doesn't get free'd.
294+
self._c_runBlock_callback = blockReady(callback)
279295
timeIndisposedMs = c_int32()
280296
m = self.lib.ps5000aRunBlock(
281297
c_int16(self.handle), c_uint32(numPreTrigSamples),
282298
c_uint32(numPostTrigSamples), c_uint32(timebase),
283299
byref(timeIndisposedMs), c_uint32(segmentIndex),
284-
c_void_p(), c_void_p())
300+
self._c_runBlock_callback, c_void_p())
285301
self.checkResult(m)
286302
return timeIndisposedMs.value
287303

picoscope/ps6000.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,16 @@ def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
268268
self.checkResult(m)
269269

270270
def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
271-
timebase, oversample, segmentIndex):
271+
timebase, oversample, segmentIndex, callback,
272+
pParameter):
272273
timeIndisposedMs = c_int32()
273274
m = self.lib.ps6000RunBlock(
274275
c_int16(self.handle), c_uint32(numPreTrigSamples),
275276
c_uint32(numPostTrigSamples), c_uint32(timebase),
276277
c_int16(oversample), byref(timeIndisposedMs),
277278
c_uint32(segmentIndex), c_void_p(), c_void_p())
279+
# According to the documentation, 'callback, pParameter' should work
280+
# instead of the last two c_void_p parameters.
278281
self.checkResult(m)
279282
return timeIndisposedMs.value
280283

0 commit comments

Comments
 (0)