spectre-core 0.0.9__py3-none-any.whl → 0.0.11__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 +171 -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} +48 -48
  30. spectre_core/plotting/_panels.py +234 -0
  31. spectre_core/post_processing/__init__.py +10 -2
  32. spectre_core/post_processing/_base.py +119 -0
  33. spectre_core/post_processing/{factory.py → _factory.py} +7 -6
  34. spectre_core/post_processing/{post_processor.py → _post_processor.py} +3 -3
  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 +13 -2
  38. spectre_core/receivers/_base.py +180 -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/_rspduo.py +227 -0
  45. spectre_core/receivers/gr/_test.py +123 -0
  46. spectre_core/receivers/library/_rsp1a.py +61 -0
  47. spectre_core/receivers/library/_rspduo.py +69 -0
  48. spectre_core/receivers/library/_sdrplay_receiver.py +185 -0
  49. spectre_core/receivers/library/_test.py +221 -0
  50. spectre_core/spectrograms/__init__.py +18 -0
  51. spectre_core/spectrograms/{analytical.py → _analytical.py} +29 -27
  52. spectre_core/spectrograms/{array_operations.py → _array_operations.py} +47 -1
  53. spectre_core/spectrograms/{spectrogram.py → _spectrogram.py} +62 -35
  54. spectre_core/spectrograms/{transform.py → _transform.py} +76 -89
  55. spectre_core/{post_processing/library → wgetting}/__init__.py +4 -5
  56. spectre_core/wgetting/_callisto.py +155 -0
  57. {spectre_core-0.0.9.dist-info → spectre_core-0.0.11.dist-info}/METADATA +1 -1
  58. spectre_core-0.0.11.dist-info/RECORD +64 -0
  59. spectre_core/cfg.py +0 -116
  60. spectre_core/chunks/library/__init__.py +0 -8
  61. spectre_core/chunks/library/callisto/__init__.py +0 -0
  62. spectre_core/chunks/library/fixed/__init__.py +0 -0
  63. spectre_core/chunks/library/sweep/__init__.py +0 -0
  64. spectre_core/chunks/library/sweep/chunk.py +0 -400
  65. spectre_core/dynamic_imports.py +0 -22
  66. spectre_core/file_handlers/base.py +0 -68
  67. spectre_core/file_handlers/configs.py +0 -271
  68. spectre_core/file_handlers/json.py +0 -40
  69. spectre_core/file_handlers/text.py +0 -21
  70. spectre_core/plotting/factory.py +0 -26
  71. spectre_core/plotting/format.py +0 -19
  72. spectre_core/plotting/library/__init__.py +0 -7
  73. spectre_core/plotting/library/frequency_cuts/panel.py +0 -74
  74. spectre_core/plotting/library/integral_over_frequency/panel.py +0 -34
  75. spectre_core/plotting/library/spectrogram/panel.py +0 -92
  76. spectre_core/plotting/library/time_cuts/panel.py +0 -77
  77. spectre_core/plotting/panel_register.py +0 -13
  78. spectre_core/post_processing/base.py +0 -132
  79. spectre_core/post_processing/library/fixed/__init__.py +0 -0
  80. spectre_core/post_processing/library/fixed/event_handler.py +0 -40
  81. spectre_core/post_processing/library/sweep/event_handler.py +0 -54
  82. spectre_core/receivers/base.py +0 -422
  83. spectre_core/receivers/library/__init__.py +0 -7
  84. spectre_core/receivers/library/rsp1a/__init__.py +0 -0
  85. spectre_core/receivers/library/rsp1a/gr/__init__.py +0 -0
  86. spectre_core/receivers/library/rsp1a/gr/fixed.py +0 -104
  87. spectre_core/receivers/library/rsp1a/gr/sweep.py +0 -129
  88. spectre_core/receivers/library/rsp1a/receiver.py +0 -68
  89. spectre_core/receivers/library/rspduo/__init__.py +0 -0
  90. spectre_core/receivers/library/rspduo/gr/__init__.py +0 -0
  91. spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +0 -114
  92. spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +0 -131
  93. spectre_core/receivers/library/rspduo/gr/tuner_2_fixed.py +0 -120
  94. spectre_core/receivers/library/rspduo/gr/tuner_2_sweep.py +0 -119
  95. spectre_core/receivers/library/rspduo/receiver.py +0 -97
  96. spectre_core/receivers/library/test/__init__.py +0 -0
  97. spectre_core/receivers/library/test/gr/__init__.py +0 -0
  98. spectre_core/receivers/library/test/gr/cosine_signal_1.py +0 -83
  99. spectre_core/receivers/library/test/gr/tagged_staircase.py +0 -93
  100. spectre_core/receivers/library/test/receiver.py +0 -203
  101. spectre_core/receivers/validators.py +0 -231
  102. spectre_core/web_fetch/callisto.py +0 -101
  103. spectre_core-0.0.9.dist-info/RECORD +0 -74
  104. /spectre_core/chunks/{chunk_register.py → _register.py} +0 -0
  105. /spectre_core/post_processing/{event_handler_register.py → _register.py} +0 -0
  106. /spectre_core/receivers/{receiver_register.py → _register.py} +0 -0
  107. {spectre_core-0.0.9.dist-info → spectre_core-0.0.11.dist-info}/LICENSE +0 -0
  108. {spectre_core-0.0.9.dist-info → spectre_core-0.0.11.dist-info}/WHEEL +0 -0
  109. {spectre_core-0.0.9.dist-info → spectre_core-0.0.11.dist-info}/top_level.txt +0 -0
