spectre-core 0.0.21__py3-none-any.whl → 0.0.23__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 (77) hide show
  1. spectre_core/_file_io/__init__.py +5 -5
  2. spectre_core/_file_io/file_handlers.py +61 -107
  3. spectre_core/batches/__init__.py +21 -4
  4. spectre_core/batches/_base.py +86 -135
  5. spectre_core/batches/_batches.py +56 -100
  6. spectre_core/batches/_factory.py +22 -21
  7. spectre_core/batches/_register.py +9 -9
  8. spectre_core/batches/plugins/_batch_keys.py +8 -7
  9. spectre_core/batches/plugins/_callisto.py +66 -98
  10. spectre_core/batches/plugins/_iq_stream.py +106 -170
  11. spectre_core/capture_configs/__init__.py +47 -18
  12. spectre_core/capture_configs/_capture_config.py +26 -53
  13. spectre_core/capture_configs/_capture_modes.py +9 -7
  14. spectre_core/capture_configs/_capture_templates.py +51 -111
  15. spectre_core/capture_configs/_parameters.py +38 -75
  16. spectre_core/capture_configs/_pconstraints.py +41 -41
  17. spectre_core/capture_configs/_pnames.py +37 -35
  18. spectre_core/capture_configs/_ptemplates.py +261 -348
  19. spectre_core/capture_configs/_pvalidators.py +99 -102
  20. spectre_core/config/__init__.py +14 -9
  21. spectre_core/config/_paths.py +19 -36
  22. spectre_core/config/_time_formats.py +7 -6
  23. spectre_core/exceptions.py +39 -1
  24. spectre_core/jobs/__init__.py +4 -7
  25. spectre_core/jobs/_duration.py +12 -0
  26. spectre_core/jobs/_jobs.py +73 -44
  27. spectre_core/jobs/_workers.py +56 -106
  28. spectre_core/logs/__init__.py +8 -3
  29. spectre_core/logs/_configure.py +14 -18
  30. spectre_core/logs/_decorators.py +7 -5
  31. spectre_core/logs/_logs.py +38 -90
  32. spectre_core/logs/_process_types.py +6 -4
  33. spectre_core/plotting/__init__.py +14 -4
  34. spectre_core/plotting/_base.py +65 -139
  35. spectre_core/plotting/_format.py +11 -9
  36. spectre_core/plotting/_panel_names.py +8 -6
  37. spectre_core/plotting/_panel_stack.py +83 -116
  38. spectre_core/plotting/_panels.py +121 -156
  39. spectre_core/post_processing/__init__.py +7 -4
  40. spectre_core/post_processing/_base.py +69 -69
  41. spectre_core/post_processing/_factory.py +15 -12
  42. spectre_core/post_processing/_post_processor.py +17 -13
  43. spectre_core/post_processing/_register.py +11 -8
  44. spectre_core/post_processing/plugins/_event_handler_keys.py +5 -4
  45. spectre_core/post_processing/plugins/_fixed_center_frequency.py +55 -48
  46. spectre_core/post_processing/plugins/_swept_center_frequency.py +200 -175
  47. spectre_core/receivers/__init__.py +10 -3
  48. spectre_core/receivers/_base.py +83 -149
  49. spectre_core/receivers/_factory.py +21 -31
  50. spectre_core/receivers/_register.py +8 -11
  51. spectre_core/receivers/_spec_names.py +18 -16
  52. spectre_core/receivers/plugins/_b200mini.py +48 -61
  53. spectre_core/receivers/plugins/_receiver_names.py +9 -7
  54. spectre_core/receivers/plugins/_rsp1a.py +45 -41
  55. spectre_core/receivers/plugins/_rspduo.py +60 -45
  56. spectre_core/receivers/plugins/_sdrplay_receiver.py +68 -84
  57. spectre_core/receivers/plugins/_test.py +137 -130
  58. spectre_core/receivers/plugins/_usrp.py +94 -86
  59. spectre_core/receivers/plugins/gr/__init__.py +2 -2
  60. spectre_core/receivers/plugins/gr/_base.py +15 -23
  61. spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
  62. spectre_core/receivers/plugins/gr/_rspduo.py +78 -90
  63. spectre_core/receivers/plugins/gr/_test.py +50 -58
  64. spectre_core/receivers/plugins/gr/_usrp.py +61 -59
  65. spectre_core/spectrograms/__init__.py +22 -14
  66. spectre_core/spectrograms/_analytical.py +109 -100
  67. spectre_core/spectrograms/_array_operations.py +40 -47
  68. spectre_core/spectrograms/_spectrogram.py +290 -323
  69. spectre_core/spectrograms/_transform.py +107 -74
  70. spectre_core/wgetting/__init__.py +2 -4
  71. spectre_core/wgetting/_callisto.py +88 -94
  72. {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/METADATA +9 -23
  73. spectre_core-0.0.23.dist-info/RECORD +79 -0
  74. {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/WHEEL +1 -1
  75. spectre_core-0.0.21.dist-info/RECORD +0 -78
  76. {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/licenses/LICENSE +0 -0
  77. {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
@@ -10,69 +10,63 @@ from scipy.signal import get_window
10
10
  from ._parameters import Parameters
11
11
  from ._pnames import PName
12
12
 
13
- # ----------------------------------------------------------------------- #
14
- # Throughout this module, repeated calls to `cast` will be seen on using the
15
- # `get_parameter_value` method. This is purely to signify intent and keep
16
- # the static type checkers happy.
17
- #
18
- # This is okay, as whenever the validators are called, the parameter values
19
- # have already been individually casted and constrained according to the
20
- # parameter templates. There is negligible runtime impact. Though a bit
21
- # of a pain for maintenance.
22
- # ----------------------------------------------------------------------- #
23
13
 
24
-
25
- def validate_window(
26
- parameters: Parameters
27
- ) -> None:
14
+ def validate_window(parameters: Parameters) -> None:
28
15
  """Ensure that the capture config describes a valid window.
29
16
 
30
17
  :param parameters: The parameters to be validated.
31
18
  :raises ValueError: If the window interval is greater than the batch size.
32
- :raises ValueError: If the specified window type cannot be fetched using
19
+ :raises ValueError: If the specified window type cannot be fetched using
33
20
  the SciPy `get_window` function.
34
21
  """
35
22
  window_size = cast(int, parameters.get_parameter_value(PName.WINDOW_SIZE))
36
23
  sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
37
- batch_size = cast(int, parameters.get_parameter_value(PName.BATCH_SIZE))
24
+ batch_size = cast(int, parameters.get_parameter_value(PName.BATCH_SIZE))
38
25
  window_type = cast(str, parameters.get_parameter_value(PName.WINDOW_TYPE))
39
-
40
- window_interval = window_size*(1 / sample_rate)
26
+
27
+ window_interval = window_size * (1 / sample_rate)
41
28
  if window_interval > batch_size:
42
- raise ValueError((f"The windowing interval must be strictly less than the batch size. "
43
- f"Computed the windowing interval to be {window_interval} [s], "
44
- f"but the batch size is {batch_size} [s]"))
45
-
29
+ raise ValueError(
30
+ (
31
+ f"The windowing interval must be strictly less than the batch size. "
32
+ f"Computed the windowing interval to be {window_interval} [s], "
33
+ f"but the batch size is {batch_size} [s]"
34
+ )
35
+ )
36
+
46
37
  try:
47
38
  _ = get_window(window_type, window_size)
48
39
  except Exception as e:
49
- raise ValueError((f"An error has occurred while validating the window. "
50
- f"Got {str(e)}"))
51
-
40
+ raise ValueError(
41
+ (f"An error has occurred while validating the window. " f"Got {str(e)}")
42
+ )
52
43
 
53
- def validate_nyquist_criterion(
54
- parameters: Parameters
55
- ) -> None:
44
+
45
+ def validate_nyquist_criterion(parameters: Parameters) -> None:
56
46
  """Ensure that the Nyquist criterion is satisfied.
57
47
 
58
48
  :param parameters: The parameters to be validated.
59
49
  :raises ValueError: If the sample rate is less than the bandwidth.
60
50
  """
61
51
  sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
62
- bandwidth = cast(float, parameters.get_parameter_value(PName.BANDWIDTH))
52
+ bandwidth = cast(float, parameters.get_parameter_value(PName.BANDWIDTH))
63
53
 
64
54
  if sample_rate < bandwidth:
65
- raise ValueError((f"Nyquist criterion has not been satisfied. "
66
- f"Sample rate must be greater than or equal to the bandwidth. "
67
- f"Got sample rate {sample_rate} [Hz], and bandwidth {bandwidth} [Hz]"))
68
-
69
-
70
- def _compute_num_steps_per_sweep(min_freq: float,
71
- max_freq: float,
72
- freq_step: float) -> int:
55
+ raise ValueError(
56
+ (
57
+ f"Nyquist criterion has not been satisfied. "
58
+ f"Sample rate must be greater than or equal to the bandwidth. "
59
+ f"Got sample rate {sample_rate} [Hz], and bandwidth {bandwidth} [Hz]"
60
+ )
61
+ )
62
+
63
+
64
+ def _compute_num_steps_per_sweep(
65
+ min_freq: float, max_freq: float, freq_step: float
66
+ ) -> int:
73
67
  """Compute the number of steps in one frequency sweep.
74
68
 
75
- The center frequency starts at `min_freq` and increments in steps of `freq_step`
69
+ The center frequency starts at `min_freq` and increments in steps of `freq_step`
76
70
  until the next step would exceed `max_freq`.
77
71
 
78
72
  :param min_freq: The minimum frequency of the sweep.
@@ -83,92 +77,91 @@ def _compute_num_steps_per_sweep(min_freq: float,
83
77
  return floor((max_freq - min_freq) / freq_step)
84
78
 
85
79
 
86
- def validate_num_steps_per_sweep(
87
- parameters: Parameters
88
- ) -> None:
80
+ def validate_num_steps_per_sweep(parameters: Parameters) -> None:
89
81
  """Ensure that there are at least two steps in frequency per sweep.
90
82
 
91
83
  :param parameters: The parameters to be validated.
92
84
  :raises ValueError: If the number of steps per sweep is less than or equal to one.
93
85
  """
94
- min_freq = cast(float, parameters.get_parameter_value(PName.MIN_FREQUENCY))
95
- max_freq = cast(float, parameters.get_parameter_value(PName.MAX_FREQUENCY))
96
- freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
86
+ min_freq = cast(float, parameters.get_parameter_value(PName.MIN_FREQUENCY))
87
+ max_freq = cast(float, parameters.get_parameter_value(PName.MAX_FREQUENCY))
88
+ freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
97
89
 
98
- num_steps_per_sweep = _compute_num_steps_per_sweep(min_freq,
99
- max_freq,
100
- freq_step)
90
+ num_steps_per_sweep = _compute_num_steps_per_sweep(min_freq, max_freq, freq_step)
101
91
  if num_steps_per_sweep <= 1:
102
- raise ValueError((f"We need strictly greater than one step per sweep. "
103
- f"Computed {num_steps_per_sweep} step per sweep"))
104
-
92
+ raise ValueError(
93
+ (
94
+ f"We need strictly greater than one step per sweep. "
95
+ f"Computed {num_steps_per_sweep} step per sweep"
96
+ )
97
+ )
98
+
105
99
 
106
- def validate_sweep_interval(
107
- parameters: Parameters
108
- ) -> None:
100
+ def validate_sweep_interval(parameters: Parameters) -> None:
109
101
  """Ensure that the sweep interval is greater than the batch size.
110
102
 
111
103
  :param parameters: The parameters to be validated.
112
104
  :raises ValueError: If the sweep interval is greater than the batch size.
113
105
  """
114
- min_freq = cast(float, parameters.get_parameter_value(PName.MIN_FREQUENCY))
115
- max_freq = cast(float, parameters.get_parameter_value(PName.MAX_FREQUENCY))
116
- freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
106
+ min_freq = cast(float, parameters.get_parameter_value(PName.MIN_FREQUENCY))
107
+ max_freq = cast(float, parameters.get_parameter_value(PName.MAX_FREQUENCY))
108
+ freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
117
109
  samples_per_step = cast(int, parameters.get_parameter_value(PName.SAMPLES_PER_STEP))
118
- batch_size = cast(int, parameters.get_parameter_value(PName.BATCH_SIZE))
119
- sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
110
+ batch_size = cast(int, parameters.get_parameter_value(PName.BATCH_SIZE))
111
+ sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
120
112
 
121
- num_steps_per_sweep = _compute_num_steps_per_sweep(min_freq,
122
- max_freq,
123
- freq_step)
113
+ num_steps_per_sweep = _compute_num_steps_per_sweep(min_freq, max_freq, freq_step)
124
114
  num_samples_per_sweep = num_steps_per_sweep * samples_per_step
125
- sweep_interval = num_samples_per_sweep * 1/sample_rate
115
+ sweep_interval = num_samples_per_sweep * 1 / sample_rate
126
116
  if sweep_interval > batch_size:
127
- raise ValueError((f"Sweep interval must be less than the batch size. "
128
- f"The computed sweep interval is {sweep_interval} [s], "
129
- f"but the given batch size is {batch_size} [s]"))
130
-
117
+ raise ValueError(
118
+ (
119
+ f"Sweep interval must be less than the batch size. "
120
+ f"The computed sweep interval is {sweep_interval} [s], "
121
+ f"but the given batch size is {batch_size} [s]"
122
+ )
123
+ )
131
124
 
132
- def validate_num_samples_per_step(
133
- parameters: Parameters
134
- ) -> None:
125
+
126
+ def validate_num_samples_per_step(parameters: Parameters) -> None:
135
127
  """Ensure that the number of samples per step is greater than the window size.
136
128
 
137
129
  :param parameters: The parameters to be validated.
138
130
  :raises ValueError: If the window size is greater than the number of samples per step.
139
131
  """
140
- window_size = cast(int, parameters.get_parameter_value(PName.WINDOW_SIZE))
132
+ window_size = cast(int, parameters.get_parameter_value(PName.WINDOW_SIZE))
141
133
  samples_per_step = cast(int, parameters.get_parameter_value(PName.SAMPLES_PER_STEP))
142
134
 
143
135
  if window_size >= samples_per_step:
144
- raise ValueError((f"Window size must be strictly less than the number of samples per step. "
145
- f"Got window size {window_size} [samples], which is more than or equal "
146
- f"to the number of samples per step {samples_per_step}"))
147
-
136
+ raise ValueError(
137
+ (
138
+ f"Window size must be strictly less than the number of samples per step. "
139
+ f"Got window size {window_size} [samples], which is more than or equal "
140
+ f"to the number of samples per step {samples_per_step}"
141
+ )
142
+ )
148
143
 
149
- def validate_non_overlapping_steps(
150
- parameters: Parameters
151
- ) -> None:
144
+
145
+ def validate_non_overlapping_steps(parameters: Parameters) -> None:
152
146
  """Ensure that the stepped spectrograms are non-overlapping in the frequency domain.
153
147
 
154
148
  :param parameters: The parameters to be validated.
155
149
  :raises NotImplementedError: If the spectrograms overlap in the frequency domain.
156
150
  """
157
-
158
- freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
151
+
152
+ freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
159
153
  sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
160
154
 
161
155
  if freq_step < sample_rate:
162
- raise NotImplementedError(f"SPECTRE does not yet support spectral steps overlapping in frequency. "
163
- f"Got frequency step {freq_step * 1e-6} [MHz] which is less than the sample "
164
- f"rate {sample_rate * 1e-6} [MHz]")
165
-
166
-
167
- def validate_step_interval(
168
- parameters: Parameters,
169
- api_retuning_latency: float
170
- ) -> None:
171
- """Ensure that the time elapsed collecting samples at a fixed frequency is greater
156
+ raise NotImplementedError(
157
+ f"SPECTRE does not yet support spectral steps overlapping in frequency. "
158
+ f"Got frequency step {freq_step * 1e-6} [MHz] which is less than the sample "
159
+ f"rate {sample_rate * 1e-6} [MHz]"
160
+ )
161
+
162
+
163
+ def validate_step_interval(parameters: Parameters, api_retuning_latency: float) -> None:
164
+ """Ensure that the time elapsed collecting samples at a fixed frequency is greater
172
165
  than the empirically derived API retuning latency.
173
166
 
174
167
  :param parameters: The parameters to be validated.
@@ -176,12 +169,14 @@ def validate_step_interval(
176
169
  :raises ValueError: If the time elapsed for a step is less than the API retuning latency.
177
170
  """
178
171
  samples_per_step = cast(int, parameters.get_parameter_value(PName.SAMPLES_PER_STEP))
179
- sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
172
+ sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
180
173
 
181
- step_interval = samples_per_step * 1/ sample_rate # [s]
174
+ step_interval = samples_per_step * 1 / sample_rate # [s]
182
175
  if step_interval < api_retuning_latency:
183
- raise ValueError(f"The computed step interval of {step_interval} [s] is of the order of empirically "
184
- f"derived api latency {api_retuning_latency} [s]; you may experience undefined behaviour!")
176
+ raise ValueError(
177
+ f"The computed step interval of {step_interval} [s] is of the order of empirically "
178
+ f"derived api latency {api_retuning_latency} [s]; you may experience undefined behaviour!"
179
+ )
185
180
 
186
181
 
187
182
  def validate_sample_rate_with_master_clock_rate(
@@ -192,17 +187,19 @@ def validate_sample_rate_with_master_clock_rate(
192
187
  :param parameters: The parameters to be validated.
193
188
  :raises ValueError: If the master clock rate is not an integer multiple of the sample rate
194
189
  """
195
- master_clock_rate = cast(int, parameters.get_parameter_value(PName.MASTER_CLOCK_RATE))
196
- sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
190
+ master_clock_rate = cast(
191
+ int, parameters.get_parameter_value(PName.MASTER_CLOCK_RATE)
192
+ )
193
+ sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
197
194
 
198
195
  if master_clock_rate % sample_rate != 0:
199
- raise ValueError(f"The master clock rate of {master_clock_rate} [Hz] is not an integer "
200
- f"multiple of the sample rate {sample_rate} [Hz].")
196
+ raise ValueError(
197
+ f"The master clock rate of {master_clock_rate} [Hz] is not an integer "
198
+ f"multiple of the sample rate {sample_rate} [Hz]."
199
+ )
201
200
 
202
201
 
203
- def validate_fixed_center_frequency(
204
- parameters: Parameters
205
- ) -> None:
202
+ def validate_fixed_center_frequency(parameters: Parameters) -> None:
206
203
  """Apply validators for capture config parameters describing fixed center frequency capture.
207
204
 
208
205
  :param parameters: The parameters to be validated.
@@ -226,6 +223,6 @@ def validate_swept_center_frequency(
226
223
  validate_num_steps_per_sweep(parameters)
227
224
  validate_num_samples_per_step(parameters)
228
225
  validate_sweep_interval(parameters)
229
-
226
+
230
227
  if api_retuning_latency is not None:
231
228
  validate_step_interval(parameters, api_retuning_latency)
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
@@ -6,14 +6,19 @@
6
6
  """General `spectre` package configurations."""
7
7
 
8
8
  from ._paths import (
9
- get_spectre_data_dir_path, get_batches_dir_path, get_configs_dir_path,
10
- get_logs_dir_path, trim_spectre_data_dir_path
11
- )
12
- from ._time_formats import (
13
- TimeFormat
9
+ get_spectre_data_dir_path,
10
+ get_batches_dir_path,
11
+ get_configs_dir_path,
12
+ get_logs_dir_path,
13
+ trim_spectre_data_dir_path,
14
14
  )
15
+ from ._time_formats import TimeFormat
15
16
 
16
17
  __all__ = [
17
- "get_spectre_data_dir_path", "get_batches_dir_path", "get_configs_dir_path", "get_logs_dir_path",
18
- "TimeFormat", "trim_spectre_data_dir_path"
19
- ]
18
+ "get_spectre_data_dir_path",
19
+ "get_batches_dir_path",
20
+ "get_configs_dir_path",
21
+ "get_logs_dir_path",
22
+ "TimeFormat",
23
+ "trim_spectre_data_dir_path",
24
+ ]
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
@@ -6,7 +6,7 @@
6
6
  File system path definitions.
7
7
 
8
8
  `spectre` uses the required environment variable `SPECTRE_DATA_DIR_PATH`
9
- and creates three directories inside it:
9
+ and creates three directories inside it:
10
10
 
11
11
  - `batches`: To hold the batched data files.
12
12
  - `logs`: To hold log files generated at runtime.
@@ -20,17 +20,16 @@ _SPECTRE_DATA_DIR_PATH = os.environ.get("SPECTRE_DATA_DIR_PATH", "NOTSET")
20
20
  if _SPECTRE_DATA_DIR_PATH == "NOTSET":
21
21
  raise ValueError("The environment variable `SPECTRE_DATA_DIR_PATH` must be set.")
22
22
 
23
- _BATCHES_DIR_PATH = os.path.join(_SPECTRE_DATA_DIR_PATH, 'batches')
24
- _LOGS_DIR_PATH = os.path.join(_SPECTRE_DATA_DIR_PATH, 'logs')
23
+ _BATCHES_DIR_PATH = os.path.join(_SPECTRE_DATA_DIR_PATH, "batches")
24
+ _LOGS_DIR_PATH = os.path.join(_SPECTRE_DATA_DIR_PATH, "logs")
25
25
  _CONFIGS_DIR_PATH = os.path.join(_SPECTRE_DATA_DIR_PATH, "configs")
26
26
 
27
27
  os.makedirs(_BATCHES_DIR_PATH, exist_ok=True)
28
- os.makedirs(_LOGS_DIR_PATH, exist_ok=True)
28
+ os.makedirs(_LOGS_DIR_PATH, exist_ok=True)
29
29
  os.makedirs(_CONFIGS_DIR_PATH, exist_ok=True)
30
30
 
31
31
 
32
- def get_spectre_data_dir_path(
33
- ) -> str:
32
+ def get_spectre_data_dir_path() -> str:
34
33
  """The default ancestral path for all `spectre` file system data.
35
34
 
36
35
  :return: The value stored by the `SPECTRE_DATA_DIR_PATH` environment variable.
@@ -39,10 +38,10 @@ def get_spectre_data_dir_path(
39
38
 
40
39
 
41
40
  def _get_date_based_dir_path(
42
- base_dir: str,
43
- year: Optional[int] = None,
44
- month: Optional[int] = None,
45
- day: Optional[int] = None
41
+ base_dir: str,
42
+ year: Optional[int] = None,
43
+ month: Optional[int] = None,
44
+ day: Optional[int] = None,
46
45
  ) -> str:
47
46
  """Append a date-based directory onto the base directory.
48
47
 
@@ -58,7 +57,7 @@ def _get_date_based_dir_path(
58
57
  raise ValueError("A day requires both a month and a year")
59
58
  if month and not year:
60
59
  raise ValueError("A month requires a year")
61
-
60
+
62
61
  date_dir_components = []
63
62
  if year:
64
63
  date_dir_components.append(f"{year:04}")
@@ -66,14 +65,12 @@ def _get_date_based_dir_path(
66
65
  date_dir_components.append(f"{month:02}")
67
66
  if day:
68
67
  date_dir_components.append(f"{day:02}")
69
-
68
+
70
69
  return os.path.join(base_dir, *date_dir_components)
71
70
 
72
71
 
73
72
  def get_batches_dir_path(
74
- year: Optional[int] = None,
75
- month: Optional[int] = None,
76
- day: Optional[int] = None
73
+ year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None
77
74
  ) -> str:
78
75
  """The directory in the file system containing the batched data files. Optionally, append
79
76
  a date-based directory to the end of the path.
@@ -83,16 +80,11 @@ def get_batches_dir_path(
83
80
  :param day: The numeric day. Defaults to None.
84
81
  :return: The directory path for batched data files, optionally with a date-based subdirectory.
85
82
  """
86
- return _get_date_based_dir_path(_BATCHES_DIR_PATH,
87
- year,
88
- month,
89
- day)
83
+ return _get_date_based_dir_path(_BATCHES_DIR_PATH, year, month, day)
90
84
 
91
85
 
92
86
  def get_logs_dir_path(
93
- year: Optional[int] = None,
94
- month: Optional[int] = None,
95
- day: Optional[int] = None
87
+ year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None
96
88
  ) -> str:
97
89
  """The directory in the file system containing the log files generated at runtime. Optionally, append
98
90
  a date-based directory to the end of the path.
@@ -102,14 +94,10 @@ def get_logs_dir_path(
102
94
  :param day: The numeric day. Defaults to None.
103
95
  :return: The directory path for log files, optionally with a date-based subdirectory.
104
96
  """
105
- return _get_date_based_dir_path(_LOGS_DIR_PATH,
106
- year,
107
- month,
108
- day)
97
+ return _get_date_based_dir_path(_LOGS_DIR_PATH, year, month, day)
109
98
 
110
99
 
111
- def get_configs_dir_path(
112
- ) -> str:
100
+ def get_configs_dir_path() -> str:
113
101
  """The directory in the file system containing the capture configs.
114
102
 
115
103
  :return: The directory path for configuration files.
@@ -117,9 +105,7 @@ def get_configs_dir_path(
117
105
  return _CONFIGS_DIR_PATH
118
106
 
119
107
 
120
- def trim_spectre_data_dir_path(
121
- full_path: str
122
- ) -> str:
108
+ def trim_spectre_data_dir_path(full_path: str) -> str:
123
109
  """Remove the `SPECTRE_DATA_DIR_PATH` prefix from a full file path.
124
110
 
125
111
  This function returns the relative path of `full_path` with respect to
@@ -133,9 +119,7 @@ def trim_spectre_data_dir_path(
133
119
  return os.path.relpath(full_path, _SPECTRE_DATA_DIR_PATH)
134
120
 
135
121
 
136
- def add_spectre_data_dir_path(
137
- rel_path: str
138
- ) -> str:
122
+ def add_spectre_data_dir_path(rel_path: str) -> str:
139
123
  """Prepend the `SPECTRE_DATA_DIR_PATH` prefix to a relative file path.
140
124
 
141
125
  This function constructs an absolute path by joining the given relative
@@ -146,4 +130,3 @@ def add_spectre_data_dir_path(
146
130
  :return: The full file path prefixed with `SPECTRE_DATA_DIR_PATH`.
147
131
  """
148
132
  return os.path.join(_SPECTRE_DATA_DIR_PATH, rel_path)
149
-
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
@@ -15,8 +15,9 @@ class TimeFormat:
15
15
  :ivar PRECISE_TIME: Format for times with microseconds (e.g., '23:59:59.123456').
16
16
  :ivar PRECISE_DATETIME: Combined date and precise time format (e.g., '2025-01-11T23:59:59.123456').
17
17
  """
18
- DATE = "%Y-%m-%d"
19
- TIME = "%H:%M:%S"
20
- DATETIME = f"{DATE}T{TIME}"
21
- PRECISE_TIME = "%H:%M:%S.%f"
22
- PRECISE_DATETIME = f"{DATE}T{PRECISE_TIME}"
18
+
19
+ DATE = "%Y-%m-%d"
20
+ TIME = "%H:%M:%S"
21
+ DATETIME = f"{DATE}T{TIME}"
22
+ PRECISE_TIME = "%H:%M:%S.%f"
23
+ PRECISE_DATETIME = f"{DATE}T{PRECISE_TIME}"
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
@@ -6,9 +6,47 @@
6
6
  `spectre` custom exceptions.
7
7
  """
8
8
 
9
+ import warnings
10
+ from functools import wraps
11
+ from typing import TypeVar, Callable, Any, cast
12
+
13
+ F = TypeVar("F", bound=Callable[..., Any])
14
+
15
+
16
+ def deprecated(message: str) -> Callable[[F], F]:
17
+ """A decorator to mark functions as deprecated.
18
+
19
+ :param message: Warning message explaining what to use instead
20
+ """
21
+
22
+ def decorator(func: F) -> F:
23
+ @wraps(func)
24
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
25
+ warnings.warn(
26
+ f"{func.__name__} is deprecated. {message}",
27
+ category=DeprecationWarning,
28
+ stacklevel=2,
29
+ )
30
+ return func(*args, **kwargs)
31
+
32
+ return cast(F, wrapper)
33
+
34
+ return decorator
35
+
36
+
9
37
  class BatchNotFoundError(KeyError): ...
38
+
39
+
10
40
  class ModeNotFoundError(KeyError): ...
41
+
42
+
11
43
  class EventHandlerNotFoundError(KeyError): ...
44
+
45
+
12
46
  class ReceiverNotFoundError(KeyError): ...
47
+
48
+
13
49
  class InvalidTagError(ValueError): ...
50
+
51
+
14
52
  class InvalidSweepMetadataError(ValueError): ...
@@ -1,14 +1,11 @@
1
- # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
5
  """Manage `spectre` jobs and workers."""
6
6
 
7
7
  from ._jobs import Job, start_job
8
- from ._workers import (
9
- Worker, make_worker, do_capture, do_post_processing
10
- )
8
+ from ._workers import Worker, make_worker
9
+ from ._duration import Duration
11
10
 
12
- __all__ = [
13
- "Job", "Worker", "make_worker", "start_job", "do_capture", "do_post_processing"
14
- ]
11
+ __all__ = ["Job", "Worker", "make_worker", "start_job", "Duration"]
@@ -0,0 +1,12 @@
1
+ # SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+
6
+ class Duration:
7
+ """A selection of set durations, in seconds."""
8
+
9
+ ONE_CENTISECOND = 0.01
10
+ ONE_DECISECOND = 0.1
11
+ ONE_SECOND = 1.0
12
+ TEN_SECONDS = 10.0