Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1899,6 +1899,7 @@
#### [niscope] Unreleased
- Added
- (Common) Added `nitlsconfig[grpc]>=1.0.0a2` to the optional gRPC dependencies.
- Added the `niscope_external_clocking.py` example.
- Changed
- Removed

Expand Down
3 changes: 3 additions & 0 deletions build/templates/tox-system_tests.ini.mako
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ deps =
% endif
% if module_name == 'nidigital':
${module_name}-system_tests: nisync
% endif
% if module_name == 'niscope':
${module_name}-system_tests: matplotlib; sys_platform != "win32"
% endif
${module_name}-system_tests: pytest
${module_name}-system_tests: coverage
Expand Down
9 changes: 9 additions & 0 deletions docs/niscope/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ Examples

`You can download all niscope examples for latest version here <https://github.com/ni/nimi-python/releases/download/niscope-1.5.0/niscope_examples.zip>`_

niscope_external_clocking.py
----------------------------

.. literalinclude:: ../../src/niscope/examples/niscope_external_clocking.py
:language: python
:linenos:
:encoding: utf8
:caption: `(niscope_external_clocking.py) <https://github.com/ni/nimi-python/blob/master/src/niscope/examples/niscope_external_clocking.py>`_

niscope_fetch.py
----------------

Expand Down
1 change: 1 addition & 0 deletions generated/niscope/tox-system_tests.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ commands =
deps =
niscope-wheel_dep: build

niscope-system_tests: matplotlib; sys_platform != "win32"
niscope-system_tests: pytest
niscope-system_tests: coverage
niscope-system_tests: numpy
Expand Down
104 changes: 104 additions & 0 deletions src/niscope/examples/niscope_external_clocking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/python

import argparse
import niscope
import pytest
import sys


def _create_chart(plt):
figure, axes = plt.subplots()
axes.set_title('Waveform')
axes.set_xlabel('Time (s)')
axes.set_ylabel('Voltage (V)')
axes.grid(True)
figure.tight_layout()
return figure, axes


def _update_chart(axes, waveforms, actual_sample_rate):
if len(axes.lines) < len(waveforms):
while len(axes.lines) < len(waveforms):
axes.plot([], [], label=waveforms[len(axes.lines)].channel)
axes.legend(loc='upper right')
for line, waveform in zip(axes.lines, waveforms):
x = [waveform.relative_initial_x + i * waveform.x_increment for i in range(len(waveform.samples))]
# read() returns samples as a memoryview, which matplotlib cannot copy
line.set_data(x, list(waveform.samples))
axes.set_title(f'Waveform (Actual Sample Rate: {actual_sample_rate:g} S/s)')
axes.relim()
axes.autoscale_view()


def example(resource_name, channels, options, min_record_length, voltage_range, voltage_offset, source, rate, divisor, multiplier, total_iterations):
import matplotlib.pyplot as plt

with niscope.Session(resource_name=resource_name, options=options) as session:
channel_session = session.channels[channels]
channel_session.configure_vertical(range=voltage_range, coupling=niscope.VerticalCoupling.DC, offset=voltage_offset)
session.configure_trigger_immediate()
session.horz_min_num_pts = min_record_length
session.samp_clk_timebase_src = source
session.samp_clk_timebase_rate = rate
session.samp_clk_timebase_div = divisor
session.sample_clock_timebase_multiplier = multiplier

figure, axes = _create_chart(plt)

iterations = 0
try:
# Run until the chart is closed, Ctrl+C is pressed, or the iteration limit is reached.
while plt.fignum_exists(figure.number) and (total_iterations is None or iterations < total_iterations):
# read() initiates the acquisition, waits for it to complete and fetches the data
waveforms = channel_session.read(num_samples=min_record_length)
_update_chart(axes, waveforms, session.horz_sample_rate)
iterations += 1
plt.pause(0.01)

# Keep the final plot window open after acquisition stops until the user closes it.
if total_iterations is not None and plt.fignum_exists(figure.number):
plt.show()
except KeyboardInterrupt:
pass
finally:
plt.close(figure)


def _main(argsv):
parser = argparse.ArgumentParser(description='Continuously acquires and plots a waveform using an external sample clock.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-n', '--resource-name', default='PXI1Slot3', help='Specifies the resource name of the device to initialize.')
parser.add_argument('-c', '--channels', default='0', help='Comma-separated list of channels to plot, one line per channel')
parser.add_argument('-v', '--voltage-range', default=1.0, type=float, help='Specifies the absolute value of the input range for a channel in volts.')
parser.add_argument('--voltage-offset', default=0.0, type=float, help='Specifies the location of the center of the range. The value is with respect to ground and is in volts.')
parser.add_argument('-l', '--min-record-length', default=1000, type=int, help='Specifies the minimum number of points you require in the waveform record for each channel.')
parser.add_argument('-s', '--source', default='VAL_CLK_IN', type=str, help='Specifies the source of the sample clock timebase, which is the timebase used to control waveform sampling.')
parser.add_argument('-r', '--rate', default=1e8, type=float, help='Specifies the frequency in hertz of the external clock used as the timebase source.')
parser.add_argument('-d', '--divisor', default=1, type=int, help='Specifies the ratio between the sample clock timebase rate and the actual sample rate, which can be slower.')
parser.add_argument('-m', '--multiplier', default=1, type=int, help='Specifies the ratio between the samp_clk_timebase_rate and the actual sample rate, which can be higher.')
parser.add_argument('--total-iterations', default=None, type=int, help='Specifies the maximum number of acquisitions; omit to run until the chart closes or Ctrl+C is pressed.')
parser.add_argument('-op', '--option-string', default='', type=str, help='Specifies the initial value of certain properties for the session.')
args = parser.parse_args(argsv)
example(args.resource_name, args.channels, args.option_string, args.min_record_length, args.voltage_range, args.voltage_offset, args.source, args.rate, args.divisor, args.multiplier, args.total_iterations)


def main():
_main(sys.argv[1:])


def test_example():
plt = pytest.importorskip('matplotlib.pyplot')
plt.switch_backend('Agg') # Avoid opening a chart window during the test
options = {'simulate': True, 'driver_setup': {'Model': '5164', 'BoardType': 'PXIe', }, }
# The simulated PXIe-5164 only accepts a 1 GHz sample clock timebase
example('PXI1Slot2', '0', options, 1000, 1.0, 0.0, 'VAL_CLK_IN', 1e9, 1, 1, 2)


def test_main():
plt = pytest.importorskip('matplotlib.pyplot')
plt.switch_backend('Agg') # Avoid opening a chart window during the test
cmd_line = ['--option-string', 'Simulate=1, DriverSetup=Model:5164; BoardType:PXIe', '--rate', '1e9', '--total-iterations', '2', ]
_main(cmd_line)


if __name__ == '__main__':
main()
Loading