spectre-core 0.0.8__py3-none-any.whl → 0.0.10__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 (109) hide show
  1. spectre_core/__init__.py +0 -3
  2. spectre_core/_file_io/__init__.py +15 -0
  3. spectre_core/_file_io/file_handlers.py +128 -0
  4. spectre_core/capture_configs/__init__.py +29 -0
  5. spectre_core/capture_configs/_capture_config.py +85 -0
  6. spectre_core/capture_configs/_capture_templates.py +222 -0
  7. spectre_core/capture_configs/_parameters.py +110 -0
  8. spectre_core/capture_configs/_pconstraints.py +82 -0
  9. spectre_core/capture_configs/_ptemplates.py +450 -0
  10. spectre_core/capture_configs/_pvalidators.py +173 -0
  11. spectre_core/chunks/__init__.py +17 -201
  12. spectre_core/chunks/{base.py → _base.py} +15 -60
  13. spectre_core/chunks/_chunks.py +200 -0
  14. spectre_core/chunks/{factory.py → _factory.py} +6 -7
  15. spectre_core/chunks/library/{callisto/chunk.py → _callisto.py} +4 -7
  16. spectre_core/chunks/library/{fixed/chunk.py → _fixed_center_frequency.py} +7 -64
  17. spectre_core/chunks/library/_swept_center_frequency.py +103 -0
  18. spectre_core/config/__init__.py +20 -0
  19. spectre_core/config/_paths.py +77 -0
  20. spectre_core/config/_time_formats.py +15 -0
  21. spectre_core/exceptions.py +4 -5
  22. spectre_core/logging/__init__.py +11 -0
  23. spectre_core/logging/_configure.py +35 -0
  24. spectre_core/logging/_decorators.py +19 -0
  25. spectre_core/{logging.py → logging/_log_handlers.py} +13 -58
  26. spectre_core/plotting/__init__.py +7 -1
  27. spectre_core/plotting/{base.py → _base.py} +40 -20
  28. spectre_core/plotting/_format.py +18 -0
  29. spectre_core/plotting/{panel_stack.py → _panel_stack.py} +50 -48
  30. spectre_core/plotting/_panels.py +234 -0
  31. spectre_core/post_processing/__init__.py +14 -0
  32. spectre_core/post_processing/_base.py +119 -0
  33. spectre_core/post_processing/_factory.py +23 -0
  34. spectre_core/post_processing/_post_processor.py +40 -0
  35. spectre_core/post_processing/library/_fixed_center_frequency.py +115 -0
  36. spectre_core/post_processing/library/_swept_center_frequency.py +382 -0
  37. spectre_core/receivers/__init__.py +12 -2
  38. spectre_core/receivers/_base.py +352 -0
  39. spectre_core/receivers/{factory.py → _factory.py} +2 -2
  40. spectre_core/receivers/_spec_names.py +20 -0
  41. spectre_core/receivers/gr/__init__.py +3 -0
  42. spectre_core/receivers/gr/_base.py +33 -0
  43. spectre_core/receivers/gr/_rsp1a.py +158 -0
  44. spectre_core/receivers/gr/_test.py +123 -0
  45. spectre_core/receivers/library/_rsp1a.py +61 -0
  46. spectre_core/receivers/library/_test.py +221 -0
  47. spectre_core/spectrograms/__init__.py +18 -0
  48. spectre_core/spectrograms/{analytical.py → _analytical.py} +29 -27
  49. spectre_core/spectrograms/{array_operations.py → _array_operations.py} +47 -1
  50. spectre_core/spectrograms/{spectrogram.py → _spectrogram.py} +62 -35
  51. spectre_core/spectrograms/{transform.py → _transform.py} +76 -89
  52. spectre_core/{receivers/library → wgetting}/__init__.py +4 -2
  53. spectre_core/wgetting/_callisto.py +155 -0
  54. {spectre_core-0.0.8.dist-info → spectre_core-0.0.10.dist-info}/METADATA +1 -1
  55. spectre_core-0.0.10.dist-info/RECORD +63 -0
  56. spectre_core/cfg.py +0 -116
  57. spectre_core/chunks/library/__init__.py +0 -8
  58. spectre_core/chunks/library/sweep/__init__.py +0 -0
  59. spectre_core/chunks/library/sweep/chunk.py +0 -400
  60. spectre_core/dynamic_imports.py +0 -22
  61. spectre_core/file_handlers/base.py +0 -68
  62. spectre_core/file_handlers/configs.py +0 -271
  63. spectre_core/file_handlers/json.py +0 -40
  64. spectre_core/file_handlers/text.py +0 -21
  65. spectre_core/plotting/factory.py +0 -26
  66. spectre_core/plotting/format.py +0 -19
  67. spectre_core/plotting/library/__init__.py +0 -7
  68. spectre_core/plotting/library/frequency_cuts/panel.py +0 -74
  69. spectre_core/plotting/library/integral_over_frequency/panel.py +0 -34
  70. spectre_core/plotting/library/spectrogram/panel.py +0 -92
  71. spectre_core/plotting/library/time_cuts/panel.py +0 -77
  72. spectre_core/plotting/panel_register.py +0 -13
  73. spectre_core/receivers/base.py +0 -415
  74. spectre_core/receivers/library/rsp1a/__init__.py +0 -0
  75. spectre_core/receivers/library/rsp1a/gr/__init__.py +0 -0
  76. spectre_core/receivers/library/rsp1a/gr/fixed.py +0 -104
  77. spectre_core/receivers/library/rsp1a/gr/sweep.py +0 -129
  78. spectre_core/receivers/library/rsp1a/receiver.py +0 -68
  79. spectre_core/receivers/library/rspduo/__init__.py +0 -0
  80. spectre_core/receivers/library/rspduo/gr/__init__.py +0 -0
  81. spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +0 -114
  82. spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +0 -131
  83. spectre_core/receivers/library/rspduo/gr/tuner_2_fixed.py +0 -120
  84. spectre_core/receivers/library/rspduo/gr/tuner_2_sweep.py +0 -119
  85. spectre_core/receivers/library/rspduo/receiver.py +0 -97
  86. spectre_core/receivers/library/test/__init__.py +0 -0
  87. spectre_core/receivers/library/test/gr/__init__.py +0 -0
  88. spectre_core/receivers/library/test/gr/cosine_signal_1.py +0 -83
  89. spectre_core/receivers/library/test/gr/tagged_staircase.py +0 -93
  90. spectre_core/receivers/library/test/receiver.py +0 -178
  91. spectre_core/receivers/validators.py +0 -193
  92. spectre_core/watchdog/__init__.py +0 -6
  93. spectre_core/watchdog/base.py +0 -105
  94. spectre_core/watchdog/factory.py +0 -22
  95. spectre_core/watchdog/library/__init__.py +0 -10
  96. spectre_core/watchdog/library/fixed/__init__.py +0 -0
  97. spectre_core/watchdog/library/fixed/event_handler.py +0 -41
  98. spectre_core/watchdog/library/sweep/event_handler.py +0 -55
  99. spectre_core/watchdog/post_processor.py +0 -50
  100. spectre_core/web_fetch/callisto.py +0 -101
  101. spectre_core-0.0.8.dist-info/RECORD +0 -74
  102. /spectre_core/chunks/{chunk_register.py → _register.py} +0 -0
  103. /spectre_core/{watchdog/event_handler_register.py → post_processing/_register.py} +0 -0
  104. /spectre_core/receivers/{receiver_register.py → _register.py} +0 -0
  105. /spectre_core/{chunks/library/callisto/__init__.py → receivers/gr/_rspduo.py} +0 -0
  106. /spectre_core/{chunks/library/fixed/__init__.py → receivers/library/_rspduo.py} +0 -0
  107. {spectre_core-0.0.8.dist-info → spectre_core-0.0.10.dist-info}/LICENSE +0 -0
  108. {spectre_core-0.0.8.dist-info → spectre_core-0.0.10.dist-info}/WHEEL +0 -0
  109. {spectre_core-0.0.8.dist-info → spectre_core-0.0.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,103 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from typing import Tuple
6
+ from dataclasses import dataclass
7
+
8
+ import numpy as np
9
+
10
+ from spectre_core.exceptions import InvalidSweepMetadataError
11
+ from spectre_core.capture_configs import CaptureModes
12
+ from ._fixed_center_frequency import BinChunk, FitsChunk
13
+ from .._register import register_chunk
14
+ from .._base import BaseChunk, ChunkFile
15
+
16
+
17
+ @dataclass
18
+ class SweepMetadata:
19
+ """Wrapper for metadata required to assign center frequencies to each IQ sample in the chunk.
20
+
21
+ center_frequencies is an ordered list containing all the center frequencies that the IQ samples
22
+ were collected at. Typically, these will be ordered in "steps", where each step corresponds to
23
+ IQ samples collected at a constant center frequency:
24
+
25
+ (freq_0, freq_1, ..., freq_M, freq_0, freq_1, ..., freq_M, ...), freq_0 < freq_1 < ... < freq_M
26
+
27
+ The n'th element of the num_samples list, tells us how many samples were collected at the n'th
28
+ element of center_frequencies:
29
+ chunks.library.fixed_center_frequency.chunk import (
30
+ BinChunk, FitsChunk
31
+ )
32
+ Number of samples: (num_samples_at_freq_0, num_samples_at_freq_1, ...)
33
+
34
+ Both these lists together allow us to map for each IQ sample, the center frequency it was collected at.
35
+ """
36
+ center_frequencies: np.ndarray
37
+ num_samples: np.ndarray
38
+
39
+
40
+ @register_chunk(CaptureModes.SWEPT_CENTER_FREQUENCY)
41
+ class _Chunk(BaseChunk):
42
+ def __init__(self, chunk_start_time, tag):
43
+ super().__init__(chunk_start_time, tag)
44
+
45
+ self.add_file(BinChunk(self.chunk_parent_path, self.chunk_name))
46
+ self.add_file(FitsChunk(self.chunk_parent_path, self.chunk_name))
47
+ self.add_file(HdrChunk(self.chunk_parent_path, self.chunk_name))
48
+
49
+
50
+ class HdrChunk(ChunkFile):
51
+ def __init__(self, chunk_parent_path: str, chunk_name: str):
52
+ super().__init__(chunk_parent_path, chunk_name, "hdr")
53
+
54
+ def read(self) -> Tuple[int, SweepMetadata]:
55
+ hdr_contents = self._read_file_contents()
56
+ millisecond_correction = self._get_millisecond_correction(hdr_contents)
57
+ center_frequencies = self._get_center_frequencies(hdr_contents)
58
+ num_samples = self._get_num_samples(hdr_contents)
59
+ self._validate_frequencies_and_samples(center_frequencies,
60
+ num_samples)
61
+ return millisecond_correction, SweepMetadata(center_frequencies, num_samples)
62
+
63
+
64
+ def _read_file_contents(self) -> np.ndarray:
65
+ with open(self.file_path, "rb") as fh:
66
+ return np.fromfile(fh, dtype=np.float32)
67
+
68
+
69
+ def _get_millisecond_correction(self, hdr_contents: np.ndarray) -> int:
70
+ ''' Millisecond correction is an integral quantity, but stored in the detached header as a 32-bit float.'''
71
+ millisecond_correction_as_float = float(hdr_contents[0])
72
+
73
+ if not millisecond_correction_as_float.is_integer():
74
+ raise TypeError(f"Expected integer value for millisecond correction, but got {millisecond_correction_as_float}")
75
+
76
+ return int(millisecond_correction_as_float)
77
+
78
+
79
+ def _get_center_frequencies(self, hdr_contents: np.ndarray) -> np.ndarray:
80
+ '''
81
+ Detached header contents are stored in (center_freq_i, num_samples_at_center_freq_i) pairs
82
+ Return only a list of center frequencies, by skipping over file contents in twos.
83
+ '''
84
+ return hdr_contents[1::2]
85
+
86
+
87
+ def _get_num_samples(self, hdr_contents: np.ndarray) -> np.ndarray:
88
+ '''
89
+ Detached header contents are stored in (center_freq_i, num_samples_at_center_freq_i) pairs
90
+ Return only the number of samples at each center frequency, by skipping over file contents in twos.
91
+ Number of samples is an integral quantity, but stored in the detached header as a 32-bit float.
92
+ Types are checked before return.
93
+ '''
94
+ num_samples_as_float = hdr_contents[2::2]
95
+ if not all(num_samples_as_float == num_samples_as_float.astype(int)):
96
+ raise InvalidSweepMetadataError("Number of samples per frequency is expected to describe an integer")
97
+ return num_samples_as_float.astype(int)
98
+
99
+
100
+ def _validate_frequencies_and_samples(self, center_frequencies: np.ndarray, num_samples: np.ndarray) -> None:
101
+ """Validates that the center frequencies and the number of samples arrays have the same length."""
102
+ if len(center_frequencies) != len(num_samples):
103
+ raise InvalidSweepMetadataError("Center frequencies and number of samples arrays are not the same length")
@@ -0,0 +1,20 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from ._paths import (
6
+ get_spectre_data_dir_path, get_chunks_dir_path, get_configs_dir_path, get_logs_dir_path
7
+ )
8
+ from ._time_formats import (
9
+ TimeFormats
10
+ )
11
+
12
+ __all__ = [
13
+ "get_spectre_data_dir_path",
14
+ "get_chunks_dir_path",
15
+ "get_configs_dir_path",
16
+ "get_logs_dir_path",
17
+ "DEFAULT_DATE_FORMAT",
18
+ "DEFAULT_TIME_FORMAT",
19
+ "DEFAULT_DATETIME_FORMAT"
20
+ ]
@@ -0,0 +1,77 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ """
6
+ SPECTRE data paths.
7
+ """
8
+
9
+ import os
10
+
11
+ _SPECTRE_DATA_DIR_PATH = os.environ.get("SPECTRE_DATA_DIR_PATH")
12
+ if _SPECTRE_DATA_DIR_PATH is None:
13
+ raise ValueError("The environment variable SPECTRE_DATA_DIR_PATH has not been set")
14
+
15
+ _CHUNKS_DIR_PATH = os.environ.get("SPECTRE_CHUNKS_DIR_PATH",
16
+ os.path.join(_SPECTRE_DATA_DIR_PATH, 'chunks'))
17
+ os.makedirs(_CHUNKS_DIR_PATH,
18
+ exist_ok=True)
19
+
20
+ _LOGS_DIR_PATH = os.environ.get("SPECTRE_LOGS_DIR_PATH",
21
+ os.path.join(_SPECTRE_DATA_DIR_PATH, 'logs'))
22
+ os.makedirs(_LOGS_DIR_PATH,
23
+ exist_ok=True)
24
+
25
+ _CONFIGS_DIR_PATH = os.environ.get("SPECTRE_CONFIGS_DIR_PATH",
26
+ os.path.join(_SPECTRE_DATA_DIR_PATH, "configs"))
27
+ os.makedirs(_CONFIGS_DIR_PATH,
28
+ exist_ok=True)
29
+
30
+
31
+ def get_spectre_data_dir_path(
32
+ ) -> str:
33
+ return _SPECTRE_DATA_DIR_PATH
34
+
35
+
36
+ def _get_date_based_dir_path(base_dir: str, year: int = None,
37
+ month: int = None, day: int = None
38
+ ) -> str:
39
+ if day and not (year and month):
40
+ raise ValueError("A day requires both a month and a year")
41
+ if month and not year:
42
+ raise ValueError("A month requires a year")
43
+
44
+ date_dir_components = []
45
+ if year:
46
+ date_dir_components.append(f"{year:04}")
47
+ if month:
48
+ date_dir_components.append(f"{month:02}")
49
+ if day:
50
+ date_dir_components.append(f"{day:02}")
51
+
52
+ return os.path.join(base_dir, *date_dir_components)
53
+
54
+
55
+ def get_chunks_dir_path(year: int = None,
56
+ month: int = None,
57
+ day: int = None
58
+ ) -> str:
59
+ return _get_date_based_dir_path(_CHUNKS_DIR_PATH,
60
+ year,
61
+ month,
62
+ day)
63
+
64
+
65
+ def get_logs_dir_path(year: int = None,
66
+ month: int = None,
67
+ day: int = None
68
+ ) -> str:
69
+ return _get_date_based_dir_path(_LOGS_DIR_PATH,
70
+ year,
71
+ month,
72
+ day)
73
+
74
+
75
+ def get_configs_dir_path(
76
+ ) -> str:
77
+ return _CONFIGS_DIR_PATH
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ """
6
+ Package-wide default datetime formats.
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+
11
+ @dataclass(frozen=True)
12
+ class TimeFormats:
13
+ TIME = "%H:%M:%S"
14
+ DATE = "%Y-%m-%d"
15
+ DATETIME = f"{DATE}T{TIME}"
@@ -2,16 +2,15 @@
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
+ """
6
+ SPECTRE custom exceptions.
7
+ """
8
+
5
9
  class ChunkNotFoundError(FileNotFoundError): ...
6
10
  class ChunkFileNotFoundError(FileNotFoundError): ...
7
11
  class SpectrogramNotFoundError(FileNotFoundError): ...
8
-
9
12
  class ModeNotFoundError(KeyError): ...
10
13
  class EventHandlerNotFoundError(KeyError): ...
11
14
  class ReceiverNotFoundError(KeyError): ...
12
- class TemplateNotFoundError(KeyError): ...
13
- class SpecificationNotFoundError(KeyError): ...
14
- class PanelNotFoundError(KeyError): ...
15
-
16
15
  class InvalidTagError(ValueError): ...
17
16
  class InvalidSweepMetadataError(ValueError): ...
@@ -0,0 +1,11 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from ._decorators import log_call
6
+ from ._configure import configure_root_logger
7
+ from ._log_handlers import LogHandler, LogHandlers
8
+
9
+ __all__ = [
10
+ "log_call", "configure_root_logger", "LogHandler", "LogHandlers"
11
+ ]
@@ -0,0 +1,35 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import os
6
+ import logging
7
+ from datetime import datetime
8
+
9
+ from spectre_core.config import TimeFormats
10
+ from ._log_handlers import LogHandler
11
+
12
+ def configure_root_logger(process_type: str,
13
+ level: int = logging.INFO
14
+ ) -> LogHandler:
15
+ system_datetime = datetime.now()
16
+ datetime_stamp = system_datetime.strftime(TimeFormats.DATETIME)
17
+ pid = os.getpid()
18
+ log_handler = LogHandler(datetime_stamp, pid, process_type)
19
+ log_handler.make_parent_path()
20
+
21
+ # configure the root logger
22
+ logger = logging.getLogger()
23
+ logger.setLevel(level)
24
+ # Remove any existing handlers to avoid duplicate logs
25
+ for handler in logger.handlers:
26
+ logger.removeHandler(handler)
27
+ # Set up file handler with specific filename
28
+ file_handler = logging.FileHandler(log_handler.file_path)
29
+ file_handler.setLevel(level)
30
+ formatter = logging.Formatter("[%(asctime)s] [%(levelname)8s] --- %(message)s (%(name)s:%(lineno)s)")
31
+ file_handler.setFormatter(formatter)
32
+ # and add it to the root logger
33
+ logger.addHandler(file_handler)
34
+
35
+ return log_handler
@@ -0,0 +1,19 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import logging
6
+ from typing import Callable
7
+ from functools import wraps
8
+
9
+ def log_call(func: Callable) -> Callable:
10
+ @wraps(func)
11
+ def wrapper(*args, **kwargs):
12
+ logger = logging.getLogger(func.__module__)
13
+ try:
14
+ logger.info(f"Calling the function: {func.__name__}")
15
+ return func(*args, **kwargs)
16
+ except Exception as e:
17
+ logger.error(f"Error in function: {func.__name__}", exc_info=True)
18
+ raise
19
+ return wrapper
@@ -8,26 +8,21 @@ _LOGGER = getLogger(__name__)
8
8
 
9
9
  import os
10
10
  import logging
11
- from typing import Callable, Optional
11
+ from dataclasses import dataclass
12
+ from typing import Optional
12
13
  import warnings
13
14
  from collections import OrderedDict
14
15
  from datetime import datetime
15
- from functools import wraps
16
16
 
17
- from spectre_core.file_handlers.text import TextHandler
18
- from spectre_core.cfg import (
19
- LOGS_DIR_PATH,
20
- DEFAULT_DATETIME_FORMAT,
21
- get_logs_dir_path
22
- )
17
+ from spectre_core._file_io import TextHandler
18
+ from spectre_core.config import get_logs_dir_path, TimeFormats
23
19
 
24
20
  PROCESS_TYPES = [
25
21
  "user",
26
22
  "worker"
27
23
  ]
28
24
 
29
-
30
- def validate_process_type(process_type: str
25
+ def _validate_process_type(process_type: str
31
26
  ) -> None:
32
27
  if process_type not in PROCESS_TYPES:
33
28
  raise ValueError(f"Invalid process type: {process_type}. Expected one of {PROCESS_TYPES}")
@@ -40,12 +35,11 @@ class LogHandler(TextHandler):
40
35
  process_type: str):
41
36
  self._datetime_stamp = datetime_stamp
42
37
  self._pid = pid
43
- validate_process_type(process_type)
38
+ _validate_process_type(process_type)
44
39
  self._process_type = process_type
45
40
 
46
- dt = datetime.strptime(datetime_stamp, DEFAULT_DATETIME_FORMAT)
47
- date_dir = os.path.join(dt.strftime("%Y"), dt.strftime("%m"), dt.strftime("%d"))
48
- parent_path = os.path.join(LOGS_DIR_PATH, date_dir)
41
+ dt = datetime.strptime(datetime_stamp, TimeFormats.DATETIME)
42
+ parent_path = get_logs_dir_path(dt.year, dt.month, dt.day)
49
43
  base_file_name = f"{datetime_stamp}_{pid}_{process_type}"
50
44
 
51
45
  super().__init__(parent_path, base_file_name, extension = "log")
@@ -164,8 +158,8 @@ class LogHandlers:
164
158
  yield from self.log_handler_list
165
159
 
166
160
 
167
- def get_log_handler_from_file_name(self,
168
- file_name: str) -> LogHandler:
161
+ def get_from_file_name(self,
162
+ file_name: str) -> LogHandler:
169
163
  # auto strip the extension if present
170
164
  file_name, _ = os.path.splitext(file_name)
171
165
  try:
@@ -174,48 +168,9 @@ class LogHandlers:
174
168
  raise FileNotFoundError(f"Log handler for file name '{file_name}' not found in log map")
175
169
 
176
170
 
177
- def get_log_handler_from_pid(self,
178
- pid: str) -> LogHandler:
171
+ def get_from_pid(self,
172
+ pid: str) -> LogHandler:
179
173
  for log_handler in self.log_handler_list:
180
174
  if log_handler.pid == pid:
181
175
  return log_handler
182
- raise FileNotFoundError(f"Log handler for PID '{pid}' not found in log map")
183
-
184
-
185
- def configure_root_logger(process_type: str,
186
- level: int = logging.INFO
187
- ) -> LogHandler:
188
- system_datetime = datetime.now()
189
- datetime_stamp = system_datetime.strftime(DEFAULT_DATETIME_FORMAT)
190
- pid = os.getpid()
191
- log_handler = LogHandler(datetime_stamp, pid, process_type)
192
- log_handler.make_parent_path()
193
-
194
- # configure the root logger
195
- logger = logging.getLogger()
196
- logger.setLevel(level)
197
- # Remove any existing handlers to avoid duplicate logs
198
- for handler in logger.handlers:
199
- logger.removeHandler(handler)
200
- # Set up file handler with specific filename
201
- file_handler = logging.FileHandler(log_handler.file_path)
202
- file_handler.setLevel(level)
203
- formatter = logging.Formatter("[%(asctime)s] [%(levelname)8s] --- %(message)s (%(name)s:%(lineno)s)")
204
- file_handler.setFormatter(formatter)
205
- # and add it to the root logger
206
- logger.addHandler(file_handler)
207
-
208
- return log_handler
209
-
210
-
211
- def log_call(func: Callable) -> Callable:
212
- @wraps(func)
213
- def wrapper(*args, **kwargs):
214
- logger = logging.getLogger(func.__module__) # Automatically get module-level logger
215
- try:
216
- logger.info(f"Calling the function: {func.__name__}")
217
- return func(*args, **kwargs)
218
- except Exception as e:
219
- logger.error(f"Error in function: {func.__name__}", exc_info=True)
220
- raise
221
- return wrapper
176
+ raise FileNotFoundError(f"Log handler for PID '{pid}' not found in log map")
@@ -2,4 +2,10 @@
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
- import spectre_core.plotting.library
5
+ from ._format import PanelFormat
6
+ from ._panels import Panels
7
+ from ._panel_stack import PanelStack
8
+
9
+ __all__ = [
10
+ "PanelFormat", "Panels", "PanelStack"
11
+ ]
@@ -5,34 +5,40 @@
5
5
  from abc import ABC, abstractmethod
6
6
  from typing import Optional
7
7
  import numpy as np
8
+ from dataclasses import dataclass
8
9
 
9
10
  from matplotlib import cm
10
11
  import matplotlib.dates as mdates
11
12
  from matplotlib.axes import Axes
12
13
  from matplotlib.figure import Figure
13
14
 
14
- from spectre_core.spectrograms.spectrogram import Spectrogram
15
+ from spectre_core.spectrograms import Spectrogram, TimeTypes
16
+ from ._format import PanelFormat
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class XAxisTypes:
21
+ TIME : str = "time"
22
+ FREQUENCY: str = "frequency"
15
23
 
16
- TIME_X_AXIS = "time"
17
- FREQUENCY_X_AXIS = "frequency"
18
24
 
19
25
  class BasePanel(ABC):
20
26
  def __init__(self,
21
27
  name: str,
22
- spectrogram: Spectrogram,
23
- time_type: str = "seconds"):
28
+ spectrogram: Spectrogram):
24
29
  self._name = name
25
30
  self._spectrogram = spectrogram
26
31
 
27
- self._validate_time_type(time_type)
28
- self._time_type = time_type
29
-
30
32
  self._x_axis_type: Optional[str] = None
31
33
  self._set_x_axis_type()
32
34
 
33
- self._ax: Optional[Axes] = None # defined while stacking
34
- self._fig: Optional[Figure] = None # defined while stacking
35
- self._identifier: Optional[str] = None # defined if specified by the user
35
+ # defined while stacking
36
+ self._panel_format: Optional[PanelFormat] = None
37
+ self._time_type: Optional[str] = None
38
+ self._ax: Optional[Axes] = None
39
+ self._fig: Optional[Figure] = None
40
+ # defined if specified by the user
41
+ self._identifier: Optional[str] = None
36
42
 
37
43
 
38
44
  @abstractmethod
@@ -71,6 +77,12 @@ class BasePanel(ABC):
71
77
  return self._time_type
72
78
 
73
79
 
80
+ @time_type.setter
81
+ def time_type(self, value: str) -> None:
82
+ self._validate_time_type(value)
83
+ self._time_type = value
84
+
85
+
74
86
  @property
75
87
  def name(self) -> str:
76
88
  if self._name is None:
@@ -83,6 +95,18 @@ class BasePanel(ABC):
83
95
  self._name = value
84
96
 
85
97
 
98
+ @property
99
+ def panel_format(self) -> PanelFormat:
100
+ if self._panel_format is None:
101
+ raise AttributeError(f"Panel format has not yet been specified for this panel")
102
+ return self._panel_format
103
+
104
+
105
+ @panel_format.setter
106
+ def panel_format(self, value: PanelFormat):
107
+ self._panel_format = value
108
+
109
+
86
110
  @property
87
111
  def ax(self) -> Axes:
88
112
  if self._ax is None:
@@ -126,7 +150,7 @@ class BasePanel(ABC):
126
150
 
127
151
  def _validate_time_type(self,
128
152
  time_type: str):
129
- valid_time_types = ["seconds", "datetimes"]
153
+ valid_time_types = [TimeTypes.SECONDS, TimeTypes.DATETIMES]
130
154
  if time_type not in valid_time_types:
131
155
  raise ValueError(f"Invalid time type. "
132
156
  f"Expected one of {valid_time_types} "
@@ -156,11 +180,11 @@ class BaseTimeSeriesPanel(BasePanel):
156
180
 
157
181
  @property
158
182
  def times(self):
159
- return self.spectrogram.times if self.time_type == "seconds" else self.spectrogram.datetimes
183
+ return self.spectrogram.times if self.time_type == TimeTypes.SECONDS else self.spectrogram.datetimes
160
184
 
161
185
 
162
186
  def annotate_x_axis(self):
163
- if self.time_type == "seconds":
187
+ if self.time_type == TimeTypes.SECONDS:
164
188
  self.ax.set_xlabel('Time [s]')
165
189
  else:
166
190
  self.ax.set_xlabel('Time [UTC]')
@@ -168,7 +192,7 @@ class BaseTimeSeriesPanel(BasePanel):
168
192
 
169
193
 
170
194
  def _set_x_axis_type(self):
171
- self._x_axis_type = TIME_X_AXIS
195
+ self._x_axis_type = XAxisTypes.TIME
172
196
 
173
197
 
174
198
 
@@ -187,8 +211,4 @@ class BaseSpectrumPanel(BasePanel):
187
211
 
188
212
 
189
213
  def _set_x_axis_type(self):
190
- self._x_axis_type = FREQUENCY_X_AXIS
191
-
192
-
193
- class CutsPanel(BasePanel):
194
- """Convenience parent class for cuts"""
214
+ self._x_axis_type = XAxisTypes.FREQUENCY
@@ -0,0 +1,18 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from dataclasses import dataclass
6
+
7
+ @dataclass
8
+ class PanelFormat:
9
+ small_size : int = 18
10
+ medium_size : int = 21
11
+ large_size : int = 24
12
+ line_width : int = 3
13
+ style : str = "dark_background"
14
+ spectrogram_cmap: str = "gnuplot2"
15
+ cuts_cmap : str = "winter"
16
+ integral_color : str = "lime"
17
+
18
+ DEFAULT_FORMAT = PanelFormat()