diff --git a/CHANGELOG.md b/CHANGELOG.md index 86a8d9eb3..f73acd916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -799,6 +799,7 @@ #### [nidmm] Unreleased - Added - Added the `nidmm_measure_ac_volts.py` example. + - Added the `nidmm_cont_acq_chart_multiple_samples.py` example. - (Common) Added `nitlsconfig[grpc]>=1.0.0a2` to the optional gRPC dependencies. - Changed - Removed diff --git a/build/templates/tox-system_tests.ini.mako b/build/templates/tox-system_tests.ini.mako index c2b3d9131..41e42c05b 100644 --- a/build/templates/tox-system_tests.ini.mako +++ b/build/templates/tox-system_tests.ini.mako @@ -76,6 +76,9 @@ deps = % endif % if module_name == 'nidigital': ${module_name}-system_tests: nisync +% endif +% if module_name == 'nidmm': + ${module_name}-system_tests: matplotlib; sys_platform != "win32" % endif ${module_name}-system_tests: pytest ${module_name}-system_tests: coverage diff --git a/docs/nidmm/examples.rst b/docs/nidmm/examples.rst index ec6b3ee3e..72dfbdebd 100644 --- a/docs/nidmm/examples.rst +++ b/docs/nidmm/examples.rst @@ -3,6 +3,15 @@ Examples `You can download all nidmm examples for latest version here `_ +nidmm_cont_acq_chart_multiple_samples.py +---------------------------------------- + +.. literalinclude:: ../../src/nidmm/examples/nidmm_cont_acq_chart_multiple_samples.py + :language: python + :linenos: + :encoding: utf8 + :caption: `(nidmm_cont_acq_chart_multiple_samples.py) `_ + nidmm_fetch_waveform.py ----------------------- diff --git a/generated/nidmm/tox-system_tests.ini b/generated/nidmm/tox-system_tests.ini index 7d155899a..807c05d20 100644 --- a/generated/nidmm/tox-system_tests.ini +++ b/generated/nidmm/tox-system_tests.ini @@ -32,6 +32,7 @@ commands = nidmm-coverage: coverage report --rcfile=../../tools/coverage_system_tests.rc deps = + nidmm-system_tests: matplotlib; sys_platform != "win32" nidmm-system_tests: pytest nidmm-system_tests: coverage nidmm-system_tests: numpy diff --git a/src/nidmm/examples/nidmm_cont_acq_chart_multiple_samples.py b/src/nidmm/examples/nidmm_cont_acq_chart_multiple_samples.py new file mode 100644 index 000000000..ecc34e46e --- /dev/null +++ b/src/nidmm/examples/nidmm_cont_acq_chart_multiple_samples.py @@ -0,0 +1,109 @@ +#!/usr/bin/python + +import argparse +import collections +import nidmm +import pytest +import sys + + +# Number of most recent measurements kept on the chart +CHART_HISTORY_SIZE = 250 + + +def _create_chart(plt): + figure, axes = plt.subplots() + (line,) = axes.plot([]) + axes.set_title('Measurements') + axes.set_xlabel('Samples') + axes.set_ylabel('Measured value') + axes.grid(True) + return figure, axes, line + + +def _update_chart(plt, axes, line, measurements, new_samples, samples_fetched): + measurements.extend(new_samples) + samples_fetched += len(new_samples) + # The x-axis keeps absolute sample numbers, so it starts after the samples dropped from the history + first_sample = samples_fetched - len(measurements) + line.set_data(range(first_sample, samples_fetched), measurements) + axes.relim() + axes.autoscale_view() + plt.pause(0.001) + return samples_fetched + + +def example(resource_name, options, function, measurement_range, digits, samples_to_fetch, total_samples): + # Imported lazily because matplotlib is unavailable on some platforms (e.g. 32-bit Python) + import matplotlib.pyplot as plt + + with nidmm.Session(resource_name=resource_name, options=options) as session: + session.configure_measurement_digits(measurement_function=nidmm.Function[function], range=measurement_range, resolution_digits=digits) + # A sample_count of 0 configures a continuous acquisition + session.configure_multi_point(trigger_count=1, sample_count=0) + + # Create an empty chart; the line is filled in as samples arrive + figure, axes, line = _create_chart(plt) + # Only the most recent CHART_HISTORY_SIZE measurements are kept + measurements = collections.deque(maxlen=CHART_HISTORY_SIZE) + + try: + with session.initiate(): + samples_fetched = 0 + + # A total_samples of None runs until the chart window is closed or Ctrl+C is pressed + while plt.fignum_exists(figure.number) and ( + total_samples is None or samples_fetched < total_samples + ): + # The last fetch only takes the samples still needed to reach total_samples + samples_in_this_fetch = samples_to_fetch + if total_samples is not None: + samples_in_this_fetch = min(samples_to_fetch, total_samples - samples_fetched) + + new_samples = session.fetch_multi_point(array_size=samples_in_this_fetch) + + # Add the new samples to the chart and get the updated total sample count + samples_fetched = _update_chart(plt, axes, line, measurements, new_samples, samples_fetched) + + # The acquisition has stopped; keep the final chart on screen until its window is closed + if total_samples 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 measurements and displays them on a chart using the NI-DMM API.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-n', '--resource-name', default='PXI1Slot2', help='Contains the resource_name of the device to initialize.') + parser.add_argument('-f', '--function', default='DC_VOLTS', choices=nidmm.Function.__members__.keys(), type=str.upper, help='Specifies the measurement_function used to acquire the measurement.') + parser.add_argument('-r', '--range', default=10, type=float, help='Specifies the range for the function specified in the Measurement_Function parameter.') + parser.add_argument('-d', '--digits', default=5.5, type=float, help='Specifies the resolution of the measurement in digits.') + parser.add_argument('-s', '--samples-to-fetch', default=4, type=int, help='Specifies the number of measurements to fetch at a time.') + parser.add_argument('--total-samples', default=None, type=int, help='Stops fetching after this many samples and keeps the chart open until its window is closed; omit to run until the chart window is closed or Ctrl+C is pressed.') + parser.add_argument('-op', '--option-string', default='', type=str, help='Sets the initial value of certain attributes for the session.') + args = parser.parse_args(argsv) + example(args.resource_name, args.option_string, args.function, args.range, args.digits, args.samples_to_fetch, args.total_samples) + + +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': '4082', 'BoardType': 'PXIe', }, } + example('PXI1Slot2', options, 'DC_VOLTS', 10, 5.5, 4, 10) + + +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:4082; BoardType:PXIe', '--total-samples', '10', ] + _main(cmd_line) + + +if __name__ == '__main__': + main()