Compare commits

...

7 Commits

8 changed files with 208 additions and 79 deletions

View File

@ -224,10 +224,10 @@ class MccDaq:
uldaq.AOutListFlag.DEFAULT, uldaq.AOutListFlag.DEFAULT,
[0, 0], [0, 0],
) )
except Exception as e: except Exception as er:
log.error("f{e}") log.error(f"{er}")
log.error("disconnection dac") log.error("disconnection dac")
self.disconnect_daq() # self.disconnect_daq()
def digital_trigger(self, ch: int = 0) -> None: def digital_trigger(self, ch: int = 0) -> None:
""" """

View File

@ -6,6 +6,8 @@ import uldaq
from IPython import embed from IPython import embed
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from scipy.signal import welch, find_peaks
import pyqtgraph as pg
from pyrelacs.devices.mccdaq import MccDaq from pyrelacs.devices.mccdaq import MccDaq
from pyrelacs.util.logging import config_logging from pyrelacs.util.logging import config_logging
@ -15,40 +17,50 @@ log = config_logging()
faulthandler.enable() faulthandler.enable()
class Calibration(MccDaq): class Calibration:
def __init__(self) -> None: def __init__(self, config, mccdaq: MccDaq) -> None:
super().__init__() self.config = config
self.mccdaq = mccdaq
self.SAMPLERATE = 40_000.0 self.SAMPLERATE = 40_000.0
self.DURATION = 5 self.DURATION = 5
self.AMPLITUDE = 1 self.AMPLITUDE = 1
self.SINFREQ = 750 self.SINFREQ = 750
@staticmethod @staticmethod
def run(nix_file: nix.File): def run(*args, **kwargs):
calb = Calibration() nix_block = args[0]
calb.check_beat(nix_file) figure = args[1]
mccdaq = args[2]
config = args[3]
calb = Calibration(config, mccdaq)
calb.check_beat(nix_block)
calb.plot(figure, nix_block)
return "finished"
def check_amplitude(self): def check_amplitude(self):
db_values = [0.0, -5.0, -10.0, -20.0, -50.0] db_values = [0.0, -5.0, -10.0, -20.0, -50.0]
colors = ["red", "green", "blue", "black", "yellow"] colors = ["red", "green", "blue", "black", "yellow"]
self.set_attenuation_level(db_channel1=0.0, db_channel2=0.0) self.mccdaq.set_attenuation_level(db_channel1=0.0, db_channel2=0.0)
# write to ananlog 1 # write to ananlog 1
t = np.arange(0, self.DURATION, 1 / self.SAMPLERATE) t = np.arange(0, self.DURATION, 1 / self.SAMPLERATE)
data = self.AMPLITUDE * np.sin(2 * np.pi * self.SINFREQ * t) data = self.AMPLITUDE * np.sin(2 * np.pi * self.SINFREQ * t)
fig, ax = plt.subplots() fig, ax = plt.subplots()
for i, db_value in enumerate(db_values): for i, db_value in enumerate(db_values):
self.set_attenuation_level(db_channel1=db_value, db_channel2=db_value) self.mccdaq.set_attenuation_level(
db_channel1=db_value, db_channel2=db_value
)
log.debug(f"{db_value}") log.debug(f"{db_value}")
stim = self.write_analog( stim = self.mccdaq.write_analog(
data, data,
[0, 0], [0, 0],
self.SAMPLERATE, self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER, ScanOption=uldaq.ScanOption.EXTTRIGGER,
) )
data_channel_one = self.read_analog( data_channel_one = self.mccdaq.read_analog(
[0, 0], [0, 0],
self.DURATION, self.DURATION,
self.SAMPLERATE, self.SAMPLERATE,
@ -57,21 +69,21 @@ class Calibration(MccDaq):
time.sleep(1) time.sleep(1)
log.debug("Starting the Scan") log.debug("Starting the Scan")
self.digital_trigger() self.mccdaq.digital_trigger()
try: try:
self.ao_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, 15) self.mccdaq.ao_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, 15)
log.debug("Scan finished") log.debug("Scan finished")
self.write_bit(channel=0, bit=0) self.mccdaq.write_bit(channel=0, bit=0)
time.sleep(1) time.sleep(1)
self.set_analog_to_zero() self.mccdaq.set_analog_to_zero()
except uldaq.ul_exception.ULException: except uldaq.ul_exception.ULException:
log.debug("Operation timed out") log.debug("Operation timed out")
# reset the diggital trigger # reset the diggital trigger
self.write_bit(channel=0, bit=0) self.mccdaq.write_bit(channel=0, bit=0)
time.sleep(1) time.sleep(1)
self.set_analog_to_zero() self.mccdaq.set_analog_to_zero()
self.disconnect_daq() # self.mccdaq.disconnect_daq()
if i == 0: if i == 0:
ax.plot(t, stim, label=f"Input_{db_value}", color=colors[i]) ax.plot(t, stim, label=f"Input_{db_value}", color=colors[i])
@ -80,34 +92,33 @@ class Calibration(MccDaq):
ax.legend() ax.legend()
plt.show() plt.show()
self.disconnect_daq() # self.mccdaq.disconnect_daq()
def check_beat(self, nix_file: nix.File): def check_beat(self, nix_block: nix.Block):
self.set_attenuation_level(db_channel1=-10.0, db_channel2=0.0) self.mccdaq.set_attenuation_level(db_channel1=-10.0, db_channel2=0.0)
t = np.arange(0, self.DURATION, 1 / self.SAMPLERATE) t = np.arange(0, self.DURATION, 1 / self.SAMPLERATE)
data = self.AMPLITUDE * np.sin(2 * np.pi * self.SINFREQ * t) data = self.AMPLITUDE * np.sin(2 * np.pi * self.SINFREQ * t)
# data = np.concatenate((data, data)) # data = np.concatenate((data, data))
db_values = [0.0, -5.0, -8.5, -10.0] db_values = [0.0, -5.0, -8.5, -10.0]
colors = ["red", "blue", "black", "green"] colors = ["red", "blue", "black", "green"]
colors_in = ["lightcoral", "lightblue", "grey", "lightgreen"] colors_in = ["lightcoral", "lightblue", "grey", "lightgreen"]
block = nix_file.create_block("Calibration", "data")
# fig, axes = plt.subplots(2, 2, sharex="col") # fig, axes = plt.subplots(2, 2, sharex="col")
for i, db_value in enumerate(db_values): for i, db_value in enumerate(db_values):
self.set_attenuation_level(db_channel1=db_value) self.mccdaq.set_attenuation_level(db_channel1=db_value)
stim = self.write_analog( stim = self.mccdaq.write_analog(
data, data,
[0, 0], [0, 0],
self.SAMPLERATE, self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER, ScanOption=uldaq.ScanOption.EXTTRIGGER,
) )
readout = self.read_analog( readout = self.mccdaq.read_analog(
[0, 1], [0, 1],
self.DURATION, self.DURATION,
self.SAMPLERATE, self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER, ScanOption=uldaq.ScanOption.EXTTRIGGER,
) )
self.digital_trigger() self.mccdaq.digital_trigger()
log.info(self.ao_device) log.info(self.mccdaq.ao_device)
ai_status = uldaq.ScanStatus.RUNNING ai_status = uldaq.ScanStatus.RUNNING
ao_status = uldaq.ScanStatus.RUNNING ao_status = uldaq.ScanStatus.RUNNING
@ -119,10 +130,10 @@ class Calibration(MccDaq):
): ):
# log.debug("Scanning") # log.debug("Scanning")
time.time_ns() time.time_ns()
ai_status = self.ai_device.get_scan_status()[0] ai_status = self.mccdaq.ai_device.get_scan_status()[0]
ao_status = self.ao_device.get_scan_status()[0] ao_status = self.mccdaq.ao_device.get_scan_status()[0]
self.write_bit(channel=0, bit=0) self.mccdaq.write_bit(channel=0, bit=0)
log.debug( log.debug(
f"Status Analog_output {ao_status}\n, Status Analog_input {ai_status}" f"Status Analog_output {ao_status}\n, Status Analog_input {ai_status}"
) )
@ -130,7 +141,7 @@ class Calibration(MccDaq):
channel1 = np.array(readout[::2]) channel1 = np.array(readout[::2])
channel2 = np.array(readout[1::2]) channel2 = np.array(readout[1::2])
stim_data = block.create_data_array( stim_data = nix_block.create_data_array(
f"stimulus_{db_value}", f"stimulus_{db_value}",
"nix.regular_sampled", "nix.regular_sampled",
shape=data.shape, shape=data.shape,
@ -143,7 +154,7 @@ class Calibration(MccDaq):
label="time", label="time",
unit="s", unit="s",
) )
fish_data = block.create_data_array( fish_data = nix_block.create_data_array(
f"fish_{db_value}", f"fish_{db_value}",
"Array", "Array",
shape=data.shape, shape=data.shape,
@ -157,7 +168,72 @@ class Calibration(MccDaq):
unit="s", unit="s",
) )
self.set_analog_to_zero() time.time_ns()
self.mccdaq.set_analog_to_zero()
def plot(self, figure, block):
self.figure = figure
self.figure.setBackground("w")
self.beat_plot = self.figure.addPlot(row=0, col=0)
self.power_plot = self.figure.addPlot(row=1, col=0)
self.beat_plot.addLegend()
self.power_plot.addLegend()
# self.power_plot.setLogMode(x=False, y=True)
colors = ["red", "green", "blue", "black", "yellow"]
for i, (stim, fish) in enumerate(
zip(list(block.data_arrays)[::2], list(block.data_arrays)[1::2])
):
f_stim, stim_power = welch(
stim[:],
fs=40_000.0,
window="flattop",
nperseg=100_000,
)
stim_power = decibel(stim_power)
stim_max_power_index = np.argmax(stim_power)
freq_stim = f_stim[stim_max_power_index]
f_fish, fish_power = welch(
fish[:],
fs=40_000.0,
window="flattop",
nperseg=100_000,
)
fish_power = decibel(fish_power)
fish_max_power_index = np.argmax(fish_power)
freq_fish = f_fish[fish_max_power_index]
beat_frequency = np.abs(freq_fish - freq_stim)
beat = stim[:] + fish[:]
beat_squared = beat**2
f, powerspec = welch(
beat_squared,
window="flattop",
fs=40_000.0,
nperseg=100_000,
)
powerspec = decibel(powerspec)
padding = 20
integration_window = powerspec[
(f > beat_frequency - padding) & (f < beat_frequency + padding)
]
peaks = find_peaks(powerspec, prominence=40)[0]
pen = pg.mkPen(colors[i])
self.beat_plot.plot(
np.arange(0, len(beat)) / 40_000.0,
beat,
pen=pen,
name=stim.name,
)
self.power_plot.plot(f, powerspec, pen=pen, name=stim.name)
self.power_plot.plot(f[peaks], powerspec[peaks], pen=None, symbol="x")
def decibel(power, ref_power=1.0, min_power=1e-20): def decibel(power, ref_power=1.0, min_power=1e-20):

