Skip to content

Commit db340a9

Browse files
committed
waveform tester exercise
1 parent f4f7804 commit db340a9

File tree

6 files changed

+299
-2
lines changed

6 files changed

+299
-2
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
*-checkpoint.ipynb
2+
*.DS_Store

Diff for: exercises/raster.png

12.2 KB
Loading

Diff for: exercises/scan_waveform_output.py

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
'''
2+
Exercise in which a DAQ board will generate hardware-timed continuous analog output from two
3+
channels without using a callback function
4+
5+
scan_waveform_output
6+
7+
Purpose
8+
You will do hardware-timed continuous analog output using sample regeneration instead of a callback function.
9+
The buffer contents is played out repeatedly: once the end of the buffer is reached, the DAQ returns to the
10+
beginning and resumes from there.
11+
12+
Monitoring the output
13+
If you lack an oscilloscope you may physically connect the analog output to
14+
an analog input and monitor this using the NI MAX test panel. You likely will need
15+
to select RSE: http://www.ni.com/white-paper/3344/en/
16+
Ask MAX to display "AI0:1"
17+
18+
Instructions
19+
- Wire AO0 to AO0 or to an osciloscope. Wire AO1 to AO1 or to an osciloscope.
20+
- You will need to edit the lines at the locations marked by the string ### EDIT
21+
- There may be hints and instructions around those lines
22+
23+
24+
Run the code at the command line thus:
25+
python scan_waveform_output
26+
27+
28+
29+
30+
For more:
31+
https://github.com/tenss/Python_DAQmx_examples
32+
33+
'''
34+
35+
import nidaqmx
36+
from nidaqmx.constants import (AcquisitionType,RegenerationMode)
37+
import numpy as np
38+
39+
class scan_waveform_output():
40+
41+
# Properties of the class defined here
42+
43+
# Parameters for the acquisition (device and channels)
44+
dev_name = '' ### EDIT (The name of the DAQ device as shown in MAX)
45+
46+
# Task configuration
47+
sample_rate = 5000 # Sample Rate in Hz
48+
galvo_amplitude = 5 # Scanner amplitude (defined as peak-to-peak/2)
49+
pixels_per_line = 256 # Number pixels per line for a sawtooth waveform (for sine wave this defines wavelength)
50+
51+
52+
x_waveform = [] # Vector containing the waveform for the x mirror (fast axis)
53+
y_waveform = [] # Vector containing the waveform for the y mirror (slow axis)
54+
55+
daq_waveforms = [] # Will contain the x and y waveform data to be sent to the DAQ
56+
57+
num_samples_per_channel = [] #The length of the waveform
58+
59+
h_task = [] # DAQmx task handle
60+
61+
62+
def __init__(self, autoconnect=False):
63+
# This method is the constructor and runs once when the class is instantiated
64+
65+
self.generate_waveforms()
66+
if autoconnect:
67+
self.create_task()
68+
#close constructor
69+
70+
71+
def generate_waveforms(self):
72+
###EDIT -- complete this method
73+
self.y_waveform =
74+
self.x_waveform =
75+
76+
# This must be a two column matrix. The first column is the waveform sent to AO0
77+
# and the second column is the waveform sent to AO1.
78+
self.daq_waveforms = np.stack() ###EDIT
79+
#close generate_waveforms
80+
81+
82+
def create_task(self):
83+
# This method sets up an NI Task that will play out the waveforms
84+
85+
# * Create a DAQmx task
86+
# http://zone.ni.com/reference/en-XX/help/370471AE-01/daqmxcfunc/daqmxcreatetask/
87+
self.h_task = nidaqmx.Task('scanwave')
88+
89+
90+
# * Set up analog output on channels 0 and 1
91+
# C equivalent - DAQmxCreateAOVoltageChan
92+
# http://zone.ni.com/reference/en-XX/help/370471AE-01/daqmxcfunc/daqmxcreateaovoltagechan/
93+
# https://nidaqmx-python.readthedocs.io/en/latest/ao_channel_collection.html
94+
connect_at = '%s/ao0:1' % self.dev_name
95+
self.h_task.ao_channels.add_ao_voltage_chan(connect_at)
96+
97+
98+
99+
# * Configure the sampling rate and the number of samples
100+
# C equivalent - DAQmxCfgSampClkTiming
101+
# http://zone.ni.com/reference/en-XX/help/370471AE-01/daqmxcfunc/daqmxcfgsampclktiming/
102+
# https://nidaqmx-python.readthedocs.io/en/latest/timing.html
103+
self.num_samples_per_channel = len(self.waveform) # The number of samples to be stored in the buffer per channel
104+
buffer_length = self.num_samples_per_channel*4 # TODO -- IS THIS NEEDED??
105+
self.h_task.timing.cfg_samp_clk_timing(rate = self.sample_rate, \
106+
samps_per_chan = buffer_length, \
107+
sample_mode = AcquisitionType.CONTINUOUS)
108+
109+
110+
# * Set up sample regeneration: i.e. the buffer contents will play continuously
111+
# http://zone.ni.com/reference/en-XX/help/370471AE-01/mxcprop/attr1453/
112+
# For more on DAQmx write properties: http://zone.ni.com/reference/en-XX/help/370469AG-01/daqmxprop/daqmxwrite/
113+
# For a discussion on regeneration mode in the context of analog output tasks see:
114+
# https://forums.ni.com/t5/Multifunction-DAQ/Continuous-write-analog-voltage-NI-cDAQ-9178-with-callbacks/td-p/4036271
115+
self.h_task.out_stream.regen_mode = RegenerationMode.ALLOW_REGENERATION
116+
print('Regeneration mode is set to: %s' % str(self.h_task.out_stream.regen_mode))
117+
118+
119+
120+
# * Write the waveform to the buffer with a 5 second timeout in case it fails
121+
# Writes doubles using DAQmxWriteAnalogF64
122+
# http://zone.ni.com/reference/en-XX/help/370471AG-01/daqmxcfunc/daqmxwriteanalogf64/
123+
self.h_task.write(, timeout=2). ###EDIT
124+
#close create_task
125+
126+
127+
#close class scan_waveform_output
128+
129+
130+
131+
if __name__ == '__main__':
132+
print('\nRunning demo for hardwareContinuousVoltageNoCallback_twoChannels\n\n')
133+
AO = hardwareContinuousVoltageNoCallback_twoChannels()
134+
AO.create_task()
135+
AO.h_task.start()
136+
input('press return to stop')
137+
AO.h_task.start()

Diff for: exercises/scanwaveforms.ipynb

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"[](raster.png)"
8+
]
9+
},
10+
{
11+
"cell_type": "markdown",
12+
"metadata": {},
13+
"source": [
14+
"In this exercise you will generate scanner X/Y waveforms capable of producing a raster pattern that moves the beam in shape over the image:\n",
15+
"![alt text](raster.png \"Title\")\n",
16+
"The goal is to generate waveforms that will produce a square image: one where the number of pixels per line equals the number of lines. Note carefully the shape of the waveform: the beam scans across each line in the same direction. "
17+
]
18+
},
19+
{
20+
"cell_type": "code",
21+
"execution_count": 4,
22+
"metadata": {},
23+
"outputs": [],
24+
"source": [
25+
"import numpy as np\n",
26+
"import matplotlib.pyplot as plt"
27+
]
28+
},
29+
{
30+
"cell_type": "code",
31+
"execution_count": 1,
32+
"metadata": {},
33+
"outputs": [],
34+
"source": [
35+
"# You will use the following two constants to help you build the waveforms. \n",
36+
"imsize = 128. # target image size in pixels (imsize x imsize)\n",
37+
"scan_amplitude = 2 # The scanners will move between -scan_amplitude and +scan_amplitude"
38+
]
39+
},
40+
{
41+
"cell_type": "code",
42+
"execution_count": null,
43+
"metadata": {},
44+
"outputs": [],
45+
"source": [
46+
"# Over the course of one frame the slow (y) scanner will move from one \n",
47+
"# extreme of the amplitude range to the other to the other once. \n",
48+
"# The number of pixels (samples) it takes to do this will be equal to \n",
49+
"# the total number of pixels in the frame.\n",
50+
"#\n",
51+
"# In one line of code, define the y waveform. (HINT: linspace)\n",
52+
"\n",
53+
"y_waveform = \n"
54+
]
55+
},
56+
{
57+
"cell_type": "code",
58+
"execution_count": null,
59+
"metadata": {},
60+
"outputs": [],
61+
"source": [
62+
"# The x mirror will move over the amplitude range once per line. \n",
63+
"#\n",
64+
"# In one line of code, define the x waveform for one scan line.\n",
65+
"# HINT: it's almost the same as what you did for y_waveform.\n",
66+
"one_scan_line = \n",
67+
"\n",
68+
"# Use the vector you created to generate the complete x waveform\n",
69+
"# for one frame. Think how many times you have to repeat the waveform\n",
70+
"x_waveform =\n"
71+
]
72+
},
73+
{
74+
"cell_type": "markdown",
75+
"metadata": {},
76+
"source": [
77+
"## Run the following cells to check your working"
78+
]
79+
},
80+
{
81+
"cell_type": "code",
82+
"execution_count": null,
83+
"metadata": {},
84+
"outputs": [],
85+
"source": [
86+
"# Y waveform\n",
87+
"plt.plot(y_waveform)\n",
88+
"plt.title('y waveform')\n",
89+
"plt.ylabel('voltage')\n",
90+
"plt.show()"
91+
]
92+
},
93+
{
94+
"cell_type": "code",
95+
"execution_count": null,
96+
"metadata": {},
97+
"outputs": [],
98+
"source": [
99+
"# X waveform\n",
100+
"plt.plot(x_waveform)\n",
101+
"plt.title('x waveform')\n",
102+
"plt.ylabel('voltage')\n",
103+
"plt.xlabel('# samples')\n",
104+
"plt.show()"
105+
]
106+
},
107+
{
108+
"cell_type": "code",
109+
"execution_count": null,
110+
"metadata": {},
111+
"outputs": [],
112+
"source": [
113+
"# The raster waveform\n",
114+
"plt.plot(x_waveform,y_waveform)\n",
115+
"plt.axis('equal')\n",
116+
"plt.axis('off')\n",
117+
"plt.show()\n",
118+
"\n",
119+
"print('X waveform length: %d\\nY waveform length: %d\\nLength of scan line in samples: %d\\n' % \\\n",
120+
" (len(y_waveform), len(x_waveform), len(one_scan_line)) )\n"
121+
]
122+
}
123+
],
124+
"metadata": {
125+
"kernelspec": {
126+
"display_name": "Python 3",
127+
"language": "python",
128+
"name": "python3"
129+
},
130+
"language_info": {
131+
"codemirror_mode": {
132+
"name": "ipython",
133+
"version": 3
134+
},
135+
"file_extension": ".py",
136+
"mimetype": "text/x-python",
137+
"name": "python",
138+
"nbconvert_exporter": "python",
139+
"pygments_lexer": "ipython3",
140+
"version": "3.7.4"
141+
}
142+
},
143+
"nbformat": 4,
144+
"nbformat_minor": 4
145+
}

Diff for: src/scanwaveforms.ipynb

+15-1
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,22 @@
5757
"plt.title('x waveform')\n",
5858
"plt.ylabel('voltage')\n",
5959
"plt.xlabel('# samples')\n",
60+
"plt.show()\n",
61+
"\n",
62+
"plt.plot(x_waveform,y_waveform_stepped)\n",
63+
"plt.ylabel('voltage')\n",
64+
"plt.xlabel('# samples')\n",
65+
"plt.axis('equal')\n",
66+
"plt.axis('off')\n",
6067
"plt.show()"
6168
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": null,
73+
"metadata": {},
74+
"outputs": [],
75+
"source": []
6276
}
6377
],
6478
"metadata": {
@@ -77,7 +91,7 @@
7791
"name": "python",
7892
"nbconvert_exporter": "python",
7993
"pygments_lexer": "ipython3",
80-
"version": "3.8.3"
94+
"version": "3.7.4"
8195
}
8296
},
8397
"nbformat": 4,

Diff for: src/waveformTester.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def line_period(self):
312312
return LP
313313
#close line_period
314314

315-
#close class waveformTester()
315+
#close class waveformTester
316316

317317

318318

0 commit comments

Comments
 (0)