@@ -1,271 +0,0 @@
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 Any, Optional, Type, Tuple
6
- from abc import ABC
7
- import ast
8
-
9
- from spectre_core.file_handlers.json import JsonHandler
10
- from spectre_core.cfg import CONFIGS_DIR_PATH
11
- from spectre_core.exceptions import InvalidTagError
12
-
13
-
14
- def _unpack_param(param: str) -> list[str, str]:
15
- """Seperate a string of the form "a=b" into a list [a,b]."""
16
- if not param or '=' not in param:
17
- raise ValueError(f'Invalid format: "{param}". Expected "KEY=VALUE".')
18
- if param.startswith('=') or param.endswith('='):
19
- raise ValueError(f'Invalid format: "{param}". Expected "KEY=VALUE".')
20
- # remove leading and trailing whitespace.
21
- param = param.strip()
22
- return param.split('=', 1)
23
-
24
-
25
- def _params_to_string_dict(params: list[str]) -> dict[str, str]:
26
- """Converts a list with string elements of the form "a=b" into a dictionary where the key value pairs are "a": "b"."""
27
- d = {}
28
- for param in params:
29
- key, value = _unpack_param(param)
30
- d[key] = value
31
- return d
32
-
33
-
34
- def _convert_to_dict(v: str) -> dict:
35
- """Evaluate literally a string containing a Python dictionary expression."""
36
- return ast.literal_eval(v)
37
-
38
-
39
- def _convert_to_bool(v: str) -> bool:
40
- """Evaluate literally a string representation of a boolean as a boolean"""
41
- v = v.lower()
42
- if v in ('true', '1', 't', 'y', 'yes'):
43
- return True
44
- if v in ('false', '0', 'f', 'n', 'no'):
45
- return False
46
- raise ValueError(f'Cannot convert {v} to bool.')
47
-
48
-
49
- def _convert_string_to_type(value: str,
50
- target_type: Type) -> Any:
51
- """Cast a string as the target type."""
52
- if target_type == bool:
53
- return _convert_to_bool(value)
54
- elif target_type == dict:
55
- return _convert_to_dict(value)
56
- return target_type(value)
57
-
58
-
59
- def _type_cast_string_dict(d: dict[str, str],
60
- type_template: dict[str, Type]) -> dict[str, Any]:
61
- """Cast the values of the input dictionary according to a type template."""
62
- casted_d = {}
63
- for key, value in d.items():
64
- target_type = type_template.get(key)
65
- if target_type is None:
66
- raise KeyError(f'Key "{key}" not found in type template. Expected keys: {list(type_template.keys())}')
67
- try:
68
- casted_d[key] = _convert_string_to_type(value, target_type)
69
- except ValueError:
70
- raise ValueError(f'Failed to convert key "{key}" with value "{value}" to {target_type.__name__}.')
71
- return casted_d
72
-
73
-
74
- def _validate_keys(d: dict[str, Any],
75
- type_template: dict[str, type],
76
- ignore_keys: Optional[list] = None) -> None:
77
- """Validate that the keys in the input dictionary map one-to-one to the input type template."""
78
- if ignore_keys is None:
79
- ignore_keys = []
80
-
81
- type_template_keys = set(type_template.keys())
82
- input_keys = set(d.keys())
83
- ignore_keys = set(ignore_keys)
84
-
85
- missing_keys = type_template_keys - input_keys
86
- invalid_keys = input_keys - type_template_keys - ignore_keys
87
-
88
- errors = []
89
-
90
- if missing_keys:
91
- errors.append(f"Missing keys: {', '.join(missing_keys)}")
92
-
93
- if invalid_keys:
94
- errors.append(f"Invalid keys: {', '.join(invalid_keys)}")
95
-
96
- if errors:
97
- raise KeyError("Key errors found! " + " ".join(errors))
98
-
99
-
100
- def _validate_types(d: dict[str, Any],
101
- type_template: dict[str, type],
102
- ignore_keys: Optional[list] = None) -> None:
103
- """Validate the types in the input dictionary are consistent with the input type template."""
104
-
105
- if ignore_keys is None:
106
- ignore_keys = []
107
-
108
- for k, v in d.items():
109
- if k in ignore_keys:
110
- continue
111
- expected_type = type_template[k]
112
- if expected_type is None:
113
- raise KeyError(f'Type not found for key "{k}" in the type template.')
114
-
115
- if not isinstance(v, expected_type):
116
- raise TypeError(f'Expected {expected_type} for "{k}", but got {type(v)}.')
117
-
118
-
119
- def validate_against_type_template(d: dict[str, Any],
120
- type_template: dict[str, type],
121
- ignore_keys: Optional[list] = None) -> None:
122
- """Validates the keys and values of the input dictionary, according to the input type template."""
123
- _validate_keys(d,
124
- type_template,
125
- ignore_keys = ignore_keys)
126
- _validate_types(d,
127
- type_template,
128
- ignore_keys = ignore_keys)
129
-
130
-
131
- def type_cast_params(params: list[str],
132
- type_template: dict[str, type]) -> dict[str, Any]:
133
- d = _params_to_string_dict(params)
134
- return _type_cast_string_dict(d,
135
- type_template)
136
-
137
-
138
-
139
- class SPECTREConfig(JsonHandler, ABC):
140
- def __init__(self,
141
- tag: str,
142
- config_type: str,
143
- **kwargs):
144
- self._validate_tag(tag)
145
- self._tag = tag
146
- self._config_type = config_type
147
-
148
- self._dict = None # cache
149
- super().__init__(CONFIGS_DIR_PATH,
150
- f"{config_type}_{tag}",
151
- **kwargs)
152
-
153
-
154
- @property
155
- def tag(self) -> str:
156
- return self._tag
157
-
158
-
159
- @property
160
- def config_type(self) -> str:
161
- return self._config_type
162
-
163
-
164
- @property
165
- def dict(self) -> dict[str, Any]:
166
- if self._dict is None:
167
- self._dict = self.read()
168
- return self._dict
169
-
170
-
171
- def _validate_tag(self, tag: str) -> None:
172
- if "_" in tag:
173
- raise InvalidTagError(f"Tags cannot contain an underscore. Received {tag}")
174
- if "callisto" in tag:
175
- raise InvalidTagError(f'"callisto" cannot be a substring in a native tag. Received "{tag}"')
176
-
177
-
178
- def __getitem__(self,
179
- key: str) -> Any:
180
- return self.dict[key]
181
-
182
-
183
- def get(self,
184
- *args,
185
- **kwargs) -> Any:
186
- return self.dict.get(*args,
187
- **kwargs)
188
-
189
-
190
- def update(self,
191
- *args,
192
- **kwargs) -> None:
193
- self.dict.update(*args, **kwargs)
194
-
195
-
196
- def items(self):
197
- return self.dict.items()
198
-
199
-
200
- def keys(self):
201
- return self.dict.keys()
202
-
203
-
204
- def values(self):
205
- return self.dict.values()
206
-
207
-
208
- class FitsConfig(SPECTREConfig):
209
-
210
- type_template = {
211
- "ORIGIN": str,
212
- "TELESCOP": str,
213
- "INSTRUME": str,
214
- "OBJECT": str,
215
- "OBS_LAT": float,
216
- "OBS_LONG": float,
217
- "OBS_ALT": float
218
- }
219
-
220
- def __init__(self,
221
- tag: str,
222
- **kwargs):
223
- super().__init__(tag,
224
- "fits",
225
- **kwargs)
226
-
227
- def get_create_fits_config_cmd(self,
228
- tag: str,
229
- as_string: bool = False) -> list[str] | str:
230
- command_as_list = ["spectre", "create", "fits-config", "-t", tag]
231
- for key, value in self.type_template.items():
232
- command_as_list += ["-p"]
233
- command_as_list += [f"{key}={value.__name__}"]
234
- if as_string:
235
- return " ".join(command_as_list)
236
- else:
237
- return command_as_list
238
-
239
-
240
- def save_params(self,
241
- params: list[str],
242
- force: bool = False
243
- ) -> None:
244
- d = type_cast_params(params,
245
- self.type_template)
246
- validate_against_type_template(d,
247
- self.type_template)
248
- self.save(d,
249
- force = force)
250
-
251
-
252
- class CaptureConfig(SPECTREConfig):
253
- def __init__(self,
254
- tag: str,
255
- **kwargs):
256
- super().__init__(tag,
257
- "capture",
258
- **kwargs)
259
-
260
-
261
- def get_receiver_metadata(self) -> Tuple[str, str]:
262
-
263
- receiver_name, mode = self.get("receiver"), self.get("mode")
264
-
265
- if receiver_name is None:
266
- raise ValueError("Invalid capture config! Receiver name is not specified.")
267
-
268
- if mode is None:
269
- raise ValueError("Invalid capture config! Receiver mode is not specified.")
270
-
271
- return receiver_name, mode
@@ -1,40 +0,0 @@
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 Any
6
- import json
7
-
8
- from spectre_core.file_handlers.base import BaseFileHandler
9
-
10
- class JsonHandler(BaseFileHandler):
11
- def __init__(self,
12
- parent_path: str,
13
- base_file_name: str,
14
- extension: str = "json",
15
- **kwargs):
16
- super().__init__(parent_path,
17
- base_file_name,
18
- extension,
19
- **kwargs)
20
-
21
-
22
- def read(self) -> dict[str, Any]:
23
- with open(self.file_path, 'r') as f:
24
- return json.load(f)
25
-
26
-
27
- def save(self,
28
- d: dict,
29
- force: bool = False) -> None:
30
- self.make_parent_path()
31
-
32
- if self.exists:
33
- if force:
34
- pass
35
- else:
36
- raise RuntimeError((f"{self.file_name} already exists, write has been abandoned. "
37
- f"You can override this functionality with `force`"))
38
-
39
- with open(self.file_path, 'w') as file:
40
- json.dump(d, file, indent=4)
@@ -1,21 +0,0 @@
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 spectre_core.file_handlers.base import BaseFileHandler
6
-
7
- class TextHandler(BaseFileHandler):
8
- def __init__(self,
9
- parent_path: str,
10
- base_file_name: str,
11
- extension: str = "txt",
12
- **kwargs):
13
- super().__init__(parent_path,
14
- base_file_name,
15
- extension,
16
- **kwargs)
17
-
18
-
19
- def read(self) -> dict:
20
- with open(self.file_path, 'r') as f:
21
- return f.read()
@@ -1,26 +0,0 @@
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 spectre_core.spectrograms.spectrogram import Spectrogram
6
- from spectre_core.plotting.base import BasePanel
7
- from spectre_core.plotting.panel_register import panels
8
- from spectre_core.exceptions import PanelNotFoundError
9
-
10
- def get_panel(panel_name: str,
11
- spectrogram: Spectrogram,
12
- time_type: str,
13
- *args,
14
- **kwargs) -> BasePanel:
15
-
16
- Panel = panels.get(panel_name)
17
- if Panel is None:
18
- valid_panels = list(panels.keys())
19
- raise PanelNotFoundError(f"Could not find panel with name {panel_name}. "
20
- f"Expected one of {valid_panels}")
21
-
22
- return Panel(panel_name,
23
- spectrogram,
24
- time_type,
25
- *args,
26
- **kwargs)
@@ -1,19 +0,0 @@
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 DefaultFormats:
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_over_frequency_color: str = "lime"
17
-
18
-
19
- DEFAULT_FORMATS = DefaultFormats()
@@ -1,7 +0,0 @@
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 spectre_core.dynamic_imports import import_target_modules
6
-
7
- import_target_modules(__file__, __name__, "panel")
@@ -1,74 +0,0 @@
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 Optional
6
- from datetime import datetime
7
-
8
- from spectre_core.spectrograms.spectrogram import FrequencyCut
9
- from spectre_core.spectrograms.spectrogram import Spectrogram
10
- from spectre_core.plotting.base import BaseSpectrumPanel, CutsPanel
11
- from spectre_core.plotting.panel_register import register_panel
12
- from spectre_core.plotting.format import DEFAULT_FORMATS
13
-
14
- FREQUENCY_CUTS_PANEL_NAME = "frequency_cuts"
15
-
16
- @register_panel(FREQUENCY_CUTS_PANEL_NAME)
17
- class Panel(BaseSpectrumPanel, CutsPanel):
18
- def __init__(self,
19
- name: str,
20
- spectrogram: Spectrogram,
21
- time_type: str = "seconds",
22
- *times: list[float | str],
23
- dBb: bool = False,
24
- peak_normalise: bool = False,
25
- cmap: str = DEFAULT_FORMATS.cuts_cmap,
26
- **kwargs):
27
- super().__init__(name,
28
- spectrogram,
29
- time_type,
30
- **kwargs)
31
- self._times = times
32
- self._cmap = cmap
33
- self._dBb = dBb
34
- self._peak_normalise = peak_normalise
35
- # map each time cut to the corresponding FrequencyCut dataclass
36
- self._frequency_cuts: Optional[dict[float | datetime, FrequencyCut]] = {}
37
-
38
-
39
- @property
40
- def frequency_cuts(self) -> dict[float | str, FrequencyCut]:
41
- if not self._frequency_cuts:
42
- for time in self._times:
43
- frequency_cut = self._spectrogram.get_frequency_cut(time,
44
- dBb = self._dBb,
45
- peak_normalise = self._peak_normalise)
46
- self._frequency_cuts[frequency_cut.time] = frequency_cut
47
- return self._frequency_cuts
48
-
49
-
50
- @property
51
- def times(self) -> list[float | datetime]:
52
- return list(self.frequency_cuts.keys())
53
-
54
-
55
- def draw(self):
56
- for time, color in self.bind_to_colors():
57
- frequency_cut = self.frequency_cuts[time]
58
- self.ax.step(self.frequencies*1e-6, # convert to MHz
59
- frequency_cut.cut,
60
- where='mid',
61
- color = color)
62
-
63
-
64
- def annotate_y_axis(self) -> None:
65
- if self._dBb:
66
- self.ax.set_ylabel('dBb')
67
- elif self._peak_normalise:
68
- return # no y-axis label
69
- else:
70
- self.ax.set_ylabel(f'{self._spectrogram.spectrum_type.capitalize()}')
71
-
72
-
73
- def bind_to_colors(self):
74
- return super().bind_to_colors(self.times, cmap = self._cmap)
@@ -1,34 +0,0 @@
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 spectre_core.plotting.base import BaseTimeSeriesPanel
6
- from spectre_core.plotting.panel_register import register_panel
7
- from spectre_core.plotting.format import DEFAULT_FORMATS
8
-
9
- INTEGRAL_OVER_FREQUENCY_PANEL_NAME = "integral_over_frequency"
10
-
11
- @register_panel(INTEGRAL_OVER_FREQUENCY_PANEL_NAME)
12
- class Panel(BaseTimeSeriesPanel):
13
- def __init__(self,
14
- *args,
15
- peak_normalise: bool = False,
16
- background_subtract: bool = False,
17
- color: str = DEFAULT_FORMATS.integral_over_frequency_color,
18
- **kwargs):
19
- super().__init__(*args, **kwargs)
20
- self._peak_normalise = peak_normalise
21
- self._background_subtract = background_subtract
22
- self._color = color
23
-
24
-
25
- def draw(self):
26
- I = self._spectrogram.integrate_over_frequency(correct_background = self._background_subtract,
27
- peak_normalise = self._peak_normalise)
28
- self.ax.step(self.times, I, where="mid", color = self._color)
29
-
30
-
31
- def annotate_y_axis(self):
32
- return # no y-axis label
33
-
34
-
@@ -1,92 +0,0 @@
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 numpy as np
6
- from matplotlib.colors import LogNorm
7
- from matplotlib.figure import Figure
8
- from matplotlib.axes import Axes
9
- from warnings import warn
10
-
11
- from spectre_core.plotting.base import BaseTimeSeriesPanel
12
- from spectre_core.plotting.panel_register import register_panel
13
- from spectre_core.plotting.base import CutsPanel
14
- from spectre_core.plotting.library.time_cuts.panel import Panel as TimeCutsPanel
15
- from spectre_core.plotting.library.frequency_cuts.panel import Panel as FrequencyCutsPanel
16
- from spectre_core.plotting.format import DEFAULT_FORMATS
17
-
18
- SPECTROGRAM_PANEL_NAME = "spectrogram"
19
-
20
- @register_panel(SPECTROGRAM_PANEL_NAME)
21
- class Panel(BaseTimeSeriesPanel):
22
- def __init__(self,
23
- *args,
24
- log_norm: bool = False,
25
- dBb: bool = False,
26
- vmin: float | None = -1,
27
- vmax: float | None = 2,
28
- cmap: str = DEFAULT_FORMATS.spectrogram_cmap,
29
- **kwargs):
30
- super().__init__(*args, **kwargs)
31
- self._log_norm = log_norm
32
- self._dBb = dBb
33
- self._vmin = vmin
34
- self._vmax = vmax
35
- self._cmap = cmap
36
-
37
-
38
- def draw(self):
39
- dynamic_spectra = self._spectrogram.dynamic_spectra_as_dBb if self._dBb else self._spectrogram.dynamic_spectra
40
-
41
- norm = LogNorm(vmin=np.nanmin(dynamic_spectra[dynamic_spectra > 0]),
42
- vmax=np.nanmax(dynamic_spectra)) if self._log_norm else None
43
-
44
-
45
- if self._log_norm and (self._vmin or self._vmax):
46
- warn("vmin/vmax will be ignored while using log_norm.")
47
- self._vmin = None
48
- self._vmax = None
49
-
50
- # Plot the spectrogram with kwargs
51
- pcm = self.ax.pcolormesh(self.times,
52
- self._spectrogram.frequencies * 1e-6,
53
- dynamic_spectra,
54
- vmin=self._vmin,
55
- vmax=self._vmax,
56
- norm=norm,
57
- cmap=self._cmap)
58
-
59
- # Add colorbar if dBb is used
60
- if self._dBb:
61
- cbar = self.fig.colorbar(pcm,
62
- ax=self.ax,
63
- ticks=np.linspace(self._vmin, self._vmax, 6, dtype=int))
64
- cbar.set_label('dBb')
65
-
66
-
67
- def annotate_y_axis(self) -> None:
68
- self.ax.set_ylabel('Frequency [MHz]')
69
- return
70
-
71
-
72
- def overlay_cuts(self, cuts_panel: CutsPanel) -> None:
73
- if isinstance(cuts_panel, TimeCutsPanel):
74
- self._overlay_time_cuts(cuts_panel)
75
- elif isinstance(cuts_panel, FrequencyCutsPanel):
76
- self._overlay_frequency_cuts(cuts_panel)
77
-
78
-
79
- def _overlay_time_cuts(self, time_cuts_panel: TimeCutsPanel) -> None:
80
- for frequency, color in time_cuts_panel.bind_to_colors():
81
- self.ax.axhline(frequency*1e-6, # convert to MHz
82
- color = color,
83
- linewidth=DEFAULT_FORMATS.line_width
84
- )
85
-
86
-
87
- def _overlay_frequency_cuts(self, frequency_cuts_panel: FrequencyCutsPanel) -> None:
88
- for time, color in frequency_cuts_panel.bind_to_colors():
89
- self.ax.axvline(time,
90
- color = color,
91
- linewidth=DEFAULT_FORMATS.line_width
92
- )
@@ -1,77 +0,0 @@
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 Optional
6
-
7
- from spectre_core.spectrograms.spectrogram import TimeCut
8
- from spectre_core.spectrograms.spectrogram import Spectrogram
9
- from spectre_core.plotting.base import BaseTimeSeriesPanel, CutsPanel
10
- from spectre_core.plotting.format import DEFAULT_FORMATS
11
- from spectre_core.plotting.panel_register import register_panel
12
-
13
- TIME_CUTS_PANEL_NAME = "time_cuts"
14
-
15
- @register_panel(TIME_CUTS_PANEL_NAME)
16
- class Panel(BaseTimeSeriesPanel, CutsPanel):
17
- def __init__(self,
18
- name: str,
19
- spectrogram: Spectrogram,
20
- time_type: str = "seconds",
21
- *frequencies: list[float],
22
- dBb: bool = False,
23
- peak_normalise: bool = False,
24
- background_subtract: bool = False,
25
- cmap: str = DEFAULT_FORMATS.cuts_cmap,
26
- **kwargs):
27
- super().__init__(name,
28
- spectrogram,
29
- time_type,
30
- **kwargs)
31
- self._frequencies = frequencies
32
- self._cmap = cmap
33
- self._dBb = dBb
34
- self._peak_normalise = peak_normalise
35
- self._background_subtract = background_subtract
36
- # map each cut frequency to the corresponding TimeCut dataclass
37
- self._time_cuts: Optional[dict[float, TimeCut]] = {}
38
-
39
-
40
- @property
41
- def time_cuts(self) -> dict[float, TimeCut]:
42
- if not self._time_cuts:
43
- for frequency in self._frequencies:
44
- time_cut = self._spectrogram.get_time_cut(frequency,
45
- dBb = self._dBb,
46
- peak_normalise = self._peak_normalise,
47
- correct_background = self._background_subtract,
48
- return_time_type=self._time_type)
49
- self._time_cuts[time_cut.frequency] = time_cut
50
- return self._time_cuts
51
-
52
-
53
- @property
54
- def frequencies(self) -> list[float]:
55
- return list(self.time_cuts.keys())
56
-
57
-
58
- def draw(self):
59
- for frequency, color in self.bind_to_colors():
60
- time_cut = self.time_cuts[frequency]
61
- self.ax.step(self.times,
62
- time_cut.cut,
63
- where='mid',
64
- color = color)
65
-
66
-
67
- def annotate_y_axis(self) -> None:
68
- if self._dBb:
69
- self.ax.set_ylabel('dBb')
70
- elif self._peak_normalise:
71
- return # no y-axis label
72
- else:
73
- self.ax.set_ylabel(f'{self._spectrogram.spectrum_type.capitalize()}')
74
-
75
-
76
- def bind_to_colors(self):
77
- return super().bind_to_colors(self.frequencies, cmap = self._cmap)
@@ -1,13 +0,0 @@
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
- # Global dictionaries to hold the mappings
7
- panels = {}
8
-
9
- def register_panel(panel_name: str):
10
- def decorator(cls):
11
- panels[panel_name] = cls
12
- return cls
13
- return decorator