View File

View File

@ -22,9 +22,7 @@ class Repro:
def __init__(self) -> None: def __init__(self) -> None:
pass pass
def run_repro( def run_repro(self, name: str, file: pathlib.Path, *args, **kwargs) -> None:
self, nix_file: nix.File, name: str, file: pathlib.Path, *args, **kwargs
) -> None:
spec = importlib.util.spec_from_file_location("rep", file) spec = importlib.util.spec_from_file_location("rep", file)
if not spec: if not spec:
log.error("Could not load the file") log.error("Could not load the file")
@ -40,7 +38,7 @@ class Repro:
log.error(f"{spec.loader} is None") log.error(f"{spec.loader} is None")
if hasattr(module, name): if hasattr(module, name):
rep_class = getattr(module, name) rep_class = getattr(module, name)
rep_class.run(nix_file) rep_class.run(*args, **kwargs)
else: else:
raise AttributeError(f"{file.name} has no {name} class") raise AttributeError(f"{file.name} has no {name} class")

View File

@ -1,3 +0,0 @@
class Sinus:
def __init__(self) -> None:
pass

View File

@ -0,0 +1,17 @@
import nixio
from pyrelacs.util.logging import config_logging
log = config_logging()
class Sinus:
def __init__(self) -> None:
pass
@staticmethod
def run(config, mccdaq, nix_block: nixio.Block, figure) -> None:
log.debug(config)
log.debug(mccdaq)
log.debug(nix_block)
log.debug(figure)

