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
@@ -13,21 +13,40 @@ from os.path import splitext
13
13
  from spectre_core._file_io import BaseFileHandler
14
14
  from spectre_core.config import get_batches_dir_path, TimeFormat
15
15
  from spectre_core.spectrograms import Spectrogram
16
+ from spectre_core.exceptions import deprecated
16
17
 
17
18
 
18
- def parse_batch_base_file_name(
19
- base_file_name: str
20
- ) -> Tuple[str, str, str]:
19
+ @deprecated(
20
+ "The terminology (base file name) is inconsistent with the `_file_io` submodule. "
21
+ f"Please use `parse_batch_file_name` instead."
22
+ )
23
+ def parse_batch_base_file_name(base_file_name: str) -> Tuple[str, str, str]:
24
+ """Included for backwards compatability - please use `parse_batch_file_name` instead."""
25
+ return parse_batch_file_name(base_file_name)
26
+
27
+
28
+ def parse_batch_file_name(file_name: str) -> Tuple[str, str, str]:
21
29
  """Parse the base file name of a batch file into a start time, tag and extension.
30
+
31
+ :param file_name: The file name of the batch file, with optional extension.
32
+ :return: The file name decomposed into its start time, tag and extension. If no extension is present,
33
+ the final element of the tuple will be an empty string.
22
34
  """
23
- batch_name, extension = splitext(base_file_name)
35
+ batch_name, extension = splitext(file_name)
36
+
37
+ num_underscores = batch_name.count("_")
38
+ if num_underscores != 1:
39
+ raise ValueError(
40
+ f"Expected exactly one underscore in the batch name '{batch_name}'. Found {num_underscores}"
41
+ )
42
+
24
43
  # strip the dot from the extension
25
- extension = extension.lstrip('.')
44
+ extension = extension.lstrip(".")
26
45
  start_time, tag = batch_name.split("_", 1)
27
46
  return start_time, tag, extension
28
47
 
29
48
 
30
- T = TypeVar('T')
49
+ T = TypeVar("T")
31
50
 
32
51
 
33
52
  class BatchFile(BaseFileHandler[T]):
@@ -37,14 +56,12 @@ class BatchFile(BaseFileHandler[T]):
37
56
 
38
57
  `<start time>_<tag>.<extension>`
39
58
 
40
- The substring `<start time>_<tag>` is referred to as the batch name. Files with the same batch name
59
+ The substring `<start time>_<tag>` is referred to as the batch name. Files with the same batch name
41
60
  belong to the same batch.
42
61
  """
62
+
43
63
  def __init__(
44
- self,
45
- batch_parent_dir_path: str,
46
- batch_name: str,
47
- extension: str
64
+ self, batch_parent_dir_path: str, batch_name: str, extension: str
48
65
  ) -> None:
49
66
  """Initialise a `BatchFile` instance.
50
67
 
@@ -52,52 +69,39 @@ class BatchFile(BaseFileHandler[T]):
52
69
  :param batch_name: Base file name, composed of the batch start time and tag.
53
70
  :param extension: File extension.
54
71
  """
55
- super().__init__(batch_parent_dir_path,
56
- batch_name,
57
- extension)
72
+ super().__init__(batch_parent_dir_path, batch_name, extension)
58
73
  self._start_time, self._tag = batch_name.split("_")
59
-
60
-
74
+
61
75
  @property
62
- def start_time(
63
- self
64
- ) -> str:
76
+ def start_time(self) -> str:
65
77
  """The start time of the batch, formatted as a string up to seconds precision."""
66
78
  return self._start_time
67
79
 
68
-
69
80
  @cached_property
70
- def start_datetime(
71
- self
72
- ) -> datetime:
81
+ def start_datetime(self) -> datetime:
73
82
  """The start time of the batch, parsed as a datetime up to seconds precision."""
74
83
  return datetime.strptime(self.start_time, TimeFormat.DATETIME)
75
84
 
76
-
77
85
  @property
78
- def tag(
79
- self
80
- ) -> str:
86
+ def tag(self) -> str:
81
87
  """The batch name tag."""
82
88
  return self._tag
83
-
84
-
89
+
90
+
85
91
  @dataclass(frozen=True)
86
92
  class _BatchExtension:
87
93
  """Supported extensions for a `BaseBatch`, inherited by all derived classes.
88
-
94
+
89
95
  :ivar PNG: Corresponds to the `.png` file extension.
