spectre-core 0.0.22__py3-none-any.whl → 0.0.24__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 (78) hide show
  1. spectre_core/__init__.py +5 -0
  2. spectre_core/_file_io/__init__.py +4 -4
  3. spectre_core/_file_io/file_handlers.py +60 -106
  4. spectre_core/batches/__init__.py +20 -3
  5. spectre_core/batches/_base.py +85 -134
  6. spectre_core/batches/_batches.py +55 -99
  7. spectre_core/batches/_factory.py +21 -20
  8. spectre_core/batches/_register.py +8 -8
  9. spectre_core/batches/plugins/_batch_keys.py +7 -6
  10. spectre_core/batches/plugins/_callisto.py +65 -97
  11. spectre_core/batches/plugins/_iq_stream.py +105 -169
  12. spectre_core/capture_configs/__init__.py +46 -17
  13. spectre_core/capture_configs/_capture_config.py +25 -52
  14. spectre_core/capture_configs/_capture_modes.py +8 -6
  15. spectre_core/capture_configs/_capture_templates.py +50 -110
  16. spectre_core/capture_configs/_parameters.py +37 -74
  17. spectre_core/capture_configs/_pconstraints.py +40 -40
  18. spectre_core/capture_configs/_pnames.py +36 -34
  19. spectre_core/capture_configs/_ptemplates.py +260 -347
  20. spectre_core/capture_configs/_pvalidators.py +99 -102
  21. spectre_core/config/__init__.py +19 -8
  22. spectre_core/config/_paths.py +25 -47
  23. spectre_core/config/_time_formats.py +6 -5
  24. spectre_core/exceptions.py +38 -0
  25. spectre_core/jobs/__init__.py +3 -6
  26. spectre_core/jobs/_duration.py +12 -0
  27. spectre_core/jobs/_jobs.py +72 -43
  28. spectre_core/jobs/_workers.py +55 -105
  29. spectre_core/logs/__init__.py +7 -2
  30. spectre_core/logs/_configure.py +13 -17
  31. spectre_core/logs/_decorators.py +6 -4
  32. spectre_core/logs/_logs.py +37 -89
  33. spectre_core/logs/_process_types.py +5 -3
  34. spectre_core/plotting/__init__.py +19 -3
  35. spectre_core/plotting/_base.py +112 -177
  36. spectre_core/plotting/_format.py +10 -8
  37. spectre_core/plotting/_panel_names.py +7 -5
  38. spectre_core/plotting/_panel_stack.py +138 -130
  39. spectre_core/plotting/_panels.py +152 -162
  40. spectre_core/post_processing/__init__.py +6 -3
  41. spectre_core/post_processing/_base.py +41 -55
  42. spectre_core/post_processing/_factory.py +14 -11
  43. spectre_core/post_processing/_post_processor.py +16 -12
  44. spectre_core/post_processing/_register.py +10 -7
  45. spectre_core/post_processing/plugins/_event_handler_keys.py +4 -3
  46. spectre_core/post_processing/plugins/_fixed_center_frequency.py +54 -47
  47. spectre_core/post_processing/plugins/_swept_center_frequency.py +199 -174
  48. spectre_core/receivers/__init__.py +9 -2
  49. spectre_core/receivers/_base.py +82 -148
  50. spectre_core/receivers/_factory.py +20 -30
  51. spectre_core/receivers/_register.py +7 -10
  52. spectre_core/receivers/_spec_names.py +17 -15
  53. spectre_core/receivers/plugins/_b200mini.py +47 -60
  54. spectre_core/receivers/plugins/_receiver_names.py +8 -6
  55. spectre_core/receivers/plugins/_rsp1a.py +44 -40
  56. spectre_core/receivers/plugins/_rspduo.py +59 -44
  57. spectre_core/receivers/plugins/_sdrplay_receiver.py +67 -83
  58. spectre_core/receivers/plugins/_test.py +136 -129
  59. spectre_core/receivers/plugins/_usrp.py +93 -85
  60. spectre_core/receivers/plugins/gr/__init__.py +1 -1
  61. spectre_core/receivers/plugins/gr/_base.py +14 -22
  62. spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
  63. spectre_core/receivers/plugins/gr/_rspduo.py +77 -89
  64. spectre_core/receivers/plugins/gr/_test.py +49 -57
  65. spectre_core/receivers/plugins/gr/_usrp.py +61 -59
  66. spectre_core/spectrograms/__init__.py +21 -13
  67. spectre_core/spectrograms/_analytical.py +108 -99
  68. spectre_core/spectrograms/_array_operations.py +39 -46
  69. spectre_core/spectrograms/_spectrogram.py +293 -324
  70. spectre_core/spectrograms/_transform.py +106 -73
  71. spectre_core/wgetting/__init__.py +1 -3
  72. spectre_core/wgetting/_callisto.py +87 -93
  73. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/METADATA +9 -23
  74. spectre_core-0.0.24.dist-info/RECORD +79 -0
  75. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/WHEEL +1 -1
  76. spectre_core-0.0.22.dist-info/RECORD +0 -78
  77. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/licenses/LICENSE +0 -0
  78. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/top_level.txt +0 -0
