spectre-core 0.0.12__py3-none-any.whl → 0.0.14__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 (88) hide show
  1. spectre_core/_file_io/__init__.py +1 -3
  2. spectre_core/_file_io/file_handlers.py +163 -58
  3. spectre_core/batches/__init__.py +10 -11
  4. spectre_core/batches/_base.py +170 -78
  5. spectre_core/batches/_batches.py +149 -99
  6. spectre_core/batches/_factory.py +56 -14
  7. spectre_core/batches/_register.py +23 -8
  8. spectre_core/batches/plugins/_batch_keys.py +16 -0
  9. spectre_core/batches/plugins/_callisto.py +183 -0
  10. spectre_core/batches/plugins/_iq_stream.py +354 -0
  11. spectre_core/capture_configs/__init__.py +17 -13
  12. spectre_core/capture_configs/_capture_config.py +93 -34
  13. spectre_core/capture_configs/_capture_modes.py +22 -0
  14. spectre_core/capture_configs/_capture_templates.py +207 -122
  15. spectre_core/capture_configs/_parameters.py +115 -42
  16. spectre_core/capture_configs/_pconstraints.py +86 -35
  17. spectre_core/capture_configs/_pnames.py +49 -0
  18. spectre_core/capture_configs/_ptemplates.py +389 -346
  19. spectre_core/capture_configs/_pvalidators.py +117 -73
  20. spectre_core/config/__init__.py +6 -8
  21. spectre_core/config/_paths.py +65 -25
  22. spectre_core/config/_time_formats.py +15 -10
  23. spectre_core/exceptions.py +2 -4
  24. spectre_core/jobs/__init__.py +14 -0
  25. spectre_core/jobs/_jobs.py +111 -0
  26. spectre_core/jobs/_workers.py +171 -0
  27. spectre_core/logs/__init__.py +17 -0
  28. spectre_core/logs/_configure.py +67 -0
  29. spectre_core/logs/_decorators.py +33 -0
  30. spectre_core/logs/_logs.py +228 -0
  31. spectre_core/logs/_process_types.py +14 -0
  32. spectre_core/plotting/__init__.py +4 -2
  33. spectre_core/plotting/_base.py +204 -102
  34. spectre_core/plotting/_format.py +17 -4
  35. spectre_core/plotting/_panel_names.py +18 -0
  36. spectre_core/plotting/_panel_stack.py +167 -53
  37. spectre_core/plotting/_panels.py +341 -141
  38. spectre_core/post_processing/__init__.py +8 -6
  39. spectre_core/post_processing/_base.py +70 -44
  40. spectre_core/post_processing/_factory.py +42 -12
  41. spectre_core/post_processing/_post_processor.py +24 -26
  42. spectre_core/post_processing/_register.py +22 -6
  43. spectre_core/post_processing/plugins/_event_handler_keys.py +16 -0
  44. spectre_core/post_processing/plugins/_fixed_center_frequency.py +129 -0
  45. spectre_core/post_processing/{library → plugins}/_swept_center_frequency.py +215 -143
  46. spectre_core/py.typed +0 -0
  47. spectre_core/receivers/__init__.py +10 -7
  48. spectre_core/receivers/_base.py +220 -69
  49. spectre_core/receivers/_factory.py +53 -7
  50. spectre_core/receivers/_register.py +30 -9
  51. spectre_core/receivers/_spec_names.py +26 -15
  52. spectre_core/receivers/plugins/__init__.py +0 -0
  53. spectre_core/receivers/plugins/_receiver_names.py +16 -0
  54. spectre_core/receivers/plugins/_rsp1a.py +59 -0
  55. spectre_core/receivers/plugins/_rspduo.py +67 -0
  56. spectre_core/receivers/plugins/_sdrplay_receiver.py +190 -0
  57. spectre_core/receivers/plugins/_test.py +218 -0
  58. spectre_core/receivers/plugins/gr/_base.py +80 -0
  59. spectre_core/receivers/{gr → plugins/gr}/_rsp1a.py +42 -52
  60. spectre_core/receivers/{gr → plugins/gr}/_rspduo.py +61 -74
  61. spectre_core/receivers/{gr → plugins/gr}/_test.py +33 -31
  62. spectre_core/spectrograms/__init__.py +5 -3
  63. spectre_core/spectrograms/_analytical.py +121 -66
  64. spectre_core/spectrograms/_array_operations.py +103 -36
  65. spectre_core/spectrograms/_spectrogram.py +380 -207
  66. spectre_core/spectrograms/_transform.py +197 -169
  67. spectre_core/wgetting/__init__.py +4 -2
  68. spectre_core/wgetting/_callisto.py +173 -118
  69. {spectre_core-0.0.12.dist-info → spectre_core-0.0.14.dist-info}/METADATA +14 -7
  70. spectre_core-0.0.14.dist-info/RECORD +75 -0
  71. {spectre_core-0.0.12.dist-info → spectre_core-0.0.14.dist-info}/WHEEL +1 -1
  72. spectre_core/batches/library/_callisto.py +0 -96
  73. spectre_core/batches/library/_fixed_center_frequency.py +0 -133
  74. spectre_core/batches/library/_swept_center_frequency.py +0 -105
  75. spectre_core/logging/__init__.py +0 -11
  76. spectre_core/logging/_configure.py +0 -35
  77. spectre_core/logging/_decorators.py +0 -19
  78. spectre_core/logging/_log_handlers.py +0 -176
  79. spectre_core/post_processing/library/_fixed_center_frequency.py +0 -114
  80. spectre_core/receivers/gr/_base.py +0 -33
  81. spectre_core/receivers/library/_rsp1a.py +0 -61
  82. spectre_core/receivers/library/_rspduo.py +0 -69
  83. spectre_core/receivers/library/_sdrplay_receiver.py +0 -185
  84. spectre_core/receivers/library/_test.py +0 -221
  85. spectre_core-0.0.12.dist-info/RECORD +0 -64
  86. /spectre_core/receivers/{gr → plugins/gr}/__init__.py +0 -0
  87. {spectre_core-0.0.12.dist-info → spectre_core-0.0.14.dist-info}/LICENSE +0 -0
  88. {spectre_core-0.0.12.dist-info → spectre_core-0.0.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,171 @@
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 logging import getLogger
6
+ _LOGGER = getLogger(__name__)
7
+
8
+ from functools import wraps
9
+ import time
10
+ from typing import Callable, TypeVar, ParamSpec
11
+ import multiprocessing
12
+
13
+ from spectre_core.logs import configure_root_logger, log_call, ProcessType
14
+ from spectre_core.capture_configs import CaptureConfig
15
+ from spectre_core.receivers import get_receiver, ReceiverName
16
+ from spectre_core.post_processing import start_post_processor
17
+
18
+
19
+ def _make_daemon_process(
20
+ name: str,
21
+ target: Callable[[], None]
22
+ ) -> multiprocessing.Process:
23
+ """
24
+ Creates and returns a daemon `multiprocessing.Process` instance.
25
+
26
+ :param name: The name to assign to the process.
27
+ :param target: The function to execute in the process.
28
+ :return: A `multiprocessing.Process` instance configured as a daemon.
29
+ """
30
+ return multiprocessing.Process(target=target,
31
+ name=name,
32
+ daemon=True)
33
+
34
+
35
+ class Worker:
36
+ """A lightweight wrapper for a `multiprocessing.Process` daemon.
37
+
38
+ Provides a very simple API to start, and restart a multiprocessing process.
39
+ """
40
+ def __init__(
41
+ self,
42
+ name: str,
43
+ target: Callable[[], None]
44
+ ) -> None:
45
+ """Initialise a `Worker` instance.
46
+
47
+ :param name: The name assigned to the process.
48
+ :param target: The callable to be executed by the worker process.
49
+ """
50
+ self._name = name
51
+ self._target = target
52
+ self._process = _make_daemon_process(name, target)
53
+
54
+
55
+ @property
56
+ def name(
57
+ self
58
+ ) -> str:
59
+ """Get the name of the worker process.
60
+
61
+ :return: The name of the multiprocessing process.
62
+ """
63
+ return self._process.name
64
+
65
+
66
+ @property
67
+ def process(
68
+ self
69
+ ) -> multiprocessing.Process:
70
+ """Access the underlying multiprocessing process.
71
+
72
+ :return: The wrapped `multiprocessing.Process` instance.
73
+ """
74
+ return self._process
75
+
76
+
77
+ def start(
78
+ self
79
+ ) -> None:
80
+ """Start the worker process.
81
+
82
+ This method runs the `target` in the background as a daemon.
83
+ """
84
+ self._process.start()
85
+
86
+
87
+ def restart(
88
+ self
89
+ ) -> None:
90
+ """Restart the worker process.
91
+
92
+ Terminates the existing process if it is alive and then starts a new process
93
+ after a brief pause.
94
+ """
95
+ _LOGGER.info(f"Restarting {self.name} worker")
96
+ if self._process.is_alive():
97
+ # forcibly stop if it is still alive
98
+ self._process.terminate()
99
+ self._process.join()
100
+ # a moment of respite
101
+ time.sleep(1)
102
+ # make a new process, as we can't start the same process again.
103
+ self._process = _make_daemon_process(self._name, self._target)
104
+ self.start()
105
+
106
+
107
+ P = ParamSpec("P")
108
+ T = TypeVar("T", bound=Callable[..., None])
109
+ def make_worker(
110
+ name: str
111
+ ) -> Callable[[Callable[P, None]], Callable[P, Worker]]:
112
+ """
113
+ Turns a function into a worker.
114
+
115
+ This decorator wraps a function, allowing it to run in a separate process
116
+ managed by a `Worker` object. Use it to easily create long-running or
117
+ isolated tasks without directly handling multiprocessing.
118
+
119
+ :param name: A human-readable name for the worker process.
120
+ :return: A decorator that creates a `Worker` to run the function in its own process.
121
+ """
122
+
123
+ def decorator(
124
+ func: Callable[P, None]
125
+ ) -> Callable[P, Worker]:
126
+ @wraps(func)
127
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> Worker:
128
+ # Worker target funcs must have no arguments
129
+ def target():
130
+ configure_root_logger(ProcessType.WORKER)
131
+ func(*args, **kwargs)
132
+ return Worker(name, target)
133
+ return wrapper
134
+ return decorator
135
+
136
+
137
+ @make_worker("capture")
138
+ @log_call
139
+ def do_capture(
140
+ tag: str,
141
+ ) -> None:
142
+ """Start capturing data from an SDR in real time.
143
+
144
+ :param tag: The capture config tag.
145
+ """
146
+ _LOGGER.info((f"Reading capture config with tag '{tag}'"))
147
+
148
+ # load the receiver and mode from the capture config file
149
+ capture_config = CaptureConfig(tag)
150
+
151
+ _LOGGER.info((f"Starting capture with the receiver '{capture_config.receiver_name}' "
152
+ f"operating in mode '{capture_config.receiver_mode}' "
153
+ f"with tag '{tag}'"))
154
+
155
+ name = ReceiverName( capture_config.receiver_name )
156
+ receiver = get_receiver(name,
157
+ capture_config.receiver_mode)
158
+ receiver.start_capture(tag)
159
+
160
+
161
+ @make_worker("post_processing")
162
+ @log_call
163
+ def do_post_processing(
164
+ tag: str,
165
+ ) -> None:
166
+ """Start post processing SDR data into spectrograms in real time.
167
+
168
+ :param tag: The capture config tag.
169
+ """
170
+ _LOGGER.info(f"Starting post processor with tag '{tag}'")
171
+ start_post_processor(tag)
@@ -0,0 +1,17 @@
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` logging configurations."""
7
+
8
+ from ._process_types import ProcessType
9
+ from ._decorators import log_call
10
+ from ._configure import configure_root_logger, get_root_logger_state
11
+ from ._logs import Log, Logs
12
+
13
+
14
+ __all__ = [
15
+ "log_call", "configure_root_logger", "Log", "Logs", "ProcessType",
16
+ "get_root_logger_state"
17
+ ]
@@ -0,0 +1,67 @@
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 typing import Tuple
8
+ from datetime import datetime
9
+
10
+ from spectre_core.config import TimeFormat
11
+ from ._logs import Log
12
+ from ._process_types import ProcessType
13
+
14
+
15
+ def configure_root_logger(
16
+ process_type: ProcessType,
17
+ level: int = logging.INFO
18
+ ) -> str:
19
+ """Configures the root logger to write logs to a file named based on
20
+ the process type, process ID, and the current system time.
21
+
22
+ :param process_type: Indicates the type of process, as defined by `ProcessType`.
23
+ :param level: The logging level, as defined in Python's `logging` module. Defaults to `logging.INFO`.
24
+ :return: The file path of the created log file.
25
+ """
26
+ # create a `spectre` log handler instance, to represent the log file.
27
+ # get the star time of the log
28
+ system_datetime = datetime.now()
29
+ start_time = system_datetime.strftime(TimeFormat.DATETIME)
30
+
31
+ # extract the process identifier, and cast as a string
32
+ pid = str( os.getpid() )
33
+
34
+ # create a file handler representing the log file
35
+ log = Log(start_time,
36
+ pid,
37
+ process_type)
38
+ log.make_parent_dir_path()
39
+
40
+ # get the root logger and set its level.
41
+ logger = logging.getLogger()
42
+ logger.setLevel(level)
43
+
44
+ # remove existing handlers
45
+ for handler in logger.handlers:
46
+ logger.removeHandler(handler)
47
+
48
+ # Set up a file handler and add it to the root logger
49
+ file_handler = logging.FileHandler(log.file_path)
50
+ file_handler.setLevel(level)
51
+ formatter = logging.Formatter("[%(asctime)s] [%(levelname)8s] --- %(message)s (%(name)s:%(lineno)s)")
52
+ file_handler.setFormatter(formatter)
53
+ logger.addHandler(file_handler)
54
+
55
+ return log.file_path
56
+
57
+
58
+ def get_root_logger_state(
59
+ ) -> Tuple[bool, int]:
60
+ """Get the state of the root logger.
61
+
62
+ :return: Whether the root logger has any handlers, and the level of the root logger.
63
+ """
64
+ root_logger = logging.getLogger()
65
+ if root_logger.handlers:
66
+ return True, root_logger.level
67
+ return False, logging.NOTSET
@@ -0,0 +1,33 @@
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, TypeVar, ParamSpec
7
+ from functools import wraps
8
+
9
+ # ParamSpec for capturing the argument types of the function
10
+ P = ParamSpec("P")
11
+ # TypeVar for capturing the return type of the function
12
+ RT = TypeVar("RT")
13
+ def log_call(
14
+ func: Callable[P, RT]
15
+ ) -> Callable[P, RT]:
16
+ """Decorator to log the execution of a function.
17
+
18
+ Logs an informational message when the decorated function is called,
19
+ and an error message if the function raises an exception.
20
+
21
+ :param func: The function to be decorated.
22
+ :return: The decorated function with added logging behaviour.
23
+ """
24
+ @wraps(func)
25
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> RT:
26
+ logger = logging.getLogger(func.__module__)
27
+ try:
28
+ logger.info(f"Calling the function: {func.__name__}")
29
+ return func(*args, **kwargs)
30
+ except Exception as e:
31
+ logger.error(f"Error in function: {func.__name__}", exc_info=True)
32
+ raise
33
+ return wrapper
@@ -0,0 +1,228 @@
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 logging import getLogger
6
+ _LOGGER = getLogger(__name__)
7
+
8
+ import os
9
+ from typing import Optional, Iterator
10
+ from collections import OrderedDict
11
+ from datetime import datetime
12
+
13
+ from spectre_core._file_io import TextHandler
14
+ from spectre_core.config import get_logs_dir_path, TimeFormat
15
+ from ._process_types import ProcessType
16
+
17
+ class Log(TextHandler):
18
+ """Interface to read log files generated by `spectre`."""
19
+ def __init__(
20
+ self,
21
+ start_time: str,
22
+ pid: str,
23
+ process_type: ProcessType
24
+ ) -> None:
25
+ """Initialise a `Log` instance.
26
+
27
+ :param start_time: The timestamp when the log file was created.
28
+ :param pid: The ID of the process writing to the log file.
29
+ :param process_type: Indicates the type of process, as defined by `ProcessType`.
30
+ """
31
+ self._start_time = start_time
32
+ self._pid = pid
33
+ self._process_type = process_type.value
34
+
35
+ dt = datetime.strptime(start_time, TimeFormat.DATETIME)
36
+ parent_path = get_logs_dir_path(dt.year, dt.month, dt.day)
37
+ base_file_name = f"{start_time}_{pid}_{process_type.value}"
38
+
39
+ super().__init__(parent_path, base_file_name, "log")
40
+
41
+
42
+ @property
43
+ def start_time(
44
+ self
45
+ ) -> str:
46
+ """The system time when the log was created."""
47
+ return self._start_time
48
+
49
+
50
+ @property
51
+ def pid(
52
+ self
53
+ ) -> str:
54
+ """The ID of the process writing to the log file."""
55
+ return self._pid
56
+
57
+
58
+ @property
59
+ def process_type(
60
+ self
61
+ ) -> str:
62
+ """Indicates the type of process, as defined by `ProcessType`."""
63
+ return self._process_type
64
+
65
+
66
+ class Logs:
67
+ """Filter and read a collection of logs generated by `spectre`."""
68
+ def __init__(
69
+ self,
70
+ process_type: Optional[ProcessType] = None,
71
+ year: Optional[int] = None,
72
+ month: Optional[int] = None,
73
+ day: Optional[int] = None
74
+ ) -> None:
75
+ """Initialise a `Logs` instance.
76
+
77
+ :param process_type: Filter by the process type. Defaults to None.
78
+ :param year: Filter by the numeric year. Defaults to None.
79
+ :param month: Filter by the numeric month. Defaults to None.
80
+ :param day: Filter by the numeric day. Defaults to None.
81
+ """
82
+ self._process_type = process_type.value if process_type is not None else None
83
+
84
+ self._log_map: dict[str, Log] = OrderedDict()
85
+ self.set_date(year, month, day)
86
+
87
+
88
+ @property
89
+ def process_type(
90
+ self
91
+ ) -> Optional[str]:
92
+ """Indicates the type of process, as defined by `ProcessType`."""
93
+ return self._process_type
94
+
95
+
96
+ @property
97
+ def year(
98
+ self
99
+ ) -> Optional[int]:
100
+ """Filter by the numeric year."""
101
+ return self._year
102
+
103
+
104
+ @property
105
+ def month(
106
+ self
107
+ ) -> Optional[int]:
108
+ """Filter by the numeric month."""
109
+ return self._month
110
+
111
+
112
+ @property
113
+ def day(
114
+ self
115
+ ) -> Optional[int]:
116
+ """Filter by the numeric day."""
117
+ return self._day
118
+
119
+
120
+ @property
121
+ def logs_dir_path(
122
+ self
123
+ ) -> str:
124
+ """The shared ancestral path for all the log files. `Logs` recursively searches
125
+ this directory to find all log files according to the date and process type."""
126
+ return get_logs_dir_path(self.year, self.month, self.day)
127
+
128
+
129
+ @property
130
+ def log_list(
131
+ self
132
+ ) -> list[Log]:
133
+ """A list of all log handlers representing files found within `logs_dir_path`."""
134
+ return list(self._log_map.values())
135
+
136
+
137
+ @property
138
+ def num_logs(
139
+ self
140
+ ) -> int:
141
+ """The number of log files found within `logs_dir_path`."""
142
+ return len(self.log_list)
143
+
144
+
145
+ @property
146
+ def file_names(
147
+ self
148
+ ) -> list[str]:
149
+ """A list of all log file names found within `logs_dir_path`."""
150
+ return list(self._log_map.keys())
151
+
152
+
153
+ def set_date(
154
+ self,
155
+ year: Optional[int],
156
+ month: Optional[int],
157
+ day: Optional[int]
158
+ ) -> None:
159
+ """Reset `logs_dir_path` according to the numeric date, and refresh the list
160
+ of available log files.
161
+
162
+ :param year: The numeric year.
163
+ :param month: The numeric month of the year.
164
+ :param day: The numeric day of the month.
165
+ """
166
+ self._year = year
167
+ self._month = month
168
+ self._day = day
169
+ self.update()
170
+
171
+
172
+ def update(
173
+ self
174
+ ) -> None:
175
+ """Perform a fresh search of all files in `logs_dir_path` for log files
176
+ according to the date and process type."""
177
+ log_files = [f for (_, _, files) in os.walk(self.logs_dir_path) for f in files]
178
+
179
+ for log_file in log_files:
180
+ file_name, _ = os.path.splitext(log_file)
181
+ log_start_time, pid, process_type = file_name.split("_")
182
+
183
+ if self.process_type and process_type != self.process_type:
184
+ continue
185
+
186
+ self._log_map[file_name] = Log(log_start_time, pid, ProcessType(process_type))
187
+
188
+ self._log_map = OrderedDict(sorted(self._log_map.items()))
189
+
190
+
191
+ def __iter__(
192
+ self
193
+ ) -> Iterator[Log]:
194
+ yield from self.log_list
195
+
196
+
197
+ def get_from_file_name(
198
+ self,
199
+ file_name: str
200
+ ) -> Log:
201
+ """Retrieve a `Log` instance based on the log file name.
202
+
203
+ :param file_name: The name of the log file (with or without extension).
204
+ :raises FileNotFoundError: If the log file name is not found.
205
+ :return: The `Log` instance corresponding to the file name.
206
+ """
207
+ # auto strip the extension if present
208
+ file_name, _ = os.path.splitext(file_name)
209
+ try:
210
+ return self._log_map[file_name]
211
+ except KeyError:
212
+ raise FileNotFoundError(f"Log handler for file name '{file_name}' not found in log map")
213
+
214
+
215
+ def get_from_pid(
216
+ self,
217
+ pid: str
218
+ ) -> Log:
219
+ """Retrieve a `Log` instance based on the process ID.
220
+
221
+ :param pid: The process ID to search for.
222
+ :raises FileNotFoundError: If a log file corresponding to the process ID is not found.
223
+ :return: The `Log` instance corresponding to the process ID.
224
+ """
225
+ for log in self.log_list:
226
+ if log.pid == pid:
227
+ return log
228
+ raise FileNotFoundError(f"Log handler for PID '{pid}' not found in log map")
@@ -0,0 +1,14 @@
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 enum import Enum
6
+
7
+ class ProcessType(Enum):
8
+ """The origin of a `spectre` process.
9
+
10
+ :ivar USER: A process is one initiated directly by the user, or part of the main user session.
11
+ :ivar WORKER: A process is one which is created and managed internally by `spectre`.
12
+ """
13
+ USER = "user"
14
+ WORKER = "worker"
@@ -2,10 +2,12 @@
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
+ """An intuitive API for plotting spectrogram data."""
6
+
5
7
  from ._format import PanelFormat
6
- from ._panels import Panels
8
+ from ._panels import SpectrogramPanel, FrequencyCutsPanel, TimeCutsPanel, IntegralOverFrequencyPanel
7
9
  from ._panel_stack import PanelStack
8
10
 
9
11
  __all__ = [
10
- "PanelFormat", "Panels", "PanelStack"
12
+ "PanelFormat", "PanelStack", "SpectrogramPanel", "FrequencyCutsPanel", "TimeCutsPanel", "IntegralOverFrequencyPanel"
11
13
  ]