|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import math |
3 | 4 | import mmap |
4 | 5 | import sys |
5 | 6 | import warnings |
6 | 7 | from functools import partial |
7 | 8 | from typing import TYPE_CHECKING, NamedTuple |
8 | 9 |
|
9 | 10 | import numpy as np |
| 11 | +import scipy as sp |
10 | 12 |
|
11 | 13 | from eitprocessing.datahandling.continuousdata import ContinuousData |
12 | 14 | from eitprocessing.datahandling.datacollection import DataCollection |
|
24 | 26 |
|
25 | 27 | load_draeger_data = partial(load_eit_data, vendor=Vendor.DRAEGER) |
26 | 28 | NAN_VALUE_INDICATOR = -1e30 |
| 29 | +SAMPLE_FREQUENCY_ESTIMATION_PRECISION = 4 |
27 | 30 |
|
28 | 31 |
|
29 | 32 | def load_from_single_path( |
@@ -184,15 +187,27 @@ def load_from_single_path( |
184 | 187 |
|
185 | 188 | def _estimate_sample_frequency(time: np.ndarray, sample_frequency: float | None) -> float: |
186 | 189 | """Estimate the sample frequency from the time axis, and check with provided sample frequency.""" |
187 | | - estimated_sample_frequency = round((len(time) - 1) / (time[-1] - time[0]), 4) |
| 190 | + unrounded_estimated_sample_frequency = 1 / sp.stats.linregress(np.arange(len(time)), time).slope |
| 191 | + |
| 192 | + # Rounds to the number of digits, rather than the number of decimals |
| 193 | + estimated_sample_frequency = round( |
| 194 | + unrounded_estimated_sample_frequency, |
| 195 | + -math.ceil(np.log10(abs(unrounded_estimated_sample_frequency))) + SAMPLE_FREQUENCY_ESTIMATION_PRECISION, |
| 196 | + ) |
188 | 197 |
|
189 | 198 | if sample_frequency is None: |
190 | 199 | return estimated_sample_frequency |
191 | 200 |
|
192 | | - if sample_frequency != estimated_sample_frequency: |
| 201 | + if not np.isclose( |
| 202 | + sample_frequency, unrounded_estimated_sample_frequency, rtol=10**-SAMPLE_FREQUENCY_ESTIMATION_PRECISION, atol=0 |
| 203 | + ): |
193 | 204 | msg = ( |
194 | | - f"Provided sample frequency ({sample_frequency}) does not match " |
195 | | - f"the estimated sample frequency ({estimated_sample_frequency})." |
| 205 | + "Provided sample frequency " |
| 206 | + f"({sample_frequency:.{SAMPLE_FREQUENCY_ESTIMATION_PRECISION + 2}f} Hz) " |
| 207 | + "does not match the estimated sample frequency " |
| 208 | + f"({unrounded_estimated_sample_frequency:.{SAMPLE_FREQUENCY_ESTIMATION_PRECISION + 2}f} Hz) " |
| 209 | + f"within {SAMPLE_FREQUENCY_ESTIMATION_PRECISION} digits. " |
| 210 | + "Note that the estimate might not be as accurate for very short signals." |
196 | 211 | ) |
197 | 212 | warnings.warn(msg, RuntimeWarning, stacklevel=2) |
198 | 213 |
|
@@ -249,7 +264,7 @@ def _read_frame( |
249 | 264 | index is non-negative. When the index is negative, no data is saved. In |
250 | 265 | any case, the event marker is returned. |
251 | 266 | """ |
252 | | - frame_time = round(reader.float64() * 24 * 60 * 60, 3) |
| 267 | + frame_time = reader.float64() * 24 * 60 * 60 |
253 | 268 | _ = reader.float32() |
254 | 269 | frame_pixel_impedance = reader.npfloat32(length=1024) |
255 | 270 | frame_pixel_impedance = np.reshape(frame_pixel_impedance, (32, 32), "C") |
|
0 commit comments