Skip to content

Commit 9ff4856

Browse files
Added Streaming class and documentation (#62)
* Added get_streaming_latest_values() * Updated run_streaming() to cast samples to int * Updated base.py - Removed un-needed errors * Added streaming.py example * Added streaming examples * Added streaming Added the following: - Basic streaming example. No display till finish. - streaming.py - Streaming using threading - streaming_threading.py - Streaming with live plot - streaming_plot_animation.py * Fixed display limit * Removed redundant lines * Update streaming_plot_animation.py Restructured streaming class example * Updated streaming_plot_animation.py - Removed need for np_list & renamed np_array * Added streaming.py to package * Edited streaming examples to use StreamingScope class * Renamed and removed streaming examples * Added docStrings and types to streaming.py functions * Incorrect mkdocs plugin switched * Fixed missing type annotation * Fixed duplication of functions * Added streaming to docs * Fixed a potentially broken link * Removed timebase from examples --------- Co-authored-by: Jamackey <31521142+Jamackey@users.noreply.github.com>
1 parent e7f92d4 commit 9ff4856

File tree

10 files changed

+368
-10
lines changed

10 files changed

+368
-10
lines changed

build-tools/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ matplotlib
66

77
mkdocs >= 1.6
88
mkdocstrings-python >= 1.16
9-
mkdocs-autorefs >= 1.4
9+
include-markdown-plugin >= 7.1.6
1010

1111
build
1212
pytest

docs/docs/ref/ps6000a/channel.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- "channel"
1010
- "!trigger"
1111
- "!buffer"
12+
- "!siggen"
1213
- "!_to_"
1314
- "!^_"
1415
show_root_toc_entry: false

docs/docs/ref/ps6000a/digital.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
filters:
55
- "!.*"
66
- "digital"
7+
- "!trigger"
78
- "!^_"
89
show_root_toc_entry: false
910
summary: true

docs/docs/ref/ps6000a/trigger.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
filters:
55
- "!.*"
66
- "trigger"
7+
- "!siggen"
78
- "!^_"
89
show_root_toc_entry: false
910
summary: true

docs/docs/ref/streaming.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Streaming
2+
Streaming in a PicoScope is a great tool for monitoring a waveform for a long period of time
3+
without the delay between block captures. Though this method of capture is fairly limited in
4+
sample rate and usability in comparison to a `block capture` and especially `rapid block capture`.
5+
6+
## Using Streaming in pyPicoSDK
7+
Streaming is implemented in this package as a class to take advantage of the benefits offered by an object-oriented approach.
8+
For a quick start-up use the [example code](https://github.com/JamesPicoTech/pyPicoSDK/blob/main/examples/streaming) and alter it to your application.
9+
10+
## Methods & Functions
11+
Below are the included public methods for streaming:
12+
::: pypicosdk.streaming.StreamingScope
13+
options:
14+
show_root_toc_entry: true
15+
summary: true

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ nav:
2121
- ref/ps6000a/retrieval.md
2222
- General Conversions: ref/conversions.md
2323
- General Constants: ref/constants.md
24+
- ref/streaming.md
2425
- Development:
2526
- Changelog: dev/changelog.md
2627
- Current Support: dev/current.md

examples/streaming/streaming.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import pypicosdk as psdk
2+
from pypicosdk.streaming import StreamingScope
3+
from matplotlib import pyplot as plt
4+
import numpy as np
5+
6+
# Capture configuration
7+
samples = int(1E9)
8+
streaming_samples = 250
9+
interval = 4
10+
unit = psdk.TIME_UNIT.NS
11+
pico_unit = psdk.PICO_TIME_UNIT.NS
12+
channel = psdk.CHANNEL.A
13+
14+
def setup_scope():
15+
scope = psdk.ps6000a()
16+
scope.open_unit()
17+
scope.set_siggen(frequency=100, pk2pk=1.6, wave_type=psdk.WAVEFORM.TRIANGLE)
18+
scope.set_channel(channel=psdk.CHANNEL.A, range=psdk.RANGE.V1)
19+
scope.set_simple_trigger(channel=psdk.CHANNEL.A, threshold_mv=0)
20+
return scope
21+
22+
def main():
23+
scope = setup_scope()
24+
stream = StreamingScope(scope)
25+
stream.config_streaming(channel, samples, interval, pico_unit, max_buffer_size=None)
26+
27+
stream.run_streaming_for_samples(5000000)
28+
29+
scope.close_unit()
30+
31+
plt.plot(stream.buffer_array)
32+
plt.show()
33+
34+
35+
if __name__ == '__main__':
36+
main()
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
Note: RACE CONDITION!
3+
The data retrieved from get_streaming data *NEEDS* to be
4+
larger than the captured data.
5+
If it's smaller, the live plot will lag behind the data.
6+
If it's larger, the streaming will ignore the empty buffers.
7+
8+
Threading:
9+
The main streaming is handled in a thread while loop.
10+
This is so that the matplotlib animation can be the main loop,
11+
so the frame update interval doesn't control the retrieval of data
12+
removing any race condition from the animation.
13+
"""
14+
15+
16+
import pypicosdk as psdk
17+
from pypicosdk.streaming import StreamingScope
18+
from matplotlib import pyplot as plt
19+
from matplotlib.animation import FuncAnimation
20+
import numpy as np
21+
import threading
22+
import time
23+
24+
# Capture configuration
25+
samples = int(1E9)
26+
streaming_samples = 250
27+
interval = 20
28+
display_samples = 1000
29+
pico_unit = psdk.PICO_TIME_UNIT.NS
30+
channel = psdk.CHANNEL.A
31+
32+
33+
fig = plt.figure()
34+
axis = plt.axes(xlim =(0, display_samples),
35+
ylim =(-32000, 32000))
36+
x = np.arange(display_samples) # Predefined x-data of length 10
37+
line, = axis.plot(x, np.zeros_like(x), lw=2) # Initialize with zeros
38+
39+
40+
def setup_scope():
41+
scope = psdk.ps6000a()
42+
scope.open_unit()
43+
scope.set_siggen(frequency=1_000_000, pk2pk=1.6, wave_type=psdk.WAVEFORM.TRIANGLE)
44+
scope.set_channel(channel=psdk.CHANNEL.A, range=psdk.RANGE.V1)
45+
scope.set_simple_trigger(channel=psdk.CHANNEL.A, threshold_mv=0)
46+
return scope
47+
48+
def streaming_thread(stream:StreamingScope):
49+
stream.run_streaming_while()
50+
51+
def animate(frame, stream:StreamingScope):
52+
data = stream.buffer_array
53+
line.set_ydata(data)
54+
return line,
55+
56+
def main():
57+
stop = False
58+
scope = setup_scope()
59+
stream = StreamingScope(scope)
60+
stream.config_streaming(channel, samples, interval, pico_unit, display_samples)
61+
62+
th = threading.Thread(target=streaming_thread, args=[stream])
63+
th.start()
64+
65+
anim = FuncAnimation(fig, animate, frames=500, fargs=(stream, ), interval=20, blit=True)
66+
plt.show()
67+
68+
stream.stop()
69+
th.join()
70+
71+
scope.close_unit()
72+
73+
74+
if __name__ == '__main__':
75+
main()

pypicosdk/base.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,6 @@ def _error_handler(self, status: int) -> None:
162162
# raise an exception as callers may poll until data is ready.
163163
if status == 407: # PICO_WAITING_FOR_DATA_BUFFERS
164164
return
165-
# Streaming related status codes indicate no new data is ready.
166-
# These should not be treated as fatal errors and simply signal
167-
# the caller to try again later.
168-
if status in [28672, 28673, 28674]:
169-
return
170165
self.close_unit()
171166
raise PicoSDKException(error_code)
172167
return
@@ -716,8 +711,8 @@ def get_values_bulk_async(
716711
to_segment_index: int,
717712
down_sample_ratio: int,
718713
down_sample_ratio_mode: int,
719-
lp_data_ready,
720-
p_parameter,
714+
lp_data_ready:ctypes.POINTER,
715+
p_parameter:ctypes.POINTER,
721716
) -> None:
722717
"""Begin asynchronous retrieval of values from multiple segments.
723718
@@ -1863,8 +1858,8 @@ def run_streaming(
18631858
self.handle,
18641859
ctypes.byref(c_sample_interval),
18651860
time_units,
1866-
max_pre_trigger_samples,
1867-
max_post_trigger_samples,
1861+
int(max_pre_trigger_samples),
1862+
int(max_post_trigger_samples),
18681863
auto_stop,
18691864
ratio,
18701865
ratio_mode,
@@ -1928,6 +1923,36 @@ def get_values(self, samples, start_index=0, segment=0, ratio=0, ratio_mode=RATI
19281923
self.is_over_range()
19291924
return total_samples.value
19301925

1926+
def get_streaming_latest_values(
1927+
self,
1928+
channel,
1929+
ratio_mode,
1930+
data_type
1931+
):
1932+
info = PICO_STREAMING_DATA_INFO(
1933+
channel_ = channel,
1934+
mode_ = ratio_mode,
1935+
type_ = data_type,
1936+
)
1937+
trigger = PICO_STREAMING_DATA_TRIGGER_INFO()
1938+
1939+
status = self._call_attr_function(
1940+
"GetStreamingLatestValues",
1941+
self.handle,
1942+
ctypes.byref(info),
1943+
1,
1944+
ctypes.byref(trigger)
1945+
)
1946+
return {
1947+
'status': status,
1948+
'no of samples': info.noOfSamples_,
1949+
'Buffer index': info.bufferIndex_,
1950+
'start index': info.startIndex_,
1951+
'overflowed?': info.overflow_,
1952+
'triggered at': trigger.triggerAt_,
1953+
'triggered?': trigger.triggered_,
1954+
'auto stopped?': trigger.autoStop_,
1955+
}
19311956

19321957
def is_over_range(self) -> list:
19331958
"""

0 commit comments

Comments
 (0)