Skip to content
Snippets Groups Projects
Commit 61d54c8a authored by Merlo, Jason's avatar Merlo, Jason
Browse files

Added mcdaq and reformateed daq class

parent b3f403fa
No related branches found
No related tags found
No related merge requests found
......@@ -11,12 +11,6 @@ Maintainer: Jason Merlo (merlojas@msu.edu)
Dependencies: nidaqmx, random, Threading, time, numpy
"""
try:
import nidaqmx # Used for NI-DAQ hardware
from nidaqmx import stream_readers
except ImportError:
print('Warning: nidaqmx module not imported')
import threading # Used for creating thread and sync events
import time
import numpy as np
......@@ -32,78 +26,25 @@ class DAQ(QtCore.QThread):
data_available_signal = QtCore.pyqtSignal(tuple)
reset_signal = QtCore.pyqtSignal() # required for VirtualDAQ
def __init__(self, daq_type="NI-DAQ",
sample_rate=44100, sample_chunk_size=4096,
# NI-DAQ specific
dev_string="Dev1/ai0:7",
sample_mode=nidaqmx.constants.AcquisitionType.FINITE):
def __init__(self, sample_rate=50000, sample_chunk_size=50000,
num_channels=8):
"""
Create sampling task on DAQ and opens I & Q channels for radars.
Emits a signal when new data is available.
arguments:
dev_string -- device and ports to initialize (default: "Dev1/ai0:7")
sample_rate -- frequency in Hz to sample at (default: 44100)
sample_mode -- finite or continuous acquisition (default: finite)
sample_chunk_size -- size of chunk to read (default/max: 4095)
"""
super().__init__()
# Copy member data
# General arguments
self.sample_rate = sample_rate
self.sample_chunk_size = sample_chunk_size
self.num_channels = num_channels
self.update_period = sample_chunk_size / sample_rate
self.daq_type = daq_type
self.paused = True
self.running = False
self.sample_num = 0
# Device specific arguments
if self.daq_type == "NI-DAQ":
self.sample_mode = sample_mode
self.dev_string = dev_string
# Get number of channels to sample
if self.dev_string[-2] == ':':
self.num_channels = int(
self.dev_string[-1]) - int(self.dev_string[-3]) + 1
else:
self.num_channels = int(self.dev_string[-1]) + 1
# Create new sampling task
try:
# Try to create sampling task
self.task = nidaqmx.Task()
self.task.ai_channels.add_ai_voltage_chan(dev_string)
self.task.timing.cfg_samp_clk_timing(
sample_rate, sample_mode=sample_mode,
samps_per_chan=sample_chunk_size)
self.in_stream = \
stream_readers.AnalogMultiChannelReader(
self.task.in_stream)
except nidaqmx._lib.DaqNotFoundError:
# On failure (ex. on mac/linux) generate random data for
# development purposes
# TODO: switch to PyDAQmx for mac/linux
# TODO: is there any reason to keep nidaqmx for windows?
# TODO: try performance comparison
self.daq_type = "FakeDAQ"
print("="*80)
print("Warning: Using fake data. nidaqmx is not "
"supported on this platform.")
print("="*80)
except nidaqmx.errors.DaqError as e:
print(e)
self.daq_type = "FakeDAQ"
print("="*80)
print("Warning: Using fake data. DAQ could not be detected.")
print("="*80)
elif self.daq_type == "PyAudioDAQ":
pass # TODO insert pyaudio support here
# Create data member to store samples
self.data = np.empty((self.num_channels, self.sample_chunk_size),)
......@@ -112,69 +53,9 @@ class DAQ(QtCore.QThread):
shape = (self.num_channels, self.sample_chunk_size)
self.ts_buffer = TimeSeries(length, shape)
# === SAMPLING ======================================================
def sample_loop(self):
"""Call get_samples forever."""
while self.running:
if self.paused:
# warning('(daq.py) daq paused...')
time.sleep(0.1) # sleep 100 ms
else:
self.get_samples()
print("Sampling thread stopped.")
def get_samples(self):
"""Read device sample buffers returning the specified sample size."""
if self.daq_type == "FakeDAQ":
sleep_time = self.sample_chunk_size / self.sample_rate
self.data = np.random.randn(
self.num_channels, self.sample_chunk_size) * 0.001 + \
np.random.randn(1) * 0.001 + 0.01
time.sleep(sleep_time)
else:
try:
read_all = nidaqmx.constants.READ_ALL_AVAILABLE
self.in_stream.read_many_sample(
self.data,
number_of_samples_per_channel=read_all,
timeout=1.0)
# print('received update')
except nidaqmx.errors.DaqError as err:
print("DAQ exception caught: {0}\n".format(err))
new_data = (self.data, self.sample_num)
# Set the update event to True once data is read in
self.data_available_signal.emit(new_data)
self.ts_buffer.append(self.data)
# Incriment sample number
self.sample_num += 1
# === CONTROL =======================================================
def close(self):
print("Stopping sampling thread...")
self.running = False
if self.daq_type == "NI-DAQ" and hasattr(self, 'task'):
self.task.close() # Close nidaq gracefully
if self.t_sampling.is_alive():
try:
self.t_sampling.join()
except Exception as e:
print("Error closing sampling thread: ", e)
def run(self):
# Spawn sampling thread
self.running = True
self.t_sampling = threading.Thread(target=self.sample_loop)
try:
if not self.t_sampling.is_alive():
print('Staring sampling thread')
self.t_sampling.start()
# self.paused = False
except RuntimeError as e:
print('Error starting sampling thread: ', e)
def pause(self):
self.paused = True
......@@ -183,6 +64,14 @@ class DAQ(QtCore.QThread):
self.ts_buffer.clear()
self.sample_num = 0
def start(self):
"""
Begin sampling process on DAQ.
To be implemented by child classes.
"""
pass
# === PROPERTIES ====================================================
@property
def type(self):
......
......@@ -23,30 +23,6 @@ class VirtualDAQ(daq.DAQ):
def __init__(self):
"""Create virtual DAQ object to play back recording (hdf5 dataset)."""
super().__init__()
# Attributes
self.sample_rate = None
self.sample_chunk_size = None
self.daq_type = None
self.num_channels = None
# start paused if no dataset is selected
self.paused = True
# Fake sampler period
self.sample_period = None
# Create data member to store samples
self.data = None
self.ds = None
# create member data to store trajectory samples for ground truth
self.ts = None
# Current time index of recording
self.sample_index = 0
# Reset/load button sample thread locking
# self.reset_lock = threading.Event()
self.t_sampling = threading.Thread(target=self.sample_loop)
def load_dataset(self, ds):
"""Select dataset to read from and loads attributes."""
......
......@@ -13,7 +13,8 @@ import time # Used for FPS calculations
class FftWidget(pg.GraphicsLayoutWidget):
def __init__(self, radar, vmax_len=100, show_max_plot=False):
def __init__(self, radar, vmax_len=100, show_max_plot=False,
fft_yrange=[-100,100], fft_xrange=[-5000,5000]):
super(FftWidget, self).__init__()
# Copy arguments to member variables
......@@ -50,8 +51,8 @@ class FftWidget(pg.GraphicsLayoutWidget):
self.nextRow()
# Calculate reasonable ranges for FFT peak outputs
fft_xrange = [-50 / self.radar.bin_size, 50 / self.radar.bin_size]
fft_yrange = [-100, 0]
# fft_xrange = [-50 / self.radar.bin_size, 50 / self.radar.bin_size]
# fft_yrange = [-100, 0]
# Add FFT plot
self.fft_plot = self.addPlot()
......@@ -63,7 +64,8 @@ class FftWidget(pg.GraphicsLayoutWidget):
self.fft_plot.setRange(disableAutoRange=True,
xRange=fft_xrange, yRange=fft_yrange)
self.fft_plot.setLimits(
xMin=fft_xrange[0], xMax=fft_xrange[1], yMin=-80, yMax=0)
xMin=fft_xrange[0], xMax=fft_xrange[1],
yMin=fft_yrange[0], yMax=fft_yrange[1])
self.fft_pw = self.fft_plot.plot()
self.fft_max_freq_line = pg.InfiniteLine(angle=90, movable=False)
self.fft_max_pwr_line = pg.InfiniteLine(angle=0, movable=False)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment