Skip to content
Open
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 @@ -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
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 == 'nidmm':
${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/nidmm/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ Examples

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

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) <https://github.com/ni/nimi-python/blob/master/src/nidmm/examples/nidmm_cont_acq_chart_multiple_samples.py>`_

nidmm_fetch_waveform.py
-----------------------

Expand Down
1 change: 1 addition & 0 deletions generated/nidmm/tox-system_tests.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
96 changes: 96 additions & 0 deletions src/nidmm/examples/nidmm_cont_acq_chart_multiple_samples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/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 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)

figure, axes = plt.subplots()
(line,) = axes.plot([])
axes.set_title('Measurements')
axes.set_xlabel('Samples')
axes.set_ylabel('Measured value')
axes.grid(True)
Comment on lines +23 to +28

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this matplotlib stuff feels like a distraction from the demonstration of how to use nidmm. @marcoskirsch what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit more involved than what we did for the nidaqmx examples:

But that might be because it's doing a chart rather than a single graph.


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 (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentionally skipped the LabVIEW STOP button behavior to keep the example simple.
The loop stops when the chart window is closed or Ctrl+C is pressed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's certainly precedent for leaving out something like in our Python examples.
niscope_fetch_forever.py terminates its fetch while loop after fetching after a configured number of samples.

This, like that, is a continuous acquisition, so perhaps we should use a similar termination mechanism?
Does your current code update the graph continuously?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to niscope_fetch_forever.py, we now have a termination mechanism using total_samples. The user can configure the total number of samples to acquire, and the acquisition will stop once that count is reached.

If total_samples is not specified, the acquisition will continue until the user terminates it by pressing Ctrl+C or closing the chart window.

Please let me know if this aligns with what you had in mind.

Does your current code update the graph continuously?

Yes, the graph is updated continuously as new measurements are fetched. After each fetch, the newly acquired measurements are added to the chart, and the graph is refreshed to display the latest measurements. The chart maintains a rolling window of the most recent 250 measurements.

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)

measurements.extend(new_samples)
samples_fetched += len(new_samples)
first_sample = samples_fetched - len(measurements)
line.set_data(range(first_sample, samples_fetched), measurements)
axes.relim()
axes.autoscale_view()
plt.pause(0.001)

# 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()