90
96
  """
97
+
91
98
  PNG: str = "png"
92
-
93
-
99
+
100
+
94
101
  class _PngFile(BatchFile[str]):
95
102
  """Stores an image visualising the data for the batch."""
96
- def __init__(
97
- self,
98
- batch_parent_dir_path: str,
99
- batch_name: str
100
- ) -> None:
103
+
104
+ def __init__(self, batch_parent_dir_path: str, batch_name: str) -> None:
101
105
  """Initialise a `_PngFile` instance.
102
106
 
103
107
  :param batch_parent_dir_path: The parent directory for the batch.
@@ -105,7 +109,6 @@ class _PngFile(BatchFile[str]):
105
109
  """
106
110
  super().__init__(batch_parent_dir_path, batch_name, _BatchExtension.PNG)
107
111
 
108
-
109
112
  def _read(self) -> str:
110
113
  """Reads the PNG file and returns it base64-encoded.
111
114
 
@@ -114,25 +117,22 @@ class _PngFile(BatchFile[str]):
114
117
  with open(self.file_path, "rb") as f:
115
118
  encoded = b64encode(f.read())
116
119
  return encoded.decode("ascii")
117
-
118
-
120
+
121
+
119
122
  class BaseBatch(ABC):
120
123
  """
121
124
  An abstract base class representing a group of data files over a common time interval.
122
125
 
123
126
  All files in a batch share a base file name and differ only by their extension.
124
- Subclasses of `BaseBatch` define the expected data for each file extension and
127
+ Subclasses of `BaseBatch` define the expected data for each file extension and
125
128
  provide an API for accessing their contents using `BatchFile` subclasses.
126
129
 
127
130
  Subclasses should expose `BatchFile` instances directly as attributes, which
128
131
  simplifies static typing. Additionally, they should call `add_file` in the constructor
129
132
  to formally register each `BatchFile`.
130
133
  """
131
- def __init__(
132
- self,
133
- start_time: str,
134
- tag: str
135
- ) -> None:
134
+
135
+ def __init__(self, start_time: str, tag: str) -> None:
136
136
  """Initialise a `BaseBatch` instance.
137
137
 
138
138
  :param start_time: Start time of the batch as a string with seconds precision.
@@ -141,113 +141,82 @@ class BaseBatch(ABC):
141
141
  self._start_time = start_time
142
142
  self._tag: str = tag
143
143
  self._start_datetime = datetime.strptime(self.start_time, TimeFormat.DATETIME)
144
- self._parent_dir_path = get_batches_dir_path(year = self.start_datetime.year,
145
- month = self.start_datetime.month,
146
- day = self.start_datetime.day)
147
-
144
+ self._parent_dir_path = get_batches_dir_path(
145
+ year=self.start_datetime.year,
146
+ month=self.start_datetime.month,
147
+ day=self.start_datetime.day,
148
+ )
149
+
148
150
  # internal register of batch files
149
151
  self._batch_files: dict[str, BatchFile] = {}
150
-
152
+
151
153
  # Add the files shared by all derived classes
152
- self._png_file = _PngFile(self.parent_dir_path, self.name)
153
- self.add_file( self._png_file )
154
-
155
-
154
+ self._png_file = _PngFile(self.parent_dir_path, self.name)
155
+ self.add_file(self._png_file)
156
+
156
157
  @property
157
158
  @abstractmethod
158
- def spectrogram_file(
159
- self
160
- ) -> BatchFile:
159
+ def spectrogram_file(self) -> BatchFile:
161
160
  """The batch file which contains spectrogram data."""
162
-
163
-
161
+
164
162
  @property
165
- def start_time(
166
- self
167
- ) -> str:
163
+ def start_time(self) -> str:
168
164
  """The start time of the batch, formatted as a string up to seconds precision."""
169
165
  return self._start_time
170
166
 
171
-
172
167
  @property
173
- def start_datetime(
174
- self
175
- ) -> datetime:
168
+ def start_datetime(self) -> datetime:
176
169
  """The start time of the batch, parsed as a datetime up to seconds precision."""
177
170
  return self._start_datetime
178
-
179
-
171
+
180
172
  @property
181
- def tag(
182
- self
183
- ) -> str:
173
+ def tag(self) -> str:
184
174
  """The batch name tag."""
185
175
  return self._tag
186
-
187
176
 
188
177
  @property
189
- def parent_dir_path(
190
- self
191
- ) -> str:
178
+ def parent_dir_path(self) -> str:
192
179
  """The parent directory for the batch."""
193
180
  return self._parent_dir_path
194
181
 
195
-
196
182
  @property
197
- def name(
198
- self
199
- ) -> str:
200
- """Return the base file name shared by all files in the batch,
183
+ def name(self) -> str:
184
+ """Return the base file name shared by all files in the batch,
201
185
  composed of the start time and the batch tag."""
