spectre-core 0.0.22__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 +4 -4
  2. spectre_core/_file_io/file_handlers.py +60 -106
  3. spectre_core/batches/__init__.py +20 -3
  4. spectre_core/batches/_base.py +85 -134
  5. spectre_core/batches/_batches.py +55 -99
  6. spectre_core/batches/_factory.py +21 -20
  7. spectre_core/batches/_register.py +8 -8
  8. spectre_core/batches/plugins/_batch_keys.py +7 -6
  9. spectre_core/batches/plugins/_callisto.py +65 -97
  10. spectre_core/batches/plugins/_iq_stream.py +105 -169
  11. spectre_core/capture_configs/__init__.py +46 -17
  12. spectre_core/capture_configs/_capture_config.py +25 -52
  13. spectre_core/capture_configs/_capture_modes.py +8 -6
  14. spectre_core/capture_configs/_capture_templates.py +50 -110
  15. spectre_core/capture_configs/_parameters.py +37 -74
  16. spectre_core/capture_configs/_pconstraints.py +40 -40
  17. spectre_core/capture_configs/_pnames.py +36 -34
  18. spectre_core/capture_configs/_ptemplates.py +260 -347
  19. spectre_core/capture_configs/_pvalidators.py +99 -102
  20. spectre_core/config/__init__.py +13 -8
  21. spectre_core/config/_paths.py +18 -35
  22. spectre_core/config/_time_formats.py +6 -5
  23. spectre_core/exceptions.py +38 -0
  24. spectre_core/jobs/__init__.py +3 -6
  25. spectre_core/jobs/_duration.py +12 -0
  26. spectre_core/jobs/_jobs.py +72 -43
  27. spectre_core/jobs/_workers.py +55 -105
  28. spectre_core/logs/__init__.py +7 -2
  29. spectre_core/logs/_configure.py +13 -17
  30. spectre_core/logs/_decorators.py +6 -4
  31. spectre_core/logs/_logs.py +37 -89
  32. spectre_core/logs/_process_types.py +5 -3
  33. spectre_core/plotting/__init__.py +13 -3
  34. spectre_core/plotting/_base.py +64 -138
  35. spectre_core/plotting/_format.py +10 -8
  36. spectre_core/plotting/_panel_names.py +7 -5
  37. spectre_core/plotting/_panel_stack.py +82 -115
  38. spectre_core/plotting/_panels.py +120 -155
  39. spectre_core/post_processing/__init__.py +6 -3
  40. spectre_core/post_processing/_base.py +41 -55
  41. spectre_core/post_processing/_factory.py +14 -11
  42. spectre_core/post_processing/_post_processor.py +16 -12
  43. spectre_core/post_processing/_register.py +10 -7
  44. spectre_core/post_processing/plugins/_event_handler_keys.py +4 -3
  45. spectre_core/post_processing/plugins/_fixed_center_frequency.py +54 -47
  46. spectre_core/post_processing/plugins/_swept_center_frequency.py +199 -174
  47. spectre_core/receivers/__init__.py +9 -2
  48. spectre_core/receivers/_base.py +82 -148
  49. spectre_core/receivers/_factory.py +20 -30
  50. spectre_core/receivers/_register.py +7 -10
  51. spectre_core/receivers/_spec_names.py +17 -15
  52. spectre_core/receivers/plugins/_b200mini.py +47 -60
  53. spectre_core/receivers/plugins/_receiver_names.py +8 -6
  54. spectre_core/receivers/plugins/_rsp1a.py +44 -40
  55. spectre_core/receivers/plugins/_rspduo.py +59 -44
  56. spectre_core/receivers/plugins/_sdrplay_receiver.py +67 -83
  57. spectre_core/receivers/plugins/_test.py +136 -129
  58. spectre_core/receivers/plugins/_usrp.py +93 -85
  59. spectre_core/receivers/plugins/gr/__init__.py +1 -1
  60. spectre_core/receivers/plugins/gr/_base.py +14 -22
  61. spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
  62. spectre_core/receivers/plugins/gr/_rspduo.py +77 -89
  63. spectre_core/receivers/plugins/gr/_test.py +49 -57
  64. spectre_core/receivers/plugins/gr/_usrp.py +61 -59
  65. spectre_core/spectrograms/__init__.py +21 -13
  66. spectre_core/spectrograms/_analytical.py +108 -99
  67. spectre_core/spectrograms/_array_operations.py +39 -46
  68. spectre_core/spectrograms/_spectrogram.py +289 -322
  69. spectre_core/spectrograms/_transform.py +106 -73
  70. spectre_core/wgetting/__init__.py +1 -3
  71. spectre_core/wgetting/_callisto.py +87 -93
  72. {spectre_core-0.0.22.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.22.dist-info → spectre_core-0.0.23.dist-info}/WHEEL +1 -1
  75. spectre_core-0.0.22.dist-info/RECORD +0 -78
  76. {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/licenses/LICENSE +0 -0
  77. {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/top_level.txt +0 -0
@@ -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
+ )
@@ -11,25 +11,22 @@ from ._register import batch_map
11
11
  from .plugins._batch_keys import BatchKey
12
12
  from .plugins._callisto import CallistoBatch
13
13
  from .plugins._iq_stream import IQStreamBatch
14
-
14
+
15
15
 
16
16
  @overload
17
17
  def get_batch_cls(
18
18
  batch_key: Literal[BatchKey.CALLISTO],
19
- ) -> Type[CallistoBatch]:
20
- ...
19
+ ) -> Type[CallistoBatch]: ...
21
20
 
