sigima 0.0.1.dev0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sigima/__init__.py +142 -2
- sigima/client/__init__.py +105 -0
- sigima/client/base.py +780 -0
- sigima/client/remote.py +469 -0
- sigima/client/stub.py +814 -0
- sigima/client/utils.py +90 -0
- sigima/config.py +444 -0
- sigima/data/logo/Sigima.svg +135 -0
- sigima/data/tests/annotations.json +798 -0
- sigima/data/tests/curve_fitting/exponential_fit.txt +511 -0
- sigima/data/tests/curve_fitting/gaussian_fit.txt +100 -0
- sigima/data/tests/curve_fitting/piecewiseexponential_fit.txt +1022 -0
- sigima/data/tests/curve_fitting/polynomial_fit.txt +100 -0
- sigima/data/tests/curve_fitting/twohalfgaussian_fit.txt +1000 -0
- sigima/data/tests/curve_formats/bandwidth.txt +201 -0
- sigima/data/tests/curve_formats/boxcar.npy +0 -0
- sigima/data/tests/curve_formats/datetime.txt +1001 -0
- sigima/data/tests/curve_formats/dynamic_parameters.txt +4000 -0
- sigima/data/tests/curve_formats/fw1e2.txt +301 -0
- sigima/data/tests/curve_formats/fwhm.txt +319 -0
- sigima/data/tests/curve_formats/multiple_curves.csv +29 -0
- sigima/data/tests/curve_formats/noised_saw.mat +0 -0
- sigima/data/tests/curve_formats/oscilloscope.csv +111 -0
- sigima/data/tests/curve_formats/other/other2/recursive2.txt +5 -0
- sigima/data/tests/curve_formats/other/recursive1.txt +5 -0
- sigima/data/tests/curve_formats/paracetamol.npy +0 -0
- sigima/data/tests/curve_formats/paracetamol.txt +1010 -0
- sigima/data/tests/curve_formats/paracetamol_dx_dy.csv +1000 -0
- sigima/data/tests/curve_formats/paracetamol_dy.csv +1001 -0
- sigima/data/tests/curve_formats/pulse1.npy +0 -0
- sigima/data/tests/curve_formats/pulse2.npy +0 -0
- sigima/data/tests/curve_formats/simple.txt +5 -0
- sigima/data/tests/curve_formats/spectrum.mca +2139 -0
- sigima/data/tests/curve_formats/square2.npy +0 -0
- sigima/data/tests/curve_formats/step.npy +0 -0
- sigima/data/tests/fabry-perot1.jpg +0 -0
- sigima/data/tests/fabry-perot2.jpg +0 -0
- sigima/data/tests/flower.npy +0 -0
- sigima/data/tests/image_formats/NF 180338201.scor-data +11003 -0
- sigima/data/tests/image_formats/binary_image.npy +0 -0
- sigima/data/tests/image_formats/binary_image.png +0 -0
- sigima/data/tests/image_formats/centroid_test.npy +0 -0
- sigima/data/tests/image_formats/coordinated_text/complex_image.txt +10011 -0
- sigima/data/tests/image_formats/coordinated_text/complex_ref_image.txt +10010 -0
- sigima/data/tests/image_formats/coordinated_text/image.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image2.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_no_unit_no_label.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_nan.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_unit.txt +14 -0
- sigima/data/tests/image_formats/fiber.csv +480 -0
- sigima/data/tests/image_formats/fiber.jpg +0 -0
- sigima/data/tests/image_formats/fiber.png +0 -0
- sigima/data/tests/image_formats/fiber.txt +480 -0
- sigima/data/tests/image_formats/gaussian_spot_with_noise.npy +0 -0
- sigima/data/tests/image_formats/mr-brain.dcm +0 -0
- sigima/data/tests/image_formats/noised_gaussian.mat +0 -0
- sigima/data/tests/image_formats/sif_reader/nd_lum_image_no_glue.sif +0 -0
- sigima/data/tests/image_formats/sif_reader/raman1.sif +0 -0
- sigima/data/tests/image_formats/tiling.txt +10 -0
- sigima/data/tests/image_formats/uint16.tiff +0 -0
- sigima/data/tests/image_formats/uint8.tiff +0 -0
- sigima/data/tests/laser_beam/TEM00_z_13.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_18.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_23.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_30.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_35.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_40.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_45.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_50.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_55.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_60.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_65.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_70.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_75.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_80.jpg +0 -0
- sigima/enums.py +195 -0
- sigima/io/__init__.py +123 -0
- sigima/io/base.py +311 -0
- sigima/io/common/__init__.py +5 -0
- sigima/io/common/basename.py +164 -0
- sigima/io/common/converters.py +189 -0
- sigima/io/common/objmeta.py +181 -0
- sigima/io/common/textreader.py +58 -0
- sigima/io/convenience.py +157 -0
- sigima/io/enums.py +17 -0
- sigima/io/ftlab.py +395 -0
- sigima/io/image/__init__.py +9 -0
- sigima/io/image/base.py +177 -0
- sigima/io/image/formats.py +1016 -0
- sigima/io/image/funcs.py +414 -0
- sigima/io/signal/__init__.py +9 -0
- sigima/io/signal/base.py +129 -0
- sigima/io/signal/formats.py +290 -0
- sigima/io/signal/funcs.py +723 -0
- sigima/objects/__init__.py +260 -0
- sigima/objects/base.py +937 -0
- sigima/objects/image/__init__.py +88 -0
- sigima/objects/image/creation.py +556 -0
- sigima/objects/image/object.py +524 -0
- sigima/objects/image/roi.py +904 -0
- sigima/objects/scalar/__init__.py +57 -0
- sigima/objects/scalar/common.py +215 -0
- sigima/objects/scalar/geometry.py +502 -0
- sigima/objects/scalar/table.py +784 -0
- sigima/objects/shape.py +290 -0
- sigima/objects/signal/__init__.py +133 -0
- sigima/objects/signal/constants.py +27 -0
- sigima/objects/signal/creation.py +1428 -0
- sigima/objects/signal/object.py +444 -0
- sigima/objects/signal/roi.py +274 -0
- sigima/params.py +405 -0
- sigima/proc/__init__.py +96 -0
- sigima/proc/base.py +381 -0
- sigima/proc/decorator.py +330 -0
- sigima/proc/image/__init__.py +513 -0
- sigima/proc/image/arithmetic.py +335 -0
- sigima/proc/image/base.py +260 -0
- sigima/proc/image/detection.py +519 -0
- sigima/proc/image/edges.py +329 -0
- sigima/proc/image/exposure.py +406 -0
- sigima/proc/image/extraction.py +458 -0
- sigima/proc/image/filtering.py +219 -0
- sigima/proc/image/fourier.py +147 -0
- sigima/proc/image/geometry.py +661 -0
- sigima/proc/image/mathops.py +340 -0
- sigima/proc/image/measurement.py +195 -0
- sigima/proc/image/morphology.py +155 -0
- sigima/proc/image/noise.py +107 -0
- sigima/proc/image/preprocessing.py +182 -0
- sigima/proc/image/restoration.py +235 -0
- sigima/proc/image/threshold.py +217 -0
- sigima/proc/image/transformations.py +393 -0
- sigima/proc/signal/__init__.py +376 -0
- sigima/proc/signal/analysis.py +206 -0
- sigima/proc/signal/arithmetic.py +551 -0
- sigima/proc/signal/base.py +262 -0
- sigima/proc/signal/extraction.py +60 -0
- sigima/proc/signal/features.py +310 -0
- sigima/proc/signal/filtering.py +484 -0
- sigima/proc/signal/fitting.py +276 -0
- sigima/proc/signal/fourier.py +259 -0
- sigima/proc/signal/mathops.py +420 -0
- sigima/proc/signal/processing.py +580 -0
- sigima/proc/signal/stability.py +175 -0
- sigima/proc/title_formatting.py +227 -0
- sigima/proc/validation.py +272 -0
- sigima/tests/__init__.py +7 -0
- sigima/tests/common/__init__.py +0 -0
- sigima/tests/common/arithmeticparam_unit_test.py +26 -0
- sigima/tests/common/basename_unit_test.py +126 -0
- sigima/tests/common/client_unit_test.py +412 -0
- sigima/tests/common/converters_unit_test.py +77 -0
- sigima/tests/common/decorator_unit_test.py +176 -0
- sigima/tests/common/examples_unit_test.py +104 -0
- sigima/tests/common/kernel_normalization_unit_test.py +242 -0
- sigima/tests/common/roi_basic_unit_test.py +73 -0
- sigima/tests/common/roi_geometry_unit_test.py +171 -0
- sigima/tests/common/scalar_builder_unit_test.py +142 -0
- sigima/tests/common/scalar_unit_test.py +991 -0
- sigima/tests/common/shape_unit_test.py +183 -0
- sigima/tests/common/stat_unit_test.py +138 -0
- sigima/tests/common/title_formatting_unit_test.py +338 -0
- sigima/tests/common/tools_coordinates_unit_test.py +60 -0
- sigima/tests/common/transformations_unit_test.py +178 -0
- sigima/tests/common/validation_unit_test.py +205 -0
- sigima/tests/conftest.py +129 -0
- sigima/tests/data.py +998 -0
- sigima/tests/env.py +280 -0
- sigima/tests/guiutils.py +163 -0
- sigima/tests/helpers.py +532 -0
- sigima/tests/image/__init__.py +28 -0
- sigima/tests/image/binning_unit_test.py +128 -0
- sigima/tests/image/blob_detection_unit_test.py +312 -0
- sigima/tests/image/centroid_unit_test.py +170 -0
- sigima/tests/image/check_2d_array_unit_test.py +63 -0
- sigima/tests/image/contour_unit_test.py +172 -0
- sigima/tests/image/convolution_unit_test.py +178 -0
- sigima/tests/image/datatype_unit_test.py +67 -0
- sigima/tests/image/edges_unit_test.py +155 -0
- sigima/tests/image/enclosingcircle_unit_test.py +88 -0
- sigima/tests/image/exposure_unit_test.py +223 -0
- sigima/tests/image/fft2d_unit_test.py +189 -0
- sigima/tests/image/filtering_unit_test.py +166 -0
- sigima/tests/image/geometry_unit_test.py +654 -0
- sigima/tests/image/hough_circle_unit_test.py +147 -0
- sigima/tests/image/imageobj_unit_test.py +737 -0
- sigima/tests/image/morphology_unit_test.py +71 -0
- sigima/tests/image/noise_unit_test.py +57 -0
- sigima/tests/image/offset_correction_unit_test.py +72 -0
- sigima/tests/image/operation_unit_test.py +518 -0
- sigima/tests/image/peak2d_limits_unit_test.py +41 -0
- sigima/tests/image/peak2d_unit_test.py +133 -0
- sigima/tests/image/profile_unit_test.py +159 -0
- sigima/tests/image/projections_unit_test.py +121 -0
- sigima/tests/image/restoration_unit_test.py +141 -0
- sigima/tests/image/roi2dparam_unit_test.py +53 -0
- sigima/tests/image/roi_advanced_unit_test.py +588 -0
- sigima/tests/image/roi_grid_unit_test.py +279 -0
- sigima/tests/image/spectrum2d_unit_test.py +40 -0
- sigima/tests/image/threshold_unit_test.py +91 -0
- sigima/tests/io/__init__.py +0 -0
- sigima/tests/io/addnewformat_unit_test.py +125 -0
- sigima/tests/io/convenience_funcs_unit_test.py +470 -0
- sigima/tests/io/coordinated_text_format_unit_test.py +495 -0
- sigima/tests/io/datetime_csv_unit_test.py +198 -0
- sigima/tests/io/imageio_formats_test.py +41 -0
- sigima/tests/io/ioregistry_unit_test.py +69 -0
- sigima/tests/io/objmeta_unit_test.py +87 -0
- sigima/tests/io/readobj_unit_test.py +130 -0
- sigima/tests/io/readwriteobj_unit_test.py +67 -0
- sigima/tests/signal/__init__.py +0 -0
- sigima/tests/signal/analysis_unit_test.py +135 -0
- sigima/tests/signal/check_1d_arrays_unit_test.py +169 -0
- sigima/tests/signal/convolution_unit_test.py +404 -0
- sigima/tests/signal/datetime_unit_test.py +176 -0
- sigima/tests/signal/fft1d_unit_test.py +303 -0
- sigima/tests/signal/filters_unit_test.py +403 -0
- sigima/tests/signal/fitting_unit_test.py +929 -0
- sigima/tests/signal/fwhm_unit_test.py +111 -0
- sigima/tests/signal/noise_unit_test.py +128 -0
- sigima/tests/signal/offset_correction_unit_test.py +34 -0
- sigima/tests/signal/operation_unit_test.py +489 -0
- sigima/tests/signal/peakdetection_unit_test.py +145 -0
- sigima/tests/signal/processing_unit_test.py +657 -0
- sigima/tests/signal/pulse/__init__.py +112 -0
- sigima/tests/signal/pulse/crossing_times_unit_test.py +123 -0
- sigima/tests/signal/pulse/plateau_detection_unit_test.py +102 -0
- sigima/tests/signal/pulse/pulse_unit_test.py +1824 -0
- sigima/tests/signal/roi_advanced_unit_test.py +392 -0
- sigima/tests/signal/signalobj_unit_test.py +603 -0
- sigima/tests/signal/stability_unit_test.py +431 -0
- sigima/tests/signal/uncertainty_unit_test.py +611 -0
- sigima/tests/vistools.py +1030 -0
- sigima/tools/__init__.py +59 -0
- sigima/tools/checks.py +290 -0
- sigima/tools/coordinates.py +308 -0
- sigima/tools/datatypes.py +26 -0
- sigima/tools/image/__init__.py +97 -0
- sigima/tools/image/detection.py +451 -0
- sigima/tools/image/exposure.py +77 -0
- sigima/tools/image/extraction.py +48 -0
- sigima/tools/image/fourier.py +260 -0
- sigima/tools/image/geometry.py +190 -0
- sigima/tools/image/preprocessing.py +165 -0
- sigima/tools/signal/__init__.py +86 -0
- sigima/tools/signal/dynamic.py +254 -0
- sigima/tools/signal/features.py +135 -0
- sigima/tools/signal/filtering.py +171 -0
- sigima/tools/signal/fitting.py +1171 -0
- sigima/tools/signal/fourier.py +466 -0
- sigima/tools/signal/interpolation.py +70 -0
- sigima/tools/signal/peakdetection.py +126 -0
- sigima/tools/signal/pulse.py +1626 -0
- sigima/tools/signal/scaling.py +50 -0
- sigima/tools/signal/stability.py +258 -0
- sigima/tools/signal/windowing.py +90 -0
- sigima/worker.py +79 -0
- sigima-1.0.0.dist-info/METADATA +233 -0
- sigima-1.0.0.dist-info/RECORD +262 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/licenses/LICENSE +29 -29
- sigima-0.0.1.dev0.dist-info/METADATA +0 -60
- sigima-0.0.1.dev0.dist-info/RECORD +0 -6
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/WHEEL +0 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
.. Scaling (see parent package :mod:`sigima.algorithms.signal`)
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
from sigima.enums import NormalizationMethod
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def normalize(
|
|
18
|
+
yin: np.ndarray,
|
|
19
|
+
parameter: NormalizationMethod = NormalizationMethod.MAXIMUM,
|
|
20
|
+
) -> np.ndarray:
|
|
21
|
+
"""Normalize input array to a given parameter.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
yin: Input array
|
|
25
|
+
parameter: Normalization parameter. Defaults to MAXIMUM
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Normalized array
|
|
29
|
+
"""
|
|
30
|
+
axis = len(yin.shape) - 1
|
|
31
|
+
if parameter == NormalizationMethod.MAXIMUM:
|
|
32
|
+
maximum = np.nanmax(yin, axis)
|
|
33
|
+
if axis == 1:
|
|
34
|
+
maximum = maximum.reshape((len(maximum), 1))
|
|
35
|
+
maxarray = np.tile(maximum, yin.shape[axis]).reshape(yin.shape)
|
|
36
|
+
return yin / maxarray
|
|
37
|
+
if parameter == NormalizationMethod.AMPLITUDE:
|
|
38
|
+
ytemp = np.array(yin, copy=True)
|
|
39
|
+
minimum = np.nanmin(yin, axis)
|
|
40
|
+
if axis == 1:
|
|
41
|
+
minimum = minimum.reshape((len(minimum), 1))
|
|
42
|
+
ytemp -= minimum
|
|
43
|
+
return normalize(ytemp, parameter=NormalizationMethod.MAXIMUM)
|
|
44
|
+
if parameter == NormalizationMethod.AREA:
|
|
45
|
+
return yin / np.nansum(yin)
|
|
46
|
+
if parameter == NormalizationMethod.ENERGY:
|
|
47
|
+
return yin / np.sqrt(np.nansum(yin * yin.conjugate()))
|
|
48
|
+
if parameter == NormalizationMethod.RMS:
|
|
49
|
+
return yin / np.sqrt(np.nanmean(yin * yin.conjugate()))
|
|
50
|
+
raise RuntimeError(f"Unsupported parameter {parameter}")
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
.. Stability Analysis (see parent package :mod:`sigima.algorithms.signal`)
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from sigima.tools.checks import check_1d_arrays
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
16
|
+
def allan_variance(x: np.ndarray, y: np.ndarray, tau_values: np.ndarray) -> np.ndarray:
|
|
17
|
+
"""
|
|
18
|
+
Calculate the Allan variance for given time and measurement values at specified
|
|
19
|
+
tau values.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
x: Time array
|
|
23
|
+
y: Measured values array
|
|
24
|
+
tau_values: Allan deviation time values
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Allan variance values
|
|
28
|
+
"""
|
|
29
|
+
if len(x) != len(y):
|
|
30
|
+
raise ValueError(
|
|
31
|
+
"Time array (x) and measured values array (y) must have the same length."
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
dt = np.mean(np.diff(x)) # Time step size
|
|
35
|
+
if not np.allclose(np.diff(x), dt):
|
|
36
|
+
raise ValueError("Time values (x) must be equally spaced.")
|
|
37
|
+
|
|
38
|
+
allan_var = []
|
|
39
|
+
for tau in tau_values:
|
|
40
|
+
m = int(round(tau / dt)) # Number of time steps in a tau
|
|
41
|
+
if m < 1:
|
|
42
|
+
raise ValueError(
|
|
43
|
+
f"Tau value {tau} is smaller than the sampling interval {dt}"
|
|
44
|
+
)
|
|
45
|
+
if m > len(y) // 2:
|
|
46
|
+
# Tau too large for reliable statistics
|
|
47
|
+
allan_var.append(np.nan)
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
# Calculate the clusters/bins
|
|
51
|
+
clusters = y[: len(y) - (len(y) % m)].reshape(-1, m)
|
|
52
|
+
bin_means = clusters.mean(axis=1)
|
|
53
|
+
|
|
54
|
+
# Calculate Allan variance using the definition
|
|
55
|
+
# σ²(τ) = 1/(2(N-1)) Σ(y_(i+1) - y_i)²
|
|
56
|
+
# where y_i are the bin means
|
|
57
|
+
squared_diff = np.sum(np.diff(bin_means) ** 2)
|
|
58
|
+
n = len(bin_means) - 1
|
|
59
|
+
|
|
60
|
+
if n > 0:
|
|
61
|
+
var = squared_diff / (2.0 * n)
|
|
62
|
+
allan_var.append(var)
|
|
63
|
+
else:
|
|
64
|
+
allan_var.append(np.nan)
|
|
65
|
+
|
|
66
|
+
return np.array(allan_var)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
70
|
+
def allan_deviation(x: np.ndarray, y: np.ndarray, tau_values: np.ndarray) -> np.ndarray:
|
|
71
|
+
"""
|
|
72
|
+
Calculate the Allan deviation for given time and measurement values at specified
|
|
73
|
+
tau values.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
x: Time array
|
|
77
|
+
y: Measured values array
|
|
78
|
+
tau_values: Allan deviation time values
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Allan deviation values
|
|
82
|
+
"""
|
|
83
|
+
return np.sqrt(allan_variance(x, y, tau_values))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
87
|
+
def overlapping_allan_variance(
|
|
88
|
+
x: np.ndarray, y: np.ndarray, tau_values: np.ndarray
|
|
89
|
+
) -> np.ndarray:
|
|
90
|
+
"""
|
|
91
|
+
Calculate the Overlapping Allan variance for given time and measurement values.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
x: Time array
|
|
95
|
+
y: Measured values array
|
|
96
|
+
tau_values: Allan deviation time values
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Overlapping Allan variance values
|
|
100
|
+
"""
|
|
101
|
+
if len(x) != len(y):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
"Time array (x) and measured values array (y) must have the same length."
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
dt = np.mean(np.diff(x)) # Time step size
|
|
107
|
+
if not np.allclose(np.diff(x), dt):
|
|
108
|
+
raise ValueError("Time values (x) must be equally spaced.")
|
|
109
|
+
|
|
110
|
+
overlapping_var = []
|
|
111
|
+
for tau in tau_values:
|
|
112
|
+
tau_bins = int(tau / dt)
|
|
113
|
+
if tau_bins <= 1 or tau_bins > len(y) / 2:
|
|
114
|
+
overlapping_var.append(np.nan)
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
m = len(y) - tau_bins # Number of overlapping segments
|
|
118
|
+
avg_values = [np.mean(y[i : i + tau_bins]) for i in range(m)]
|
|
119
|
+
diff = np.diff(avg_values)
|
|
120
|
+
overlapping_var.append(0.5 * np.mean(np.array(diff) ** 2))
|
|
121
|
+
|
|
122
|
+
return np.array(overlapping_var)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
126
|
+
def modified_allan_variance(
|
|
127
|
+
x: np.ndarray, y: np.ndarray, tau_values: np.ndarray
|
|
128
|
+
) -> np.ndarray:
|
|
129
|
+
"""
|
|
130
|
+
Calculate the Modified Allan variance for given time and measurement values.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
x: Time array
|
|
134
|
+
y: Measured values array
|
|
135
|
+
tau_values: Modified Allan deviation time values
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Modified Allan variance values
|
|
139
|
+
"""
|
|
140
|
+
if len(x) != len(y):
|
|
141
|
+
raise ValueError(
|
|
142
|
+
"Time array (x) and measured values array (y) must have the same length."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
dt = np.mean(np.diff(x))
|
|
146
|
+
if not np.allclose(np.diff(x), dt):
|
|
147
|
+
raise ValueError("Time values (x) must be equally spaced.")
|
|
148
|
+
|
|
149
|
+
mod_allan_var = []
|
|
150
|
+
for tau in tau_values:
|
|
151
|
+
tau_bins = int(tau / dt)
|
|
152
|
+
if tau_bins <= 1 or tau_bins > len(y) / 2:
|
|
153
|
+
mod_allan_var.append(np.nan)
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
m = int(len(y) / tau_bins)
|
|
157
|
+
reshaped = y[: m * tau_bins].reshape(m, tau_bins)
|
|
158
|
+
|
|
159
|
+
avg_values = reshaped.mean(axis=1)
|
|
160
|
+
squared_diff = (np.diff(avg_values)) ** 2
|
|
161
|
+
mod_allan_var.append(np.mean(squared_diff) / (2 * (tau_bins**2)))
|
|
162
|
+
|
|
163
|
+
return np.array(mod_allan_var)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
167
|
+
def hadamard_variance(
|
|
168
|
+
x: np.ndarray, y: np.ndarray, tau_values: np.ndarray
|
|
169
|
+
) -> np.ndarray:
|
|
170
|
+
"""
|
|
171
|
+
Calculate the Hadamard variance for given time and measurement values.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
x: Time array
|
|
175
|
+
y: Measured values array
|
|
176
|
+
tau_values: Hadamard deviation time values
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Hadamard variance values
|
|
180
|
+
"""
|
|
181
|
+
if len(x) != len(y):
|
|
182
|
+
raise ValueError(
|
|
183
|
+
"Time array (x) and measured values array (y) must have the same length."
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
dt = np.mean(np.diff(x))
|
|
187
|
+
if not np.allclose(np.diff(x), dt):
|
|
188
|
+
raise ValueError("Time values (x) must be equally spaced.")
|
|
189
|
+
|
|
190
|
+
hadamard_var = []
|
|
191
|
+
for tau in tau_values:
|
|
192
|
+
tau_bins = int(tau / dt)
|
|
193
|
+
if tau_bins <= 1 or tau_bins > len(y) / 3:
|
|
194
|
+
hadamard_var.append(np.nan)
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
m = len(y) - 2 * tau_bins
|
|
198
|
+
avg_values = [np.mean(y[i : i + tau_bins]) for i in range(m)]
|
|
199
|
+
diff = np.diff(avg_values, n=2) # Second differences
|
|
200
|
+
hadamard_var.append(np.mean(diff**2) / 6)
|
|
201
|
+
|
|
202
|
+
return np.array(hadamard_var)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
206
|
+
def total_variance(x: np.ndarray, y: np.ndarray, tau_values: np.ndarray) -> np.ndarray:
|
|
207
|
+
"""
|
|
208
|
+
Calculate the Total variance for given time and measurement values.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
x: Time array
|
|
212
|
+
y: Measured values array
|
|
213
|
+
tau_values: Total variance time values
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Total variance values
|
|
217
|
+
"""
|
|
218
|
+
if len(x) != len(y):
|
|
219
|
+
raise ValueError(
|
|
220
|
+
"Time array (x) and measured values array (y) must have the same length."
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
dt = np.mean(np.diff(x))
|
|
224
|
+
if not np.allclose(np.diff(x), dt):
|
|
225
|
+
raise ValueError("Time values (x) must be equally spaced.")
|
|
226
|
+
|
|
227
|
+
total_var = []
|
|
228
|
+
for tau in tau_values:
|
|
229
|
+
tau_bins = int(tau / dt)
|
|
230
|
+
if tau_bins <= 1 or tau_bins > len(y) / 2:
|
|
231
|
+
total_var.append(np.nan)
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
m = int(len(y) / tau_bins)
|
|
235
|
+
reshaped = y[: m * tau_bins].reshape(m, tau_bins)
|
|
236
|
+
|
|
237
|
+
avg_values = reshaped.mean(axis=1)
|
|
238
|
+
squared_diff = np.diff(avg_values) ** 2
|
|
239
|
+
total_var.append(np.mean(squared_diff))
|
|
240
|
+
|
|
241
|
+
return np.array(total_var)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@check_1d_arrays(x_evenly_spaced=True)
|
|
245
|
+
def time_deviation(x: np.ndarray, y: np.ndarray, tau_values: np.ndarray) -> np.ndarray:
|
|
246
|
+
"""
|
|
247
|
+
Calculate the Time Deviation (TDEV) for given time and measurement values.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
x: Time array
|
|
251
|
+
y: Measured values array
|
|
252
|
+
tau_values: Time deviation time values
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Time deviation values
|
|
256
|
+
"""
|
|
257
|
+
allan_var = allan_variance(x, y, tau_values)
|
|
258
|
+
return np.sqrt(allan_var) * tau_values
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
.. Windowing (see parent package :mod:`sigima.algorithms.signal`)
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Callable
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import scipy.signal.windows
|
|
14
|
+
|
|
15
|
+
from sigima.enums import WindowingMethod
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_window(method: WindowingMethod) -> Callable[[int], np.ndarray]:
|
|
19
|
+
"""Get the window function.
|
|
20
|
+
|
|
21
|
+
.. note::
|
|
22
|
+
|
|
23
|
+
The window functions are from `scipy.signal.windows` and `numpy`.
|
|
24
|
+
All functions take an integer argument that specifies the length of the window,
|
|
25
|
+
and return a NumPy array of the same length.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
method: Windowing function method.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Window function.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If the method is not recognized.
|
|
35
|
+
"""
|
|
36
|
+
win_func = {
|
|
37
|
+
WindowingMethod.BARTHANN: scipy.signal.windows.barthann,
|
|
38
|
+
WindowingMethod.BARTLETT: np.bartlett,
|
|
39
|
+
WindowingMethod.BLACKMAN: np.blackman,
|
|
40
|
+
WindowingMethod.BLACKMAN_HARRIS: scipy.signal.windows.blackmanharris,
|
|
41
|
+
WindowingMethod.BOHMAN: scipy.signal.windows.bohman,
|
|
42
|
+
WindowingMethod.BOXCAR: scipy.signal.windows.boxcar,
|
|
43
|
+
WindowingMethod.COSINE: scipy.signal.windows.cosine,
|
|
44
|
+
WindowingMethod.EXPONENTIAL: scipy.signal.windows.exponential,
|
|
45
|
+
WindowingMethod.FLAT_TOP: scipy.signal.windows.flattop,
|
|
46
|
+
WindowingMethod.HAMMING: np.hamming,
|
|
47
|
+
WindowingMethod.HANN: np.hanning,
|
|
48
|
+
WindowingMethod.LANCZOS: scipy.signal.windows.lanczos,
|
|
49
|
+
WindowingMethod.NUTTALL: scipy.signal.windows.nuttall,
|
|
50
|
+
WindowingMethod.PARZEN: scipy.signal.windows.parzen,
|
|
51
|
+
WindowingMethod.TAYLOR: scipy.signal.windows.taylor,
|
|
52
|
+
}.get(method)
|
|
53
|
+
if win_func is not None:
|
|
54
|
+
return win_func
|
|
55
|
+
raise ValueError(f"Invalid window type {method.value}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def apply_window(
|
|
59
|
+
y: np.ndarray,
|
|
60
|
+
method: WindowingMethod = WindowingMethod.HAMMING,
|
|
61
|
+
alpha: float = 0.5,
|
|
62
|
+
beta: float = 14.0,
|
|
63
|
+
sigma: float = 7.0,
|
|
64
|
+
) -> np.ndarray:
|
|
65
|
+
"""Apply windowing to the input data.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
x: X data.
|
|
69
|
+
y: Y data.
|
|
70
|
+
method: Windowing function. Defaults to "HAMMING".
|
|
71
|
+
alpha: Tukey window parameter. Defaults to 0.5.
|
|
72
|
+
beta: Kaiser window parameter. Defaults to 14.0.
|
|
73
|
+
sigma: Gaussian window parameter. Defaults to 7.0.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Windowed Y data.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ValueError: If the method is not recognized.
|
|
80
|
+
"""
|
|
81
|
+
# Cases with parameters:
|
|
82
|
+
if method == WindowingMethod.GAUSSIAN:
|
|
83
|
+
return y * scipy.signal.windows.gaussian(len(y), sigma)
|
|
84
|
+
if method == WindowingMethod.KAISER:
|
|
85
|
+
return y * np.kaiser(len(y), beta)
|
|
86
|
+
if method == WindowingMethod.TUKEY:
|
|
87
|
+
return y * scipy.signal.windows.tukey(len(y), alpha)
|
|
88
|
+
# Cases without parameters:
|
|
89
|
+
win_func = get_window(method)
|
|
90
|
+
return y * win_func(len(y))
|
sigima/worker.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Worker (:mod:`sigima.worker`)
|
|
5
|
+
------------------------------
|
|
6
|
+
|
|
7
|
+
This module provides a minimal interface for executing long-running operations
|
|
8
|
+
with support for progress reporting and cancellation.
|
|
9
|
+
|
|
10
|
+
It defines a generic protocol (`CallbackWorkerProtocol`) and a console-based
|
|
11
|
+
implementation (`TextCallbackWorker`), suitable for non-GUI environments.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
|
|
15
|
+
.. code-block:: python
|
|
16
|
+
|
|
17
|
+
from sigima.worker import TextCallbackWorker
|
|
18
|
+
|
|
19
|
+
def long_task(worker=None):
|
|
20
|
+
for i in range(10):
|
|
21
|
+
if worker and worker.was_canceled():
|
|
22
|
+
return None
|
|
23
|
+
worker.set_progress(i / 10)
|
|
24
|
+
return "done"
|
|
25
|
+
|
|
26
|
+
worker = TextCallbackWorker()
|
|
27
|
+
result = long_task(worker=worker)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from typing import Protocol, runtime_checkable
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@runtime_checkable
|
|
34
|
+
class CallbackWorkerProtocol(Protocol):
|
|
35
|
+
"""Protocol defining the minimal interface for progress/cancel-aware workers."""
|
|
36
|
+
|
|
37
|
+
def set_progress(self, value: float) -> None:
|
|
38
|
+
"""Update progress (between 0.0 and 1.0)."""
|
|
39
|
+
|
|
40
|
+
def was_canceled(self) -> bool:
|
|
41
|
+
"""Check whether the operation has been canceled."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TextCallbackWorker:
|
|
45
|
+
"""Minimal worker implementation for console-based environments.
|
|
46
|
+
|
|
47
|
+
Provides `set_progress()` and `was_canceled()` methods for use in long-running
|
|
48
|
+
computations. Intended for use in `sigima` where no GUI (e.g. Qt) is available.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
_canceled: Whether the operation was canceled.
|
|
52
|
+
_progress: Most recent progress value.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
self._canceled = False
|
|
57
|
+
self._progress = 0.0
|
|
58
|
+
|
|
59
|
+
def set_progress(self, value: float) -> None:
|
|
60
|
+
"""Set the progress of the operation (prints to console).
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
value: Float between 0.0 and 1.0.
|
|
64
|
+
"""
|
|
65
|
+
self._progress = min(max(value, 0.0), 1.0)
|
|
66
|
+
percent = int(self._progress * 100)
|
|
67
|
+
print(f"[sigima] Progress: {percent}%")
|
|
68
|
+
|
|
69
|
+
def was_canceled(self) -> bool:
|
|
70
|
+
"""Return whether the operation has been canceled."""
|
|
71
|
+
return self._canceled
|
|
72
|
+
|
|
73
|
+
def cancel(self) -> None:
|
|
74
|
+
"""Cancel the operation."""
|
|
75
|
+
self._canceled = True
|
|
76
|
+
|
|
77
|
+
def get_progress(self) -> float:
|
|
78
|
+
"""Return the current progress value."""
|
|
79
|
+
return self._progress
|