202
186
  return f"{self._start_time}_{self._tag}"
203
-
204
-
187
+
205
188
  @property
206
- def extensions(
207
- self
208
- ) -> list[str]:
189
+ def extensions(self) -> list[str]:
209
190
  """All defined file extensions for the batch."""
210
191
  return list(self._batch_files.keys())
211
-
212
-
192
+
213
193
  @property
214
- def batch_files(
215
- self
216
- ) -> dict[str, BatchFile]:
194
+ def batch_files(self) -> dict[str, BatchFile]:
217
195
  """Map each file extension in the batch to the corresponding batch file instance.
218
-
196
+
219
197
  Use `add_file` to add a file to the batch.
220
198
  """
221
199
  return self._batch_files
222
-
223
-
200
+
224
201
  @property
225
- def png_file(
226
- self
227
- ) -> _PngFile:
202
+ def png_file(self) -> _PngFile:
228
203
  """The batch file corresponding to the `.png` extension."""
229
204
  return self._png_file
230
-
231
205
 
232
- def add_file(
233
- self,
234
- batch_file: BatchFile
235
- ) -> None:
206
+ def add_file(self, batch_file: BatchFile) -> None:
236
207
  """Add an instance of a batch file to the batch.
237
208
 
238
209
  :param batch_file: The `BatchFile` instance to add to the batch.
239
210
  :raises ValueError: If the `BatchFile` instance does not have a defined file extension.
240
211
  """
241
212
  if batch_file.extension is None:
242
- raise ValueError(f"The `BatchFile` must have a defined file extension. "
243
- f"Received '{batch_file.extension}.")
213
+ raise ValueError(
214
+ f"The `BatchFile` must have a defined file extension. "
215
+ f"Received '{batch_file.extension}."
216
+ )
244
217
  self._batch_files[batch_file.extension] = batch_file
245
-
246
218
 
247
- def get_file(
248
- self,
249
- extension: str
250
- ) -> BatchFile:
219
+ def get_file(self, extension: str) -> BatchFile:
251
220
  """Get a batch file instance from the batch, according to the file extension.
252
221
 
253
222
  :param extension: The file extension of the batch file.
@@ -257,13 +226,11 @@ class BaseBatch(ABC):
257
226
  try:
258
227
  return self._batch_files[extension]
259
228
  except KeyError:
260
- raise NotImplementedError(f"A batch file with extension '{extension}' is not implemented for this batch.")
261
-
229
+ raise NotImplementedError(
230
+ f"A batch file with extension '{extension}' is not implemented for this batch."
231
+ )
262
232
 
263
- def delete_file(
264
- self,
265
- extension: str
266
- ) -> None:
233
+ def delete_file(self, extension: str) -> None:
267
234
  """Delete a file from the batch, according to the file extension.
268
235
 
269
236
  :param extension: The file extension of the batch file.
@@ -272,11 +239,7 @@ class BaseBatch(ABC):
272
239
  batch_file = self.get_file(extension)
273
240
  batch_file.delete()
274
241
 
275
-
276
- def has_file(
277
- self,
278
- extension: str
279
- ) -> bool:
242
+ def has_file(self, extension: str) -> bool:
280
243
  """Determine the existance of a batch file in the file system.
281
244
 
282
245
  :param extension: The file extension of the batch file.
@@ -287,22 +250,10 @@ class BaseBatch(ABC):
287
250
  return batch_file.exists
288
251
  except FileNotFoundError:
289
252
  return False
290
-
291
-
292
- def read_spectrogram(
293
- self
294
- ) -> Spectrogram:
253
+
254
+ def read_spectrogram(self) -> Spectrogram:
295
255
  """Read and return the spectrogram data stored in the batch.
296
256
 
297
257
  :return: The spectrogram stored by the batch `spectrogram_file`.
298
258
  """
299
259
  return self.spectrogram_file.read()
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
@@ -10,22 +10,23 @@ from datetime import datetime
10
10
  from spectre_core.config import TimeFormat
11
11
  from spectre_core.spectrograms import Spectrogram, time_chop, join_spectrograms
12
12
  from spectre_core.config import get_batches_dir_path
13
- from spectre_core.exceptions import (
14
- BatchNotFoundError
15
- )
16
- from ._base import BaseBatch, parse_batch_base_file_name
13
+ from spectre_core.exceptions import BatchNotFoundError
14
+ from ._base import BaseBatch, parse_batch_file_name
15
+
16
+ T = TypeVar("T", bound=BaseBatch)
17
+
17
18
 
18
- T = TypeVar('T', bound=BaseBatch)
19
19
  class Batches(Generic[T]):
20
20
  """Managed collection of `Batch` instances for a given tag. Provides a simple