22
21
 
23
22
  @overload
24
23
  def get_batch_cls(
25
24
  batch_key: Literal[BatchKey.IQ_STREAM],
26
- ) -> Type[IQStreamBatch]:
27
- ...
28
-
29
-
25
+ ) -> Type[IQStreamBatch]: ...
26
+
27
+
30
28
  @overload
31
- def get_batch_cls(batch_key: BatchKey) -> Type[BaseBatch]:
32
- ...
29
+ def get_batch_cls(batch_key: BatchKey) -> Type[BaseBatch]: ...
33
30
 
34
31
 
35
32
  def get_batch_cls(
@@ -44,14 +41,14 @@ def get_batch_cls(
44
41
  batch_cls = batch_map.get(batch_key)
45
42
  if batch_cls is None:
46
43
  valid_batch_keys = list(batch_map.keys())
47
- raise BatchNotFoundError(f"No batch found for the batch key: {batch_key}. "
48
- f"Valid batch keys are: {valid_batch_keys}")
44
+ raise BatchNotFoundError(
45
+ f"No batch found for the batch key: {batch_key}. "
46
+ f"Valid batch keys are: {valid_batch_keys}"
47
+ )
49
48
  return batch_cls
50
49
 
51
50
 
52
- def get_batch_cls_from_tag(
53
- tag: str
54
- ) -> Type[BaseBatch]:
51
+ def get_batch_cls_from_tag(tag: str) -> Type[BaseBatch]:
55
52
  # if the tag is reserved (i.e., corresponds to third-party spectrogram data)
56
53
  # directly fetch the right class.
57
54
  if "callisto" in tag:
@@ -61,9 +58,13 @@ def get_batch_cls_from_tag(
61
58
  else:
62
59
  capture_config = CaptureConfig(tag)
63
60
  if PName.BATCH_KEY not in capture_config.parameters.name_list:
64
- raise ValueError(f"Could not infer batch class from the tag 'tag'. "
65
- f"A parameter with name `{PName.BATCH_KEY.value}` "
66
- f"does not exist.")
67
-
68
- batch_key = BatchKey( cast(str, capture_config.get_parameter_value( PName.BATCH_KEY) ) )
69
- return get_batch_cls( batch_key )
61
+ raise ValueError(
62
+ f"Could not infer batch class from the tag 'tag'. "
63
+ f"A parameter with name `{PName.BATCH_KEY.value}` "
64
+ f"does not exist."
65
+ )
66
+
67
+ batch_key = BatchKey(
68
+ cast(str, capture_config.get_parameter_value(PName.BATCH_KEY))
69
+ )
70
+ return get_batch_cls(batch_key)
@@ -10,21 +10,21 @@ from .plugins._batch_keys import BatchKey
10
10
  # Map populated at runtime via the `register_batch` decorator.
11
11
  batch_map: dict[BatchKey, Type[BaseBatch]] = {}
12
12
 
13
- T = TypeVar('T', bound=BaseBatch)
14
- def register_batch(
15
- batch_key: BatchKey
16
- ) -> Callable[[Type[T]], Type[T]]:
13
+ T = TypeVar("T", bound=BaseBatch)
14
+
15
+
16
+ def register_batch(batch_key: BatchKey) -> Callable[[Type[T]], Type[T]]:
17
17
  """Decorator to register a `BaseBatch` subclass under a specified `BatchKey`.
18
18
 
19
19
  :param batch_key: The key to register the `BaseBatch` subclass under.
20
20
  :raises ValueError: If the provided `batch_key` is already registered.
21
21
  :return: A decorator that registers the `BaseBatch` subclass under the given `batch_key`.
22
22
  """
23
- def decorator(
24
- cls: Type[T]
25
- ) -> Type[T]:
23
+
24
+ def decorator(cls: Type[T]) -> Type[T]:
26
25
  if batch_key in batch_map:
27
26
  raise ValueError(f"A batch with key '{batch_key}' is already registered!")
28
27
  batch_map[batch_key] = cls
29
28
  return cls
30
- return decorator
29
+
30
+ return decorator
@@ -4,13 +4,14 @@
4
4
 
5
5
  from enum import Enum
6
6
 
7
- class BatchKey(Enum):
7
+
8
+ class BatchKey(Enum):
8
9
  """Key bound to a `Batch` plugin class.
9
-
10
- :ivar IQ_STREAM: Represents the default batch data generated by `spectre`,
10
+
11
+ :ivar IQ_STREAM: Represents the default batch data generated by `spectre`,
11
12
  containing IQ stream data and other data derived from it.
12
13
  :ivar CALLISTO: Represents FITS files generated by the e-Callisto network.
13
14
  """
14
- IQ_STREAM = "iq_stream"
15
- CALLISTO = "callisto"
16
-
15
+
16
+ IQ_STREAM = "iq_stream"
17
+ CALLISTO = "callisto"
@@ -21,107 +21,89 @@ from .._register import register_batch
21
21
  @dataclass(frozen=True)
22
22
  class _BatchExtension:
23
23
  """Supported extensions for a `CallistoBatch`.
24
-
24
+
25
25
  :ivar FITS: Corresponds to the `.fits` file extension.
26
26
  """
27
+
27
28
  FITS: str = "fits"
28
-
29
-
29
+
30
+
30
31
  class _FitsFile(BatchFile[Spectrogram]):
31
32
  """Stores spectrogram data in the FITS format generated by the e-Callisto network."""
32
- def __init__(
33
- self,
34
- parent_dir_path: str,
35
- base_file_name: str
36
- ) -> None:
33
+
34
+ def __init__(self, parent_dir_path: str, base_file_name: str) -> None:
37
35
  """Initialise a `_FitsFile` instance.
38
36
 
39
37
  :param parent_dir_path: The parent directory for the batch.
40
38
  :param base_file_name: The batch name.
41
39
  """
42
- super().__init__(parent_dir_path,
43
- base_file_name,
44
- _BatchExtension.FITS)
45
-
46
-
47
- def _read(
48
- self
49
- ) -> Spectrogram:
40
+ super().__init__(parent_dir_path, base_file_name, _BatchExtension.FITS)
41
+
42
+ def _read(self) -> Spectrogram:
50
43
  """Parses a FITS file to generate a `Spectrogram` instance.
51
44
 
52
- Reverses the spectra along the frequency axis and converts units to linearised
45
+ Reverses the spectra along the frequency axis and converts units to linearised
53
46
  values if necessary. Infers the spectrum type from the `BUNIT` header.
54
47
 
55
48
  :raises NotImplementedError: If the `BUNIT` header value represents an unsupported spectrum type.
56
49
  :return: A `Spectrogram` instance containing the parsed FITS file data.
57
50
  """
58
- with fits.open(self.file_path, mode='readonly') as hdulist:
59
- primary_hdu = self._get_primary_hdu(hdulist)
60
- dynamic_spectra = self._get_dynamic_spectra(primary_hdu)
61
- spectrogram_start_datetime = self._get_spectrogram_start_datetime(primary_hdu)
62
- bintable_hdu = self._get_bintable_hdu(hdulist)
63
- times = self._get_times(bintable_hdu)
64
- frequencies = self._get_frequencies(bintable_hdu)
65
- bunit = self._get_bunit(primary_hdu)
51
+ with fits.open(self.file_path, mode="readonly") as hdulist:
52
+ primary_hdu = self._get_primary_hdu(hdulist)
53
+ dynamic_spectra = self._get_dynamic_spectra(primary_hdu)
54
+ spectrogram_start_datetime = self._get_spectrogram_start_datetime(
55
+ primary_hdu
56
+ )
57
+ bintable_hdu = self._get_bintable_hdu(hdulist)
58
+ times = self._get_times(bintable_hdu)
59
+ frequencies = self._get_frequencies(bintable_hdu)
60
+ bunit = self._get_bunit(primary_hdu)
66
61
 
67
62
  # bunit is interpreted as a SpectrumUnit
68
63
  spectrum_unit = SpectrumUnit(bunit)
69
64
  if spectrum_unit == SpectrumUnit.DIGITS:
70
- dynamic_spectra_linearised = self._convert_units_to_linearised(dynamic_spectra)
71
-
72
- return Spectrogram(dynamic_spectra_linearised[::-1, :], # reverse the spectra along the frequency axis
73
- times,
74
- frequencies[::-1], # sort the frequencies in ascending order
75
- self.tag,
76
- spectrum_unit,
77
- spectrogram_start_datetime)
65
+ dynamic_spectra_linearised = self._convert_units_to_linearised(
66
+ dynamic_spectra
67
+ )
68
+
69
+ return Spectrogram(
70
+ dynamic_spectra_linearised[
71
+ ::-1, :
72
+ ], # reverse the spectra along the frequency axis
73
+ times,
74
+ frequencies[::-1], # sort the frequencies in ascending order
75
+ self.tag,
76
+ spectrum_unit,
77
+ spectrogram_start_datetime,
78
+ )
78
79
  else:
79
- raise NotImplementedError(f"SPECTRE does not currently support spectrum type with BUNITS '{spectrum_unit}'")
80
+ raise NotImplementedError(
81
+ f"SPECTRE does not currently support spectrum type with BUNITS '{spectrum_unit}'"
82
+ )
80
83
 
84
+ def _get_primary_hdu(self, hdulist: HDUList) -> PrimaryHDU:
85
+ return hdulist["PRIMARY"]
81
86
 
82
- def _get_primary_hdu(
83
- self, hdulist: HDUList
84
- ) -> PrimaryHDU:
85
- return hdulist['PRIMARY']
86
-
87
-
88
- def _get_bintable_hdu(
89
- self,
90
- hdulist: HDUList
91
- ) -> BinTableHDU:
87
+ def _get_bintable_hdu(self, hdulist: HDUList) -> BinTableHDU:
92
88
  return hdulist[1]
93
-
94
-
95
- def _get_dynamic_spectra(
96
- self,
97
- primary_hdu: PrimaryHDU
98
- ) -> npt.NDArray[np.float32]:
99
- return primary_hdu.data.astype(np.float32)
100
89
 
90
+ def _get_dynamic_spectra(self, primary_hdu: PrimaryHDU) -> npt.NDArray[np.float32]:
91
+ return primary_hdu.data.astype(np.float32)
101
92
 
102
- def _get_spectrogram_start_datetime(
103
- self,
104
- primary_hdu: PrimaryHDU
105
- ) -> datetime:
106
- date_obs = primary_hdu.header['DATE-OBS']
107
- time_obs = primary_hdu.header['TIME-OBS']
93
+ def _get_spectrogram_start_datetime(self, primary_hdu: PrimaryHDU) -> datetime:
94
+ date_obs = primary_hdu.header["DATE-OBS"]
95
+ time_obs = primary_hdu.header["TIME-OBS"]
108
96
  return datetime.strptime(f"{date_obs}T{time_obs}", "%Y/%m/%dT%H:%M:%S.%f")
109
97
 
110
-
111
- def _get_bunit(
112
- self,
113
- primary_hdu: PrimaryHDU
114
- ) -> str:
115
- return primary_hdu.header['BUNIT']
116
-
98
+ def _get_bunit(self, primary_hdu: PrimaryHDU) -> str:
99
+ return primary_hdu.header["BUNIT"]
117
100
 
118
101
  def _convert_units_to_linearised(
119
- self,
120
- raw_digits: npt.NDArray[np.float32]
102
+ self, raw_digits: npt.NDArray[np.float32]
121
103
  ) -> npt.NDArray[np.float32]:
122
104
  """Converts spectrogram data from raw digit values to linearised units.
123
105
 
124
- Applies a transformation based on ADC specifications to convert raw values
106
+ Applies a transformation based on ADC specifications to convert raw values
125
107
  to dB and then to linearised units.
126
108
 
127
109
  :param dynamic_spectra: Raw dynamic spectra in digit values.
@@ -130,54 +112,40 @@ class _FitsFile(BatchFile[Spectrogram]):
130
112
  # conversion as per ADC specs [see email from C. Monstein]
131
113
  dB = (raw_digits / 255) * (2500 / 25)
132
114
  return 10 ** (dB / 10)
133
-
134
-
135
- def _get_times(
136
- self,
137
- bintable_hdu: BinTableHDU
138
- ) -> npt.NDArray[np.float32]:
115
+
116
+ def _get_times(self, bintable_hdu: BinTableHDU) -> npt.NDArray[np.float32]:
139
117
  """Extracts the elapsed times for each spectrum in seconds, with the first spectrum set to t=0
140
118
  by convention.
141
119
  """
142
- return bintable_hdu.data['TIME'][0] # already in seconds
120
+ return bintable_hdu.data["TIME"][0] # already in seconds
143
121
 
144
-
145
- def _get_frequencies(
146
- self,
147
- bintable_hdu: BinTableHDU
148
- ) -> npt.NDArray[np.float32]:
122
+ def _get_frequencies(self, bintable_hdu: BinTableHDU) -> npt.NDArray[np.float32]:
149
123
  """Extracts the frequencies for each spectral component."""
150
- frequencies_MHz = bintable_hdu.data['FREQUENCY'][0]
151
- return frequencies_MHz * 1e6 # convert to Hz
152
-
153
-
124
+ frequencies_MHz = bintable_hdu.data["FREQUENCY"][0]
125
+ return frequencies_MHz * 1e6 # convert to Hz
126
+
127
+
154
128
  @register_batch(BatchKey.CALLISTO)
155
129
  class CallistoBatch(BaseBatch):
156
130
  """A batch of data generated by the e-Callisto network.
157
-
131
+
158
132
  Supports the following file extensions:
159
133
  - `.fits` (via the `spectrogram_file` attribute)
160
134
  """
161
- def __init__(
162
- self,
163
- start_time: str,
164
- tag: str
165
- ) -> None:
135
+
136
+ def __init__(self, start_time: str, tag: str) -> None:
166
137
  """Initialise a `CallistoBatch` instance.
167
138
 
168
139
  :param start_time: The start time of the batch.
169
140
  :param tag: The batch name tag.
170
141
  """
171
- super().__init__(start_time, tag)
142
+ super().__init__(start_time, tag)
172
143
  self._fits_file = _FitsFile(self.parent_dir_path, self.name)
173
-
144
+
174
145
  # add files formally to the batch
175
- self.add_file( self.spectrogram_file )
176
-
177
-
146
+ self.add_file(self.spectrogram_file)
147
+
178
148
  @property
179
- def spectrogram_file(
180
- self
181
- ) -> _FitsFile:
149
+ def spectrogram_file(self) -> _FitsFile:
182
150
  """The batch file corresponding to the `.fits` extension."""
183
151
  return self._fits_file