spectre-core 0.0.19__py3-none-any.whl → 0.0.21__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.
- spectre_core/batches/__init__.py +2 -2
- spectre_core/batches/_base.py +72 -2
- spectre_core/batches/_batches.py +11 -16
- spectre_core/batches/plugins/_iq_stream.py +0 -1
- spectre_core/capture_configs/_capture_config.py +2 -5
- spectre_core/logs/__init__.py +3 -3
- spectre_core/logs/_logs.py +16 -5
- spectre_core/plotting/_base.py +7 -2
- spectre_core/plotting/_panel_stack.py +56 -12
- spectre_core/spectrograms/_transform.py +10 -10
- spectre_core/wgetting/_callisto.py +9 -8
- {spectre_core-0.0.19.dist-info → spectre_core-0.0.21.dist-info}/METADATA +3 -3
- {spectre_core-0.0.19.dist-info → spectre_core-0.0.21.dist-info}/RECORD +16 -16
- {spectre_core-0.0.19.dist-info → spectre_core-0.0.21.dist-info}/WHEEL +1 -1
- {spectre_core-0.0.19.dist-info → spectre_core-0.0.21.dist-info/licenses}/LICENSE +0 -0
- {spectre_core-0.0.19.dist-info → spectre_core-0.0.21.dist-info}/top_level.txt +0 -0
spectre_core/batches/__init__.py
CHANGED
@@ -10,12 +10,12 @@ from .plugins._batch_keys import BatchKey
|
|
10
10
|
from .plugins._iq_stream import IQStreamBatch, IQMetadata
|
11
11
|
from .plugins._callisto import CallistoBatch
|
12
12
|
|
13
|
-
from ._base import BaseBatch, BatchFile
|
13
|
+
from ._base import BaseBatch, BatchFile, parse_batch_base_file_name
|
14
14
|
from ._batches import Batches
|
15
15
|
from ._factory import get_batch_cls, get_batch_cls_from_tag
|
16
16
|
|
17
17
|
__all__ = [
|
18
18
|
"IQStreamBatch", "IQMetadata", "CallistoBatch", "BaseBatch", "BatchFile",
|
19
|
-
"Batches", "get_batch_cls", "BatchKey", "get_batch_cls_from_tag"
|
19
|
+
"Batches", "get_batch_cls", "BatchKey", "get_batch_cls_from_tag", "parse_batch_base_file_name"
|
20
20
|
]
|
21
21
|
|
spectre_core/batches/_base.py
CHANGED
@@ -3,17 +3,33 @@
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
5
|
from datetime import datetime
|
6
|
-
from typing import TypeVar
|
6
|
+
from typing import TypeVar, Tuple
|
7
|
+
from base64 import b64encode
|
7
8
|
from functools import cached_property
|
8
9
|
from abc import ABC, abstractmethod
|
10
|
+
from dataclasses import dataclass
|
11
|
+
from os.path import splitext
|
9
12
|
|
10
13
|
from spectre_core._file_io import BaseFileHandler
|
11
14
|
from spectre_core.config import get_batches_dir_path, TimeFormat
|
12
15
|
from spectre_core.spectrograms import Spectrogram
|
13
16
|
|
14
17
|
|
18
|
+
def parse_batch_base_file_name(
|
19
|
+
base_file_name: str
|
20
|
+
) -> Tuple[str, str, str]:
|
21
|
+
"""Parse the base file name of a batch file into a start time, tag and extension.
|
22
|
+
"""
|
23
|
+
batch_name, extension = splitext(base_file_name)
|
24
|
+
# strip the dot from the extension
|
25
|
+
extension = extension.lstrip('.')
|
26
|
+
start_time, tag = batch_name.split("_", 1)
|
27
|
+
return start_time, tag, extension
|
28
|
+
|
29
|
+
|
15
30
|
T = TypeVar('T')
|
16
31
|
|
32
|
+
|
17
33
|
class BatchFile(BaseFileHandler[T]):
|
18
34
|
"""Abstract base class for files belonging to a batch, identified by their file extension.
|
19
35
|
|
@@ -64,6 +80,40 @@ class BatchFile(BaseFileHandler[T]):
|
|
64
80
|
) -> str:
|
65
81
|
"""The batch name tag."""
|
66
82
|
return self._tag
|
83
|
+
|
84
|
+
|
85
|
+
@dataclass(frozen=True)
|
86
|
+
class _BatchExtension:
|
87
|
+
"""Supported extensions for a `BaseBatch`, inherited by all derived classes.
|
88
|
+
|
89
|
+
:ivar PNG: Corresponds to the `.png` file extension.
|
90
|
+
"""
|
91
|
+
PNG: str = "png"
|
92
|
+
|
93
|
+
|
94
|
+
class _PngFile(BatchFile[str]):
|
95
|
+
"""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:
|
101
|
+
"""Initialise a `_PngFile` instance.
|
102
|
+
|
103
|
+
:param batch_parent_dir_path: The parent directory for the batch.
|
104
|
+
:param batch_name: The batch name.
|
105
|
+
"""
|
106
|
+
super().__init__(batch_parent_dir_path, batch_name, _BatchExtension.PNG)
|
107
|
+
|
108
|
+
|
109
|
+
def _read(self) -> str:
|
110
|
+
"""Reads the PNG file and returns it base64-encoded.
|
111
|
+
|
112
|
+
:return: Base64-encoded string representation of the image.
|
113
|
+
"""
|
114
|
+
with open(self.file_path, "rb") as f:
|
115
|
+
encoded = b64encode(f.read())
|
116
|
+
return encoded.decode("ascii")
|
67
117
|
|
68
118
|
|
69
119
|
class BaseBatch(ABC):
|
@@ -98,6 +148,10 @@ class BaseBatch(ABC):
|
|
98
148
|
# internal register of batch files
|
99
149
|
self._batch_files: dict[str, BatchFile] = {}
|
100
150
|
|
151
|
+
# 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
|
+
|
101
155
|
|
102
156
|
@property
|
103
157
|
@abstractmethod
|
@@ -166,6 +220,14 @@ class BaseBatch(ABC):
|
|
166
220
|
"""
|
167
221
|
return self._batch_files
|
168
222
|
|
223
|
+
|
224
|
+
@property
|
225
|
+
def png_file(
|
226
|
+
self
|
227
|
+
) -> _PngFile:
|
228
|
+
"""The batch file corresponding to the `.png` extension."""
|
229
|
+
return self._png_file
|
230
|
+
|
169
231
|
|
170
232
|
def add_file(
|
171
233
|
self,
|
@@ -235,4 +297,12 @@ class BaseBatch(ABC):
|
|
235
297
|
:return: The spectrogram stored by the batch `spectrogram_file`.
|
236
298
|
"""
|
237
299
|
return self.spectrogram_file.read()
|
238
|
-
|
300
|
+
|
301
|
+
|
302
|
+
|
303
|
+
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
|
308
|
+
|
spectre_core/batches/_batches.py
CHANGED
@@ -13,7 +13,7 @@ from spectre_core.config import get_batches_dir_path
|
|
13
13
|
from spectre_core.exceptions import (
|
14
14
|
BatchNotFoundError
|
15
15
|
)
|
16
|
-
from ._base import BaseBatch
|
16
|
+
from ._base import BaseBatch, parse_batch_base_file_name
|
17
17
|
|
18
18
|
T = TypeVar('T', bound=BaseBatch)
|
19
19
|
class Batches(Generic[T]):
|
@@ -144,9 +144,7 @@ class Batches(Generic[T]):
|
|
144
144
|
# get a list of all batch file names in the batches directory path
|
145
145
|
batch_file_names = [f for (_, _, files) in os.walk(self.batches_dir_path) for f in files]
|
146
146
|
for batch_file_name in batch_file_names:
|
147
|
-
|
148
|
-
batch_name, _ = os.path.splitext(batch_file_name)
|
149
|
-
start_time, tag = batch_name.split("_", 1)
|
147
|
+
start_time, tag, _ = parse_batch_base_file_name(batch_file_name)
|
150
148
|
if tag == self._tag:
|
151
149
|
self._batch_map[start_time] = self.batch_cls(start_time, tag)
|
152
150
|
|
@@ -207,25 +205,22 @@ class Batches(Generic[T]):
|
|
207
205
|
return self._get_from_start_time(subscript)
|
208
206
|
elif isinstance(subscript, int):
|
209
207
|
return self._get_from_index(subscript)
|
208
|
+
|
210
209
|
|
211
|
-
|
212
|
-
def get_spectrogram_from_range(
|
210
|
+
def get_spectrogram(
|
213
211
|
self,
|
214
|
-
|
215
|
-
|
212
|
+
start_datetime: datetime,
|
213
|
+
end_datetime: datetime
|
216
214
|
) -> Spectrogram:
|
217
215
|
"""
|
218
216
|
Retrieve a spectrogram spanning the specified time range.
|
219
217
|
|
220
|
-
:param
|
221
|
-
:param
|
218
|
+
:param start_datetime: The start time of the range (inclusive).
|
219
|
+
:param end_datetime: The end time of the range (inclusive).
|
222
220
|
:raises FileNotFoundError: If no spectrogram data is available within the specified time range.
|
223
221
|
:return: A spectrogram created by stitching together data from all matching batches.
|
224
222
|
"""
|
225
|
-
|
226
|
-
start_datetime = datetime.strptime(start_time, TimeFormat.DATETIME)
|
227
|
-
end_datetime = datetime.strptime(end_time, TimeFormat.DATETIME)
|
228
|
-
|
223
|
+
|
229
224
|
spectrograms = []
|
230
225
|
for batch in self:
|
231
226
|
# skip batches without spectrogram data
|
@@ -238,10 +233,10 @@ class Batches(Generic[T]):
|
|
238
233
|
|
239
234
|
# Check if the batch overlaps with the input time range
|
240
235
|
if start_datetime <= upper_bound and lower_bound <= end_datetime:
|
241
|
-
spectrograms.append( time_chop(spectrogram,
|
236
|
+
spectrograms.append( time_chop(spectrogram, start_datetime, end_datetime) )
|
242
237
|
|
243
238
|
if spectrograms:
|
244
239
|
return join_spectrograms(spectrograms)
|
245
240
|
else:
|
246
241
|
raise FileNotFoundError(f"No spectrogram data found for the time range "
|
247
|
-
f"{
|
242
|
+
f"{start_datetime} to {end_datetime}.")
|
@@ -317,7 +317,6 @@ class IQStreamBatch(BaseBatch):
|
|
317
317
|
:param tag: The batch name tag.
|
318
318
|
"""
|
319
319
|
super().__init__(start_time, tag)
|
320
|
-
self._batch_files = {}
|
321
320
|
self._fits_file = _FitsFile(self.parent_dir_path, self.name)
|
322
321
|
self._bin_file = _BinFile (self.parent_dir_path, self.name)
|
323
322
|
self._hdr_file = _HdrFile (self.parent_dir_path, self.name)
|
@@ -42,7 +42,7 @@ class CaptureConfig(JsonHandler):
|
|
42
42
|
self._validate_tag(tag)
|
43
43
|
self._tag = tag
|
44
44
|
super().__init__(get_configs_dir_path(),
|
45
|
-
|
45
|
+
tag)
|
46
46
|
|
47
47
|
@property
|
48
48
|
def tag(
|
@@ -60,11 +60,8 @@ class CaptureConfig(JsonHandler):
|
|
60
60
|
|
61
61
|
Some substrings are reserved for third-party spectrogram data.
|
62
62
|
"""
|
63
|
-
if "_" in tag:
|
64
|
-
raise InvalidTagError(f"Tags cannot contain an underscore. Received {tag}")
|
65
|
-
|
66
63
|
if "callisto" in tag:
|
67
|
-
raise ValueError(f"The substring `callisto` is not allowed in a capture config tag.")
|
64
|
+
raise ValueError(f"The substring `callisto` is reserved, and is not allowed in a capture config tag.")
|
68
65
|
|
69
66
|
|
70
67
|
@property
|
spectre_core/logs/__init__.py
CHANGED
@@ -8,10 +8,10 @@
|
|
8
8
|
from ._process_types import ProcessType
|
9
9
|
from ._decorators import log_call
|
10
10
|
from ._configure import configure_root_logger, get_root_logger_state
|
11
|
-
from ._logs import Log, Logs
|
11
|
+
from ._logs import Log, Logs, parse_log_base_file_name
|
12
12
|
|
13
13
|
|
14
14
|
__all__ = [
|
15
15
|
"log_call", "configure_root_logger", "Log", "Logs", "ProcessType",
|
16
|
-
"get_root_logger_state"
|
17
|
-
]
|
16
|
+
"get_root_logger_state", "parse_log_base_file_name"
|
17
|
+
]
|
spectre_core/logs/_logs.py
CHANGED
@@ -6,7 +6,7 @@ from logging import getLogger
|
|
6
6
|
_LOGGER = getLogger(__name__)
|
7
7
|
|
8
8
|
import os
|
9
|
-
from typing import Optional, Iterator
|
9
|
+
from typing import Optional, Iterator, Tuple
|
10
10
|
from collections import OrderedDict
|
11
11
|
from datetime import datetime
|
12
12
|
|
@@ -14,6 +14,17 @@ from spectre_core._file_io import TextHandler
|
|
14
14
|
from spectre_core.config import get_logs_dir_path, TimeFormat
|
15
15
|
from ._process_types import ProcessType
|
16
16
|
|
17
|
+
|
18
|
+
def parse_log_base_file_name(
|
19
|
+
base_file_name: str
|
20
|
+
) -> Tuple[str, str, str]:
|
21
|
+
"""Parse the base file name of a log into a start time, process ID and process type.
|
22
|
+
"""
|
23
|
+
file_name, _ = os.path.splitext(base_file_name)
|
24
|
+
log_start_time, pid, process_type = file_name.split("_")
|
25
|
+
return log_start_time, pid, process_type
|
26
|
+
|
27
|
+
|
17
28
|
class Log(TextHandler):
|
18
29
|
"""Interface to read log files generated by `spectre`."""
|
19
30
|
def __init__(
|
@@ -177,13 +188,13 @@ class Logs:
|
|
177
188
|
log_files = [f for (_, _, files) in os.walk(self.logs_dir_path) for f in files]
|
178
189
|
|
179
190
|
for log_file in log_files:
|
180
|
-
|
181
|
-
log_start_time, pid, process_type =
|
191
|
+
|
192
|
+
log_start_time, pid, process_type = parse_log_base_file_name(log_file)
|
182
193
|
|
183
194
|
if self.process_type and process_type != self.process_type:
|
184
195
|
continue
|
185
196
|
|
186
|
-
self._log_map[
|
197
|
+
self._log_map[log_file] = Log(log_start_time, pid, ProcessType(process_type))
|
187
198
|
|
188
199
|
self._log_map = OrderedDict(sorted(self._log_map.items()))
|
189
200
|
|
@@ -225,4 +236,4 @@ class Logs:
|
|
225
236
|
for log in self.log_list:
|
226
237
|
if log.pid == pid:
|
227
238
|
return log
|
228
|
-
raise FileNotFoundError(f"Log handler for PID '{pid}' not
|
239
|
+
raise FileNotFoundError(f"Log handler for PID '{pid}' could not be identified")
|
spectre_core/plotting/_base.py
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
from abc import ABC, abstractmethod
|
6
6
|
from typing import Optional, Literal
|
7
7
|
from enum import Enum
|
8
|
+
from datetime import datetime
|
8
9
|
|
9
10
|
import numpy.typing as npt
|
10
11
|
import numpy as np
|
@@ -13,6 +14,7 @@ from matplotlib.axes import Axes
|
|
13
14
|
from matplotlib.figure import Figure
|
14
15
|
|
15
16
|
from spectre_core.spectrograms import Spectrogram, TimeType
|
17
|
+
from spectre_core.config import TimeFormat
|
16
18
|
from ._format import PanelFormat
|
17
19
|
from ._panel_names import PanelName
|
18
20
|
|
@@ -284,8 +286,11 @@ class BaseTimeSeriesPanel(BasePanel):
|
|
284
286
|
if self.time_type == TimeType.RELATIVE:
|
285
287
|
self.ax.set_xlabel('Time [s]')
|
286
288
|
else:
|
287
|
-
|
288
|
-
self.
|
289
|
+
#TODO: Adapt for time ranges greater than one day
|
290
|
+
start_date = datetime.strftime(self.spectrogram.start_datetime.astype(datetime),
|
291
|
+
TimeFormat.DATE)
|
292
|
+
self.ax.set_xlabel(f'Time [UTC] (Start Date: {start_date})')
|
293
|
+
self.ax.xaxis.set_major_formatter(mdates.DateFormatter(TimeFormat.TIME))
|
289
294
|
|
290
295
|
|
291
296
|
|
@@ -2,18 +2,22 @@
|
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
|
+
import os
|
5
6
|
import numpy as np
|
6
|
-
import numpy.typing as npt
|
7
7
|
from typing import Optional, Tuple, cast
|
8
8
|
import matplotlib.pyplot as plt
|
9
9
|
from matplotlib.figure import Figure
|
10
10
|
from matplotlib.axes import Axes
|
11
|
+
from matplotlib import use
|
12
|
+
from datetime import datetime
|
11
13
|
|
12
14
|
from spectre_core.spectrograms import TimeType
|
15
|
+
from spectre_core.config import TimeFormat, get_batches_dir_path
|
13
16
|
from ._base import BasePanel, XAxisType
|
14
17
|
from ._format import PanelFormat
|
15
|
-
from ._panels import
|
16
|
-
|
18
|
+
from ._panels import (
|
19
|
+
PanelName, SpectrogramPanel, TimeCutsPanel, FrequencyCutsPanel
|
20
|
+
)
|
17
21
|
|
18
22
|
def _is_cuts_panel(
|
19
23
|
panel: BasePanel
|
@@ -43,17 +47,22 @@ class PanelStack:
|
|
43
47
|
self,
|
44
48
|
panel_format: PanelFormat = PanelFormat(),
|
45
49
|
time_type: TimeType = TimeType.RELATIVE,
|
46
|
-
figsize: Tuple[int, int] = (
|
50
|
+
figsize: Tuple[int, int] = (15, 8),
|
51
|
+
non_interactive: bool = False
|
47
52
|
) -> None:
|
48
53
|
"""Initialize an instance of `PanelStack`.
|
49
54
|
|
50
55
|
:param panel_format: Formatting applied across all panels in the stack. Defaults to `PanelFormat()`.
|
51
56
|
:param time_type: The type of time assigned to spectrograms, defaults to `TimeType.RELATIVE`.
|
52
|
-
:param figsize: The size of the `matplotlib` figure as (width, height). Defaults to (
|
57
|
+
:param figsize: The size of the `matplotlib` figure as (width, height). Defaults to (15, 8).
|
53
58
|
"""
|
54
59
|
self._panel_format = panel_format
|
55
60
|
self._time_type = time_type
|
56
61
|
self._figsize = figsize
|
62
|
+
|
63
|
+
if non_interactive:
|
64
|
+
# Use a non-interactive matplotlib backend, which can only write files.
|
65
|
+
use('agg')
|
57
66
|
|
58
67
|
self._panels : list[BasePanel] = []
|
59
68
|
self._superimposed_panels: list[BasePanel] = []
|
@@ -116,14 +125,15 @@ class PanelStack:
|
|
116
125
|
def add_panel(
|
117
126
|
self,
|
118
127
|
panel: BasePanel,
|
119
|
-
identifier: Optional[str] = None
|
128
|
+
identifier: Optional[str] = None,
|
129
|
+
panel_format: Optional[PanelFormat] = None
|
120
130
|
) -> None:
|
121
131
|
"""Add a panel to the stack.
|
122
132
|
|
123
133
|
:param panel: An instance of a `BasePanel` subclass to be added to the stack.
|
124
134
|
:param identifier: An optional string to link the panel with others for superimposing.
|
125
135
|
"""
|
126
|
-
panel.panel_format = self._panel_format
|
136
|
+
panel.panel_format = panel_format or self._panel_format
|
127
137
|
panel.time_type = self._time_type
|
128
138
|
if identifier:
|
129
139
|
panel.identifier = identifier
|
@@ -133,7 +143,8 @@ class PanelStack:
|
|
133
143
|
def superimpose_panel(
|
134
144
|
self,
|
135
145
|
panel: BasePanel,
|
136
|
-
identifier: Optional[str] = None
|
146
|
+
identifier: Optional[str] = None,
|
147
|
+
panel_format: Optional[PanelFormat] = None
|
137
148
|
) -> None:
|
138
149
|
"""Superimpose a panel onto an existing panel in the stack.
|
139
150
|
|
@@ -142,7 +153,8 @@ class PanelStack:
|
|
142
153
|
"""
|
143
154
|
if identifier:
|
144
155
|
panel.identifier = identifier
|
145
|
-
panel.panel_format = self._panel_format
|
156
|
+
panel.panel_format = panel_format or self._panel_format
|
157
|
+
panel.time_type = self._time_type
|
146
158
|
self._superimposed_panels.append(panel)
|
147
159
|
|
148
160
|
|
@@ -234,11 +246,13 @@ class PanelStack:
|
|
234
246
|
if _is_cuts_panel(super_panel):
|
235
247
|
self._overlay_cuts(super_panel)
|
236
248
|
|
237
|
-
|
238
|
-
def show(
|
249
|
+
def _make_figure(
|
239
250
|
self
|
240
251
|
) -> None:
|
241
|
-
"""
|
252
|
+
"""Make the panel stack figure."""
|
253
|
+
if self.num_panels < 1:
|
254
|
+
raise ValueError(f"There must be at least one panel in the stack.")
|
255
|
+
|
242
256
|
self._init_plot_style()
|
243
257
|
self._create_figure_and_axes()
|
244
258
|
self._assign_axes()
|
@@ -258,4 +272,34 @@ class PanelStack:
|
|
258
272
|
self._overlay_cuts(panel)
|
259
273
|
|
260
274
|
self._overlay_superimposed_panels()
|
275
|
+
|
276
|
+
def show(
|
277
|
+
self
|
278
|
+
) -> None:
|
279
|
+
"""Display the panel stack figure."""
|
280
|
+
self._make_figure()
|
261
281
|
plt.show()
|
282
|
+
|
283
|
+
|
284
|
+
def save(
|
285
|
+
self,
|
286
|
+
) -> str:
|
287
|
+
"""Save the panel stack figure as a batch file under the tag of the first
|
288
|
+
panel in the stack. The file format is `png`.
|
289
|
+
|
290
|
+
:return: The file path of the newly created batch file containing the figure.
|
291
|
+
"""
|
292
|
+
self._make_figure()
|
293
|
+
first_panel = self._panels[0]
|
294
|
+
|
295
|
+
start_dt = cast(datetime, first_panel.spectrogram.start_datetime.astype(datetime))
|
296
|
+
batch_name = f"{start_dt.strftime(TimeFormat.DATETIME)}_{first_panel.spectrogram.tag}"
|
297
|
+
batch_file_path = os.path.join(
|
298
|
+
get_batches_dir_path(start_dt.year, start_dt.month, start_dt.day),
|
299
|
+
f"{batch_name}.png"
|
300
|
+
)
|
301
|
+
plt.savefig(batch_file_path)
|
302
|
+
return batch_file_path
|
303
|
+
|
304
|
+
|
305
|
+
|
@@ -58,30 +58,30 @@ def frequency_chop(
|
|
58
58
|
|
59
59
|
def time_chop(
|
60
60
|
spectrogram: Spectrogram,
|
61
|
-
|
62
|
-
|
61
|
+
start_datetime: datetime,
|
62
|
+
end_datetime: datetime
|
63
63
|
) -> Spectrogram:
|
64
64
|
"""
|
65
65
|
Extracts a portion of the spectrogram within the specified time range.
|
66
66
|
|
67
67
|
:param spectrogram: The input spectrogram to process.
|
68
|
-
:param
|
69
|
-
:param
|
68
|
+
:param start_datetime: The starting time of the desired range.
|
69
|
+
:param end_datetime: The ending time of the desired range.
|
70
70
|
:raises ValueError: If the specified time range is entirely outside the spectrogram's time range.
|
71
71
|
:raises ValueError: If the start and end indices for the time range are identical.
|
72
72
|
:return: A new spectrogram containing only the specified time range.
|
73
73
|
"""
|
74
|
-
|
75
|
-
|
74
|
+
start_datetime64 = np.datetime64(start_datetime)
|
75
|
+
end_datetime64 = np.datetime64(end_datetime)
|
76
76
|
|
77
|
-
is_entirely_below_time_range = (
|
78
|
-
is_entirely_above_time_range = (
|
77
|
+
is_entirely_below_time_range = (start_datetime64 < spectrogram.datetimes[0] and end_datetime64 < spectrogram.datetimes[0])
|
78
|
+
is_entirely_above_time_range = (start_datetime64 > spectrogram.datetimes[-1] and end_datetime64 > spectrogram.datetimes[-1])
|
79
79
|
if is_entirely_below_time_range or is_entirely_above_time_range:
|
80
80
|
raise ValueError(f"Requested time interval is entirely out of range of the input spectrogram.")
|
81
81
|
|
82
82
|
# find the index of the nearest matching spectrums in the spectrogram.
|
83
|
-
start_index = find_closest_index(
|
84
|
-
end_index = find_closest_index(
|
83
|
+
start_index = find_closest_index(start_datetime64, spectrogram.datetimes)
|
84
|
+
end_index = find_closest_index(end_datetime64, spectrogram.datetimes)
|
85
85
|
|
86
86
|
# enforce distinct start and end indices
|
87
87
|
if start_index == end_index:
|
@@ -6,10 +6,11 @@ import os
|
|
6
6
|
import subprocess
|
7
7
|
import shutil
|
8
8
|
import gzip
|
9
|
-
from datetime import datetime
|
9
|
+
from datetime import datetime, date
|
10
|
+
from typing import Tuple
|
10
11
|
|
11
12
|
from spectre_core.config import (
|
12
|
-
get_spectre_data_dir_path, get_batches_dir_path, TimeFormat
|
13
|
+
get_spectre_data_dir_path, get_batches_dir_path, TimeFormat
|
13
14
|
)
|
14
15
|
|
15
16
|
from enum import Enum
|
@@ -136,12 +137,12 @@ def _unzip_file_to_batches(
|
|
136
137
|
Decompress a `.fit.gz` file and save it as a `.fits` batch file.
|
137
138
|
|
138
139
|
:param gz_path: Path to the `.fit.gz` file.
|
139
|
-
:return: The file path of the newly created batch file,
|
140
|
+
:return: The file path of the newly created batch file, as absolute paths within the container's file system.
|
140
141
|
"""
|
141
142
|
fits_path = _get_batch_path(gz_path)
|
142
143
|
with gzip.open(gz_path, "rb") as f_in, open(fits_path, "wb") as f_out:
|
143
144
|
shutil.copyfileobj(f_in, f_out)
|
144
|
-
return
|
145
|
+
return f_out.name
|
145
146
|
|
146
147
|
|
147
148
|
def _unzip_to_batches(
|
@@ -152,7 +153,7 @@ def _unzip_to_batches(
|
|
152
153
|
batch files.
|
153
154
|
|
154
155
|
:param tmp_dir: Path to the temporary directory containing `.gz` files.
|
155
|
-
:return: A list of file names of all newly created batch files,
|
156
|
+
:return: A list of file names of all newly created batch files, as absolute paths within the container's file system.
|
156
157
|
"""
|
157
158
|
batch_file_names = []
|
158
159
|
for entry in os.scandir(tmp_dir):
|
@@ -195,7 +196,7 @@ def download_callisto_data(
|
|
195
196
|
year: int,
|
196
197
|
month: int,
|
197
198
|
day: int
|
198
|
-
) -> list[str]:
|
199
|
+
) -> Tuple[list[str], date]:
|
199
200
|
"""
|
200
201
|
Download and decompress e-Callisto FITS files, saving them as `spectre` batch files.
|
201
202
|
|
@@ -203,7 +204,7 @@ def download_callisto_data(
|
|
203
204
|
:param year: Year of the observation.
|
204
205
|
:param month: Month of the observation.
|
205
206
|
:param day: Day of the observation.
|
206
|
-
:return: A list of file names of all newly created batch files,
|
207
|
+
:return: A list of file names of all newly created batch files, as absolute paths within the container's file system. Additionally, return the start date shared by all batch files.
|
207
208
|
"""
|
208
209
|
tmp_dir = os.path.join(get_spectre_data_dir_path(), "tmp")
|
209
210
|
# if there are any residual files in the temporary directory, remove them.
|
@@ -212,4 +213,4 @@ def download_callisto_data(
|
|
212
213
|
os.makedirs(tmp_dir, exist_ok=True)
|
213
214
|
|
214
215
|
_wget_callisto_data(instrument_code.value, year, month, day, tmp_dir)
|
215
|
-
return sorted( _unzip_to_batches(tmp_dir) )
|
216
|
+
return sorted( _unzip_to_batches(tmp_dir) ), date(year, month, day)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: spectre-core
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.21
|
4
4
|
Summary: The core Python package used by the spectre program.
|
5
5
|
Maintainer-email: Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
@@ -691,7 +691,7 @@ Requires-Dist: scipy==1.12.0
|
|
691
691
|
Requires-Dist: astropy==6.0.1
|
692
692
|
Requires-Dist: matplotlib==3.5.0
|
693
693
|
Requires-Dist: watchdog==4.0.0
|
694
|
-
|
694
|
+
Dynamic: license-file
|
695
695
|
|
696
696
|
# spectre-core
|
697
697
|
|
@@ -3,16 +3,16 @@ spectre_core/exceptions.py,sha256=ccr-n88W0c_DKcsKMAe09QHX1iG2LUEqOMYHcGB-_do,45
|
|
3
3
|
spectre_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
spectre_core/_file_io/__init__.py,sha256=RZAtX8pFtdtTlxoxxttxJf1Wcg8lDFXgKMe1rcgaFP8,351
|
5
5
|
spectre_core/_file_io/file_handlers.py,sha256=dJ3rMcTGbNnEXbYZgVoaos4p6qKGs-rWNeY8nmluQCQ,6402
|
6
|
-
spectre_core/batches/__init__.py,sha256=
|
7
|
-
spectre_core/batches/_base.py,sha256=
|
8
|
-
spectre_core/batches/_batches.py,sha256=
|
6
|
+
spectre_core/batches/__init__.py,sha256=AIE3S3D3z__Ox557W95EILiv-BYoxjyRODMo2vu7My0,743
|
7
|
+
spectre_core/batches/_base.py,sha256=0-eYk7AINGMMUyecAqBEQ97tWi765MUHysGi2gvdVTI,9079
|
8
|
+
spectre_core/batches/_batches.py,sha256=2tgP2Mfwlay2bkblruS2mAfgfmdP3Doq841tKb8rq1U,7670
|
9
9
|
spectre_core/batches/_factory.py,sha256=ApshWhbhZ-CSoQBrlcYaC5W5p_w_v4pxOdOdlrF7YIA,2277
|
10
10
|
spectre_core/batches/_register.py,sha256=dSQC8KXj_jG8EiPwmKPdV0HSSalIZLaWt-8E29sczJM,1085
|
11
11
|
spectre_core/batches/plugins/_batch_keys.py,sha256=8v0KE1n0NAQX0i9SwwB4Lgkct7Q_jna-H5S0Gs6p1qg,544
|
12
12
|
spectre_core/batches/plugins/_callisto.py,sha256=ijm-VzGGlLQJjB2eQWw-04R6belCJ_NzSL9Jr7VTu2Q,6259
|
13
|
-
spectre_core/batches/plugins/_iq_stream.py,sha256=
|
13
|
+
spectre_core/batches/plugins/_iq_stream.py,sha256=YnNMrsD-K_otgN_4cR1pPJaddXpEaP4KkVVv0zIDs8w,12552
|
14
14
|
spectre_core/capture_configs/__init__.py,sha256=bhwp1Kf2llzd8HpkMPxeM_ZJoIoCFLQoMZJ44AAH6s0,1738
|
15
|
-
spectre_core/capture_configs/_capture_config.py,sha256=
|
15
|
+
spectre_core/capture_configs/_capture_config.py,sha256=VdiHvv5vdFKaB15EwNYyizFmtkoJHrzTiumDvHLI9HM,4430
|
16
16
|
spectre_core/capture_configs/_capture_modes.py,sha256=uFBMwHYJCDqqQfYJvAUKzKXTmSBANRZMLlRSKV8WRb8,898
|
17
17
|
spectre_core/capture_configs/_capture_templates.py,sha256=nSAxhOh1DeBzYY16evgOTkDuxRugGG5weJsICUouLPQ,10353
|
18
18
|
spectre_core/capture_configs/_parameters.py,sha256=9KoNuwzDKtnyeju53fkImi1SeUjDn89cNwDL8zxX-YU,5563
|
@@ -26,16 +26,16 @@ spectre_core/config/_time_formats.py,sha256=gS0j5zIvBhnV7KMYvTloloIbVwmCYn8MMKn3
|
|
26
26
|
spectre_core/jobs/__init__.py,sha256=WKTvxvpciedm6tsKjU02iXJhIdNsMDt-BnMVwVme2Bo,412
|
27
27
|
spectre_core/jobs/_jobs.py,sha256=gGpxsLZZ7EdXBYGH-r_pKnRGWSapr78E5SK_VnulaGg,3844
|
28
28
|
spectre_core/jobs/_workers.py,sha256=9GPEJVqIFuOAXpY9gfJxX-0_UPJ6RBtUe5qC5JLiYNw,5023
|
29
|
-
spectre_core/logs/__init__.py,sha256=
|
29
|
+
spectre_core/logs/__init__.py,sha256=cUIjKXzxT9pzITVOsv5zkX97WnMZNHEUt9_orKuaHMw,539
|
30
30
|
spectre_core/logs/_configure.py,sha256=NlR3BEwhj5k0qlTooOLb6OFRbYcDT05eWN-9Sl9Hkbk,2208
|
31
31
|
spectre_core/logs/_decorators.py,sha256=u_Ixpr6aeKiSjM0mtqHuGXVGawNjgJPRvIpAj0zPSdQ,1169
|
32
|
-
spectre_core/logs/_logs.py,sha256=
|
32
|
+
spectre_core/logs/_logs.py,sha256=ZNYtJN_A_StRfJJFkv5rTmQ2z2427YIxXGkd_6NQY1Y,6900
|
33
33
|
spectre_core/logs/_process_types.py,sha256=qnKfeXPk-6NOlNf4XtD8D3tYbA_ZVhHSZAKSd2MiVmY,488
|
34
34
|
spectre_core/plotting/__init__.py,sha256=9zhmCphR4DSHlWfJhHeWPbvIrh8Kycov5Ta6woSrUZg,517
|
35
|
-
spectre_core/plotting/_base.py,sha256=
|
35
|
+
spectre_core/plotting/_base.py,sha256=yRZ6pf_d1mm35yn4AU8Hu2X50rCeB0KX1yEygDbpYo4,8547
|
36
36
|
spectre_core/plotting/_format.py,sha256=gWnj12PfgODQRTCCEyH2_mY7GY0brFbmolBEJfDtO6c,1393
|
37
37
|
spectre_core/plotting/_panel_names.py,sha256=xeXUXAhzDoPYCgtjy_6zbwADHBtB0-jARnFOW-YTcAc,830
|
38
|
-
spectre_core/plotting/_panel_stack.py,sha256=
|
38
|
+
spectre_core/plotting/_panel_stack.py,sha256=M2gKJf9Uxr6nf2KldGfVa5TDa1n-wEk-ZS5svE3D7Qo,10263
|
39
39
|
spectre_core/plotting/_panels.py,sha256=IaJ8EDLu5pvsN6kklNWc1ectCfgUsxwN0EKwT2wiFNo,16030
|
40
40
|
spectre_core/post_processing/__init__.py,sha256=PVr7AhKK5at0o3BzNlJeC_Z_J8ZNATjGVY7PYg7fSew,624
|
41
41
|
spectre_core/post_processing/_base.py,sha256=gtycVjtAStN5sW4KuzxVQngLzYCx3k-9B5IfmMZnu_g,5815
|
@@ -68,11 +68,11 @@ spectre_core/spectrograms/__init__.py,sha256=AsiOmn9XrAAHUvK-fdhRddAxX4M1Wd6TCtd
|
|
68
68
|
spectre_core/spectrograms/_analytical.py,sha256=Axnt9JOJnWXRRuVU5nHPz5QU09KoWqNZkR5NnTX6kMY,11356
|
69
69
|
spectre_core/spectrograms/_array_operations.py,sha256=79vddwWqR5i6OkeD5L_84t8svslpmzW4b8uxbiCQl0I,7553
|
70
70
|
spectre_core/spectrograms/_spectrogram.py,sha256=WhHEt_QpmzspDqYlzdZcJ8CAXxRfs8-JfP0T3NHpjLQ,28205
|
71
|
-
spectre_core/spectrograms/_transform.py,sha256=
|
71
|
+
spectre_core/spectrograms/_transform.py,sha256=_Kw9XfGNuhBqHfwPxNL4d-KIZJgzEX449ew1e17_k00,11698
|
72
72
|
spectre_core/wgetting/__init__.py,sha256=UkS0Z0wuuqpoZ1EL35wJcDpjBiAaZgdZ7064yGESxNE,341
|
73
|
-
spectre_core/wgetting/_callisto.py,sha256=
|
74
|
-
spectre_core-0.0.
|
75
|
-
spectre_core-0.0.
|
76
|
-
spectre_core-0.0.
|
77
|
-
spectre_core-0.0.
|
78
|
-
spectre_core-0.0.
|
73
|
+
spectre_core/wgetting/_callisto.py,sha256=jMvv75d6152KmasZlydc4nXxuX0nAl4snSXzXOVwVGU,7597
|
74
|
+
spectre_core-0.0.21.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
75
|
+
spectre_core-0.0.21.dist-info/METADATA,sha256=HcteVzzTLeZCUSUnCfqHNud70AutsvI6tgztzBPokzE,42094
|
76
|
+
spectre_core-0.0.21.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
77
|
+
spectre_core-0.0.21.dist-info/top_level.txt,sha256=-UsyjpFohXgZpgcZ9QbVeXhsIyF3Am8RxNFNDV_Ta2Y,13
|
78
|
+
spectre_core-0.0.21.dist-info/RECORD,,
|
File without changes
|
File without changes
|