21
21
  interface for read operations on batched data files."""
22
+
22
23
  def __init__(
23
- self,
24
+ self,
24
25
  tag: str,
25
26
  batch_cls: Type[T],
26
- year: Optional[int] = None,
27
- month: Optional[int] = None,
28
- day: Optional[int] = None
27
+ year: Optional[int] = None,
28
+ month: Optional[int] = None,
29
+ day: Optional[int] = None,
29
30
  ) -> None:
30
31
  """Initialise a `Batches` instance.
31
32
 
@@ -40,85 +41,54 @@ class Batches(Generic[T]):
40
41
  self._batch_map: dict[str, T] = OrderedDict()
41
42
  self.set_date(year, month, day)
42
43
 
43
-
44
44
  @property
45
- def tag(
46
- self
47
- ) -> str:
45
+ def tag(self) -> str:
48
46
  """The batch name tag."""
49
47
  return self._tag
50
48
 
51
-
52
49
  @property
53
- def batch_cls(
54
- self
55
- ) -> Type[T]:
50
+ def batch_cls(self) -> Type[T]:
56
51
  """The `Batch` class used to read the batched files."""
57
52
  return self._batch_cls
58
53
 
59
-
60
54
  @property
61
- def year(
62
- self
63
- ) -> Optional[int]:
55
+ def year(self) -> Optional[int]:
64
56
  """The numeric year, to filter batch files."""
65
57
  return self._year
66
58
 
67
-
68
- @property
69
- def month(
70
- self
71
- ) -> Optional[int]:
59
+ @property
60
+ def month(self) -> Optional[int]:
72
61
  """The numeric month of the year, to filter batch files."""
73
62
  return self._month
74
-
75
63
 
76
64
  @property
77
- def day(
78
- self
79
- ) -> Optional[int]:
65
+ def day(self) -> Optional[int]:
80
66
  """The numeric day of the year, to filter batch files."""
81
67
  return self._day
82
-
83
68
 
84
69
  @property
85
- def batches_dir_path(
86
- self
87
- ) -> str:
70
+ def batches_dir_path(self) -> str:
88
71
  """The shared ancestral path for all the batches. `Batches` recursively searches
89
72
  this directory to find all batches whose batch name contains `tag`."""
90
73
  return get_batches_dir_path(self.year, self.month, self.day)
91
-
92
74
 
93
75
  @property
94
- def batch_list(
95
- self
96
- ) -> list[T]:
76
+ def batch_list(self) -> list[T]:
97
77
  """A list of all batches found within `batches_dir_path`."""
98
- return list(self._batch_map.values())
99
-
78
+ return list(self._batch_map.values())
100
79
 
101
80
  @property
102
- def start_times(
103
- self
104
- ) -> list[str]:
81
+ def start_times(self) -> list[str]:
105
82
  """The start times of each batch found within `batches_dir_path`."""
106
83
  return list(self._batch_map.keys())
107
84
 
108
-
109
85
  @property
110
- def num_batches(
111
- self
112
- ) -> int:
86
+ def num_batches(self) -> int:
113
87
  """The total number of batches found within `batches_dir_path`."""
114
88
  return len(self.batch_list)
115
89
 
116
-
117
90
  def set_date(
118
- self,
119
- year: Optional[int],
120
- month: Optional[int],
121
- day: Optional[int]
91
+ self, year: Optional[int], month: Optional[int], day: Optional[int]
122
92
  ) -> None:
123
93
  """Reset `batches_dir_path` according to the numeric date, and refresh the list
124
94
  of available batches.
@@ -132,72 +102,57 @@ class Batches(Generic[T]):
132
102
  self._day = day
133
103
  self.update()
134
104
 
135
-
136
- def update(
137
- self
138
- ) -> None:
139
- """Perform a fresh search all files in `batches_dir_path` for batches
105
+ def update(self) -> None:
106
+ """Perform a fresh search all files in `batches_dir_path` for batches
140
107
  with `tag` in the batch name."""
141
108
  # reset cache
142
- self._batch_map = OrderedDict()
143
-
109
+ self._batch_map = OrderedDict()
110
+
144
111
  # get a list of all batch file names in the batches directory path
145
- batch_file_names = [f for (_, _, files) in os.walk(self.batches_dir_path) for f in files]
112
+ batch_file_names = [
113
+ f for (_, _, files) in os.walk(self.batches_dir_path) for f in files
114
+ ]
146
115
  for batch_file_name in batch_file_names:
147
- start_time, tag, _ = parse_batch_base_file_name(batch_file_name)
116
+ start_time, tag, _ = parse_batch_file_name(batch_file_name)
148
117
  if tag == self._tag:
149
118
  self._batch_map[start_time] = self.batch_cls(start_time, tag)
150
-
119
+
151
120
  self._batch_map = OrderedDict(sorted(self._batch_map.items()))
152
-
153
121
 
154
- def __iter__(
155
- self
156
- ) -> Iterator[T]:
122
+ def __iter__(self) -> Iterator[T]:
157
123
  """Iterate over the stored batch instances."""
158
124
  yield from self.batch_list
159
-
160
-
161
- def __len__(
162
- self
163
- ):
164
- return self.num_batches
165
125
 
126
+ def __len__(self):
127
+ return self.num_batches
166
128
 
167
- def _get_from_start_time(
168
- self,
169
- start_time: str
170
- ) -> T:
129
+ def _get_from_start_time(self, start_time: str) -> T:
171
130
  """Find and return the `Batch` instance based on the string formatted start time."""
172
131
  try:
173
132
  return self._batch_map[start_time]
174
133
  except KeyError:
175
- raise BatchNotFoundError(f"Batch with start time {start_time} could not be found within {self.batches_dir_path}")
134
+ raise BatchNotFoundError(
135
+ f"Batch with start time {start_time} could not be found within {self.batches_dir_path}"
136
+ )
176
137
 
177
-
178
- def _get_from_index(
179
- self,
180
- index: int
181
- ) -> T:
138
+ def _get_from_index(self, index: int) -> T:
182
139
  """Find and return the `Batch` instance based on its numeric index.
183
-
140
+
184
141
  Batches are ordered sequentially in time, so index `0` corresponds to the oldest
185
142
  `Batch` with respect to the start time.
186
143
  """
187
144
  if self.num_batches == 0:
188
145
  raise BatchNotFoundError("No batches are available")
189
146
  elif index > self.num_batches:
190
- raise IndexError(f"Index '{index}' is greater than the number of batches '{self.num_batches}'")
147
+ raise IndexError(
148
+ f"Index '{index}' is greater than the number of batches '{self.num_batches}'"
149
+ )
191
150
  return self.batch_list[index]
192
151
 
193
-
194
- def __getitem__(
195
- self,
196
- subscript: str | int
197
- ) -> T:
152
+ def __getitem__(self, subscript: str | int) -> T:
198
153
  """Get a `Batch` instanced based on either the start time or chronological index.
199
154
 
200
- :param subscript: If the subscript is a string, interpreted as a formatted start time.
155
+ :param subscript: If the subscript is a string, interpreted as a formatted start time.
201
156
  If the subscript is an integer, it is interpreted as a chronological index.
202
157
  :return: The corresponding `BaseBatch` subclass.
203
158
  """
@@ -205,22 +160,19 @@ class Batches(Generic[T]):
205
160
  return self._get_from_start_time(subscript)
206
161
  elif isinstance(subscript, int):
207
162
  return self._get_from_index(subscript)
208
-
209
163
 
210
164
  def get_spectrogram(
211
- self,
212
- start_datetime: datetime,
213
- end_datetime: datetime
165
+ self, start_datetime: datetime, end_datetime: datetime
214
166
  ) -> Spectrogram:
215
167
  """
216
- Retrieve a spectrogram spanning the specified time range.
168
+ Retrieve a spectrogram spanning the specified time range.
217
169
 
218
170
  :param start_datetime: The start time of the range (inclusive).
219
171
  :param end_datetime: The end time of the range (inclusive).
220
172
  :raises FileNotFoundError: If no spectrogram data is available within the specified time range.
221
173
  :return: A spectrogram created by stitching together data from all matching batches.
222
174
  """
223
-
175
+
224
176
  spectrograms = []
225
177
  for batch in self:
226
178
  # skip batches without spectrogram data
@@ -233,10 +185,14 @@ class Batches(Generic[T]):
233
185
 
234
186
  # Check if the batch overlaps with the input time range
235
187
  if start_datetime <= upper_bound and lower_bound <= end_datetime:
236
- spectrograms.append( time_chop(spectrogram, start_datetime, end_datetime) )
188
+ spectrograms.append(
189
+ time_chop(spectrogram, start_datetime, end_datetime)
190
+ )
237
191
 
238
192
  if spectrograms:
239
193
  return join_spectrograms(spectrograms)
240
194
  else:
241
- raise FileNotFoundError(f"No spectrogram data found for the time range "
242
- f"{start_datetime} to {end_datetime}.")
195
+ raise FileNotFoundError(
196
+ f"No spectrogram data found for the time range "
197
+ f"{start_datetime} to {end_datetime}."
198
+ )