9 Commits

Author SHA1 Message Date
19c6b90d5c [test] add buffer test stub 2024-10-02 10:28:50 +02:00
a7b62c5b3a [ringbuffer] first implementation 2024-10-02 10:28:36 +02:00
wendtalexander
54f0d61fc9 [dataio] commenting out databuffer 2024-10-02 08:10:08 +02:00
wendtalexander
815838eab7 [dataio] adding class for buffer 2024-10-02 08:09:45 +02:00
wendtalexander
314e609472 [refactoring] removing imports 2024-10-01 18:16:33 +02:00
wendtalexander
2719d49eb0 [refacorting] adding DataBuffer 2024-10-01 12:04:47 +02:00
wendtalexander
a0c524326d [refactroring] renaming class 2024-10-01 12:04:06 +02:00
wendtalexander
c2285f3750 [project] adding buffer class 2024-10-01 12:03:41 +02:00
wendtalexander
9327d1bac9 [refactoring] renaming file 2024-10-01 12:03:25 +02:00
6 changed files with 256 additions and 11 deletions

81
pyrelacs/dataio/buffer.py Normal file
View File

@@ -0,0 +1,81 @@
import time
import faulthandler
from collections import deque
from pyqtgraph import transformToArray
import uldaq
import numpy as np
from IPython import embed
import matplotlib.pyplot as plt
from pyrelacs.util.logging import config_logging
log = config_logging()
faulthandler.enable()
class DataBuffer:
def __init__(self, channels, samples):
self.channels = channels
self.samples = samples
def read_analog_continously(
self,
device: uldaq.DaqDevice,
samplerate: float = 40_000.0,
):
data_array = []
max_len_buffer = self.channels * self.samples
self.buffer = deque(maxlen=max_len_buffer)
samples_per_channel = 40_000
self.device = device
self.ai_device = self.device.get_ai_device()
data_analog_input = uldaq.create_float_buffer(
self.channels, samples_per_channel
)
er = self.ai_device.a_in_scan(
0,
1,
uldaq.AiInputMode.SINGLE_ENDED,
uldaq.Range.BIP10VOLTS,
samples_per_channel,
samplerate,
uldaq.ScanOption.CONTINUOUS,
uldaq.AInScanFlag.DEFAULT,
data=data_analog_input,
)
daq_status = uldaq.ScanStatus.IDLE
while daq_status == uldaq.ScanStatus.IDLE:
daq_status = self.ai_device.get_scan_status()[0]
prev_count = 0
prev_index = 0
while daq_status != uldaq.ScanStatus.IDLE:
daq_status, transfer_status = self.ai_device.get_scan_status()
# The index into the data buffer immediately following the last sample transferred.
curren_index = transfer_status.current_index
# total samples since start of the scan
total_samples = transfer_status.current_total_count
# The number of samples per channel transferred since the scan started
channel_samples = transfer_status.current_scan_count
self.ai_device.scan_stop()
if __name__ == "__main__":
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
try:
daq_device = uldaq.DaqDevice(devices[0])
except uldaq.ul_exception.ULException as e:
log.error("Did not found daq devices, please connect one")
raise e
daq_device.connect()
buf = DataBuffer(channels=2, samples=100_000)
buf.read_analog_continously(daq_device)

View File

@@ -0,0 +1,110 @@
import numpy as np
class CircBuffer:
def __init__(self, size: int, channels: int = 1):
self._size = size
self._channels = channels
self._buffer = np.zeros((channels, size), dtype=np.double) # or dtype of your choice
self._index = [0 for i in range(channels)]
self._is_full = [False for i in range(channels)]
self._totalcount = [0 for i in range(channels)]
self._overflows = [0 for i in range(channels)]
@property
def size(self):
return self._size
@property
def channel_count(self):
return self._channels
def is_full(self, channel: int = 0):
return self._is_full[channel]
def write_index(self, channel: int = 0):
return self._index[channel]
def append(self, item, channel: int = 0):
self._buffer[channel, self.write_index(channel)] = item
self._index[channel] = (self.write_index(channel) + 1) % self._size
self._totalcount[channel] += 1
if self._index[channel] == 0:
self._is_full[channel] = True
self._overflows[channel] += 1
def get_all(self, channel: int = 0):
"""
Return all valid values from the specified channel
"""
if self._is_full[channel]:
return np.concatenate((self._buffer[channel, self._index[channel]:],
self._buffer[channel, :self._index[channel]]))
else:
return self._buffer[channel, :self._index[channel]]
def has_value(self, index, channel):
if index < 0 and self.is_full(channel):
return True
elif index < 0 and not self.is_full(channel):
return False
if index >= self.size:
return False
if index > self.write_index(channel) and self.is_full(channel):
return True
if index == self.write_index(channel) and self._totalcount[channel] == 0:
return False
return True
def valid_range(self, channel: int = 0):
""" Return the start index that are valid within the buffer
"""
start = 0
count = 0
if self._totalcount[channel] == 0:
return start, count
if not self.is_full(channel):
count = self._totalcount[channel]
else:
count = self.size
return start, count
def get(self, index: int = -1, channel: int = 0):
# easy case first, we can spare the effort of further checking
if index >= 0 and index < self.write_index(channel):
return self._buffer[channel, index]
if index < 0:
index = self.write_index() - 1
if self.has_value(index, channel):
return self._buffer[channel, index]
else:
raise IndexError(f"Invalid index {index} on ring buffer for channel{channel}")
def read(self, start, count=1, channel=0):
""" Reads a numpy array from buffer"""
if start < 0 or count < 0:
raise IndexError(f"Invalid start ({start}) or count ({count}) for channel{channel}")
if count == 1:
return np.array(self.get(start, channel))
vs, vc = self.valid_range(channel)
if start > self._totalcount[channel]:
raise IndexError(f"Invalid start index {start} is invalid with totalcount {self._totalcount[channel]} for channel{channel}")
if start > self.size:
raise IndexError(f"Invalid start index {start} for buffer with size {self.size}")
if count > self.size:
count = self.size
if count > vc:
count = vc
if (start + count) < self.size:
return self._buffer[channel, start:start + count]
else:
return np.concatenate((self._buffer[channel, start:],
self._buffer[channel, :count - self.size + start]))

