Compare commits
4 Commits
936f2d5708
...
514ed1aa32
Author | SHA1 | Date |
---|---|---|
Anne de Jong | 514ed1aa32 | |
Anne de Jong | 0be8dd71d9 | |
Anne de Jong | 2cd4c616b3 | |
Anne de Jong | 311a1274bf |
|
@ -44,22 +44,13 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
|
|||
: DaqConfiguration(config), DeviceInfo(devinfo) {
|
||||
DEBUGTRACE_ENTER;
|
||||
|
||||
if (duplexMode()) {
|
||||
if (neninchannels() == 0) {
|
||||
throw rte("Duplex mode enabled, but no input channels enabled");
|
||||
}
|
||||
|
||||
if (nenoutchannels() == 0) {
|
||||
throw rte("Duplex mode enabled, but no output channels enabled");
|
||||
}
|
||||
}
|
||||
if(!duplexMode() && monitorOutput) {
|
||||
throw rte("Output monitoring only allowed when running in duplex mode");
|
||||
throw rte("Duplex mode requires enabling both input and output channels. Please make sure at least one output channel is enabled, or disable hardware output loopback in DAQ configuration.");
|
||||
}
|
||||
|
||||
if (!hasInternalOutputMonitor && monitorOutput) {
|
||||
throw rte(
|
||||
"Output monitor flag set, but device does not have output monitor");
|
||||
"Output monitor flag set, but device does not have hardware output monitor.");
|
||||
}
|
||||
|
||||
if (!config.match(devinfo)) {
|
||||
|
|
|
@ -35,12 +35,14 @@ DaqConfiguration::DaqConfiguration(const DeviceInfo &device) {
|
|||
us i = 0;
|
||||
for (auto &inch : inchannel_config) {
|
||||
inch.name = "Unnamed input channel " + std::to_string(i);
|
||||
inch.rangeIndex = device.prefInputRangeIndex;
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (auto &outch : outchannel_config) {
|
||||
outch.name = "Unnamed output channel " + std::to_string(i);
|
||||
outch.rangeIndex = device.prefOutputRangeIndex;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,14 +68,26 @@ public:
|
|||
us prefFramesPerBlockIndex = 0;
|
||||
|
||||
/**
|
||||
* @brief Available ranges for the input, i.e. +/- 1V and/or +/- 10 V etc.
|
||||
* @brief Available ranges for the input, i.e. +/- 1V and/or +/- 10 V etc.
|
||||
*/
|
||||
dvec availableInputRanges;
|
||||
|
||||
/**
|
||||
* @brief Its preffered range
|
||||
* @brief Available ranges for the output, i.e. +/- 1V and/or +/- 10 V etc.
|
||||
*/
|
||||
dvec availableOutputRanges;
|
||||
|
||||
/**
|
||||
* @brief Its preffered input range
|
||||
*/
|
||||
int prefInputRangeIndex = 0;
|
||||
|
||||
/**
|
||||
* @brief Its preffered output range
|
||||
*/
|
||||
int prefOutputRangeIndex = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief The number of input channels available for the device
|
||||
*/
|
||||
|
@ -125,13 +137,29 @@ public:
|
|||
bool duplexModeForced = false;
|
||||
|
||||
/**
|
||||
* @brief The physical quantity of the output signal. For 'normal' audio
|
||||
* @brief Indicates whether the device is able to run in duplex mode. If false,
|
||||
* devices cannot run in duplex mode, and the `duplexModeForced` flag is meaningless.
|
||||
*/
|
||||
bool hasDuplexMode = false;
|
||||
|
||||
/**
|
||||
* @brief The physical quantity of the input signal from DAQ. For 'normal' audio
|
||||
* interfaces, this is typically a 'number' between +/- full scale. For some
|
||||
* real DAQ devices however, the input quantity corresponds to a physical signal,
|
||||
* such a Volts.
|
||||
*/
|
||||
|
||||
DaqChannel::Qty physicalInputQty = DaqChannel::Qty::Number;
|
||||
|
||||
/**
|
||||
* @brief The physical quantity of the output signal from DAQ. For 'normal' audio
|
||||
* devices, this is typically a 'number' between +/- full scale. For some
|
||||
* devices however, the output quantity corresponds to a physical signal,
|
||||
* real DAQ devices however, the input quantity corresponds to a physical signal,
|
||||
* such a Volts.
|
||||
*/
|
||||
DaqChannel::Qty physicalOutputQty = DaqChannel::Qty::Number;
|
||||
|
||||
|
||||
/**
|
||||
* @brief String representation of DeviceInfo
|
||||
*
|
||||
|
|
|
@ -99,6 +99,7 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
|
|||
d.ninchannels = devinfo.inputChannels;
|
||||
|
||||
d.availableInputRanges = {1.0};
|
||||
d.availableOutputRanges = {1.0};
|
||||
|
||||
RtAudioFormat formats = devinfo.nativeFormats;
|
||||
if (formats & RTAUDIO_SINT8) {
|
||||
|
|
|
@ -68,6 +68,7 @@ void fillUlDaqDeviceInfo(DeviceInfoList &devinfolist) {
|
|||
}
|
||||
|
||||
devinfo.physicalOutputQty = DaqChannel::Qty::Voltage;
|
||||
devinfo.physicalInputQty = DaqChannel::Qty::Voltage;
|
||||
|
||||
devinfo.availableDataTypes.push_back(
|
||||
DataTypeDescriptor::DataType::dtype_fl64);
|
||||
|
@ -79,7 +80,9 @@ void fillUlDaqDeviceInfo(DeviceInfoList &devinfolist) {
|
|||
devinfo.availableFramesPerBlock = {512, 1024, 2048, 4096, 8192};
|
||||
|
||||
devinfo.availableInputRanges = {1.0, 10.0};
|
||||
devinfo.availableOutputRanges = {10.0};
|
||||
devinfo.prefInputRangeIndex = 0;
|
||||
devinfo.prefOutputRangeIndex = 0;
|
||||
|
||||
devinfo.ninchannels = 4;
|
||||
devinfo.noutchannels = 1;
|
||||
|
@ -90,6 +93,7 @@ void fillUlDaqDeviceInfo(DeviceInfoList &devinfolist) {
|
|||
|
||||
devinfo.hasInternalOutputMonitor = true;
|
||||
|
||||
devinfo.hasDuplexMode = true;
|
||||
devinfo.duplexModeForced = true;
|
||||
|
||||
// Finally, this devinfo is pushed back in list
|
||||
|
|
|
@ -29,6 +29,9 @@ void init_deviceinfo(py::module& m) {
|
|||
devinfo.def_readonly("availableInputRanges",
|
||||
&DeviceInfo::availableInputRanges);
|
||||
devinfo.def_readonly("prefInputRangeIndex", &DeviceInfo::prefInputRangeIndex);
|
||||
devinfo.def_readonly("availableOutputRanges",
|
||||
&DeviceInfo::availableOutputRanges);
|
||||
devinfo.def_readonly("prefOutputRangeIndex", &DeviceInfo::prefOutputRangeIndex);
|
||||
|
||||
devinfo.def_readonly("ninchannels", &DeviceInfo::ninchannels);
|
||||
devinfo.def_readonly("noutchannels", &DeviceInfo::noutchannels);
|
||||
|
@ -36,7 +39,10 @@ void init_deviceinfo(py::module& m) {
|
|||
devinfo.def_readonly("hasInputACCouplingSwitch",
|
||||
&DeviceInfo::hasInputACCouplingSwitch);
|
||||
|
||||
devinfo.def_readonly("hasDuplexMode", &DeviceInfo::hasDuplexMode);
|
||||
devinfo.def_readonly("duplexModeForced", &DeviceInfo::duplexModeForced);
|
||||
devinfo.def_readonly("hasInternalOutputMonitor", &DeviceInfo::hasInternalOutputMonitor);
|
||||
|
||||
devinfo.def_readonly("physicalInputQty", &DeviceInfo::physicalInputQty);
|
||||
devinfo.def_readonly("physicalOutputQty", &DeviceInfo::physicalOutputQty);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ requires-python = ">=3.10"
|
|||
description = "Library for Acoustic Signal Processing"
|
||||
license = { "file" = "LICENSE" }
|
||||
authors = [{ "name" = "J.A. de Jong", "email" = "j.a.dejong@ascee.nl" }]
|
||||
version = "1.1.0"
|
||||
version = "1.3.0"
|
||||
|
||||
keywords = ["DSP", "DAQ", "Signal processing"]
|
||||
|
||||
|
|
|
@ -62,8 +62,9 @@ class Qty:
|
|||
name: str
|
||||
# I.e.: Pascal
|
||||
unit_name: str
|
||||
# I.e.: Pa
|
||||
# I.e.: -, Pa, V
|
||||
unit_symb: str
|
||||
|
||||
# I.e.: ('dB SPL') <== tuple of possible level units
|
||||
level_unit: object
|
||||
# Contains a tuple of possible level names, including its reference value.
|
||||
|
@ -92,6 +93,18 @@ class Qty:
|
|||
"""
|
||||
return self.cpp_enum.value
|
||||
|
||||
@property
|
||||
def unit_symb_eq(self):
|
||||
"""Unit symbol to be used in equations
|
||||
|
||||
Returns:
|
||||
String: V, Pa, 1,
|
||||
"""
|
||||
if self.unit_symb != '-':
|
||||
return self.unit_symb
|
||||
else:
|
||||
return '1'
|
||||
|
||||
|
||||
|
||||
@unique
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
"""!
|
||||
Author: J.A. de Jong - ASCEE
|
||||
|
||||
|
@ -127,7 +126,7 @@ def scaleBlockSens(block, sens):
|
|||
fac = 2 ** (8 * sw - 1) - 1
|
||||
else:
|
||||
fac = 1.0
|
||||
return block.astype(LASP_NUMPY_FLOAT_TYPE) / fac / sens[np.newaxis, :]
|
||||
return block.astype(LASP_NUMPY_FLOAT_TYPE) / fac / sens[np.newaxis,:]
|
||||
|
||||
|
||||
class IterRawData:
|
||||
|
@ -200,7 +199,7 @@ class IterRawData:
|
|||
# print(f'block: {block}, starto: {start_offset}, stopo {stop_offset}')
|
||||
|
||||
self.i += 1
|
||||
return fa[block, start_offset:stop_offset, :][:, self.channels]
|
||||
return fa[block, start_offset:stop_offset,:][:, self.channels]
|
||||
|
||||
|
||||
class IterData(IterRawData):
|
||||
|
@ -236,9 +235,6 @@ class Measurement:
|
|||
# Full filepath
|
||||
self.fn = fn
|
||||
|
||||
# Folder, Base filename + extension
|
||||
self.folder, self.fn_base = os.path.split(fn)
|
||||
|
||||
# Open the h5 file in read-plus mode, to allow for changing the
|
||||
# measurement comment.
|
||||
with h5.File(fn, "r") as f:
|
||||
|
@ -366,15 +362,23 @@ class Measurement:
|
|||
def rename(self, newname: str):
|
||||
"""
|
||||
Try to rename the measurement file.
|
||||
|
||||
Args:
|
||||
newname: New name, with or without extension
|
||||
"""
|
||||
_ , ext = os.path.splitext(newname)
|
||||
# Add proper extension if new name is given without extension.
|
||||
if ext != DOTMEXT:
|
||||
newname = newname + DOTMEXT
|
||||
|
||||
newname_full = str(pathlib.Path(self.folder) / newname)
|
||||
# Folder, Base filename + extension
|
||||
folder, _ = os.path.split(self.fn)
|
||||
|
||||
newname_full = str(pathlib.Path(folder) / newname)
|
||||
os.rename(self.fn, newname_full)
|
||||
|
||||
self.fn = newname_full
|
||||
|
||||
def genNewUUID(self):
|
||||
"""
|
||||
Create new UUID for measurement and store in file.
|
||||
|
@ -420,7 +424,8 @@ class Measurement:
|
|||
# Last resort, see if we can find the right measurement in the same folder
|
||||
if m is None:
|
||||
try:
|
||||
m = Measurement.fromFolderWithUUID(required_uuid, self.folder, skip=[self.name])
|
||||
folder, _ = os.path.split(self.fn)
|
||||
m = Measurement.fromFolderWithUUID(required_uuid, folder, skip=[self.name])
|
||||
logging.info('Found reference measurement in folder with correct UUID. Updating name of reference measurement')
|
||||
# Update the measurement file name in the list, such that next time it
|
||||
# can be opened just by its name.
|
||||
|
@ -498,13 +503,14 @@ class Measurement:
|
|||
|
||||
raise RuntimeError(f'Measurement with UUID {uuid_str} could not be found.')
|
||||
|
||||
def setAttribute(self, attrname, value):
|
||||
def setAttribute(self, attrname: str, value):
|
||||
"""
|
||||
Set an attribute in the measurement file, and keep a local copy in
|
||||
memory for efficient accessing.
|
||||
|
||||
Args:
|
||||
atrname
|
||||
attrname: name of attribute, a string
|
||||
value: the value. Should be anything that can be stored as an attribute in HDF5.
|
||||
"""
|
||||
with self.file("r+") as f:
|
||||
# Update comment attribute in the file
|
||||
|
@ -536,7 +542,8 @@ class Measurement:
|
|||
@property
|
||||
def name(self):
|
||||
"""Returns filename base without extension."""
|
||||
return os.path.splitext(self.fn_base)[0]
|
||||
_, fn = os.path.split(self.fn)
|
||||
return os.path.splitext(fn)[0]
|
||||
|
||||
@property
|
||||
def channelNames(self):
|
||||
|
@ -970,6 +977,22 @@ class Measurement:
|
|||
|
||||
wavfile.write(fn, int(self.samplerate), data.astype(newtype))
|
||||
|
||||
@staticmethod
|
||||
def fromFile(fn):
|
||||
"""
|
||||
Try to open measurement from a given file name. First checks
|
||||
whether the measurement is already open. Otherwise it might
|
||||
happen that a Measurement object is created twice for the same backing file, which we do not allow.
|
||||
"""
|
||||
# See if the base part of the filename is referring to a file that is already open
|
||||
with h5.File(fn, 'r') as f:
|
||||
uuid = f.attrs['UUID']
|
||||
|
||||
if uuid in Measurement.uuid_s.keys():
|
||||
return Measurement.uuid_s[uuid]
|
||||
|
||||
return Measurement(fn)
|
||||
|
||||
@staticmethod
|
||||
def fromtxt(
|
||||
fn,
|
||||
|
@ -1048,8 +1071,8 @@ class Measurement:
|
|||
sensitivity,
|
||||
mfn,
|
||||
timestamp=None,
|
||||
qtys: List[SIQtys] = None,
|
||||
channelNames: List[str] = None,
|
||||
qtys: List[SIQtys]=None,
|
||||
channelNames: List[str]=None,
|
||||
force=False,
|
||||
) -> Measurement:
|
||||
"""
|
||||
|
@ -1111,7 +1134,6 @@ class Measurement:
|
|||
if len(qtys) != nchannels:
|
||||
raise RuntimeError("Illegal length of qtys list given")
|
||||
|
||||
|
||||
with h5.File(mfn, "w") as hf:
|
||||
hf.attrs["samplerate"] = samplerate
|
||||
hf.attrs["sensitivity"] = sensitivity
|
||||
|
|
|
@ -9,6 +9,7 @@ import numpy as np
|
|||
from .lasp_atomic import Atomic
|
||||
from .lasp_cpp import InDataHandler, StreamMgr
|
||||
from .lasp_version import LASP_VERSION_MAJOR, LASP_VERSION_MINOR
|
||||
import uuid
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
@ -139,6 +140,7 @@ class Recording:
|
|||
f.attrs["blocksize"] = blocksize
|
||||
f.attrs["sensitivity"] = [ch.sensitivity for ch in in_ch]
|
||||
f.attrs["channelNames"] = [ch.name for ch in in_ch]
|
||||
f.attrs["UUID"] = str(uuid.uuid1())
|
||||
|
||||
# Add the start delay here, as firstFrames() is called right after the
|
||||
# constructor is called. time.time() returns a floating point
|
||||
|
|
Loading…
Reference in New Issue