paradigma 0.3.2__py3-none-any.whl → 0.4.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.
Files changed (122) hide show
  1. paradigma/assets/gait_detection_clf_package.pkl +0 -0
  2. paradigma/assets/gait_filtering_clf_package.pkl +0 -0
  3. paradigma/assets/ppg_quality_clf_package.pkl +0 -0
  4. paradigma/assets/tremor_detection_clf_package.pkl +0 -0
  5. paradigma/classification.py +115 -0
  6. paradigma/config.py +314 -0
  7. paradigma/constants.py +48 -7
  8. paradigma/feature_extraction.py +811 -547
  9. paradigma/pipelines/__init__.py +0 -0
  10. paradigma/pipelines/gait_pipeline.py +727 -0
  11. paradigma/pipelines/heart_rate_pipeline.py +426 -0
  12. paradigma/pipelines/heart_rate_utils.py +780 -0
  13. paradigma/pipelines/tremor_pipeline.py +299 -0
  14. paradigma/preprocessing.py +363 -0
  15. paradigma/segmenting.py +396 -0
  16. paradigma/testing.py +416 -0
  17. paradigma/util.py +393 -16
  18. {paradigma-0.3.2.dist-info → paradigma-0.4.0.dist-info}/METADATA +58 -14
  19. paradigma-0.4.0.dist-info/RECORD +22 -0
  20. {paradigma-0.3.2.dist-info → paradigma-0.4.0.dist-info}/WHEEL +1 -1
  21. paradigma/gait_analysis.py +0 -415
  22. paradigma/gait_analysis_config.py +0 -266
  23. paradigma/heart_rate_analysis.py +0 -127
  24. paradigma/heart_rate_analysis_config.py +0 -9
  25. paradigma/heart_rate_util.py +0 -173
  26. paradigma/imu_preprocessing.py +0 -232
  27. paradigma/ppg/classifier/LR_PPG_quality.pkl +0 -0
  28. paradigma/ppg/classifier/LR_model.mat +0 -0
  29. paradigma/ppg/feat_extraction/acc_feature.m +0 -20
  30. paradigma/ppg/feat_extraction/peakdet.m +0 -64
  31. paradigma/ppg/feat_extraction/ppg_features.m +0 -53
  32. paradigma/ppg/glob_functions/extract_hr_segments.m +0 -37
  33. paradigma/ppg/glob_functions/extract_overlapping_segments.m +0 -23
  34. paradigma/ppg/glob_functions/jsonlab/AUTHORS.txt +0 -41
  35. paradigma/ppg/glob_functions/jsonlab/ChangeLog.txt +0 -74
  36. paradigma/ppg/glob_functions/jsonlab/LICENSE_BSD.txt +0 -25
  37. paradigma/ppg/glob_functions/jsonlab/LICENSE_GPLv3.txt +0 -699
  38. paradigma/ppg/glob_functions/jsonlab/README.txt +0 -394
  39. paradigma/ppg/glob_functions/jsonlab/examples/.svn/entries +0 -368
  40. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/demo_jsonlab_basic.m.svn-base +0 -180
  41. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/demo_ubjson_basic.m.svn-base +0 -180
  42. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/example1.json.svn-base +0 -23
  43. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/example2.json.svn-base +0 -22
  44. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/example3.json.svn-base +0 -11
  45. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/example4.json.svn-base +0 -34
  46. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/jsonlab_basictest.matlab.svn-base +0 -662
  47. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/jsonlab_selftest.m.svn-base +0 -27
  48. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/jsonlab_selftest.matlab.svn-base +0 -144
  49. paradigma/ppg/glob_functions/jsonlab/examples/.svn/text-base/jsonlab_speedtest.m.svn-base +0 -21
  50. paradigma/ppg/glob_functions/jsonlab/examples/demo_jsonlab_basic.m +0 -180
  51. paradigma/ppg/glob_functions/jsonlab/examples/demo_ubjson_basic.m +0 -180
  52. paradigma/ppg/glob_functions/jsonlab/examples/example1.json +0 -23
  53. paradigma/ppg/glob_functions/jsonlab/examples/example2.json +0 -22
  54. paradigma/ppg/glob_functions/jsonlab/examples/example3.json +0 -11
  55. paradigma/ppg/glob_functions/jsonlab/examples/example4.json +0 -34
  56. paradigma/ppg/glob_functions/jsonlab/examples/jsonlab_basictest.matlab +0 -662
  57. paradigma/ppg/glob_functions/jsonlab/examples/jsonlab_selftest.m +0 -27
  58. paradigma/ppg/glob_functions/jsonlab/examples/jsonlab_selftest.matlab +0 -144
  59. paradigma/ppg/glob_functions/jsonlab/examples/jsonlab_speedtest.m +0 -21
  60. paradigma/ppg/glob_functions/jsonlab/jsonopt.m +0 -32
  61. paradigma/ppg/glob_functions/jsonlab/loadjson.m +0 -566
  62. paradigma/ppg/glob_functions/jsonlab/loadubjson.m +0 -528
  63. paradigma/ppg/glob_functions/jsonlab/mergestruct.m +0 -33
  64. paradigma/ppg/glob_functions/jsonlab/savejson.m +0 -475
  65. paradigma/ppg/glob_functions/jsonlab/saveubjson.m +0 -504
  66. paradigma/ppg/glob_functions/jsonlab/varargin2struct.m +0 -40
  67. paradigma/ppg/glob_functions/sample_prob_final.m +0 -49
  68. paradigma/ppg/glob_functions/synchronization.m +0 -76
  69. paradigma/ppg/glob_functions/tsdf_scan_meta.m +0 -22
  70. paradigma/ppg/hr_functions/Long_TFD_JOT.m +0 -37
  71. paradigma/ppg/hr_functions/PPG_TFD_HR.m +0 -59
  72. paradigma/ppg/hr_functions/TFD toolbox JOT/.gitignore +0 -4
  73. paradigma/ppg/hr_functions/TFD toolbox JOT/CHANGELOG.md +0 -23
  74. paradigma/ppg/hr_functions/TFD toolbox JOT/LICENCE.md +0 -27
  75. paradigma/ppg/hr_functions/TFD toolbox JOT/README.md +0 -251
  76. paradigma/ppg/hr_functions/TFD toolbox JOT/README.pdf +0 -0
  77. paradigma/ppg/hr_functions/TFD toolbox JOT/common/gen_Doppler_kern.m +0 -142
  78. paradigma/ppg/hr_functions/TFD toolbox JOT/common/gen_Doppler_lag_kern.m +0 -314
  79. paradigma/ppg/hr_functions/TFD toolbox JOT/common/gen_lag_kern.m +0 -123
  80. paradigma/ppg/hr_functions/TFD toolbox JOT/dec_tfd.m +0 -154
  81. paradigma/ppg/hr_functions/TFD toolbox JOT/decimated_TFDs/dec_di_gdtfd.m +0 -194
  82. paradigma/ppg/hr_functions/TFD toolbox JOT/decimated_TFDs/dec_li_gdtfd.m +0 -200
  83. paradigma/ppg/hr_functions/TFD toolbox JOT/decimated_TFDs/dec_nonsep_gdtfd.m +0 -229
  84. paradigma/ppg/hr_functions/TFD toolbox JOT/decimated_TFDs/dec_sep_gdtfd.m +0 -241
  85. paradigma/ppg/hr_functions/TFD toolbox JOT/full_TFDs/di_gdtfd.m +0 -157
  86. paradigma/ppg/hr_functions/TFD toolbox JOT/full_TFDs/li_gdtfd.m +0 -190
  87. paradigma/ppg/hr_functions/TFD toolbox JOT/full_TFDs/nonsep_gdtfd.m +0 -196
  88. paradigma/ppg/hr_functions/TFD toolbox JOT/full_TFDs/sep_gdtfd.m +0 -199
  89. paradigma/ppg/hr_functions/TFD toolbox JOT/full_tfd.m +0 -144
  90. paradigma/ppg/hr_functions/TFD toolbox JOT/load_curdir.m +0 -13
  91. paradigma/ppg/hr_functions/TFD toolbox JOT/pics/decimated_TFDs_examples.png +0 -0
  92. paradigma/ppg/hr_functions/TFD toolbox JOT/pics/full_TFDs_examples.png +0 -0
  93. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/check_dec_params_seq.m +0 -79
  94. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/dispEE.m +0 -9
  95. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/dispVars.m +0 -26
  96. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/disp_bytes.m +0 -25
  97. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/fold_vector_full.m +0 -40
  98. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/fold_vector_half.m +0 -34
  99. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/gen_LFM.m +0 -29
  100. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/get_analytic_signal.m +0 -76
  101. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/get_window.m +0 -176
  102. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/isreal_fn.m +0 -11
  103. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/padWin.m +0 -97
  104. paradigma/ppg/hr_functions/TFD toolbox JOT/utils/vtfd.m +0 -149
  105. paradigma/ppg/preprocessing/preprocessing_imu.m +0 -15
  106. paradigma/ppg/preprocessing/preprocessing_ppg.m +0 -13
  107. paradigma/ppg_preprocessing.py +0 -313
  108. paradigma/preprocessing_config.py +0 -69
  109. paradigma/quantification.py +0 -58
  110. paradigma/tremor/TremorFeaturesAndClassification.m +0 -345
  111. paradigma/tremor/feat_extraction/DerivativesExtract.m +0 -22
  112. paradigma/tremor/feat_extraction/ExtractBandSignalsRMS.m +0 -72
  113. paradigma/tremor/feat_extraction/MFCCExtract.m +0 -100
  114. paradigma/tremor/feat_extraction/PSDBandPower.m +0 -52
  115. paradigma/tremor/feat_extraction/PSDEst.m +0 -63
  116. paradigma/tremor/feat_extraction/PSDExtrAxis.m +0 -88
  117. paradigma/tremor/feat_extraction/PSDExtrOpt.m +0 -95
  118. paradigma/tremor/preprocessing/InterpData.m +0 -32
  119. paradigma/tremor/weekly_aggregates/WeeklyAggregates.m +0 -295
  120. paradigma/windowing.py +0 -219
  121. paradigma-0.3.2.dist-info/RECORD +0 -108
  122. {paradigma-0.3.2.dist-info → paradigma-0.4.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,115 @@
1
+ import numpy as np
2
+ import pickle
3
+
4
+ from pathlib import Path
5
+ from sklearn.base import BaseEstimator
6
+ from typing import Any, Optional
7
+
8
+ class ClassifierPackage:
9
+ def __init__(self, classifier: Optional[BaseEstimator] = None,
10
+ threshold: Optional[float] = None,
11
+ scaler: Optional[Any] = None):
12
+ """
13
+ Initialize the ClassifierPackage with a classifier, threshold, and scaler.
14
+
15
+ Parameters
16
+ ----------
17
+ classifier
18
+ The trained classifier.
19
+ threshold : float
20
+ The classification threshold.
21
+ scaler
22
+ The trained scaler (e.g., StandardScaler or MinMaxScaler).
23
+ """
24
+ self.classifier = classifier
25
+ self.threshold = threshold
26
+ self.scaler = scaler
27
+
28
+ def transform_features(self, X) -> np.ndarray:
29
+ """
30
+ Transform the input features using the scaler.
31
+
32
+ Parameters
33
+ ----------
34
+ X : np.ndarray
35
+ The input features.
36
+
37
+ Return
38
+ ------
39
+ np.ndarray
40
+ The transformed features.
41
+ """
42
+ if not self.scaler:
43
+ return X
44
+ return self.scaler.transform(X)
45
+
46
+ def predict_proba(self, X) -> float:
47
+ """
48
+ Make predictions using the classifier and apply the threshold.
49
+
50
+ Parameters
51
+ ----------
52
+ X : np.ndarray
53
+ The input features.
54
+
55
+ Return
56
+ ------
57
+ float
58
+ The predicted probability.
59
+
60
+ """
61
+ if not self.classifier:
62
+ raise ValueError("Classifier is not loaded.")
63
+ return self.classifier.predict_proba(X)[:, 1]
64
+
65
+ def predict(self, X) -> int:
66
+ """
67
+ Make predictions using the classifier and apply the threshold.
68
+
69
+ Parameters
70
+ ----------
71
+ X : np.ndarray
72
+ The input features.
73
+
74
+ Return
75
+ ------
76
+ int
77
+ The predicted class.
78
+
79
+ """
80
+ if not self.classifier:
81
+ raise ValueError("Classifier is not loaded.")
82
+ return int(self.predict_proba(X) >= self.threshold)
83
+
84
+ def save(self, filepath: str | Path) -> None:
85
+ """
86
+ Save the ClassifierPackage to a file.
87
+
88
+ Parameters
89
+ ----------
90
+ filepath : str
91
+ The path to the file.
92
+ """
93
+ with open(filepath, 'wb') as f:
94
+ pickle.dump(self, f)
95
+
96
+ @classmethod
97
+ def load(cls, filepath: str | Path):
98
+ """
99
+ Load a ClassifierPackage from a file.
100
+
101
+ Parameters
102
+ ----------
103
+ filepath : str
104
+ The path to the file.
105
+
106
+ Return
107
+ ------
108
+ ClassifierPackage
109
+ The loaded classifier package.
110
+ """
111
+ try:
112
+ with open(filepath, 'rb') as f:
113
+ return pickle.load(f)
114
+ except Exception as e:
115
+ raise ValueError(f"Failed to load classifier package: {e}") from e
paradigma/config.py ADDED
@@ -0,0 +1,314 @@
1
+ from typing import Dict, List
2
+ from paradigma.constants import DataColumns, DataUnits
3
+ import numpy as np
4
+
5
+ class BaseConfig:
6
+ def __init__(self) -> None:
7
+ self.meta_filename = ''
8
+ self.values_filename = ''
9
+ self.time_filename = ''
10
+
11
+ def set_sensor(self, sensor: str) -> None:
12
+ """Sets the sensor and derived filenames"""
13
+ self.sensor: str = sensor
14
+ self.set_filenames(sensor)
15
+
16
+ def set_filenames(self, prefix: str) -> None:
17
+ """Sets the filenames based on the prefix. This method is duplicated from `gaits_analysis_config.py`.
18
+
19
+ Parameters
20
+ ----------
21
+ prefix : str
22
+ The prefix for the filenames.
23
+ """
24
+ self.meta_filename = f"{prefix}_meta.json"
25
+ self.time_filename = f"{prefix}_time.bin"
26
+ self.values_filename = f"{prefix}_values.bin"
27
+
28
+ class IMUConfig(BaseConfig):
29
+
30
+ def __init__(self) -> None:
31
+ super().__init__()
32
+
33
+ self.set_filenames('IMU')
34
+
35
+ self.acceleration_units = DataUnits.ACCELERATION
36
+ self.rotation_units = DataUnits.ROTATION
37
+
38
+ self.axes = ["x", "y", "z"]
39
+
40
+ self.accelerometer_cols: List[str] = [
41
+ DataColumns.ACCELEROMETER_X,
42
+ DataColumns.ACCELEROMETER_Y,
43
+ DataColumns.ACCELEROMETER_Z,
44
+ ]
45
+ self.gyroscope_cols: List[str] = [
46
+ DataColumns.GYROSCOPE_X,
47
+ DataColumns.GYROSCOPE_Y,
48
+ DataColumns.GYROSCOPE_Z,
49
+ ]
50
+ self.gravity_cols: List[str] = [
51
+ DataColumns.GRAV_ACCELEROMETER_X,
52
+ DataColumns.GRAV_ACCELEROMETER_Y,
53
+ DataColumns.GRAV_ACCELEROMETER_Z,
54
+ ]
55
+
56
+ self.d_channels_accelerometer = {
57
+ DataColumns.ACCELEROMETER_X: self.acceleration_units,
58
+ DataColumns.ACCELEROMETER_Y: self.acceleration_units,
59
+ DataColumns.ACCELEROMETER_Z: self.acceleration_units,
60
+ }
61
+ self.d_channels_gyroscope = {
62
+ DataColumns.GYROSCOPE_X: self.rotation_units,
63
+ DataColumns.GYROSCOPE_Y: self.rotation_units,
64
+ DataColumns.GYROSCOPE_Z: self.rotation_units,
65
+ }
66
+ self.d_channels_imu = {**self.d_channels_accelerometer, **self.d_channels_gyroscope}
67
+
68
+ self.sampling_frequency = 100
69
+ self.lower_cutoff_frequency = 0.2
70
+ self.upper_cutoff_frequency = 3.5
71
+ self.filter_order = 4
72
+
73
+
74
+ class PPGConfig(BaseConfig):
75
+
76
+ def __init__(self) -> None:
77
+ super().__init__()
78
+
79
+ self.set_filenames('PPG')
80
+
81
+ self.ppg_colname = DataColumns.PPG
82
+
83
+ self.sampling_frequency = 30
84
+ self.lower_cutoff_frequency = 0.4
85
+ self.upper_cutoff_frequency = 3.5
86
+ self.filter_order = 4
87
+
88
+ self.d_channels_ppg = {
89
+ DataColumns.PPG: DataUnits.NONE
90
+ }
91
+
92
+
93
+ # Domain base configs
94
+ class GaitConfig(IMUConfig):
95
+
96
+ def __init__(self, step) -> None:
97
+ super().__init__()
98
+
99
+ self.set_sensor('accelerometer')
100
+
101
+ # ----------
102
+ # Segmenting
103
+ # ----------
104
+ self.max_segment_gap_s = 1.5
105
+ self.min_segment_length_s = 1.5
106
+
107
+ if step == 'gait':
108
+ self.window_length_s: float = 6
109
+ self.window_step_length_s: float = 1
110
+ else:
111
+ self.window_length_s: float = 3
112
+ self.window_step_length_s: float = self.window_length_s * 0.25
113
+
114
+ # -----------------
115
+ # Feature extraction
116
+ # -----------------
117
+ self.window_type: str = "hann"
118
+ self.spectrum_low_frequency: int = 0
119
+ self.spectrum_high_frequency: int = int(self.sampling_frequency / 2)
120
+
121
+ # Power in specified frequency bands
122
+ self.d_frequency_bandwidths: Dict[str, List[float]] = {
123
+ "power_below_gait": [0.2, 0.7],
124
+ "power_gait": [0.7, 3.5],
125
+ "power_tremor": [3.5, 8],
126
+ "power_above_tremor": [8, 25],
127
+ }
128
+
129
+ # Mel frequency cepstral coefficients
130
+ self.mfcc_low_frequency: int = 0
131
+ self.mfcc_high_frequency: int = 25
132
+ self.mfcc_n_dct_filters: int = 15
133
+ self.mfcc_n_coefficients: int = 12
134
+
135
+ # Dominant frequency of first harmonic of arm swing
136
+ self.angle_fmin: float = 0.5
137
+ self.angle_fmax: float = 1.5
138
+
139
+ # -----------------
140
+ # TSDF data storage
141
+ # -----------------
142
+ self.d_channels_values: Dict[str, str] = {
143
+ "accelerometer_std_norm": DataUnits.GRAVITY,
144
+ "accelerometer_x_grav_mean": DataUnits.GRAVITY,
145
+ "accelerometer_y_grav_mean": DataUnits.GRAVITY,
146
+ "accelerometer_z_grav_mean": DataUnits.GRAVITY,
147
+ "accelerometer_x_grav_std": DataUnits.GRAVITY,
148
+ "accelerometer_y_grav_std": DataUnits.GRAVITY,
149
+ "accelerometer_z_grav_std": DataUnits.GRAVITY,
150
+ "accelerometer_x_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY,
151
+ "accelerometer_y_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY,
152
+ "accelerometer_z_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY,
153
+ "accelerometer_x_power_gait": DataUnits.POWER_SPECTRAL_DENSITY,
154
+ "accelerometer_y_power_gait": DataUnits.POWER_SPECTRAL_DENSITY,
155
+ "accelerometer_z_power_gait": DataUnits.POWER_SPECTRAL_DENSITY,
156
+ "accelerometer_x_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
157
+ "accelerometer_y_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
158
+ "accelerometer_z_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
159
+ "accelerometer_x_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
160
+ "accelerometer_y_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
161
+ "accelerometer_z_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
162
+ "accelerometer_x_dominant_frequency": DataUnits.FREQUENCY,
163
+ "accelerometer_y_dominant_frequency": DataUnits.FREQUENCY,
164
+ "accelerometer_z_dominant_frequency": DataUnits.FREQUENCY,
165
+ }
166
+
167
+ for mfcc_coef in range(1, self.mfcc_n_coefficients + 1):
168
+ self.d_channels_values[f"accelerometer_mfcc_{mfcc_coef}"] = DataUnits.GRAVITY
169
+
170
+ if step == 'arm_activity':
171
+ for mfcc_coef in range(1, self.mfcc_n_coefficients + 1):
172
+ self.d_channels_values[f"gyroscope_mfcc_{mfcc_coef}"] = DataUnits.GRAVITY
173
+
174
+
175
+ class TremorConfig(IMUConfig):
176
+
177
+ def __init__(self, step: str | None = None) -> None:
178
+ """
179
+ Parameters
180
+ ----------
181
+ step : str (optional)
182
+ The step of the tremor pipeline. Can be 'features' or 'classification'.
183
+ """
184
+ super().__init__()
185
+
186
+ self.set_sensor('gyroscope')
187
+
188
+ # ----------
189
+ # Segmenting
190
+ # ----------
191
+ self.window_length_s: float = 4
192
+ self.window_step_length_s: float = 4
193
+
194
+ # -----------------
195
+ # Feature extraction
196
+ # -----------------
197
+ self.window_type = 'hann'
198
+
199
+ # Power spectral density
200
+ self.overlap_fraction: float = 0.8
201
+ self.segment_length_s: float = 3
202
+ self.spectral_resolution: float = 0.25
203
+ self.fmin_peak_search: float = 1
204
+ self.fmax_peak_search: float = 25
205
+ self.fmin_below_rest_tremor: float = 0.5
206
+ self.fmax_below_rest_tremor: float = 3
207
+ self.fmin_rest_tremor: float = 3
208
+ self.fmax_rest_tremor: float = 7
209
+
210
+ # Mel frequency cepstral coefficients
211
+ self.fmin_mfcc: float = 0
212
+ self.fmax_mfcc: float = 25
213
+ self.n_dct_filters_mfcc: int = 15
214
+ self.n_coefficients_mfcc: int = 12
215
+
216
+ # --------------
217
+ # Classification
218
+ # --------------
219
+ self.movement_threshold: float = 50
220
+
221
+ # -----------
222
+ # Aggregation
223
+ # -----------
224
+ self.aggregates_tremor_power: List[str] = ['mode', 'median', '90p']
225
+
226
+ # -----------------
227
+ # TSDF data storage
228
+ # -----------------
229
+ if step == 'features':
230
+ self.d_channels_values: Dict[str, str] = {}
231
+ for mfcc_coef in range(1, self.n_coefficients_mfcc + 1):
232
+ self.d_channels_values[f"mfcc_{mfcc_coef}"] = "unitless"
233
+
234
+ self.d_channels_values["freq_peak"] = "Hz"
235
+ self.d_channels_values["below_tremor_power"] = "(deg/s)^2"
236
+ self.d_channels_values["tremor_power"] = "(deg/s)^2"
237
+ elif step == 'classification':
238
+ self.d_channels_values = {
239
+ DataColumns.PRED_TREMOR_PROBA: "probability",
240
+ DataColumns.PRED_TREMOR_LOGREG: "boolean",
241
+ DataColumns.PRED_TREMOR_CHECKED: "boolean",
242
+ DataColumns.PRED_ARM_AT_REST: "boolean"
243
+ }
244
+
245
+
246
+ class HeartRateConfig(PPGConfig):
247
+ def __init__(self, sensor: str = 'ppg', min_window_length_s: int = 30) -> None:
248
+ super().__init__()
249
+
250
+ # ----------
251
+ # Segmenting
252
+ # ----------
253
+ self.window_length_s: int = 6
254
+ self.window_step_length_s: int = 1
255
+ self.window_overlap_s = self.window_length_s - self.window_step_length_s
256
+
257
+ self.accelerometer_cols = IMUConfig().accelerometer_cols
258
+
259
+ # -----------------------
260
+ # Signal quality analysis
261
+ # -----------------------
262
+ self.freq_band_physio = [0.75, 3] # Hz
263
+ self.bandwidth = 0.2 # Hz
264
+ self.freq_bin_resolution = 0.05 # Hz
265
+
266
+ # ---------------------
267
+ # Heart rate estimation
268
+ # ---------------------
269
+ self.set_tfd_length(min_window_length_s) # Set tfd length to default of 30 seconds
270
+ self.threshold_sqa = 0.5
271
+ self.threshold_sqa_accelerometer = 0.13
272
+
273
+ hr_est_length = 2
274
+ self.hr_est_samples = hr_est_length * self.sampling_frequency
275
+
276
+ # Time-frequency distribution parameters
277
+ self.kern_type = 'sep'
278
+ win_type_doppler = 'hamm'
279
+ win_type_lag = 'hamm'
280
+ win_length_doppler = 8
281
+ win_length_lag = 1
282
+ doppler_samples = self.sampling_frequency * win_length_doppler
283
+ lag_samples = win_length_lag * self.sampling_frequency
284
+ self.kern_params = {
285
+ 'doppler': {
286
+ 'win_length': doppler_samples,
287
+ 'win_type': win_type_doppler,
288
+ },
289
+ 'lag': {
290
+ 'win_length': lag_samples,
291
+ 'win_type': win_type_lag,
292
+ }
293
+ }
294
+
295
+ self.set_sensor(sensor)
296
+
297
+ def set_tfd_length(self, tfd_length: int):
298
+ self.tfd_length = tfd_length
299
+ self.min_hr_samples = int(round(self.tfd_length * self.sampling_frequency))
300
+
301
+ def set_sensor(self, sensor):
302
+ self.sensor = sensor
303
+
304
+ if sensor not in ['ppg', 'imu']:
305
+ raise ValueError(f"Invalid sensor type: {sensor}")
306
+
307
+ if sensor == 'imu':
308
+ self.sampling_frequency = IMUConfig().sampling_frequency
309
+ else:
310
+ self.sampling_frequency = PPGConfig().sampling_frequency
311
+
312
+ self.window_length_welch = 3 * self.sampling_frequency
313
+ self.overlap_welch_window = self.window_length_welch // 2
314
+ self.nfft = len(np.arange(0, self.sampling_frequency/2, self.freq_bin_resolution))*2
paradigma/constants.py CHANGED
@@ -14,17 +14,52 @@ class DataColumns():
14
14
  GYROSCOPE_Z : str = "gyroscope_z"
15
15
  PPG : str = "green"
16
16
  TIME : str = "time"
17
+ SEGMENT_NR : str = "segment_nr"
18
+ SEGMENT_CAT: str = "segment_category"
17
19
 
18
- # The following are used in gait analysis
19
- GRAV_ACCELEROMETER_X : str = "grav_accelerometer_x"
20
- GRAV_ACCELEROMETER_Y : str = "grav_accelerometer_y"
21
- GRAV_ACCELEROMETER_Z : str = "grav_accelerometer_z"
20
+ # Gait
21
+ GRAV_ACCELEROMETER_X : str = "accelerometer_x_grav"
22
+ GRAV_ACCELEROMETER_Y : str = "accelerometer_y_grav"
23
+ GRAV_ACCELEROMETER_Z : str = "accelerometer_z_grav"
24
+ PRED_GAIT_PROBA: str = "pred_gait_proba"
22
25
  PRED_GAIT : str = "pred_gait"
23
- PRED_ARM_SWING : str = "pred_arm_swing"
26
+ PRED_NO_OTHER_ARM_ACTIVITY_PROBA: str = "pred_no_other_arm_activity_proba"
27
+ PRED_NO_OTHER_ARM_ACTIVITY : str = "pred_no_other_arm_activity"
24
28
  ANGLE : str = "angle"
25
- ANGLE_SMOOTH : str = "angle_smooth"
26
29
  VELOCITY : str = "velocity"
27
- SEGMENT_NR : str = "segment_nr"
30
+ DOMINANT_FREQUENCY: str = "dominant_frequency"
31
+ RANGE_OF_MOTION: str = "range_of_motion"
32
+ PEAK_VELOCITY: str = "peak_velocity"
33
+
34
+ # The following are used in tremor analysis
35
+ PRED_TREMOR_PROBA: str = "pred_tremor_proba"
36
+ PRED_TREMOR_LOGREG : str = "pred_tremor_logreg"
37
+ PRED_TREMOR_CHECKED : str = "pred_tremor_checked"
38
+ PRED_ARM_AT_REST: str = "pred_arm_at_rest"
39
+
40
+ # Constants for PPG features
41
+ VARIANCE: str = "variance"
42
+ MEAN: str = "mean"
43
+ MEDIAN: str = "median"
44
+ KURTOSIS: str = "kurtosis"
45
+ SKEWNESS: str = "skewness"
46
+ DOMINANT_FREQUENCY: str = "dominant_frequency"
47
+ RELATIVE_POWER: str = "relative_power"
48
+ SPECTRAL_ENTROPY: str = "spectral_entropy"
49
+ SIGNAL_NOISE_RATIO: str = "signal_noise_ratio"
50
+ SECOND_HIGHEST_PEAK: str = "second_highest_peak"
51
+ POWER_RATIO: str = "power_ratio"
52
+
53
+ # Constants for PPG SQA feature using accerometer data
54
+ ACC_POWER_RATIO: str = "acc_power_ratio"
55
+
56
+ # Constants for SQA classification
57
+ PRED_SQA_PROBA: str = "pred_sqa_proba"
58
+ PRED_SQA_ACC_LABEL: str = "pred_sqa_acc_label"
59
+ PRED_SQA: str = "pred_sqa"
60
+
61
+ # Constants for heart rate
62
+ HEART_RATE: str = "heart_rate"
28
63
 
29
64
  @dataclass(frozen=True)
30
65
  class DataUnits():
@@ -57,9 +92,15 @@ class TimeUnit():
57
92
  """
58
93
  RELATIVE_MS : str = "relative_ms"
59
94
  """ The time is relative to the start time in milliseconds. """
95
+ RELATIVE_S : str = "relative_s"
96
+ """ The time is relative to the start time in seconds. """
60
97
  ABSOLUTE_MS : str = "absolute_ms"
61
98
  """ The time is absolute in milliseconds. """
99
+ ABSOLUTE_S : str = "absolute_s"
100
+ """ The time is absolute in seconds. """
62
101
  DIFFERENCE_MS : str = "difference_ms"
63
102
  """ The time is the difference between consecutive samples in milliseconds. """
103
+ DIFFERENCE_S : str = "difference_s"
104
+ """ The time is the difference between consecutive samples in seconds. """
64
105
 
65
106
  UNIX_TICKS_MS: int = 1000