diff --git a/asammdf/blocks/bus_logging_utils.py b/asammdf/blocks/bus_logging_utils.py
index 554ccca62..ed39e07cd 100644
--- a/asammdf/blocks/bus_logging_utils.py
+++ b/asammdf/blocks/bus_logging_utils.py
@@ -7,18 +7,24 @@
def apply_conversion(vals, signal, ignore_value2text_conversion):
a, b = float(signal.factor), float(signal.offset)
- if signal.values and not ignore_value2text_conversion:
+ if signal.values:
+ if ignore_value2text_conversion:
+ if (a, b) != (1, 0):
+ vals = vals * a
+ if b:
+ vals += b
+ else:
- conv = {}
- for i, (val, text) in enumerate(signal.values.items()):
- conv[f"upper_{i}"] = val
- conv[f"lower_{i}"] = val
- conv[f"text_{i}"] = text
+ conv = {}
+ for i, (val, text) in enumerate(signal.values.items()):
+ conv[f"upper_{i}"] = val
+ conv[f"lower_{i}"] = val
+ conv[f"text_{i}"] = text
- conv["default"] = from_dict({"a": a, "b": b})
+ conv["default"] = from_dict({"a": a, "b": b})
- conv = from_dict(conv)
- vals = conv.convert(vals)
+ conv = from_dict(conv)
+ vals = conv.convert(vals)
else:
@@ -331,7 +337,7 @@ def extract_mux(
"samples": samples if raw else apply_conversion(samples, sig, ignore_value2text_conversion),
"t": t_,
"invalidation_bits": (
- np.isclose(samples, max_val)
+ np.isclose(apply_conversion(samples, sig, ignore_value2text_conversion=True), max_val)
if len(samples.shape) == 1
else np.zeros(len(samples), dtype=bool)
),
diff --git a/asammdf/blocks/cutils.c b/asammdf/blocks/cutils.c
index b8b0e674c..1297de848 100644
--- a/asammdf/blocks/cutils.c
+++ b/asammdf/blocks/cutils.c
@@ -149,17 +149,16 @@ static PyObject* sort_data_block(PyObject* self, PyObject* args)
static PyObject* extract(PyObject* self, PyObject* args)
{
int i=0, count, max=0;
- bool is_byte_array;
int pos=0;
int size;
- PyObject *signal_data;
+ PyObject *signal_data, *is_byte_array;
unsigned char *buf;
PyArrayObject *vals;
PyArray_Descr *descr;
void *addr;
unsigned char * addr2;
- if(!PyArg_ParseTuple(args, "Op", &signal_data, &is_byte_array))
+ if(!PyArg_ParseTuple(args, "OO", &signal_data, &is_byte_array))
{
snprintf(err_string, 1024, "extract was called with wrong parameters");
PyErr_SetString(PyExc_ValueError, err_string);
@@ -180,7 +179,7 @@ static PyObject* extract(PyObject* self, PyObject* args)
count++;
}
- if (is_byte_array)
+ if (PyObject_IsTrue(is_byte_array))
{
npy_intp dims[2];
diff --git a/asammdf/blocks/mdf_common.py b/asammdf/blocks/mdf_common.py
index 21c43c0a2..d454d35b7 100644
--- a/asammdf/blocks/mdf_common.py
+++ b/asammdf/blocks/mdf_common.py
@@ -122,13 +122,22 @@ def _validate_channel_selection(
entries = self.channels_db[name]
if len(entries) > 1:
- message = (
- f'Multiple occurrences for channel "{name}": {entries}. '
- 'Provide both "group" and "index" arguments'
- " to select another data group"
- )
- logger.exception(message)
- raise MdfException(message)
+ if self._raise_on_mutiple_occurences:
+ message = (
+ f'Multiple occurrences for channel "{name}": {entries}. '
+ 'Provide both "group" and "index" arguments'
+ " to select another data group"
+ )
+ logger.exception(message)
+ raise MdfException(message)
+ else:
+ message = (
+ f'Multiple occurrences for channel "{name}": {entries}. '
+ 'Returning the first occurence since the MDF obejct was '
+ 'configured to not raise an exception in this case.'
+ )
+ logger.warning(message)
+ gp_nr, ch_nr = entries[0]
else:
gp_nr, ch_nr = entries[0]
@@ -153,12 +162,22 @@ def _validate_channel_selection(
raise MdfException(message)
else:
- message = (
- f'Multiple occurrences for channel "{name}" in group {group}. '
- 'Provide also the "index" argument'
- " to select the desired channel"
- )
- raise MdfException(message)
+ if self._raise_on_mutiple_occurences:
+ message = (
+ f'Multiple occurrences for channel "{name}": {entries}. '
+ 'Provide both "group" and "index" arguments'
+ " to select another data group"
+ )
+ logger.exception(message)
+ raise MdfException(message)
+ else:
+ message = (
+ f'Multiple occurrences for channel "{name}": {entries}. '
+ 'Returning the first occurence since the MDF obejct was '
+ 'configured to not raise an exception in this case.'
+ )
+ logger.warning(message)
+ gp_nr, ch_nr = entries[0]
else:
if (group, index) in self.channels_db[name]:
ch_nr = index
diff --git a/asammdf/blocks/mdf_v3.py b/asammdf/blocks/mdf_v3.py
index 0079f3fb8..8aec07a0b 100644
--- a/asammdf/blocks/mdf_v3.py
+++ b/asammdf/blocks/mdf_v3.py
@@ -170,6 +170,11 @@ def __init__(self, name=None, version="3.30", channels=(), **kwargs):
self._write_fragment_size = 4 * 2 ** 20
self._single_bit_uint_as_bool = False
self._integer_interpolation = 0
+ self._float_interpolation = 1
+ self._raise_on_multiple_occurrences = True
+ self._use_display_names = False
+ self.copy_on_get = False
+ self.raise_on_multiple_occurrences = True
self._si_map = {}
self._cc_map = {}
@@ -942,15 +947,28 @@ def _read(self, mapped=False):
def configure(
self,
*,
+ from_other=None,
read_fragment_size=None,
write_fragment_size=None,
use_display_names=None,
single_bit_uint_as_bool=None,
integer_interpolation=None,
copy_on_get=None,
+ float_interpolation=None,
+ raise_on_multiple_occurrences=None,
):
"""configure MDF parameters
+ The default values for the options are the following:
+ * read_fragment_size = 0
+ * write_fragment_size = 4MB
+ * use_display_names = False
+ * single_bit_uint_as_bool = False
+ * integer_interpolation = 0 (ffill - use previous sample)
+ * float_interpolation = 1 (linear interpolation)
+ * copy_on_get = False
+ * raise_on_multiple_occurrences = True
+
Parameters
----------
read_fragment_size : int
@@ -981,8 +999,37 @@ def configure(
copy_on_get : bool
copy arrays in the get method
+ float_interpolation : int
+ interpolation mode for float channels:
+
+ * 0 - repeat previous sample
+ * 1 - use linear interpolation
+
+ .. versionadded:: 6.2.0
+
+ raise_on_multiple_occurrences : bool
+ raise exception when there are multiple channel occurrences in the file and
+ the `get` call is ambiguos; default True
+
+ .. versionadded:: 6.2.0
+
+ from_other : MDF
+ copy configuration options from other MDF
+
+ .. versionadded:: 6.2.0
+
"""
+ if from_other is not None:
+ self._read_fragment_size = from_other._read_fragment_size
+ self._write_fragment_size = from_other._write_fragment_size
+ self._use_display_names = from_other._use_display_names
+ self._single_bit_uint_as_bool = from_other._single_bit_uint_as_bool
+ self._integer_interpolation = from_other._integer_interpolation
+ self.copy_on_get = from_other.copy_on_get
+ self._float_interpolation = from_other._float_interpolation
+ self._raise_on_multiple_occurrences = from_other._raise_on_multiple_occurrences
+
if read_fragment_size is not None:
self._read_fragment_size = int(read_fragment_size)
@@ -1001,6 +1048,12 @@ def configure(
if copy_on_get is not None:
self.copy_on_get = copy_on_get
+ if float_interpolation in (0, 1):
+ self._float_interpolation = int(float_interpolation)
+
+ if raise_on_multiple_occurrences is not None:
+ self._raise_on_multiple_occurrences = bool(raise_on_multiple_occurrences)
+
def add_trigger(self, group, timestamp, pre_time=0, post_time=0, comment=""):
"""add trigger to data group
@@ -1147,7 +1200,8 @@ def append(
return
version = self.version
- interp_mode = self._integer_interpolation
+ integer_interp_mode = self._integer_interpolation
+ float_interp_mode = self._float_interpolation
# check if the signals have a common timebase
# if not interpolate the signals using the union of all timbases
@@ -1165,7 +1219,11 @@ def append(
times = [s.timestamps for s in signals]
timestamps = unique(concatenate(times)).astype(float64)
signals = [
- s.interp(timestamps, interpolation_mode=interp_mode)
+ s.interp(
+ timestamps,
+ integer_interpolation_mode=integer_interp_mode,
+ float_interpolation_mode=float_interp_mode,
+ )
for s in signals
]
times = None
@@ -2829,7 +2887,11 @@ def get(
vals = (
Signal(vals, timestamps, name="_")
- .interp(t, interpolation_mode=self._integer_interpolation)
+ .interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation
+ )
.samples
)
@@ -2964,7 +3026,11 @@ def get(
vals = (
Signal(vals, timestamps, name="_")
- .interp(t, interpolation_mode=self._integer_interpolation)
+ .interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation,
+ )
.samples
)
diff --git a/asammdf/blocks/mdf_v4.py b/asammdf/blocks/mdf_v4.py
index befa2dad2..c8a456223 100755
--- a/asammdf/blocks/mdf_v4.py
+++ b/asammdf/blocks/mdf_v4.py
@@ -340,6 +340,7 @@ def __init__(self, name=None, version="4.10", channels=(), **kwargs):
self._tempfile = TemporaryFile()
self._file = None
+ self._raise_on_multiple_occurrences = True
self._read_fragment_size = 0 * 2 ** 20
self._write_fragment_size = 4 * 2 ** 20
self._use_display_names = kwargs.get("use_display_names", False)
@@ -350,8 +351,10 @@ def __init__(self, name=None, version="4.10", channels=(), **kwargs):
self._decryption_function = kwargs.get("decryption_function", None)
self.copy_on_get = kwargs.get("copy_on_get", True)
self.compact_vlsd = kwargs.get("compact_vlsd", False)
+ self.raise_on_multiple_occurrences = True
self._single_bit_uint_as_bool = False
self._integer_interpolation = 0
+ self._float_interpolation = 1
self.virtual_groups = {} # master group 2 referencing groups
self.virtual_groups_map = {} # group index 2 master group
@@ -2353,14 +2356,27 @@ def get_invalidation_bits(self, group_index, channel, fragment):
def configure(
self,
*,
+ from_other=None,
read_fragment_size=None,
write_fragment_size=None,
use_display_names=None,
single_bit_uint_as_bool=None,
integer_interpolation=None,
copy_on_get=None,
+ float_interpolation=None,
+ raise_on_multiple_occurrences=None,
):
- """configure MDF parameters
+ """configure MDF parameters.
+
+ The default values for the options are the following:
+ * read_fragment_size = 0
+ * write_fragment_size = 4MB
+ * use_display_names = False
+ * single_bit_uint_as_bool = False
+ * integer_interpolation = 0 (ffill - use previous sample)
+ * float_interpolation = 1 (linear interpolation)
+ * copy_on_get = False
+ * raise_on_multiple_occurrences = True
Parameters
----------
@@ -2392,8 +2408,37 @@ def configure(
copy_on_get : bool
copy arrays in the get method
+ float_interpolation : int
+ interpolation mode for float channels:
+
+ * 0 - repeat previous sample
+ * 1 - use linear interpolation
+
+ .. versionadded:: 6.2.0
+
+ raise_on_multiple_occurrences : bool
+ raise exception when there are multiple channel occurrences in the file and
+ the `get` call is ambiguos; default True
+
+ .. versionadded:: 6.2.0
+
+ from_other : MDF
+ copy configuration options from other MDF
+
+ .. versionadded:: 6.2.0
+
"""
+ if from_other is not None:
+ self._read_fragment_size = from_other._read_fragment_size
+ self._write_fragment_size = from_other._write_fragment_size
+ self._use_display_names = from_other._use_display_names
+ self._single_bit_uint_as_bool = from_other._single_bit_uint_as_bool
+ self._integer_interpolation = from_other._integer_interpolation
+ self.copy_on_get = from_other.copy_on_get
+ self._float_interpolation = from_other._float_interpolation
+ self.raise_on_multiple_occurrences = from_other.raise_on_multiple_occurrences
+
if read_fragment_size is not None:
self._read_fragment_size = int(read_fragment_size)
@@ -2412,6 +2457,12 @@ def configure(
if copy_on_get is not None:
self.copy_on_get = copy_on_get
+ if float_interpolation in (0, 1):
+ self._float_interpolation = int(float_interpolation)
+
+ if raise_on_multiple_occurrences is not None:
+ self.raise_on_multiple_occurrences = bool(raise_on_multiple_occurrences)
+
def append(
self,
signals,
@@ -2496,8 +2547,6 @@ def append(
if not signals:
return
- interp_mode = self._integer_interpolation
-
prepare_record = True
# check if the signals have a common timebase
@@ -2516,7 +2565,12 @@ def append(
times = [s.timestamps for s in signals]
t = unique(concatenate(times)).astype(float64)
signals = [
- s.interp(t, interpolation_mode=interp_mode) for s in signals
+ s.interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation,
+ )
+ for s in signals
]
times = None
else:
@@ -6368,7 +6422,11 @@ def _get_structure(
vals = Signal(
vals, timestamps, name="_", invalidation_bits=invalidation_bits
- ).interp(t, interpolation_mode=self._integer_interpolation)
+ ).interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation,
+ )
vals, timestamps, invalidation_bits = (
vals.samples,
@@ -6669,7 +6727,11 @@ def _get_array(
vals = Signal(
vals, timestamps, name="_", invalidation_bits=invalidation_bits
- ).interp(t, interpolation_mode=self._integer_interpolation)
+ ).interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation,
+ )
vals, timestamps, invalidation_bits = (
vals.samples,
@@ -6811,7 +6873,11 @@ def _get_scalar(
vals = Signal(
vals, timestamps, name="_", invalidation_bits=invalidation_bits
- ).interp(t, interpolation_mode=self._integer_interpolation)
+ ).interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation,
+ )
vals, timestamps, invalidation_bits = (
vals.samples,
@@ -7098,7 +7164,11 @@ def _get_scalar(
vals = Signal(
vals, timestamps, name="_", invalidation_bits=invalidation_bits
- ).interp(t, interpolation_mode=self._integer_interpolation)
+ ).interp(
+ t,
+ integer_interpolation_mode=self._integer_interpolation,
+ float_interpolation_mode=self._float_interpolation,
+ )
vals, timestamps, invalidation_bits = (
vals.samples,
diff --git a/asammdf/blocks/utils.py b/asammdf/blocks/utils.py
index fa145573d..448438394 100644
--- a/asammdf/blocks/utils.py
+++ b/asammdf/blocks/utils.py
@@ -197,14 +197,16 @@ def get_text_v3(address, stream, mapped=False, decode=True):
if block_id != b"TX":
return "" if decode else b""
(size,) = UINT16_uf(stream, address + 2)
- text_bytes = stream[address + 4 : address + size].strip(b" \r\t\n\0")
+ text_bytes = (
+ stream[address + 4 : address + size].split(b"\0")[0].rstrip(b" \r\t\n\0")
+ )
else:
stream.seek(address)
block_id = stream.read(2)
if block_id != b"TX":
return "" if decode else b""
size = UINT16_u(stream.read(2))[0] - 4
- text_bytes = stream.read(size).strip(b" \r\t\n\0")
+ text_bytes = stream.read(size).split(b"\0")[0].rstrip(b" \r\t\n\0")
if decode:
try:
text = text_bytes.decode("latin-1")
@@ -244,13 +246,15 @@ def get_text_v4(address, stream, mapped=False, decode=True):
block_id, size = BLK_COMMON_uf(stream, address)
if block_id not in (b"##TX", b"##MD"):
return "" if decode else b""
- text_bytes = stream[address + 24 : address + size].strip(b" \r\t\n\0")
+ text_bytes = (
+ stream[address + 24 : address + size].split(b"\0")[0].rstrip(b" \r\t\n\0")
+ )
else:
stream.seek(address)
block_id, size = BLK_COMMON_u(stream.read(24))
if block_id not in (b"##TX", b"##MD"):
return "" if decode else b""
- text_bytes = stream.read(size - 24).strip(b" \r\t\n\0")
+ text_bytes = stream.read(size - 24).split(b"\0")[0].rstrip(b" \r\t\n\0")
if decode:
try:
diff --git a/asammdf/gui/ui/batch_widget.py b/asammdf/gui/ui/batch_widget.py
index 51762774a..cea58821e 100644
--- a/asammdf/gui/ui/batch_widget.py
+++ b/asammdf/gui/ui/batch_widget.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Form implementation generated from reading ui file 'batch_widget.ui'
+# Form implementation generated from reading ui file 'batch_widget.UI'
#
# Created by: PyQt5 UI code generator 5.15.1
#
@@ -377,6 +377,82 @@ def setupUi(self, batch_widget):
self.raw_mat.setObjectName("raw_mat")
self.gridLayout_18.addWidget(self.raw_mat, 3, 0, 1, 1)
self.output_options.addWidget(self.MAT_2)
+ self.CSV = QtWidgets.QWidget()
+ self.CSV.setObjectName("CSV")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.CSV)
+ self.gridLayout_2.setContentsMargins(2, 2, 2, 2)
+ self.gridLayout_2.setSpacing(2)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.single_time_base_csv = QtWidgets.QCheckBox(self.CSV)
+ self.single_time_base_csv.setObjectName("single_time_base_csv")
+ self.gridLayout_2.addWidget(self.single_time_base_csv, 0, 0, 1, 2)
+ self.time_from_zero_csv = QtWidgets.QCheckBox(self.CSV)
+ self.time_from_zero_csv.setObjectName("time_from_zero_csv")
+ self.gridLayout_2.addWidget(self.time_from_zero_csv, 1, 0, 1, 2)
+ self.time_as_date_csv = QtWidgets.QCheckBox(self.CSV)
+ self.time_as_date_csv.setObjectName("time_as_date_csv")
+ self.gridLayout_2.addWidget(self.time_as_date_csv, 2, 0, 1, 2)
+ self.raw_csv = QtWidgets.QCheckBox(self.CSV)
+ self.raw_csv.setObjectName("raw_csv")
+ self.gridLayout_2.addWidget(self.raw_csv, 3, 0, 1, 2)
+ self.line_34 = QtWidgets.QFrame(self.CSV)
+ self.line_34.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_34.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_34.setObjectName("line_34")
+ self.gridLayout_2.addWidget(self.line_34, 4, 0, 1, 2)
+ self.use_display_names_csv = QtWidgets.QCheckBox(self.CSV)
+ self.use_display_names_csv.setObjectName("use_display_names_csv")
+ self.gridLayout_2.addWidget(self.use_display_names_csv, 5, 0, 1, 2)
+ self.label_67 = QtWidgets.QLabel(self.CSV)
+ self.label_67.setObjectName("label_67")
+ self.gridLayout_2.addWidget(self.label_67, 6, 0, 1, 1)
+ self.empty_channels_csv = QtWidgets.QComboBox(self.CSV)
+ self.empty_channels_csv.setObjectName("empty_channels_csv")
+ self.gridLayout_2.addWidget(self.empty_channels_csv, 6, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.CSV)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 7, 0, 1, 1)
+ self.delimiter = QtWidgets.QLineEdit(self.CSV)
+ self.delimiter.setMaxLength(1)
+ self.delimiter.setClearButtonEnabled(False)
+ self.delimiter.setObjectName("delimiter")
+ self.gridLayout_2.addWidget(self.delimiter, 7, 1, 1, 1)
+ self.doublequote = QtWidgets.QCheckBox(self.CSV)
+ self.doublequote.setChecked(True)
+ self.doublequote.setObjectName("doublequote")
+ self.gridLayout_2.addWidget(self.doublequote, 8, 0, 1, 1)
+ self.label_15 = QtWidgets.QLabel(self.CSV)
+ self.label_15.setObjectName("label_15")
+ self.gridLayout_2.addWidget(self.label_15, 9, 0, 1, 1)
+ self.escapechar = QtWidgets.QLineEdit(self.CSV)
+ self.escapechar.setInputMask("")
+ self.escapechar.setMaxLength(1)
+ self.escapechar.setObjectName("escapechar")
+ self.gridLayout_2.addWidget(self.escapechar, 9, 1, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.CSV)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_2.addWidget(self.label_4, 10, 0, 1, 1)
+ self.lineterminator = QtWidgets.QLineEdit(self.CSV)
+ self.lineterminator.setObjectName("lineterminator")
+ self.gridLayout_2.addWidget(self.lineterminator, 10, 1, 1, 1)
+ self.label_5 = QtWidgets.QLabel(self.CSV)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout_2.addWidget(self.label_5, 11, 0, 1, 1)
+ self.quotechar = QtWidgets.QLineEdit(self.CSV)
+ self.quotechar.setMaxLength(1)
+ self.quotechar.setObjectName("quotechar")
+ self.gridLayout_2.addWidget(self.quotechar, 11, 1, 1, 1)
+ self.label_6 = QtWidgets.QLabel(self.CSV)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout_2.addWidget(self.label_6, 12, 0, 1, 1)
+ self.quoting = QtWidgets.QComboBox(self.CSV)
+ self.quoting.setObjectName("quoting")
+ self.quoting.addItem("")
+ self.quoting.addItem("")
+ self.quoting.addItem("")
+ self.quoting.addItem("")
+ self.gridLayout_2.addWidget(self.quoting, 12, 1, 1, 1)
+ self.output_options.addWidget(self.CSV)
self.verticalLayout_21.addWidget(self.output_options)
self.verticalLayout_15.addWidget(self.groupBox_11)
self.groupBox = QtWidgets.QGroupBox(self.convert_tab)
@@ -478,42 +554,85 @@ def setupUi(self, batch_widget):
self.gridLayout_10.setContentsMargins(2, 2, 2, 2)
self.gridLayout_10.setSpacing(2)
self.gridLayout_10.setObjectName("gridLayout_10")
+ self.ignore_invalid_signals_csv = QtWidgets.QCheckBox(self.groupBox_3)
+ self.ignore_invalid_signals_csv.setObjectName("ignore_invalid_signals_csv")
+ self.gridLayout_10.addWidget(self.ignore_invalid_signals_csv, 3, 1, 1, 1)
self.time_from_zero_bus = QtWidgets.QCheckBox(self.groupBox_3)
self.time_from_zero_bus.setObjectName("time_from_zero_bus")
self.gridLayout_10.addWidget(self.time_from_zero_bus, 1, 1, 1, 1)
+ self.empty_channels_bus = QtWidgets.QComboBox(self.groupBox_3)
+ self.empty_channels_bus.setObjectName("empty_channels_bus")
+ self.gridLayout_10.addWidget(self.empty_channels_bus, 5, 2, 1, 2)
+ self.label_8 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout_10.addWidget(self.label_8, 6, 1, 1, 1)
self.label_28 = QtWidgets.QLabel(self.groupBox_3)
self.label_28.setObjectName("label_28")
self.gridLayout_10.addWidget(self.label_28, 4, 1, 1, 1)
- self.line_13 = QtWidgets.QFrame(self.groupBox_3)
- self.line_13.setFrameShape(QtWidgets.QFrame.HLine)
- self.line_13.setFrameShadow(QtWidgets.QFrame.Sunken)
- self.line_13.setObjectName("line_13")
- self.gridLayout_10.addWidget(self.line_13, 7, 1, 1, 3)
- self.export_raster_bus = QtWidgets.QDoubleSpinBox(self.groupBox_3)
- self.export_raster_bus.setDecimals(6)
- self.export_raster_bus.setObjectName("export_raster_bus")
- self.gridLayout_10.addWidget(self.export_raster_bus, 4, 2, 1, 2)
self.extract_bus_csv_btn = QtWidgets.QPushButton(self.groupBox_3)
icon9 = QtGui.QIcon()
icon9.addPixmap(QtGui.QPixmap(":/csv.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.extract_bus_csv_btn.setIcon(icon9)
self.extract_bus_csv_btn.setObjectName("extract_bus_csv_btn")
- self.gridLayout_10.addWidget(self.extract_bus_csv_btn, 8, 1, 1, 3)
+ self.gridLayout_10.addWidget(self.extract_bus_csv_btn, 13, 1, 1, 3)
+ self.export_raster_bus = QtWidgets.QDoubleSpinBox(self.groupBox_3)
+ self.export_raster_bus.setDecimals(6)
+ self.export_raster_bus.setObjectName("export_raster_bus")
+ self.gridLayout_10.addWidget(self.export_raster_bus, 4, 2, 1, 2)
+ self.lineterminator_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.lineterminator_bus.setObjectName("lineterminator_bus")
+ self.gridLayout_10.addWidget(self.lineterminator_bus, 9, 2, 1, 1)
+ self.label_14 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_14.setObjectName("label_14")
+ self.gridLayout_10.addWidget(self.label_14, 10, 1, 1, 1)
+ self.quotechar_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.quotechar_bus.setMaxLength(1)
+ self.quotechar_bus.setObjectName("quotechar_bus")
+ self.gridLayout_10.addWidget(self.quotechar_bus, 10, 2, 1, 1)
self.single_time_base_bus = QtWidgets.QCheckBox(self.groupBox_3)
self.single_time_base_bus.setObjectName("single_time_base_bus")
self.gridLayout_10.addWidget(self.single_time_base_bus, 0, 1, 1, 1)
- self.ignore_invalid_signals_csv = QtWidgets.QCheckBox(self.groupBox_3)
- self.ignore_invalid_signals_csv.setObjectName("ignore_invalid_signals_csv")
- self.gridLayout_10.addWidget(self.ignore_invalid_signals_csv, 3, 1, 1, 1)
- self.empty_channels_bus = QtWidgets.QComboBox(self.groupBox_3)
- self.empty_channels_bus.setObjectName("empty_channels_bus")
- self.gridLayout_10.addWidget(self.empty_channels_bus, 5, 2, 1, 2)
self.label_29 = QtWidgets.QLabel(self.groupBox_3)
self.label_29.setObjectName("label_29")
self.gridLayout_10.addWidget(self.label_29, 5, 1, 1, 1)
+ self.doublequote_bus = QtWidgets.QCheckBox(self.groupBox_3)
+ self.doublequote_bus.setChecked(True)
+ self.doublequote_bus.setObjectName("doublequote_bus")
+ self.gridLayout_10.addWidget(self.doublequote_bus, 7, 1, 1, 1)
+ self.delimiter_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.delimiter_bus.setMaxLength(1)
+ self.delimiter_bus.setClearButtonEnabled(False)
+ self.delimiter_bus.setObjectName("delimiter_bus")
+ self.gridLayout_10.addWidget(self.delimiter_bus, 6, 2, 1, 1)
+ self.line_13 = QtWidgets.QFrame(self.groupBox_3)
+ self.line_13.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_13.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_13.setObjectName("line_13")
+ self.gridLayout_10.addWidget(self.line_13, 12, 1, 1, 3)
+ self.escapechar_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.escapechar_bus.setInputMask("")
+ self.escapechar_bus.setMaxLength(1)
+ self.escapechar_bus.setObjectName("escapechar_bus")
+ self.gridLayout_10.addWidget(self.escapechar_bus, 8, 2, 1, 1)
self.bus_time_as_date = QtWidgets.QCheckBox(self.groupBox_3)
self.bus_time_as_date.setObjectName("bus_time_as_date")
self.gridLayout_10.addWidget(self.bus_time_as_date, 2, 1, 1, 1)
+ self.label_13 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_13.setObjectName("label_13")
+ self.gridLayout_10.addWidget(self.label_13, 9, 1, 1, 1)
+ self.label_9 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_9.setObjectName("label_9")
+ self.gridLayout_10.addWidget(self.label_9, 8, 1, 1, 1)
+ self.label_7 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout_10.addWidget(self.label_7, 11, 1, 1, 1)
+ self.quoting_bus = QtWidgets.QComboBox(self.groupBox_3)
+ self.quoting_bus.setObjectName("quoting_bus")
+ self.quoting_bus.addItem("")
+ self.quoting_bus.addItem("")
+ self.quoting_bus.addItem("")
+ self.quoting_bus.addItem("")
+ self.gridLayout_10.addWidget(self.quoting_bus, 11, 2, 1, 1)
self.gridLayout_11.addWidget(self.groupBox_3, 2, 1, 1, 1)
self.groupBox_2 = QtWidgets.QGroupBox(self.extract_bus_tab)
self.groupBox_2.setObjectName("groupBox_2")
@@ -589,8 +708,10 @@ def setupUi(self, batch_widget):
self.gridLayout_9.addWidget(self.splitter, 1, 0, 1, 1)
self.retranslateUi(batch_widget)
- self.aspects.setCurrentIndex(0)
+ self.aspects.setCurrentIndex(1)
self.output_options.setCurrentIndex(0)
+ self.quoting.setCurrentIndex(1)
+ self.quoting_bus.setCurrentIndex(1)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(batch_widget)
@@ -662,6 +783,26 @@ def retranslateUi(self, batch_widget):
self.time_as_date_mat.setText(_translate("batch_widget", "Time as date"))
self.label_74.setText(_translate("batch_widget", "Empty channels"))
self.raw_mat.setText(_translate("batch_widget", "Raw values"))
+ self.single_time_base_csv.setText(_translate("batch_widget", "Single time base"))
+ self.time_from_zero_csv.setText(_translate("batch_widget", "Time from 0s"))
+ self.time_as_date_csv.setText(_translate("batch_widget", "Time as date"))
+ self.raw_csv.setText(_translate("batch_widget", "Raw values"))
+ self.use_display_names_csv.setText(_translate("batch_widget", "Use display names"))
+ self.label_67.setText(_translate("batch_widget", "Empty channels"))
+ self.label_2.setText(_translate("batch_widget", "Delimiter"))
+ self.delimiter.setText(_translate("batch_widget", ","))
+ self.doublequote.setText(_translate("batch_widget", "Double quote"))
+ self.label_15.setText(_translate("batch_widget", "Escape Char"))
+ self.escapechar.setPlaceholderText(_translate("batch_widget", "None"))
+ self.label_4.setText(_translate("batch_widget", "Line Terminator"))
+ self.lineterminator.setText(_translate("batch_widget", "\\r\\n"))
+ self.label_5.setText(_translate("batch_widget", "Quote Char"))
+ self.quotechar.setText(_translate("batch_widget", "\""))
+ self.label_6.setText(_translate("batch_widget", "Quoting"))
+ self.quoting.setItemText(0, _translate("batch_widget", "ALL"))
+ self.quoting.setItemText(1, _translate("batch_widget", "MINIMAL"))
+ self.quoting.setItemText(2, _translate("batch_widget", "NONNUMERIC"))
+ self.quoting.setItemText(3, _translate("batch_widget", "NONE"))
self.groupBox.setTitle(_translate("batch_widget", "Output folder"))
self.modify_output_folder.setPlaceholderText(_translate("batch_widget", "please select an output folder"))
self.apply_btn.setText(_translate("batch_widget", "Apply"))
@@ -677,15 +818,29 @@ def retranslateUi(self, batch_widget):
self.stack_btn.setText(_translate("batch_widget", "Stack"))
self.aspects.setTabText(self.aspects.indexOf(self.stack_tab), _translate("batch_widget", "Stack"))
self.groupBox_3.setTitle(_translate("batch_widget", "CSV"))
+ self.ignore_invalid_signals_csv.setToolTip(_translate("batch_widget", "checks if all samples are eauql to the maximum teoretical signal value"))
+ self.ignore_invalid_signals_csv.setText(_translate("batch_widget", "Ignore invalid signals"))
self.time_from_zero_bus.setText(_translate("batch_widget", "Time from 0s"))
+ self.label_8.setText(_translate("batch_widget", "Delimiter"))
self.label_28.setText(_translate("batch_widget", "Raster"))
- self.export_raster_bus.setSuffix(_translate("batch_widget", "s"))
self.extract_bus_csv_btn.setText(_translate("batch_widget", "Export to CSV "))
+ self.export_raster_bus.setSuffix(_translate("batch_widget", "s"))
+ self.lineterminator_bus.setText(_translate("batch_widget", "\\r\\n"))
+ self.label_14.setText(_translate("batch_widget", "Quote Char"))
+ self.quotechar_bus.setText(_translate("batch_widget", "\""))
self.single_time_base_bus.setText(_translate("batch_widget", "Single time base"))
- self.ignore_invalid_signals_csv.setToolTip(_translate("batch_widget", "checks if all samples are eauql to the maximum teoretical signal value"))
- self.ignore_invalid_signals_csv.setText(_translate("batch_widget", "Ignore invalid signals"))
self.label_29.setText(_translate("batch_widget", "Empty channels"))
+ self.doublequote_bus.setText(_translate("batch_widget", "Double quote"))
+ self.delimiter_bus.setText(_translate("batch_widget", ","))
+ self.escapechar_bus.setPlaceholderText(_translate("batch_widget", "None"))
self.bus_time_as_date.setText(_translate("batch_widget", "Time as date"))
+ self.label_13.setText(_translate("batch_widget", "Line Terminator"))
+ self.label_9.setText(_translate("batch_widget", "Escape Char"))
+ self.label_7.setText(_translate("batch_widget", "Quoting"))
+ self.quoting_bus.setItemText(0, _translate("batch_widget", "ALL"))
+ self.quoting_bus.setItemText(1, _translate("batch_widget", "MINIMAL"))
+ self.quoting_bus.setItemText(2, _translate("batch_widget", "NONNUMERIC"))
+ self.quoting_bus.setItemText(3, _translate("batch_widget", "NONE"))
self.groupBox_2.setTitle(_translate("batch_widget", "MDF"))
self.extract_bus_btn.setText(_translate("batch_widget", "Extract Bus signals"))
self.label__1.setText(_translate("batch_widget", "Compression"))
diff --git a/asammdf/gui/ui/batch_widget.ui b/asammdf/gui/ui/batch_widget.ui
index 03de48a1a..accc7e0f4 100644
--- a/asammdf/gui/ui/batch_widget.ui
+++ b/asammdf/gui/ui/batch_widget.ui
@@ -24,7 +24,7 @@
QTabWidget::West
- 0
+ 1
false
@@ -871,6 +871,192 @@
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ Single time base
+
+
+
+ -
+
+
+ Time from 0s
+
+
+
+ -
+
+
+ Time as date
+
+
+
+ -
+
+
+ Raw values
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Use display names
+
+
+
+ -
+
+
+ Empty channels
+
+
+
+ -
+
+
+ -
+
+
+ Delimiter
+
+
+
+ -
+
+
+ ,
+
+
+ 1
+
+
+ false
+
+
+
+ -
+
+
+ Double quote
+
+
+ true
+
+
+
+ -
+
+
+ Escape Char
+
+
+
+ -
+
+
+
+
+
+ 1
+
+
+ None
+
+
+
+ -
+
+
+ Line Terminator
+
+
+
+ -
+
+
+ \r\n
+
+
+
+ -
+
+
+ Quote Char
+
+
+
+ -
+
+
+ "
+
+
+ 1
+
+
+
+ -
+
+
+ Quoting
+
+
+
+ -
+
+
+ 1
+
+
-
+
+ ALL
+
+
+ -
+
+ MINIMAL
+
+
+ -
+
+ NONNUMERIC
+
+
+ -
+
+ NONE
+
+
+
+
+
+
@@ -1102,6 +1288,16 @@
2
+ -
+
+
+ checks if all samples are eauql to the maximum teoretical signal value
+
+
+ Ignore invalid signals
+
+
+
-
@@ -1109,6 +1305,16 @@
+ -
+
+
+ -
+
+
+ Delimiter
+
+
+
-
@@ -1116,10 +1322,14 @@
- -
-
-
- Qt::Horizontal
+
-
+
+
+ Export to CSV
+
+
+
+ :/csv.png:/csv.png
@@ -1133,14 +1343,27 @@
- -
-
+
-
+
- Export to CSV
+ \r\n
-
-
- :/csv.png:/csv.png
+
+
+ -
+
+
+ Quote Char
+
+
+
+ -
+
+
+ "
+
+
+ 1
@@ -1151,23 +1374,53 @@
- -
-
-
- checks if all samples are eauql to the maximum teoretical signal value
-
+
-
+
- Ignore invalid signals
+ Empty channels
- -
-
+
-
+
+
+ Double quote
+
+
+ true
+
+
- -
-
+
-
+
- Empty channels
+ ,
+
+
+ 1
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+
+
+ 1
+
+
+ None
@@ -1178,6 +1431,54 @@
+ -
+
+
+ Line Terminator
+
+
+
+ -
+
+
+ Escape Char
+
+
+
+ -
+
+
+ Quoting
+
+
+
+ -
+
+
+ 1
+
+
-
+
+ ALL
+
+
+ -
+
+ MINIMAL
+
+
+ -
+
+ NONNUMERIC
+
+
+ -
+
+ NONE
+
+
+
+
diff --git a/asammdf/gui/ui/file_widget.py b/asammdf/gui/ui/file_widget.py
index eaf8cd9dc..786e25db1 100644
--- a/asammdf/gui/ui/file_widget.py
+++ b/asammdf/gui/ui/file_widget.py
@@ -381,6 +381,82 @@ def setupUi(self, file_widget):
self.raw_mat.setObjectName("raw_mat")
self.gridLayout_3.addWidget(self.raw_mat, 3, 0, 1, 1)
self.output_options.addWidget(self.MAT)
+ self.CSV = QtWidgets.QWidget()
+ self.CSV.setObjectName("CSV")
+ self.gridLayout = QtWidgets.QGridLayout(self.CSV)
+ self.gridLayout.setContentsMargins(2, 2, 2, 2)
+ self.gridLayout.setSpacing(2)
+ self.gridLayout.setObjectName("gridLayout")
+ self.quotechar = QtWidgets.QLineEdit(self.CSV)
+ self.quotechar.setMaxLength(1)
+ self.quotechar.setObjectName("quotechar")
+ self.gridLayout.addWidget(self.quotechar, 11, 1, 1, 2)
+ self.label_5 = QtWidgets.QLabel(self.CSV)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout.addWidget(self.label_5, 11, 0, 1, 1)
+ self.use_display_names_csv = QtWidgets.QCheckBox(self.CSV)
+ self.use_display_names_csv.setObjectName("use_display_names_csv")
+ self.gridLayout.addWidget(self.use_display_names_csv, 5, 0, 1, 3)
+ self.single_time_base_csv = QtWidgets.QCheckBox(self.CSV)
+ self.single_time_base_csv.setObjectName("single_time_base_csv")
+ self.gridLayout.addWidget(self.single_time_base_csv, 0, 0, 1, 3)
+ self.lineterminator = QtWidgets.QLineEdit(self.CSV)
+ self.lineterminator.setObjectName("lineterminator")
+ self.gridLayout.addWidget(self.lineterminator, 10, 1, 1, 2)
+ self.time_as_date_csv = QtWidgets.QCheckBox(self.CSV)
+ self.time_as_date_csv.setObjectName("time_as_date_csv")
+ self.gridLayout.addWidget(self.time_as_date_csv, 2, 0, 1, 3)
+ self.raw_csv = QtWidgets.QCheckBox(self.CSV)
+ self.raw_csv.setObjectName("raw_csv")
+ self.gridLayout.addWidget(self.raw_csv, 3, 0, 1, 3)
+ self.label_2 = QtWidgets.QLabel(self.CSV)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 7, 0, 1, 1)
+ self.label_3 = QtWidgets.QLabel(self.CSV)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 9, 0, 1, 1)
+ self.escapechar = QtWidgets.QLineEdit(self.CSV)
+ self.escapechar.setInputMask("")
+ self.escapechar.setMaxLength(1)
+ self.escapechar.setObjectName("escapechar")
+ self.gridLayout.addWidget(self.escapechar, 9, 1, 1, 2)
+ self.line_32 = QtWidgets.QFrame(self.CSV)
+ self.line_32.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_32.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_32.setObjectName("line_32")
+ self.gridLayout.addWidget(self.line_32, 4, 0, 1, 3)
+ self.time_from_zero_csv = QtWidgets.QCheckBox(self.CSV)
+ self.time_from_zero_csv.setObjectName("time_from_zero_csv")
+ self.gridLayout.addWidget(self.time_from_zero_csv, 1, 0, 1, 3)
+ self.label_4 = QtWidgets.QLabel(self.CSV)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 10, 0, 1, 1)
+ self.doublequote = QtWidgets.QCheckBox(self.CSV)
+ self.doublequote.setChecked(True)
+ self.doublequote.setObjectName("doublequote")
+ self.gridLayout.addWidget(self.doublequote, 8, 0, 1, 1)
+ self.label_6 = QtWidgets.QLabel(self.CSV)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout.addWidget(self.label_6, 12, 0, 1, 1)
+ self.quoting = QtWidgets.QComboBox(self.CSV)
+ self.quoting.setObjectName("quoting")
+ self.quoting.addItem("")
+ self.quoting.addItem("")
+ self.quoting.addItem("")
+ self.quoting.addItem("")
+ self.gridLayout.addWidget(self.quoting, 12, 1, 1, 2)
+ self.delimiter = QtWidgets.QLineEdit(self.CSV)
+ self.delimiter.setMaxLength(1)
+ self.delimiter.setClearButtonEnabled(False)
+ self.delimiter.setObjectName("delimiter")
+ self.gridLayout.addWidget(self.delimiter, 7, 1, 1, 2)
+ self.label_66 = QtWidgets.QLabel(self.CSV)
+ self.label_66.setObjectName("label_66")
+ self.gridLayout.addWidget(self.label_66, 6, 0, 1, 1)
+ self.empty_channels_csv = QtWidgets.QComboBox(self.CSV)
+ self.empty_channels_csv.setObjectName("empty_channels_csv")
+ self.gridLayout.addWidget(self.empty_channels_csv, 6, 1, 1, 2)
+ self.output_options.addWidget(self.CSV)
self.verticalLayout_20.addWidget(self.output_options)
self.verticalLayout_3.addWidget(self.groupBox_10)
spacerItem6 = QtWidgets.QSpacerItem(20, 2, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
@@ -402,46 +478,90 @@ def setupUi(self, file_widget):
self.gridLayout_8.setObjectName("gridLayout_8")
self.groupBox_3 = QtWidgets.QGroupBox(self.extract_bus_tab)
self.groupBox_3.setObjectName("groupBox_3")
- self.gridLayout_7 = QtWidgets.QGridLayout(self.groupBox_3)
- self.gridLayout_7.setContentsMargins(2, 2, 2, 2)
- self.gridLayout_7.setSpacing(2)
- self.gridLayout_7.setObjectName("gridLayout_7")
- self.line_13 = QtWidgets.QFrame(self.groupBox_3)
- self.line_13.setFrameShape(QtWidgets.QFrame.HLine)
- self.line_13.setFrameShadow(QtWidgets.QFrame.Sunken)
- self.line_13.setObjectName("line_13")
- self.gridLayout_7.addWidget(self.line_13, 7, 1, 1, 3)
- self.label_25 = QtWidgets.QLabel(self.groupBox_3)
- self.label_25.setObjectName("label_25")
- self.gridLayout_7.addWidget(self.label_25, 5, 1, 1, 1)
+ self.gridLayout_6 = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout_6.setContentsMargins(-1, 2, 2, 2)
+ self.gridLayout_6.setSpacing(2)
+ self.gridLayout_6.setObjectName("gridLayout_6")
+ self.label_10 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_10.setObjectName("label_10")
+ self.gridLayout_6.addWidget(self.label_10, 11, 0, 1, 1)
self.single_time_base_bus = QtWidgets.QCheckBox(self.groupBox_3)
self.single_time_base_bus.setObjectName("single_time_base_bus")
- self.gridLayout_7.addWidget(self.single_time_base_bus, 0, 1, 1, 1)
+ self.gridLayout_6.addWidget(self.single_time_base_bus, 0, 0, 1, 4)
+ self.doublequote_bus = QtWidgets.QCheckBox(self.groupBox_3)
+ self.doublequote_bus.setChecked(True)
+ self.doublequote_bus.setObjectName("doublequote_bus")
+ self.gridLayout_6.addWidget(self.doublequote_bus, 7, 0, 1, 1)
self.label_23 = QtWidgets.QLabel(self.groupBox_3)
self.label_23.setObjectName("label_23")
- self.gridLayout_7.addWidget(self.label_23, 4, 1, 1, 1)
+ self.gridLayout_6.addWidget(self.label_23, 4, 0, 1, 1)
+ self.label_25 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_25.setObjectName("label_25")
+ self.gridLayout_6.addWidget(self.label_25, 5, 0, 1, 2)
+ self.label_9 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_9.setObjectName("label_9")
+ self.gridLayout_6.addWidget(self.label_9, 8, 0, 1, 1)
+ self.label_7 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout_6.addWidget(self.label_7, 12, 0, 1, 1)
+ self.label_11 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_11.setObjectName("label_11")
+ self.gridLayout_6.addWidget(self.label_11, 10, 0, 1, 2)
+ self.bus_time_as_date = QtWidgets.QCheckBox(self.groupBox_3)
+ self.bus_time_as_date.setObjectName("bus_time_as_date")
+ self.gridLayout_6.addWidget(self.bus_time_as_date, 2, 0, 1, 3)
+ self.time_from_zero_bus = QtWidgets.QCheckBox(self.groupBox_3)
+ self.time_from_zero_bus.setObjectName("time_from_zero_bus")
+ self.gridLayout_6.addWidget(self.time_from_zero_bus, 1, 0, 1, 3)
+ self.label_8 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout_6.addWidget(self.label_8, 6, 0, 1, 1)
+ self.ignore_invalid_signals_csv = QtWidgets.QCheckBox(self.groupBox_3)
+ self.ignore_invalid_signals_csv.setObjectName("ignore_invalid_signals_csv")
+ self.gridLayout_6.addWidget(self.ignore_invalid_signals_csv, 3, 0, 1, 3)
self.export_raster_bus = QtWidgets.QDoubleSpinBox(self.groupBox_3)
self.export_raster_bus.setDecimals(6)
self.export_raster_bus.setObjectName("export_raster_bus")
- self.gridLayout_7.addWidget(self.export_raster_bus, 4, 2, 1, 2)
- self.ignore_invalid_signals_csv = QtWidgets.QCheckBox(self.groupBox_3)
- self.ignore_invalid_signals_csv.setObjectName("ignore_invalid_signals_csv")
- self.gridLayout_7.addWidget(self.ignore_invalid_signals_csv, 3, 1, 1, 1)
+ self.gridLayout_6.addWidget(self.export_raster_bus, 4, 3, 1, 1)
self.empty_channels_bus = QtWidgets.QComboBox(self.groupBox_3)
self.empty_channels_bus.setObjectName("empty_channels_bus")
- self.gridLayout_7.addWidget(self.empty_channels_bus, 5, 2, 1, 2)
- self.time_from_zero_bus = QtWidgets.QCheckBox(self.groupBox_3)
- self.time_from_zero_bus.setObjectName("time_from_zero_bus")
- self.gridLayout_7.addWidget(self.time_from_zero_bus, 1, 1, 1, 1)
- self.bus_time_as_date = QtWidgets.QCheckBox(self.groupBox_3)
- self.bus_time_as_date.setObjectName("bus_time_as_date")
- self.gridLayout_7.addWidget(self.bus_time_as_date, 2, 1, 1, 1)
+ self.gridLayout_6.addWidget(self.empty_channels_bus, 5, 3, 1, 1)
+ self.delimiter_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.delimiter_bus.setMaxLength(1)
+ self.delimiter_bus.setClearButtonEnabled(False)
+ self.delimiter_bus.setObjectName("delimiter_bus")
+ self.gridLayout_6.addWidget(self.delimiter_bus, 6, 3, 1, 1)
+ self.escapechar_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.escapechar_bus.setInputMask("")
+ self.escapechar_bus.setMaxLength(1)
+ self.escapechar_bus.setObjectName("escapechar_bus")
+ self.gridLayout_6.addWidget(self.escapechar_bus, 8, 3, 1, 1)
+ self.lineterminator_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.lineterminator_bus.setObjectName("lineterminator_bus")
+ self.gridLayout_6.addWidget(self.lineterminator_bus, 10, 3, 1, 1)
+ self.quotechar_bus = QtWidgets.QLineEdit(self.groupBox_3)
+ self.quotechar_bus.setMaxLength(1)
+ self.quotechar_bus.setObjectName("quotechar_bus")
+ self.gridLayout_6.addWidget(self.quotechar_bus, 11, 3, 1, 1)
+ self.quoting_bus = QtWidgets.QComboBox(self.groupBox_3)
+ self.quoting_bus.setObjectName("quoting_bus")
+ self.quoting_bus.addItem("")
+ self.quoting_bus.addItem("")
+ self.quoting_bus.addItem("")
+ self.quoting_bus.addItem("")
+ self.gridLayout_6.addWidget(self.quoting_bus, 12, 3, 1, 1)
self.extract_bus_csv_btn = QtWidgets.QPushButton(self.groupBox_3)
icon8 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap(":/csv.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.extract_bus_csv_btn.setIcon(icon8)
self.extract_bus_csv_btn.setObjectName("extract_bus_csv_btn")
- self.gridLayout_7.addWidget(self.extract_bus_csv_btn, 8, 1, 1, 3)
+ self.gridLayout_6.addWidget(self.extract_bus_csv_btn, 14, 3, 1, 1)
+ self.line_13 = QtWidgets.QFrame(self.groupBox_3)
+ self.line_13.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_13.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_13.setObjectName("line_13")
+ self.gridLayout_6.addWidget(self.line_13, 13, 0, 1, 4)
+ self.gridLayout_6.setColumnStretch(0, 1)
self.gridLayout_8.addWidget(self.groupBox_3, 3, 1, 1, 1)
self.output_info_bus = QtWidgets.QTextEdit(self.extract_bus_tab)
self.output_info_bus.setReadOnly(True)
@@ -453,38 +573,40 @@ def setupUi(self, file_widget):
self.gridLayout_5.setContentsMargins(2, 2, 2, 2)
self.gridLayout_5.setSpacing(2)
self.gridLayout_5.setObjectName("gridLayout_5")
+ self.label_26 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_26.setText("")
+ self.label_26.setObjectName("label_26")
+ self.gridLayout_5.addWidget(self.label_26, 3, 1, 1, 1)
self.extract_bus_btn = QtWidgets.QPushButton(self.groupBox_2)
icon9 = QtGui.QIcon()
icon9.addPixmap(QtGui.QPixmap(":/down.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.extract_bus_btn.setIcon(icon9)
self.extract_bus_btn.setObjectName("extract_bus_btn")
- self.gridLayout_5.addWidget(self.extract_bus_btn, 5, 0, 1, 2)
+ self.gridLayout_5.addWidget(self.extract_bus_btn, 5, 1, 1, 2)
+ spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout_5.addItem(spacerItem7, 5, 0, 1, 1)
self.label__1 = QtWidgets.QLabel(self.groupBox_2)
self.label__1.setObjectName("label__1")
self.gridLayout_5.addWidget(self.label__1, 0, 0, 1, 1)
+ self.label_24 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_24.setObjectName("label_24")
+ self.gridLayout_5.addWidget(self.label_24, 1, 0, 1, 1)
+ self.ignore_invalid_signals_mdf = QtWidgets.QCheckBox(self.groupBox_2)
+ self.ignore_invalid_signals_mdf.setObjectName("ignore_invalid_signals_mdf")
+ self.gridLayout_5.addWidget(self.ignore_invalid_signals_mdf, 2, 0, 1, 1)
self.extract_bus_compression = QtWidgets.QComboBox(self.groupBox_2)
self.extract_bus_compression.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.extract_bus_compression.setObjectName("extract_bus_compression")
- self.gridLayout_5.addWidget(self.extract_bus_compression, 0, 1, 1, 1)
+ self.gridLayout_5.addWidget(self.extract_bus_compression, 0, 1, 1, 2)
self.extract_bus_format = QtWidgets.QComboBox(self.groupBox_2)
self.extract_bus_format.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.extract_bus_format.setObjectName("extract_bus_format")
- self.gridLayout_5.addWidget(self.extract_bus_format, 1, 1, 1, 1)
- self.label_24 = QtWidgets.QLabel(self.groupBox_2)
- self.label_24.setObjectName("label_24")
- self.gridLayout_5.addWidget(self.label_24, 1, 0, 1, 1)
- self.ignore_invalid_signals_mdf = QtWidgets.QCheckBox(self.groupBox_2)
- self.ignore_invalid_signals_mdf.setObjectName("ignore_invalid_signals_mdf")
- self.gridLayout_5.addWidget(self.ignore_invalid_signals_mdf, 2, 0, 1, 2)
- self.label_26 = QtWidgets.QLabel(self.groupBox_2)
- self.label_26.setText("")
- self.label_26.setObjectName("label_26")
- self.gridLayout_5.addWidget(self.label_26, 3, 0, 1, 1)
+ self.gridLayout_5.addWidget(self.extract_bus_format, 1, 1, 1, 2)
self.line_12 = QtWidgets.QFrame(self.groupBox_2)
self.line_12.setFrameShape(QtWidgets.QFrame.HLine)
self.line_12.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line_12.setObjectName("line_12")
- self.gridLayout_5.addWidget(self.line_12, 4, 0, 1, 2)
+ self.gridLayout_5.addWidget(self.line_12, 4, 0, 1, 3)
self.gridLayout_8.addWidget(self.groupBox_2, 3, 0, 1, 1)
self.tabWidget = QtWidgets.QTabWidget(self.extract_bus_tab)
self.tabWidget.setObjectName("tabWidget")
@@ -517,6 +639,10 @@ def setupUi(self, file_widget):
self.verticalLayout_7.addWidget(self.lin_database_list)
self.tabWidget.addTab(self.tab_2, "")
self.gridLayout_8.addWidget(self.tabWidget, 2, 0, 1, 2)
+ self.gridLayout_8.setColumnStretch(0, 1)
+ self.gridLayout_8.setColumnStretch(1, 1)
+ self.gridLayout_8.setColumnStretch(2, 1)
+ self.gridLayout_8.setColumnStretch(3, 1)
self.aspects.addTab(self.extract_bus_tab, icon9, "")
self.info_tab = QtWidgets.QWidget()
self.info_tab.setObjectName("info_tab")
@@ -542,8 +668,10 @@ def setupUi(self, file_widget):
self.verticalLayout.addWidget(self.aspects)
self.retranslateUi(file_widget)
- self.aspects.setCurrentIndex(0)
+ self.aspects.setCurrentIndex(1)
self.output_options.setCurrentIndex(0)
+ self.quoting.setCurrentIndex(1)
+ self.quoting_bus.setCurrentIndex(1)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(file_widget)
@@ -619,17 +747,51 @@ def retranslateUi(self, file_widget):
self.time_as_date_mat.setText(_translate("file_widget", "Time as date"))
self.label_68.setText(_translate("file_widget", "Empty channels"))
self.raw_mat.setText(_translate("file_widget", "Raw values"))
+ self.quotechar.setText(_translate("file_widget", "\""))
+ self.label_5.setText(_translate("file_widget", "Quote Char"))
+ self.use_display_names_csv.setText(_translate("file_widget", "Use display names"))
+ self.single_time_base_csv.setText(_translate("file_widget", "Single time base"))
+ self.lineterminator.setText(_translate("file_widget", "\\r\\n"))
+ self.time_as_date_csv.setText(_translate("file_widget", "Time as date"))
+ self.raw_csv.setText(_translate("file_widget", "Raw values"))
+ self.label_2.setText(_translate("file_widget", "Delimiter"))
+ self.label_3.setText(_translate("file_widget", "Escape Char"))
+ self.escapechar.setPlaceholderText(_translate("file_widget", "None"))
+ self.time_from_zero_csv.setText(_translate("file_widget", "Time from 0s"))
+ self.label_4.setText(_translate("file_widget", "Line Terminator"))
+ self.doublequote.setText(_translate("file_widget", "Double quote"))
+ self.label_6.setText(_translate("file_widget", "Quoting"))
+ self.quoting.setItemText(0, _translate("file_widget", "ALL"))
+ self.quoting.setItemText(1, _translate("file_widget", "MINIMAL"))
+ self.quoting.setItemText(2, _translate("file_widget", "NONNUMERIC"))
+ self.quoting.setItemText(3, _translate("file_widget", "NONE"))
+ self.delimiter.setText(_translate("file_widget", ","))
+ self.label_66.setText(_translate("file_widget", "Empty channels"))
self.apply_btn.setText(_translate("file_widget", "Apply"))
self.aspects.setTabText(self.aspects.indexOf(self.modify), _translate("file_widget", "Modify && Export"))
self.groupBox_3.setTitle(_translate("file_widget", "CSV"))
- self.label_25.setText(_translate("file_widget", "Empty channels"))
+ self.label_10.setText(_translate("file_widget", "Quote Char"))
self.single_time_base_bus.setText(_translate("file_widget", "Single time base"))
+ self.doublequote_bus.setText(_translate("file_widget", "Double quote"))
self.label_23.setText(_translate("file_widget", "Raster"))
- self.export_raster_bus.setSuffix(_translate("file_widget", "s"))
+ self.label_25.setText(_translate("file_widget", "Empty channels"))
+ self.label_9.setText(_translate("file_widget", "Escape Char"))
+ self.label_7.setText(_translate("file_widget", "Quoting"))
+ self.label_11.setText(_translate("file_widget", "Line Terminator"))
+ self.bus_time_as_date.setText(_translate("file_widget", "Time as date"))
+ self.time_from_zero_bus.setText(_translate("file_widget", "Time from 0s"))
+ self.label_8.setText(_translate("file_widget", "Delimiter"))
self.ignore_invalid_signals_csv.setToolTip(_translate("file_widget", "checks if all samples are eauql to the maximum teoretical signal value"))
self.ignore_invalid_signals_csv.setText(_translate("file_widget", "Ignore invalid signals"))
- self.time_from_zero_bus.setText(_translate("file_widget", "Time from 0s"))
- self.bus_time_as_date.setText(_translate("file_widget", "Time as date"))
+ self.export_raster_bus.setSuffix(_translate("file_widget", "s"))
+ self.delimiter_bus.setText(_translate("file_widget", ","))
+ self.escapechar_bus.setPlaceholderText(_translate("file_widget", "None"))
+ self.lineterminator_bus.setText(_translate("file_widget", "\\r\\n"))
+ self.quotechar_bus.setText(_translate("file_widget", "\""))
+ self.quoting_bus.setItemText(0, _translate("file_widget", "ALL"))
+ self.quoting_bus.setItemText(1, _translate("file_widget", "MINIMAL"))
+ self.quoting_bus.setItemText(2, _translate("file_widget", "NONNUMERIC"))
+ self.quoting_bus.setItemText(3, _translate("file_widget", "NONE"))
self.extract_bus_csv_btn.setText(_translate("file_widget", "Export to CSV "))
self.groupBox_2.setTitle(_translate("file_widget", "MDF"))
self.extract_bus_btn.setText(_translate("file_widget", "Extract Bus signals"))
diff --git a/asammdf/gui/ui/file_widget.ui b/asammdf/gui/ui/file_widget.ui
index 5f63de194..8239c55da 100644
--- a/asammdf/gui/ui/file_widget.ui
+++ b/asammdf/gui/ui/file_widget.ui
@@ -38,7 +38,7 @@
QTabWidget::West
- 0
+ 1
false
@@ -895,6 +895,192 @@
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ "
+
+
+ 1
+
+
+
+ -
+
+
+ Quote Char
+
+
+
+ -
+
+
+ Use display names
+
+
+
+ -
+
+
+ Single time base
+
+
+
+ -
+
+
+ \r\n
+
+
+
+ -
+
+
+ Time as date
+
+
+
+ -
+
+
+ Raw values
+
+
+
+ -
+
+
+ Delimiter
+
+
+
+ -
+
+
+ Escape Char
+
+
+
+ -
+
+
+
+
+
+ 1
+
+
+ None
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Time from 0s
+
+
+
+ -
+
+
+ Line Terminator
+
+
+
+ -
+
+
+ Double quote
+
+
+ true
+
+
+
+ -
+
+
+ Quoting
+
+
+
+ -
+
+
+ 1
+
+
-
+
+ ALL
+
+
+ -
+
+ MINIMAL
+
+
+ -
+
+ NONNUMERIC
+
+
+ -
+
+ NONE
+
+
+
+
+ -
+
+
+ ,
+
+
+ 1
+
+
+ false
+
+
+
+ -
+
+
+ Empty channels
+
+
+
+ -
+
+
+
+
@@ -936,16 +1122,13 @@
Bus Logging
-
+
-
CSV
-
-
- 2
-
+
2
@@ -958,45 +1141,87 @@
2
-
-
-
-
- Qt::Horizontal
+
-
+
+
+ Quote Char
- -
-
+
-
+
- Empty channels
+ Single time base
- -
-
+
-
+
- Single time base
+ Double quote
+
+
+ true
- -
+
-
Raster
- -
-
-
- s
+
-
+
+
+ Empty channels
-
- 6
+
+
+ -
+
+
+ Escape Char
- -
+
-
+
+
+ Quoting
+
+
+
+ -
+
+
+ Line Terminator
+
+
+
+ -
+
+
+ Time as date
+
+
+
+ -
+
+
+ Time from 0s
+
+
+
+ -
+
+
+ Delimiter
+
+
+
+ -
checks if all samples are eauql to the maximum teoretical signal value
@@ -1006,24 +1231,90 @@
- -
+
-
+
+
+ s
+
+
+ 6
+
+
+
+ -
- -
-
+
-
+
- Time from 0s
+ ,
+
+
+ 1
+
+
+ false
- -
-
+
-
+
+
+
+
+
+ 1
+
+
+ None
+
+
+
+ -
+
- Time as date
+ \r\n
+
+
+
+ -
+
+
+ "
+
+ 1
+
+
+
+ -
+
+
+ 1
+
+
-
+
+ ALL
+
+
+ -
+
+ MINIMAL
+
+
+ -
+
+ NONNUMERIC
+
+
+ -
+
+ NONE
+
+
- -
+
-
Export to CSV
@@ -1034,6 +1325,13 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
@@ -1065,7 +1363,14 @@
2
- -
+
-
+
+
+
+
+
+
+ -
Extract Bus signals
@@ -1076,6 +1381,19 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
-
@@ -1083,20 +1401,6 @@
- -
-
-
- QComboBox::AdjustToContents
-
-
-
- -
-
-
- QComboBox::AdjustToContents
-
-
-
-
@@ -1104,7 +1408,7 @@
- -
+
-
checks if all samples are eauql to the maximum teoretical signal value
@@ -1114,14 +1418,21 @@
- -
-
-
-
+
-
+
+
+ QComboBox::AdjustToContents
+
+
+
+ -
+
+
+ QComboBox::AdjustToContents
- -
+
-
Qt::Horizontal
diff --git a/asammdf/gui/widgets/batch.py b/asammdf/gui/widgets/batch.py
index f98f998ce..8eb5b7513 100644
--- a/asammdf/gui/widgets/batch.py
+++ b/asammdf/gui/widgets/batch.py
@@ -23,7 +23,7 @@
class BatchWidget(Ui_batch_widget, QtWidgets.QWidget):
- def __init__(self, ignore_value2text_conversions=False, integer_interpolation=2, *args, **kwargs):
+ def __init__(self, ignore_value2text_conversions=False, integer_interpolation=2, float_interpolation=1, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setupUi(self)
@@ -32,6 +32,7 @@ def __init__(self, ignore_value2text_conversions=False, integer_interpolation=2,
self.ignore_value2text_conversions = ignore_value2text_conversions
self.integer_interpolation = integer_interpolation
+ self.float_interpolation = float_interpolation
self.progress = None
self.files_list = MinimalListWidget()
@@ -346,6 +347,12 @@ def extract_bus_csv_logging(self, event):
empty_channels = self.empty_channels_bus.currentText()
raster = self.export_raster_bus.value() or None
time_as_date = self.bus_time_as_date.checkState() == QtCore.Qt.Checked
+ delimiter = self.delimiter_bus.text() or ','
+ doublequote = self.doublequote_bus.checkState() == QtCore.Qt.Checked
+ escapechar = self.escapechar_bus.text() or None
+ lineterminator = self.lineterminator_bus.text().replace("\\r", "\r").replace("\\n", "\n")
+ quotechar = self.quotechar_bus.text() or '"'
+ quoting = self.quoting_bus.currentText()
count = self.files_list.count()
@@ -451,7 +458,10 @@ def extract_bus_csv_logging(self, event):
f'Saving extracted Bus logging file {i+1} to "{file_name}"'
)
- mdf_.configure(integer_interpolation=self.integer_interpolation)
+ mdf_.configure(
+ integer_interpolation=self.integer_interpolation,
+ float_interpolation=self.float_interpolation,
+ )
target = mdf_.export
kwargs = {
@@ -463,6 +473,12 @@ def extract_bus_csv_logging(self, event):
"raster": raster or None,
"time_as_date": time_as_date,
"ignore_value2text_conversions": self.ignore_value2text_conversions,
+ "delimiter": delimiter,
+ "doublequote": doublequote,
+ "escapechar": escapechar,
+ "lineterminator": lineterminator,
+ "quotechar": quotechar,
+ "quoting": quoting,
}
run_thread_with_progress(
@@ -1062,6 +1078,30 @@ def _current_options(self):
"raw": self.raw_mat.checkState() == QtCore.Qt.Checked,
}
+ elif output_format == "CSV":
+
+ new = {
+ "single_time_base": self.single_time_base_csv.checkState()
+ == QtCore.Qt.Checked,
+ "time_from_zero": self.time_from_zero_csv.checkState()
+ == QtCore.Qt.Checked,
+ "time_as_date": self.time_as_date_csv.checkState() == QtCore.Qt.Checked,
+ "use_display_names": self.use_display_names_csv.checkState()
+ == QtCore.Qt.Checked,
+ "reduce_memory_usage": False,
+ "compression": False,
+ "empty_channels": self.empty_channels_csv.currentText(),
+ "raw": self.raw_csv.checkState() == QtCore.Qt.Checked,
+ "delimiter": self.delimiter.text() or ',',
+ "doublequote": self.doublequote.checkState() == QtCore.Qt.Checked,
+ "escapechar": self.escapechar.text() or None,
+ "lineterminator": self.lineterminator.text().replace("\\r", "\r").replace("\\n", "\n"),
+ "quotechar": self.quotechar.text() or '"',
+ 'quoting': self.quoting.currentText(),
+ "mat_format": None,
+ "oned_as": None,
+ }
+
else:
new = {
@@ -1189,6 +1229,7 @@ def apply_processing(self, event):
mdf_file.configure(
read_fragment_size=split_size,
integer_interpolation=self.integer_interpolation,
+ float_interpolation=self.float_interpolation,
)
mdf = None
@@ -1229,6 +1270,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=self.integer_interpolation,
+ float_interpolation=self.float_interpolation,
)
if opts.needs_cut:
@@ -1284,6 +1326,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=self.integer_interpolation,
+ float_interpolation=self.float_interpolation,
)
if opts.needs_resample:
@@ -1344,6 +1387,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=self.integer_interpolation,
+ float_interpolation=self.float_interpolation,
)
if output_format == "MDF":
@@ -1396,6 +1440,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=self.integer_interpolation,
+ float_interpolation=self.float_interpolation,
)
if output_folder is not None:
diff --git a/asammdf/gui/widgets/file.py b/asammdf/gui/widgets/file.py
index e4133cc93..8a04f5fcf 100644
--- a/asammdf/gui/widgets/file.py
+++ b/asammdf/gui/widgets/file.py
@@ -223,6 +223,8 @@ def __init__(
progress.setValue(90)
+ self.output_options.setCurrentIndex(0)
+
self.mdf_version.insertItems(0, SUPPORTED_VERSIONS)
self.mdf_compression.insertItems(
0, ("no compression", "deflate", "transposed deflate")
@@ -246,6 +248,7 @@ def __init__(
self.empty_channels.insertItems(0, ("skip", "zeros"))
self.empty_channels_bus.insertItems(0, ("skip", "zeros"))
self.empty_channels_mat.insertItems(0, ("skip", "zeros"))
+ self.empty_channels_csv.insertItems(0, ("skip", "zeros"))
self.mat_format.insertItems(0, ("4", "5", "7.3"))
self.oned_as.insertItems(0, ("row", "column"))
@@ -676,6 +679,9 @@ def output_format_changed(self, name):
self.export_compression_mat.clear()
self.export_compression_mat.addItems(["enabled", "disabled"])
self.export_compression_mat.setCurrentIndex(0)
+ elif name == "CSV":
+ self.output_options.setCurrentIndex(3)
+
else:
self.output_options.setCurrentIndex(1)
if name == "Parquet":
@@ -1515,6 +1521,12 @@ def extract_bus_csv_logging(self, event):
empty_channels = self.empty_channels_bus.currentText()
raster = self.export_raster_bus.value()
time_as_date = self.bus_time_as_date.checkState() == QtCore.Qt.Checked
+ delimiter = self.delimiter_bus.text() or ','
+ doublequote = self.doublequote_bus.checkState() == QtCore.Qt.Checked
+ escapechar = self.escapechar_bus.text() or None
+ lineterminator = self.lineterminator_bus.text().replace("\\r", "\r").replace("\\n", "\n")
+ quotechar = self.quotechar_bus.text() or '"'
+ quoting = self.quoting_bus.currentText()
file_name, _ = QtWidgets.QFileDialog.getSaveFileName(
self,
@@ -1557,7 +1569,10 @@ def extract_bus_csv_logging(self, event):
# then save it
progress.setLabelText(f'Saving file to "{file_name}"')
- mdf.configure(integer_interpolation=self.mdf._integer_interpolation)
+ mdf.configure(
+ integer_interpolation=self.mdf._integer_interpolation,
+ float_interpolation=self.mdf._float_interpolation,
+ )
target = mdf.export
kwargs = {
@@ -1569,6 +1584,12 @@ def extract_bus_csv_logging(self, event):
"raster": raster or None,
"time_as_date": time_as_date,
"ignore_value2text_conversions": self.ignore_value2text_conversions,
+ "delimiter": delimiter,
+ "doublequote": doublequote,
+ "escapechar": escapechar,
+ "lineterminator": lineterminator,
+ "quotechar": quotechar,
+ "quoting": quoting,
}
run_thread_with_progress(
@@ -1831,6 +1852,30 @@ def _current_options(self):
"raw": self.raw_mat.checkState() == QtCore.Qt.Checked,
}
+ elif output_format == "CSV":
+
+ new = {
+ "single_time_base": self.single_time_base_csv.checkState()
+ == QtCore.Qt.Checked,
+ "time_from_zero": self.time_from_zero_csv.checkState()
+ == QtCore.Qt.Checked,
+ "time_as_date": self.time_as_date_csv.checkState() == QtCore.Qt.Checked,
+ "use_display_names": self.use_display_names_csv.checkState()
+ == QtCore.Qt.Checked,
+ "reduce_memory_usage": False,
+ "compression": False,
+ "empty_channels": self.empty_channels_csv.currentText(),
+ "raw": self.raw_csv.checkState() == QtCore.Qt.Checked,
+ "delimiter": self.delimiter.text() or ',',
+ "doublequote": self.doublequote.checkState() == QtCore.Qt.Checked,
+ "escapechar": self.escapechar.text() or None,
+ "lineterminator": self.lineterminator.text().replace("\\r", "\r").replace("\\n", "\n"),
+ "quotechar": self.quotechar.text() or '"',
+ 'quoting': self.quoting.currentText(),
+ "mat_format": None,
+ "oned_as": None,
+ }
+
else:
new = {
@@ -2002,6 +2047,7 @@ def apply_processing(self, event):
mdf = None
progress = None
integer_interpolation = self.mdf._integer_interpolation
+ float_interpolation = self.mdf._float_interpolation
if needs_filter:
@@ -2038,6 +2084,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=integer_interpolation,
+ float_interpolation=float_interpolation,
)
if opts.needs_cut:
@@ -2093,6 +2140,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=integer_interpolation,
+ float_interpolation=float_interpolation,
)
if opts.needs_resample:
@@ -2151,6 +2199,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=integer_interpolation,
+ float_interpolation=float_interpolation,
)
if output_format == "MDF":
@@ -2198,6 +2247,7 @@ def apply_processing(self, event):
read_fragment_size=split_size,
write_fragment_size=split_size,
integer_interpolation=integer_interpolation,
+ float_interpolation=float_interpolation,
)
# then save it
diff --git a/asammdf/gui/widgets/main.py b/asammdf/gui/widgets/main.py
index d43451ec8..3249300f0 100644
--- a/asammdf/gui/widgets/main.py
+++ b/asammdf/gui/widgets/main.py
@@ -37,7 +37,17 @@ def __init__(self, files=None, *args, **kwargs):
)[0]
)
- self.batch = BatchWidget(self.ignore_value2text_conversions, self.integer_interpolation)
+ self.float_interpolation = int(
+ self._settings.value(
+ "float_interpolation", "1 - linear interpolation"
+ )[0]
+ )
+
+ self.batch = BatchWidget(
+ self.ignore_value2text_conversions,
+ self.integer_interpolation,
+ self.float_interpolation,
+ )
self.stackedWidget.addWidget(self.batch)
widget = QtWidgets.QWidget()
@@ -246,6 +256,25 @@ def __init__(self, files=None, *args, **kwargs):
submenu.setToolTipsVisible(True)
menu.addMenu(submenu)
+ # float interpolation menu
+ theme_option = QtWidgets.QActionGroup(self)
+
+ for option in ("0 - repeat previous sample", "1 - linear interpolation"):
+
+ action = QtWidgets.QAction(option, menu)
+ action.setCheckable(True)
+ theme_option.addAction(action)
+ action.triggered.connect(partial(self.set_float_interpolation, option))
+
+ if option == self._settings.value("float_interpolation", "1 - linear interpolation"):
+ action.setChecked(True)
+ action.triggered.emit()
+
+ submenu = QtWidgets.QMenu("Float interpolation", self.menubar)
+ submenu.addActions(theme_option.actions())
+ submenu.setToolTipsVisible(True)
+ menu.addMenu(submenu)
+
# plot option menu
plot_actions = QtWidgets.QActionGroup(self)
@@ -669,6 +698,19 @@ def set_integer_interpolation(self, option):
self.batch.integer_interpolation = option
+ def set_float_interpolation(self, option):
+ self._settings.setValue("float_interpolation", option)
+
+ option = int(option[0])
+ self.float_interpolation = option
+
+ count = self.files.count()
+
+ for i in range(count):
+ self.files.widget(i).mdf.configure(float_interpolation=option)
+
+ self.batch.float_interpolation = option
+
def set_plot_xaxis(self, option):
self._settings.setValue("plot_xaxis", option)
if option == "seconds":
diff --git a/asammdf/mdf.py b/asammdf/mdf.py
index a51772efe..1de3b7b7a 100644
--- a/asammdf/mdf.py
+++ b/asammdf/mdf.py
@@ -516,8 +516,9 @@ def convert(self, version):
**self._kwargs
)
- interpolation_mode = self._integer_interpolation
- out.configure(integer_interpolation=interpolation_mode)
+ integer_interpolation_mode = self._integer_interpolation
+ float_interpolation_mode = self._float_interpolation
+ out.configure(from_other=self)
out.header.start_time = self.header.start_time
@@ -619,8 +620,9 @@ def cut(
**self._kwargs,
)
- interpolation_mode = self._integer_interpolation
- out.configure(integer_interpolation=interpolation_mode)
+ integer_interpolation_mode = self._integer_interpolation
+ float_interpolation_mode = self._float_interpolation
+ out.configure(from_other=self)
self.configure(copy_on_get=False)
@@ -743,7 +745,8 @@ def cut(
fragment_start,
fragment_stop,
include_ends,
- interpolation_mode=interpolation_mode,
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
.timestamps
)
@@ -756,7 +759,8 @@ def cut(
master[0],
master[-1],
include_ends=include_ends,
- interpolation_mode=interpolation_mode,
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
for sig in signals
]
@@ -836,7 +840,7 @@ def cut(
return out
def export(self, fmt, filename=None, **kwargs):
- """export *MDF* to other formats. The *MDF* file name is used is
+ r"""export *MDF* to other formats. The *MDF* file name is used is
available, else the *filename* argument must be provided.
The *pandas* export option was removed. you should use the method
@@ -866,7 +870,7 @@ def export(self, fmt, filename=None, **kwargs):
filename : string | pathlib.Path
export file name
- **kwargs
+ \*\*kwargs
* `single_time_base`: resample all channels to common time base,
default *False*
@@ -884,23 +888,23 @@ def export(self, fmt, filename=None, **kwargs):
component channels. If *True* this can be very slow. If *False*
only the component channels are saved, and their names will be
prefixed with the parent channel.
- * reduce_memory_usage : bool
+ * `reduce_memory_usage` : bool
reduce memory usage by converting all float columns to float32 and
searching for minimum dtype that can reprezent the values found
in integer columns; default *False*
- * compression : str
+ * `compression` : str
compression to be used
* for ``parquet`` : "GZIP" or "SANPPY"
* for ``hfd5`` : "gzip", "lzf" or "szip"
* for ``mat`` : bool
- * time_as_date (False) : bool
+ * `time_as_date` (False) : bool
export time as local timezone datetimee; only valid for CSV export
.. versionadded:: 5.8.0
- * ignore_value2text_conversions (False) : bool
+ * `ignore_value2text_conversions` (False) : bool
valid only for the channels that have value to text conversions and
if *raw=False*. If this is True then the raw numeric values will be
used, and the conversion will not be applied.
@@ -912,6 +916,37 @@ def export(self, fmt, filename=None, **kwargs):
.. versionadded:: 6.0.0
+ * delimiter (',') : str
+ only valid for CSV: see cpython documentation for csv.Dialect.delimiter
+
+ .. versionadded:: 6.2.0
+
+ * doublequote (True) : bool
+ only valid for CSV: see cpython documentation for csv.Dialect.doublequote
+
+ .. versionadded:: 6.2.0
+
+ * escapechar (None) : str
+ only valid for CSV: see cpython documentation for csv.Dialect.escapechar
+
+ .. versionadded:: 6.2.0
+
+ * lineterminator ("\\r\\n") : str
+ only valid for CSV: see cpython documentation for csv.Dialect.lineterminator
+
+ .. versionadded:: 6.2.0
+
+ * quotechar ('"') : str
+ only valid for CSV: see cpython documentation for csv.Dialect.quotechar
+
+ .. versionadded:: 6.2.0
+
+ * quoting ("MINIMAL") : str
+ only valid for CSV: see cpython documentation for csv.Dialect.quoting. Use the
+ last part of the quoting constant name
+
+ .. versionadded:: 6.2.0
+
"""
@@ -1174,6 +1209,24 @@ def export(self, fmt, filename=None, **kwargs):
self._callback(i + 1, groups_nr)
elif fmt == "csv":
+ fmtparams = {
+ "delimiter": kwargs.get("delimiter", ",")[0],
+ "doublequote": kwargs.get("doublequote", True),
+ "lineterminator": kwargs.get("lineterminator", '\r\n'),
+ "quotechar": kwargs.get("quotechar", '"')[0],
+ }
+
+ quoting = kwargs.get("quoting", "MINIMAL").upper()
+ quoting = getattr(csv, f"QUOTE_{quoting}")
+
+ fmtparams["quoting"] = quoting
+
+ escapechar = kwargs.get("escapechar", None)
+ if escapechar is not None:
+ escapechar = escapechar[0]
+
+ fmtparams["escapechar"] = escapechar
+
if single_time_base:
filename = filename.with_suffix(".csv")
message = f'Writing csv export to file "{filename}"'
@@ -1213,7 +1266,7 @@ def export(self, fmt, filename=None, **kwargs):
with open(filename, "w", newline="") as csvfile:
- writer = csv.writer(csvfile)
+ writer = csv.writer(csvfile, **fmtparams)
names_row = [df.index.name, *df.columns]
writer.writerow(names_row)
@@ -1293,7 +1346,7 @@ def export(self, fmt, filename=None, **kwargs):
df.index.name = "timestamps"
with open(group_csv_name, "w", newline="") as csvfile:
- writer = csv.writer(csvfile)
+ writer = csv.writer(csvfile, **fmtparams)
if hasattr(self, "can_logging_db") and self.can_logging_db:
@@ -1325,9 +1378,6 @@ def export(self, fmt, filename=None, **kwargs):
df.index.to_list(),
*(df[name].to_list() for name in df),
]
- count = len(df.index)
-
- count = len(df.index)
for i, row in enumerate(zip(*vals)):
writer.writerow(row)
@@ -1536,17 +1586,18 @@ def filter(self, channels, version=None):
# group channels by group index
gps = self.included_channels(channels=channels)
- self.configure(copy_on_get=False)
-
mdf = MDF(
version=version,
**self._kwargs,
)
- interpolation_mode = self._integer_interpolation
- mdf.configure(integer_interpolation=interpolation_mode)
+ integer_interpolation_mode = self._integer_interpolation
+ float_interpolation_mode = self._float_interpolation
+ mdf.configure(from_other=self)
mdf.header.start_time = self.header.start_time
+ self.configure(copy_on_get=False)
+
if self.name:
origin = self.name.name
else:
@@ -1805,8 +1856,9 @@ def concatenate(
**kwargs,
)
- interpolation_mode = mdf._integer_interpolation
- merged.configure(integer_interpolation=interpolation_mode)
+ integer_interpolation_mode = mdf._integer_interpolation
+ float_interpolation_mode = mdf._float_interpolation
+ merged.configure(from_other=mdf)
merged.header.start_time = oldest
@@ -2080,8 +2132,10 @@ def stack(files, version="4.10", sync=True, **kwargs):
callback=callback, **kwargs,
)
- interpolation_mode = mdf._integer_interpolation
- stacked.configure(integer_interpolation=interpolation_mode)
+ integer_interpolation_mode = mdf._integer_interpolation
+ float_interpolation_mode = mdf._float_interpolation
+ stacked.configure(from_other=mdf)
+
if sync:
stacked.header.start_time = oldest
@@ -2252,7 +2306,7 @@ def iter_groups(
* a channel name who's timestamps will be used as raster (starting with asammdf 5.5.0)
* an array (starting with asammdf 5.5.0)
- see `resample` for examples of urisng this argument
+ see `resample` for examples of using this argument
.. versionadded:: 5.21.0
@@ -2430,8 +2484,9 @@ def resample(self, raster, version=None, time_from_zero=False):
**self._kwargs,
)
- interpolation_mode = self._integer_interpolation
- mdf.configure(integer_interpolation=interpolation_mode)
+ integer_interpolation_mode = self._integer_interpolation
+ float_interpolation_mode = self._float_interpolation
+ mdf.configure(from_other=self)
mdf.header.start_time = self.header.start_time
@@ -2472,7 +2527,11 @@ def resample(self, raster, version=None, time_from_zero=False):
sigs = self.select(channels, raw=True)
sigs = [
- sig.interp(raster, interpolation_mode=interpolation_mode)
+ sig.interp(
+ raster,
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
+ )
for sig in sigs
]
@@ -3100,7 +3159,7 @@ def get_group(
* a channel name who's timestamps will be used as raster (starting with asammdf 5.5.0)
* an array (starting with asammdf 5.5.0)
- see `resample` for examples of urisng this argument
+ see `resample` for examples of using this argument
Returns
-------
@@ -3155,7 +3214,13 @@ def iter_to_dataframe(
Parameters
----------
channels : list
- filter a subset of channels; default *None*
+ list of items to be filtered (default None); each item can be :
+
+ * a channel name string
+ * (channel name, group index, channel index) list or tuple
+ * (channel name, group index) list or tuple
+ * (None, group index, channel index) list or tuple
+
raster : float | np.array | str
new raster that can be
@@ -3163,7 +3228,7 @@ def iter_to_dataframe(
* a channel name who's timestamps will be used as raster (starting with asammdf 5.5.0)
* an array (starting with asammdf 5.5.0)
- see `resample` for examples of urisng this argument
+ see `resample` for examples of using this argument
time_from_zero : bool
adjust time channel to start from 0; default *True*
@@ -3530,7 +3595,13 @@ def to_dataframe(
Parameters
----------
channels : list
- filter a subset of channels; default *None*
+ list of items to be filtered (default None); each item can be :
+
+ * a channel name string
+ * (channel name, group index, channel index) list or tuple
+ * (channel name, group index) list or tuple
+ * (None, group index, channel index) list or tuple
+
raster : float | np.array | str
new raster that can be
@@ -3538,7 +3609,7 @@ def to_dataframe(
* a channel name who's timestamps will be used as raster (starting with asammdf 5.5.0)
* an array (starting with asammdf 5.5.0)
- see `resample` for examples of urisng this argument
+ see `resample` for examples of using this argument
time_from_zero : bool
adjust time channel to start from 0; default *True*
@@ -4632,9 +4703,12 @@ def whereis(self, channel, source_name=None, source_path=None):
()
"""
- occurrences = self._filter_occurrences(
- self.channels_db[channel], source_name=source_name, source_path=source_path
- )
+ try:
+ occurrences = self._filter_occurrences(
+ self.channels_db[channel], source_name=source_name, source_path=source_path
+ )
+ except:
+ occurrences = tuple()
return tuple(occurrences)
diff --git a/asammdf/signal.py b/asammdf/signal.py
index 7352225ec..aa7209afa 100644
--- a/asammdf/signal.py
+++ b/asammdf/signal.py
@@ -389,7 +389,15 @@ def update(val):
except Exception as err:
print(err)
- def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
+ def cut(
+ self,
+ start=None,
+ stop=None,
+ include_ends=True,
+ interpolation_mode=None,
+ integer_interpolation_mode=None,
+ float_interpolation_mode=1,
+ ):
"""
Cuts the signal according to the *start* and *stop* values, by using
the insertion indexes in the signal's *time* axis.
@@ -405,10 +413,36 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
If *start* and *stop* are found in the original timestamps, then
the new samples will be computed using interpolation. Default *True*
interpolation_mode : int
+ interpolation mode for integer signals; default 0. You should use the new *integer_interpolation_mode*
+ argument since this will be deprecated in a later release
+
+ * 0 - repeat previous samples
+ * 1 - linear interpolation
+ * 2 - hybrid interpolation: channels with integer data type (raw values) that have a
+ conversion that outputs float values will use linear interpolation, otherwise
+ the previous sample is used
+
+ .. versionchanged:: 6.2.0
+ added hybrid mode interpolation
+
+ integer_interpolation_mode : int
interpolation mode for integer signals; default 0
* 0 - repeat previous samples
* 1 - linear interpolation
+ * 2 - hybrid interpolation: channels with integer data type (raw values) that have a
+ conversion that outputs float values will use linear interpolation, otherwise
+ the previous sample is used
+
+ .. versionadded:: 6.2.0
+
+ float_interpolation_mode : int
+ interpolation mode for float channels; default 1
+
+ * 0 - repeat previous sample
+ * 1 - use linear interpolation
+
+ .. versionadded:: 6.2.0
Returns
-------
@@ -422,6 +456,20 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
0.98, 10.48
"""
+ if integer_interpolation_mode is None:
+ if interpolation_mode is not None:
+ integer_interpolation_mode = interpolation_mode
+ else:
+ integer_interpolation_mode = 0
+ else:
+ integer_interpolation_mode = 0
+
+ if integer_interpolation_mode not in (0, 1, 2):
+ raise MdfException("Integer interpolation mode should be one of (0, 1, 2)")
+
+ if float_interpolation_mode not in (0, 1):
+ raise MdfException("Float interpolation mode should be one of (0, 1)")
+
ends = (start, stop)
if len(self) == 0:
result = Signal(
@@ -498,7 +546,9 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
and ends[-1] < self.timestamps[-1]
):
interpolated = self.interp(
- [ends[1]], interpolation_mode=interpolation_mode
+ [ends[1]],
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
samples = np.append(
self.samples[:stop], interpolated.samples, axis=0
@@ -568,7 +618,9 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
and ends[0] > self.timestamps[0]
):
interpolated = self.interp(
- [ends[0]], interpolation_mode=interpolation_mode
+ [ends[0]],
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
samples = np.append(
interpolated.samples, self.samples[start:], axis=0
@@ -636,7 +688,9 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
if start == stop:
if include_ends:
interpolated = self.interp(
- np.unique(ends), interpolation_mode=interpolation_mode
+ np.unique(ends),
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
samples = interpolated.samples
timestamps = np.array(
@@ -666,7 +720,9 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
and ends[-1] < self.timestamps[-1]
):
interpolated = self.interp(
- [ends[1]], interpolation_mode=interpolation_mode
+ [ends[1]],
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
samples = np.append(samples, interpolated.samples, axis=0)
timestamps = np.append(timestamps, ends[1])
@@ -681,7 +737,9 @@ def cut(self, start=None, stop=None, include_ends=True, interpolation_mode=0):
and ends[0] > self.timestamps[0]
):
interpolated = self.interp(
- [ends[0]], interpolation_mode=interpolation_mode
+ [ends[0]],
+ integer_interpolation_mode=integer_interpolation_mode,
+ float_interpolation_mode=float_interpolation_mode,
)
samples = np.append(interpolated.samples, samples, axis=0)
timestamps = np.append(ends[0], timestamps)
@@ -776,7 +834,13 @@ def extend(self, other):
return result
- def interp(self, new_timestamps, interpolation_mode=0):
+ def interp(
+ self,
+ new_timestamps,
+ interpolation_mode=None,
+ integer_interpolation_mode=None,
+ float_interpolation_mode=1,
+ ):
"""returns a new *Signal* interpolated using the *new_timestamps*
Parameters
@@ -784,7 +848,8 @@ def interp(self, new_timestamps, interpolation_mode=0):
new_timestamps : np.array
timestamps used for interpolation
interpolation_mode : int
- interpolation mode for integer signals; default 0
+ interpolation mode for integer signals; default 0. You should use the new *integer_interpolation_mode*
+ argument since this will be deprecated in a later release
* 0 - repeat previous samples
* 1 - linear interpolation
@@ -795,6 +860,25 @@ def interp(self, new_timestamps, interpolation_mode=0):
.. versionchanged:: 6.2.0
added hybrid mode interpolation
+ integer_interpolation_mode : int
+ interpolation mode for integer signals; default 0
+
+ * 0 - repeat previous samples
+ * 1 - linear interpolation
+ * 2 - hybrid interpolation: channels with integer data type (raw values) that have a
+ conversion that outputs float values will use linear interpolation, otherwise
+ the previous sample is used
+
+ .. versionadded:: 6.2.0
+
+ float_interpolation_mode : int
+ interpolation mode for float channels; default 1
+
+ * 0 - repeat previous sample
+ * 1 - use linear interpolation
+
+ .. versionadded:: 6.2.0
+
Returns
-------
signal : Signal
@@ -802,6 +886,20 @@ def interp(self, new_timestamps, interpolation_mode=0):
"""
+ if integer_interpolation_mode is None:
+ if interpolation_mode is not None:
+ integer_interpolation_mode = interpolation_mode
+ else:
+ integer_interpolation_mode = 0
+ else:
+ integer_interpolation_mode = 0
+
+ if integer_interpolation_mode not in (0, 1, 2):
+ raise MdfException("Integer interpolation mode should be one of (0, 1, 2)")
+
+ if float_interpolation_mode not in (0, 1):
+ raise MdfException("Float interpolation mode should be one of (0, 1)")
+
if not len(self.samples) or not len(new_timestamps):
return Signal(
self.samples[:0].copy(),
@@ -837,28 +935,43 @@ def interp(self, new_timestamps, interpolation_mode=0):
kind = self.samples.dtype.kind
if kind == "f":
- s = np.interp(new_timestamps, self.timestamps, self.samples)
-
- if self.invalidation_bits is not None:
+ if float_interpolation_mode == 0:
idx = np.searchsorted(
self.timestamps, new_timestamps, side="right"
)
idx -= 1
idx = np.clip(idx, 0, idx[-1])
- invalidation_bits = self.invalidation_bits[idx]
+ s = self.samples[idx]
+
+ if self.invalidation_bits is not None:
+ invalidation_bits = self.invalidation_bits[idx]
+ else:
+ invalidation_bits = None
+
else:
- invalidation_bits = None
+ s = np.interp(new_timestamps, self.timestamps, self.samples)
+
+ if self.invalidation_bits is not None:
+ idx = np.searchsorted(
+ self.timestamps, new_timestamps, side="right"
+ )
+ idx -= 1
+ idx = np.clip(idx, 0, idx[-1])
+ invalidation_bits = self.invalidation_bits[idx]
+ else:
+ invalidation_bits = None
+
elif kind in "ui":
- if interpolation_mode == 2:
+ if integer_interpolation_mode == 2:
if self.raw and self.conversion:
kind = self.conversion.convert(self.samples[:1]).dtype.kind
if kind == "f":
- interpolation_mode = 1
+ integer_interpolation_mode = 1
- if interpolation_mode == 2:
- interpolation_mode = 0
+ if integer_interpolation_mode == 2:
+ integer_interpolation_mode = 0
- if interpolation_mode == 1:
+ if integer_interpolation_mode == 1:
s = np.interp(
new_timestamps, self.timestamps, self.samples
).astype(self.samples.dtype)
@@ -871,7 +984,7 @@ def interp(self, new_timestamps, interpolation_mode=0):
invalidation_bits = self.invalidation_bits[idx]
else:
invalidation_bits = None
- elif interpolation_mode == 0:
+ elif integer_interpolation_mode == 0:
idx = np.searchsorted(
self.timestamps, new_timestamps, side="right"
)