Skip to content

Commit 5904d6d

Browse files
author
neil.hamilton
committed
Add ps2000 streaming mode examples including some extra variants
1 parent 0de4fac commit 5904d6d

File tree

4 files changed

+413
-0
lines changed

4 files changed

+413
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from ctypes import POINTER, c_int16, c_uint32
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
6+
from picosdk.ps2000 import ps2000
7+
from picosdk.functions import assert_pico2000_ok
8+
from picosdk.PicoDeviceEnums import picoEnum
9+
from picosdk.ctypes_wrapper import C_CALLBACK_FUNCTION_FACTORY
10+
11+
import time
12+
13+
CALLBACK = C_CALLBACK_FUNCTION_FACTORY(
14+
None,
15+
POINTER(POINTER(c_int16)),
16+
c_int16,
17+
c_uint32,
18+
c_int16,
19+
c_int16,
20+
c_uint32
21+
)
22+
23+
adc_values = []
24+
25+
26+
def get_overview_buffers(buffers, _overflow, _triggered_at, _triggered, _auto_stop, n_values):
27+
adc_values.extend(buffers[0][0:n_values])
28+
29+
30+
callback = CALLBACK(get_overview_buffers)
31+
32+
33+
def adc_to_mv(values, range_, bitness=16):
34+
v_ranges = [10, 20, 50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000]
35+
36+
return [(x * v_ranges[range_]) / (2**(bitness - 1) - 1) for x in values]
37+
38+
39+
with ps2000.open_unit() as device:
40+
print('Device info: {}'.format(device.info))
41+
42+
res = ps2000.ps2000_set_channel(
43+
device.handle,
44+
picoEnum.PICO_CHANNEL['PICO_CHANNEL_A'],
45+
True,
46+
picoEnum.PICO_COUPLING['PICO_DC'],
47+
ps2000.PS2000_VOLTAGE_RANGE['PS2000_50MV'],
48+
)
49+
assert_pico2000_ok(res)
50+
51+
res = ps2000.ps2000_run_streaming_ns(
52+
device.handle,
53+
500,
54+
2,
55+
100_000,
56+
False,
57+
1,
58+
50_000
59+
)
60+
assert_pico2000_ok(res)
61+
62+
start_time = time.time_ns()
63+
64+
while time.time_ns() - start_time < 60_000_000:
65+
ps2000.ps2000_get_streaming_last_values(
66+
device.handle,
67+
callback
68+
)
69+
70+
end_time = time.time_ns()
71+
72+
ps2000.ps2000_stop(device.handle)
73+
74+
mv_values = adc_to_mv(adc_values, ps2000.PS2000_VOLTAGE_RANGE['PS2000_50MV'])
75+
76+
fig, ax = plt.subplots()
77+
78+
ax.set_xlabel('time/ms')
79+
ax.set_ylabel('voltage/mV')
80+
ax.plot(np.linspace(0, (end_time - start_time) * 1e-6, len(mv_values)), mv_values)
81+
82+
plt.show()
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
from time import time_ns
2+
from ctypes import POINTER, c_int16, c_uint32
3+
4+
import matplotlib.pyplot as plt
5+
import numpy as np
6+
7+
from picosdk.ps2000 import ps2000
8+
from picosdk.functions import assert_pico2000_ok
9+
from picosdk.ctypes_wrapper import C_CALLBACK_FUNCTION_FACTORY
10+
11+
from enum import IntEnum
12+
13+
14+
class Channel(IntEnum):
15+
PS2000_CHANNEL_A = 0
16+
PS2000_CHANNEL_B = 1
17+
18+
19+
class PotentialRange(IntEnum):
20+
PS2000_10MV = 0
21+
PS2000_20MV = 1
22+
PS2000_50MV = 2
23+
PS2000_100MV = 3
24+
PS2000_200MV = 4
25+
PS2000_500MV = 5
26+
PS2000_1V = 6
27+
PS2000_2V = 7
28+
PS2000_5V = 8
29+
PS2000_10V = 9
30+
PS2000_20V = 10
31+
32+
33+
class TimeUnit(IntEnum):
34+
FEMTOSECOND = 0
35+
PICOSECOND = 1
36+
NANOSECOND = 2
37+
MICROSECOND = 3
38+
MILLISECOND = 4
39+
SECOND = 5
40+
41+
42+
CALLBACK = C_CALLBACK_FUNCTION_FACTORY(None, POINTER(POINTER(c_int16)), c_int16, c_uint32, c_int16, c_int16, c_uint32)
43+
44+
45+
# reimplement this because the other one only takes ctypes
46+
def adc_to_mv(values, range_, bitness=16):
47+
v_ranges = [10, 20, 50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000]
48+
49+
return [(x * v_ranges[range_]) / (2**(bitness - 1) - 1) for x in values]
50+
51+
52+
def determine_time_unit(interval_ns):
53+
unit = 0
54+
units = ['ns', 'us', 'ms', 's']
55+
56+
while interval_ns > 5_000:
57+
interval_ns /= 1000
58+
unit += 1
59+
60+
return interval_ns, units[unit]
61+
62+
63+
class StreamingDevice:
64+
def __init__(self, gather_values, potential_range=PotentialRange.PS2000_50MV):
65+
self.device = ps2000.open_unit()
66+
self.potential_range = potential_range
67+
68+
self.gather_values = gather_values
69+
70+
res = ps2000.ps2000_set_channel(self.device.handle, Channel.PS2000_CHANNEL_A, True, True, potential_range)
71+
assert_pico2000_ok(res)
72+
73+
# start 'fast-streaming' mode
74+
res = ps2000.ps2000_run_streaming_ns(
75+
self.device.handle,
76+
500,
77+
TimeUnit.NANOSECOND,
78+
100_000,
79+
False,
80+
1,
81+
50_000
82+
)
83+
assert_pico2000_ok(res)
84+
85+
self.start_time = time_ns()
86+
self.end_time = time_ns()
87+
88+
def close(self):
89+
ps2000.ps2000_stop(self.device.handle)
90+
self.device.close()
91+
92+
def gather(self):
93+
adc_values = []
94+
95+
def get_overview_buffers(buffers, _overflow, _triggered_at, _triggered, _auto_stop, n_values):
96+
adc_values.extend(buffers[0][0:n_values])
97+
98+
callback = CALLBACK(get_overview_buffers)
99+
100+
while len(adc_values) < self.gather_values:
101+
ps2000.ps2000_get_streaming_last_values(
102+
self.device.handle,
103+
callback
104+
)
105+
106+
self.end_time = time_ns()
107+
108+
return adc_to_mv(adc_values, self.potential_range)
109+
110+
111+
stream = StreamingDevice(6_000_000)
112+
values = stream.gather()
113+
stream.close()
114+
115+
print('Values gathered: {}'.format(len(values)))
116+
117+
fig, ax = plt.subplots()
118+
interval, units = determine_time_unit(stream.end_time - stream.start_time)
119+
120+
ax.set_xlabel('time/{}'.format(units))
121+
ax.set_ylabel('voltage/mV')
122+
ax.plot(np.linspace(0, interval, len(values)), values)
123+
124+
plt.show()
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import asyncio
2+
import concurrent.futures
3+
import functools
4+
5+
from ctypes import POINTER, c_int16, c_uint32
6+
7+
from picosdk.ps2000 import ps2000
8+
from picosdk.functions import assert_pico2000_ok
9+
from picosdk.ctypes_wrapper import C_CALLBACK_FUNCTION_FACTORY
10+
11+
from enum import IntEnum
12+
13+
14+
class Channel(IntEnum):
15+
PS2000_CHANNEL_A = 0
16+
PS2000_CHANNEL_B = 1
17+
18+
19+
class PotentialRange(IntEnum):
20+
PS2000_20MV = 1
21+
PS2000_50MV = 2
22+
PS2000_100MV = 3
23+
PS2000_200MV = 4
24+
PS2000_500MV = 5
25+
PS2000_1V = 6
26+
PS2000_2V = 7
27+
PS2000_5V = 8
28+
PS2000_10V = 9
29+
PS2000_20V = 10
30+
31+
32+
CALLBACK = C_CALLBACK_FUNCTION_FACTORY(None, POINTER(POINTER(c_int16)), c_int16, c_uint32, c_int16, c_int16, c_uint32)
33+
34+
35+
# reimplement this because the other one only takes ctypes
36+
def adc_to_mv(values, range_, bitness=16):
37+
v_ranges = [10, 20, 50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000]
38+
39+
return [(x * v_ranges[range_]) / (2**(bitness - 1) - 1) for x in values]
40+
41+
42+
class StreamingDevice:
43+
def __init__(self, potential_range=PotentialRange.PS2000_50MV):
44+
self.device = ps2000.open_unit()
45+
self.potential_range = potential_range
46+
# create an event loop
47+
self.loop = asyncio.new_event_loop()
48+
# create a thread-pool executor
49+
self.executor = concurrent.futures.ThreadPoolExecutor()
50+
51+
self.waiting_blocks = {}
52+
53+
res = ps2000.ps2000_set_channel(self.device.handle, Channel.PS2000_CHANNEL_A, True, True, potential_range)
54+
assert_pico2000_ok(res)
55+
56+
# start 'fast-streaming' mode
57+
res = ps2000.ps2000_run_streaming_ns(
58+
self.device.handle,
59+
100,
60+
3,
61+
30_000,
62+
False,
63+
1,
64+
15_000
65+
)
66+
assert_pico2000_ok(res)
67+
68+
async def process_value(self, mv_value):
69+
# overload
70+
pass
71+
72+
def get_streamed_values(self, block_id):
73+
def get_overview_buffers(buffers, _overflow, _triggered_at, _triggered, _auto_stop, n_values):
74+
adc_values = buffers[0][0:n_values]
75+
76+
mv_values = adc_to_mv(adc_values, self.potential_range)
77+
78+
self.waiting_blocks[block_id] = mv_values
79+
80+
callback = CALLBACK(get_overview_buffers)
81+
82+
res = ps2000.ps2000_get_streaming_last_values(
83+
self.device.handle,
84+
callback
85+
)
86+
assert_pico2000_ok(res)
87+
88+
def start(self):
89+
async def gather_values():
90+
block_id = 0
91+
while True:
92+
await asyncio.sleep(0.5)
93+
94+
await self.loop.run_in_executor(self.executor, functools.partial(self.get_streamed_values, block_id))
95+
96+
block_id += 1
97+
98+
async def poll_waiting():
99+
next_block = 0
100+
while True:
101+
if block := self.waiting_blocks.get(next_block):
102+
next_block += 1
103+
for n in block:
104+
await self.process_value(n)
105+
106+
await asyncio.sleep(0.25)
107+
108+
try:
109+
self.loop.create_task(gather_values())
110+
self.loop.run_until_complete(poll_waiting())
111+
finally:
112+
self.loop.close()
113+
ps2000.ps2000_stop(self.device.handle)
114+
ps2000.ps2000_close(self.device.handle)
115+
116+
117+
stream = StreamingDevice()
118+
stream.start()

0 commit comments

Comments
 (0)