View File

@@ -11,7 +11,7 @@ from pyrelacs.util.logging import config_logging
log = config_logging() log = config_logging()
class MccDac: class MccDaq:
""" """
Represents the Digital/Analog Converter from Meassuring Computing. Represents the Digital/Analog Converter from Meassuring Computing.
provides methods for writing and reading the Analog / Digital input and output. provides methods for writing and reading the Analog / Digital input and output.
@@ -43,6 +43,7 @@ class MccDac:
except uldaq.ul_exception.ULException: except uldaq.ul_exception.ULException:
self.disconnect_dac() self.disconnect_dac()
self.connect_dac() self.connect_dac()
self.ai_device = self.daq_device.get_ai_device() self.ai_device = self.daq_device.get_ai_device()
self.ao_device = self.daq_device.get_ao_device() self.ao_device = self.daq_device.get_ao_device()
self.dio_device = self.daq_device.get_dio_device() self.dio_device = self.daq_device.get_dio_device()

View File

@@ -6,10 +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
from scipy.signal import find_peaks
from pyrelacs.devices.mccdac import MccDac from pyrelacs.devices.mccdaq import MccDaq
from pyrelacs.util.logging import config_logging from pyrelacs.util.logging import config_logging
log = config_logging() log = config_logging()
@@ -17,7 +15,7 @@ log = config_logging()
faulthandler.enable() faulthandler.enable()
class Calibration(MccDac): class Calibration(MccDaq):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.SAMPLERATE = 40_000.0 self.SAMPLERATE = 40_000.0

View File

@@ -11,19 +11,20 @@ from PyQt6.QtWidgets import (
QStatusBar, QStatusBar,
) )
import uldaq import uldaq
import numpy as np
import nixio as nix import nixio as nix
import pyqtgraph as pg import pyqtgraph as pg
from pathlib import Path as path from pathlib import Path as path
from scipy.signal import welch, find_peaks
from pyrelacs.worker import Worker from pyrelacs.worker import Worker
from pyrelacs.repros.repros import Repro from pyrelacs.repros.repros import Repro
from pyrelacs.util.logging import config_logging from pyrelacs.dataio.buffer import DataBuffer
from pyrelacs.ui.about import AboutDialog from pyrelacs.ui.about import AboutDialog
from pyrelacs.ui.plots.calibration import CalibrationPlot from pyrelacs.ui.plots.calibration import CalibrationPlot
from pyrelacs.util.logging import config_logging
log = config_logging() log = config_logging()
_root = path(__file__).parent.parent _root = path(__file__).parent.parent
@@ -51,6 +52,7 @@ class PyRelacs(QMainWindow):
self.threadpool = QThreadPool() self.threadpool = QThreadPool()
self.repros = Repro() self.repros = Repro()
# self.buffers = DataBuffer(channels=1, samples=100_000)
self.text = QPlainTextEdit() self.text = QPlainTextEdit()
self.text.setReadOnly(True) self.text.setReadOnly(True)
@@ -179,9 +181,6 @@ class PyRelacs(QMainWindow):
except uldaq.ul_exception.ULException as e: except uldaq.ul_exception.ULException as e:
log.error(f"Could not Connect to DAQ: {e}") log.error(f"Could not Connect to DAQ: {e}")
self.daq_connect_button.setDisabled(True) self.daq_connect_button.setDisabled(True)
else:
log.debug("Already handeld the error")
pass
def disconnect_dac(self): def disconnect_dac(self):
try: try:

56
test/test_buffer.py Normal file
View File

@@ -0,0 +1,56 @@
import pytest
import numpy as np
from usbdaq import RingBuffer
def test_init():
buff = RingBuffer(1000, 2)
assert buff.size == 1000
assert buff.channel_count == 2
def test_hasvalue():
buff = RingBuffer(1000, 2)
assert buff.has_value(0, 0) == False
assert buff.has_value(-1, 0) == False
buff.append(10, 0)
assert buff.write_index(0) == 1
assert buff.write_index(1) == 0
assert buff.has_value(0, 0) == True
assert buff.has_value(0, 1) == False
buff.append(10, 1)
assert buff.write_index(1) == 1
assert buff.has_value(0, 1) == True
for i in range(1100):
buff.append(i, 0)
buff.append(i, 1)
assert buff.write_index(0) == buff.write_index(1)
assert buff.has_value(0, 0) == True
assert buff.has_value(0, 1) == True
assert buff.has_value(buff.write_index(0), 0) == True
assert buff.has_value(buff.write_index(1), 1) == True
def test_validrange():
pass
def test_write():
buff = RingBuffer(1000, 2)
samplecount = 1000
def test_get():
pass
def test_read():
pass