@@ -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
-
24
-
25
- def validate_window(
26
- parameters: Parameters
27
- ) -> None:
13
+
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)
@@ -5,15 +5,26 @@
5
5
 
6
6
  """General `spectre` package configurations."""
7
7
 
8
+ import os
9
+
8
10
  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
11
+ get_spectre_data_dir_path,
12
+ get_batches_dir_path,
13
+ get_configs_dir_path,
14
+ get_logs_dir_path,
15
+ trim_spectre_data_dir_path,
14
16
  )
17
+ from ._time_formats import TimeFormat
18
+
19
+ os.makedirs(get_batches_dir_path(), exist_ok=True)
20
+ os.makedirs(get_logs_dir_path(), exist_ok=True)
21
+ os.makedirs(get_configs_dir_path(), exist_ok=True)
15
22
 
16
23
  __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
- ]
24
+ "get_spectre_data_dir_path",
25
+ "get_batches_dir_path",
26
+ "get_configs_dir_path",
27
+ "get_logs_dir_path",
28
+ "TimeFormat",
29
+ "trim_spectre_data_dir_path",
30
+ ]
@@ -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.
@@ -16,33 +16,25 @@ and creates three directories inside it:
16
16
  import os
17
17
  from typing import Optional
18
18
 
19
- _SPECTRE_DATA_DIR_PATH = os.environ.get("SPECTRE_DATA_DIR_PATH", "NOTSET")
20
- if _SPECTRE_DATA_DIR_PATH == "NOTSET":
21
- raise ValueError("The environment variable `SPECTRE_DATA_DIR_PATH` must be set.")
22
19
 
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
- _CONFIGS_DIR_PATH = os.path.join(_SPECTRE_DATA_DIR_PATH, "configs")
26
-
27
- os.makedirs(_BATCHES_DIR_PATH, exist_ok=True)
28
- os.makedirs(_LOGS_DIR_PATH, exist_ok=True)
29
- os.makedirs(_CONFIGS_DIR_PATH, exist_ok=True)
30
-
31
-
32
- def get_spectre_data_dir_path(
33
- ) -> str:
20
+ def get_spectre_data_dir_path() -> str:
34
21
  """The default ancestral path for all `spectre` file system data.
35
22
 
36
23
  :return: The value stored by the `SPECTRE_DATA_DIR_PATH` environment variable.
37
24
  """
25
+ _SPECTRE_DATA_DIR_PATH = os.environ.get("SPECTRE_DATA_DIR_PATH", "NOTSET")
26
+ if _SPECTRE_DATA_DIR_PATH == "NOTSET":
27
+ raise ValueError(
28
+ "The environment variable `SPECTRE_DATA_DIR_PATH` must be set."
29
+ )
38
30
  return _SPECTRE_DATA_DIR_PATH
39
31
 
40
32
 
41
33
  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
34
+ base_dir: str,
35
+ year: Optional[int] = None,
36
+ month: Optional[int] = None,
37
+ day: Optional[int] = None,
46
38
  ) -> str:
47
39
  """Append a date-based directory onto the base directory.
48
40
 
@@ -58,7 +50,7 @@ def _get_date_based_dir_path(
58
50
  raise ValueError("A day requires both a month and a year")
59
51
  if month and not year:
60
52
  raise ValueError("A month requires a year")
61
-
53
+
62
54
  date_dir_components = []
63
55
  if year:
64
56
  date_dir_components.append(f"{year:04}")
@@ -66,14 +58,12 @@ def _get_date_based_dir_path(
66
58
  date_dir_components.append(f"{month:02}")
67
59
  if day:
68
60
  date_dir_components.append(f"{day:02}")
69
-
61
+
70
62
  return os.path.join(base_dir, *date_dir_components)
71
63
 
72
64
 
73
65
  def get_batches_dir_path(
74
- year: Optional[int] = None,
75
- month: Optional[int] = None,
76
- day: Optional[int] = None
66
+ year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None
77
67
  ) -> str:
78
68
  """The directory in the file system containing the batched data files. Optionally, append
79
69
  a date-based directory to the end of the path.
@@ -83,16 +73,12 @@ def get_batches_dir_path(
83
73
  :param day: The numeric day. Defaults to None.
84
74
  :return: The directory path for batched data files, optionally with a date-based subdirectory.
85
75
  """
86
- return _get_date_based_dir_path(_BATCHES_DIR_PATH,
87
- year,
88
- month,
89
- day)
76
+ batches_dir_path = os.path.join(get_spectre_data_dir_path(), "batches")
77
+ return _get_date_based_dir_path(batches_dir_path, year, month, day)
90
78
 
91
79
 
92
80
  def get_logs_dir_path(
93
- year: Optional[int] = None,
94
- month: Optional[int] = None,
95
- day: Optional[int] = None
81
+ year: Optional[int] = None, month: Optional[int] = None, day: Optional[int] = None
96
82
  ) -> str:
97
83
  """The directory in the file system containing the log files generated at runtime. Optionally, append
98
84
  a date-based directory to the end of the path.
@@ -102,24 +88,19 @@ def get_logs_dir_path(
102
88
  :param day: The numeric day. Defaults to None.
103
89
  :return: The directory path for log files, optionally with a date-based subdirectory.
104
90
  """
105
- return _get_date_based_dir_path(_LOGS_DIR_PATH,
106
- year,
107
- month,
108
- day)
91
+ logs_dir_path = os.path.join(get_spectre_data_dir_path(), "logs")
92
+ return _get_date_based_dir_path(logs_dir_path, year, month, day)
109
93
 
110
94
 
111
- def get_configs_dir_path(
112
- ) -> str:
95
+ def get_configs_dir_path() -> str:
113
96
  """The directory in the file system containing the capture configs.
114
97
 
115
98
  :return: The directory path for configuration files.
116
99
  """
117
- return _CONFIGS_DIR_PATH
100
+ return os.path.join(get_spectre_data_dir_path(), "configs")
118
101
 
119
102
 
120
- def trim_spectre_data_dir_path(
121
- full_path: str
122
- ) -> str:
103
+ def trim_spectre_data_dir_path(full_path: str) -> str:
123
104
  """Remove the `SPECTRE_DATA_DIR_PATH` prefix from a full file path.
124
105
 
125
106
  This function returns the relative path of `full_path` with respect to
@@ -130,12 +111,10 @@ def trim_spectre_data_dir_path(
130
111
  :param full_path: The full file path to be trimmed.
131
112
  :return: The relative path with `SPECTRE_DATA_DIR_PATH` removed.
132
113
  """
133
- return os.path.relpath(full_path, _SPECTRE_DATA_DIR_PATH)
114
+ return os.path.relpath(full_path, get_spectre_data_dir_path())
134
115
 
135
116
 
136
- def add_spectre_data_dir_path(
137
- rel_path: str
138
- ) -> str:
117
+ def add_spectre_data_dir_path(rel_path: str) -> str:
139
118
  """Prepend the `SPECTRE_DATA_DIR_PATH` prefix to a relative file path.
140
119
 
141
120
  This function constructs an absolute path by joining the given relative
@@ -145,5 +124,4 @@ def add_spectre_data_dir_path(
145
124
  :param rel_path: The relative file path to be appended.
146
125
  :return: The full file path prefixed with `SPECTRE_DATA_DIR_PATH`.
147
126
  """
148
- return os.path.join(_SPECTRE_DATA_DIR_PATH, rel_path)
149
-
127
+ return os.path.join(get_spectre_data_dir_path(), rel_path)
@@ -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}"
@@ -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): ...
@@ -5,10 +5,7 @@
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