View File

@ -7,7 +7,10 @@ from PyQt6.QtGui import QAction, QIcon, QKeySequence
from PyQt6.QtCore import Qt, QSize, QThreadPool, QMutex from PyQt6.QtCore import Qt, QSize, QThreadPool, QMutex
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QGridLayout, QGridLayout,
QPushButton,
QTabWidget,
QToolBar, QToolBar,
QVBoxLayout,
QWidget, QWidget,
QMainWindow, QMainWindow,
QPlainTextEdit, QPlainTextEdit,
@ -52,6 +55,8 @@ class PyRelacs(QMainWindow):
self.mccdaq = MccDaq() self.mccdaq = MccDaq()
end = time.time() end = time.time()
log.debug(f"Connection to DAQ took {end - start}") log.debug(f"Connection to DAQ took {end - start}")
else:
self.mccdaq = None
self.repros = Repro() self.repros = Repro()
@ -60,6 +65,8 @@ class PyRelacs(QMainWindow):
) # Ensure icons are displayed with text ) # Ensure icons are displayed with text
self.setWindowTitle("PyRelacs") self.setWindowTitle("PyRelacs")
self.create_nix_file(f"{_root}/test.nix", self.config.metadata)
self.mutex = QMutex() self.mutex = QMutex()
self.figure = pg.GraphicsLayoutWidget() self.figure = pg.GraphicsLayoutWidget()
@ -73,10 +80,13 @@ class PyRelacs(QMainWindow):
self.setStatusBar(QStatusBar(self)) self.setStatusBar(QStatusBar(self))
self.create_actions() self.create_actions()
self.create_toolbars() self.create_toolbars()
self.repro_tabs = QTabWidget()
self.create_repros_tabs()
layout = QGridLayout() layout = QGridLayout()
layout.addWidget(self.figure, 0, 0, 2, 2) layout.addWidget(self.figure, 0, 0, 2, 2)
layout.addWidget(self.text, 2, 0, 1, 2) layout.addWidget(self.repro_tabs, 2, 0, 2, 2)
layout.addWidget(self.text, 4, 0, 1, 1)
widget = QWidget() widget = QWidget()
widget.setLayout(layout) widget.setLayout(layout)
@ -104,14 +114,13 @@ class PyRelacs(QMainWindow):
self.continously_plot = Continously(self.figure, self.buffer) self.continously_plot = Continously(self.figure, self.buffer)
# self.continously_plot.plot() # self.continously_plot.plot()
if self.config.settings.daq: if self.mccdaq:
log.debug("Creating Daq Generator") log.debug("Creating Daq Generator")
self.daq_producer = DaqProducer(self.buffer, self.mccdaq.daq_device, [1, 1]) self.daq_producer = DaqProducer(self.buffer, self.mccdaq.daq_device, [1, 1])
log.debug("Creating Sinus Generator") log.debug("Creating Sinus Generator")
self.sinus_producer = SinProducer(self.buffer) self.sinus_producer = SinProducer(self.buffer)
self.nix_writer = NixWriter(self.buffer) self.nix_writer = NixWriter(self.buffer)
self.create_nix_file(f"{_root}/test.nix", self.config.metadata)
def create_actions(self): def create_actions(self):
self._rlx_exitaction = QAction(QIcon(":/icons/exit.png"), "Exit", self) self._rlx_exitaction = QAction(QIcon(":/icons/exit.png"), "Exit", self)
@ -127,7 +136,7 @@ class PyRelacs(QMainWindow):
self._daq_connectaction = QAction( self._daq_connectaction = QAction(
QIcon(":icons/connect.png"), "Connect DAQ", self QIcon(":icons/connect.png"), "Connect DAQ", self
) )
if self.config.settings.daq: if self.mccdaq:
self._daq_connectaction.setStatusTip("Connect to daq device") self._daq_connectaction.setStatusTip("Connect to daq device")
# self._daq_connectaction.setShortcut(QKeySequence("Alt+d")) # self._daq_connectaction.setShortcut(QKeySequence("Alt+d"))
self._daq_connectaction.triggered.connect(self.mccdaq.connect_dac) self._daq_connectaction.triggered.connect(self.mccdaq.connect_dac)
@ -146,7 +155,7 @@ class PyRelacs(QMainWindow):
# # self._daq_calibaction.setShortcut(QKeySequence("Alt+d")) # # self._daq_calibaction.setShortcut(QKeySequence("Alt+d"))
# self._daq_calibaction.triggered.connect(self.calibration_plot.plot) # self._daq_calibaction.triggered.connect(self.calibration_plot.plot)
self._run_action = QAction(QIcon(":/icons/record.png"), "Run", self) self._run_action = QAction(QIcon(":/icons/record.png"), "RunDAQ", self)
self._run_action.triggered.connect(self.run_daq) self._run_action.triggered.connect(self.run_daq)
self._run_sinus_action = QAction(QIcon(":/icons/record.png"), "Sinus", self) self._run_sinus_action = QAction(QIcon(":/icons/record.png"), "Sinus", self)
@ -211,18 +220,60 @@ class PyRelacs(QMainWindow):
daq_toolbar.addAction(self._record) daq_toolbar.addAction(self._record)
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, daq_toolbar) self.addToolBar(Qt.ToolBarArea.TopToolBarArea, daq_toolbar)
repro_toolbar = QToolBar("Repros") def create_repros_tabs(self):
repro_names, file_names = self.repros.names_of_repros( repro_names, file_names = self.repros.names_of_repros(
include_repros=self.config.settings.repros include_repros=self.config.settings.repros
) )
nix_blocks = {
rep: self.nix_file.create_block(f"{rep}", "Data Repro")
for rep in repro_names
}
figures_repros = {rep: pg.GraphicsLayoutWidget() for rep in repro_names}
for rep, fn in zip(repro_names, file_names): for rep, fn in zip(repro_names, file_names):
repro_action = QAction(rep, self) tab = QWidget()
repro_action.setStatusTip(rep) tab_layout = QGridLayout()
repro_action.triggered.connect(
lambda checked, n=rep, f=fn: self.run_repro(n, f) run_repro_button = QPushButton(f"Run {rep}")
run_repro_button.setCheckable(True)
run_repro_button.clicked.connect(
lambda checked, n=rep, f=fn: self.run_repro(
n,
f,
nix_blocks,
figures_repros,
self.mccdaq,
self.config,
)
) )
repro_toolbar.addAction(repro_action) tab_layout.addWidget(run_repro_button, 0, 0, 1, 0)
self.addToolBar(Qt.ToolBarArea.BottomToolBarArea, repro_toolbar) tab_layout.addWidget(figures_repros[rep], 1, 0, 1, 1)
tab.setLayout(tab_layout)
self.repro_tabs.addTab(tab, f"{rep}")
def run_repro(
self,
name_of_repro: str,
file_of_repro: str,
nix_block,
figures,
*args,
):
self.text.appendPlainText(f"started Repro {name_of_repro}, {file_of_repro}")
nix_block_repro = nix_block[name_of_repro]
figure_repro = figures[name_of_repro]
worker = Worker(
self.repros.run_repro,
name_of_repro,
file_of_repro,
nix_block_repro,
figure_repro,
*args[-2:],
)
worker.signals.result.connect(self.print_output)
worker.signals.finished.connect(self.thread_complete)
worker.signals.progress.connect(self.progress_fn)
self.threadpool.start(worker)
def create_nix_file(self, file_path, metadata): def create_nix_file(self, file_path, metadata):
self.nix_file = nixio.File.open( self.nix_file = nixio.File.open(
@ -311,17 +362,6 @@ class PyRelacs(QMainWindow):
if hasattr(PyRelacs, "daq_device"): if hasattr(PyRelacs, "daq_device"):
log.debug("Stopping DAQ") log.debug("Stopping DAQ")
self.daq_producer.stop_aquisition() self.daq_producer.stop_aquisition()
embed()
exit()
def run_repro(self, n, fn):
self.text.appendPlainText(f"started Repro {n}, {fn}")
worker = Worker(self.repros.run_repro, self.nix_calibration, n, fn)
worker.signals.result.connect(self.print_output)
worker.signals.finished.connect(self.thread_complete)
worker.signals.progress.connect(self.progress_fn)
self.threadpool.start(worker)
def add_to_textfield(self, s: str): def add_to_textfield(self, s: str):
self.text.appendPlainText(s) self.text.appendPlainText(s)
@ -330,7 +370,7 @@ class PyRelacs(QMainWindow):
log.info("exit button!") log.info("exit button!")
self.stop_recording() self.stop_recording()
self.add_to_textfield("exiting") self.add_to_textfield("exiting")
if self.config.settings.daq: if self.mccdaq:
self.mccdaq.disconnect_daq() self.mccdaq.disconnect_daq()
log.info("closing GUI") log.info("closing GUI")
self.close() self.close()

View File

@ -16,6 +16,7 @@ class Continously:
self.figure = figure self.figure = figure
self.buffer = buffer self.buffer = buffer
self.last_plotted_index = 0 self.last_plotted_index = 0
self.timer = QTimer()
def plot(self, *args, **kwargs): def plot(self, *args, **kwargs):
self.figure.setBackground("w") self.figure.setBackground("w")
@ -36,7 +37,6 @@ class Continously:
) )
# self.plot_index = 0 # self.plot_index = 0
self.timer = QTimer()
self.CHUNK_PLOT = int(self.buffer.samplerate / 6) self.CHUNK_PLOT = int(self.buffer.samplerate / 6)
self.PLOT_HISTORY = 500_000 # The amount of data you want to keep on screen self.PLOT_HISTORY = 500_000 # The amount of data you want to keep on screen
self.timer.setInterval(150) self.timer.setInterval(150)
@ -70,18 +70,19 @@ class Continously:
def stop_plotting(self): def stop_plotting(self):
self.timer.stop() self.timer.stop()
total_count = self.buffer.totalcount() if self.last_plotted_index > 0:
times, items = self.buffer.read( total_count = self.buffer.totalcount()
self.last_plotted_index, times, items = self.buffer.read(
extend=total_count - self.last_plotted_index, self.last_plotted_index,
) extend=total_count - self.last_plotted_index,
self.time = np.concatenate((self.time, times))[-self.PLOT_HISTORY :] )
self.data = np.concatenate((self.data, items))[-self.PLOT_HISTORY :] self.time = np.concatenate((self.time, times))[-self.PLOT_HISTORY :]
self.line.setData( self.data = np.concatenate((self.data, items))[-self.PLOT_HISTORY :]
self.time, self.line.setData(
self.data, self.time,
) self.data,
self.last_plotted_index += total_count - self.last_plotted_index )
self.last_plotted_index += total_count - self.last_plotted_index
def refresh(self): def refresh(self):
self.continous_ax.enableAutoRange() self.continous_ax.enableAutoRange()