spectre-core 0.0.7__py3-none-any.whl → 0.0.9__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 (26) hide show
  1. spectre_core/plotting/panel_stack.py +3 -1
  2. spectre_core/{watchdog → post_processing}/__init__.py +1 -1
  3. spectre_core/post_processing/base.py +132 -0
  4. spectre_core/{watchdog → post_processing}/factory.py +4 -4
  5. spectre_core/{watchdog → post_processing}/library/fixed/event_handler.py +8 -9
  6. spectre_core/{watchdog → post_processing}/library/sweep/event_handler.py +9 -10
  7. spectre_core/post_processing/post_processor.py +40 -0
  8. spectre_core/receivers/base.py +11 -4
  9. spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +6 -2
  10. spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +8 -12
  11. spectre_core/receivers/library/rspduo/gr/tuner_2_fixed.py +120 -0
  12. spectre_core/receivers/library/rspduo/gr/tuner_2_sweep.py +119 -0
  13. spectre_core/receivers/library/rspduo/receiver.py +34 -5
  14. spectre_core/receivers/library/test/receiver.py +45 -20
  15. spectre_core/receivers/validators.py +67 -29
  16. spectre_core/web_fetch/callisto.py +1 -1
  17. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/METADATA +2 -2
  18. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/RECORD +24 -22
  19. spectre_core/watchdog/base.py +0 -105
  20. spectre_core/watchdog/post_processor.py +0 -50
  21. /spectre_core/{watchdog → post_processing}/event_handler_register.py +0 -0
  22. /spectre_core/{watchdog → post_processing}/library/__init__.py +0 -0
  23. /spectre_core/{watchdog → post_processing}/library/fixed/__init__.py +0 -0
  24. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/LICENSE +0 -0
  25. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/WHEEL +0 -0
  26. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,12 @@
4
4
 
5
5
  from spectre_core.receivers.base import SDRPlayReceiver
6
6
  from spectre_core.receivers.receiver_register import register_receiver
7
- from spectre_core.receivers.library.rspduo.gr import tuner_1_fixed, tuner_1_sweep
7
+ from spectre_core.receivers.library.rspduo.gr import (
8
+ tuner_1_fixed,
9
+ tuner_1_sweep,
10
+ tuner_2_fixed,
11
+ tuner_2_sweep
12
+ )
8
13
  from spectre_core.file_handlers.configs import CaptureConfig
9
14
 
10
15
 
@@ -17,14 +22,18 @@ class Receiver(SDRPlayReceiver):
17
22
  def _set_capture_methods(self) -> None:
18
23
  self._capture_methods = {
19
24
  "tuner-1-fixed": self.__tuner_1_fixed,
20
- "tuner-1-sweep": self.__tuner_1_sweep
25
+ "tuner-1-sweep": self.__tuner_1_sweep,
26
+ "tuner-2-fixed": self.__tuner_2_fixed,
27
+ "tuner-2-sweep": self.__tuner_2_sweep
21
28
  }
22
29
 
23
30
 
24
31
  def _set_validators(self) -> None:
25
32
  self._validators = {
26
33
  "tuner-1-fixed": self.__tuner_1_fixed_validator,
27
- "tuner-1-sweep": self.__tuner_1_sweep_validator
34
+ "tuner-1-sweep": self.__tuner_1_sweep_validator,
35
+ "tuner-2-fixed": self.__tuner_2_fixed_validator,
36
+ "tuner-2-sweep": self.__tuner_2_sweep_validator,
28
37
  }
29
38
  return
30
39
 
@@ -33,6 +42,8 @@ class Receiver(SDRPlayReceiver):
33
42
  self._type_templates = {
34
43
  "tuner-1-fixed": self._get_default_type_template("fixed"),
35
44
  "tuner-1-sweep": self._get_default_type_template("sweep"),
45
+ "tuner-2-fixed": self._get_default_type_template("fixed"),
46
+ "tuner-2-sweep": self._get_default_type_template("sweep"),
36
47
  }
37
48
 
38
49
  def _set_specifications(self) -> None:
@@ -55,14 +66,32 @@ class Receiver(SDRPlayReceiver):
55
66
 
56
67
  def __tuner_1_sweep(self, capture_config: CaptureConfig) -> None:
57
68
  tuner_1_sweep.main(capture_config)
69
+
70
+
71
+ def __tuner_2_fixed(self, capture_config: CaptureConfig) -> None:
72
+ tuner_2_fixed.main(capture_config)
58
73
 
59
74
 
75
+ def __tuner_2_sweep(self, capture_config: CaptureConfig) -> None:
76
+ tuner_2_sweep.main(capture_config)
77
+
78
+
60
79
  def __tuner_1_fixed_validator(self, capture_config: CaptureConfig) -> None:
61
80
  self._default_fixed_validator(capture_config)
62
81
  self._sdrplay_validator(capture_config)
63
-
64
-
82
+
83
+
65
84
  def __tuner_1_sweep_validator(self, capture_config: CaptureConfig) -> None:
85
+ self._default_sweep_validator(capture_config)
86
+ self._sdrplay_validator(capture_config)
87
+
88
+
89
+ def __tuner_2_fixed_validator(self, capture_config: CaptureConfig) -> None:
90
+ self._default_fixed_validator(capture_config)
91
+ self._sdrplay_validator(capture_config)
92
+
93
+
94
+ def __tuner_2_sweep_validator(self, capture_config: CaptureConfig) -> None:
66
95
  self._default_sweep_validator(capture_config)
67
96
  self._sdrplay_validator(capture_config)
68
97
 
@@ -46,6 +46,7 @@ class Receiver(SPECTREReceiver):
46
46
  "hop": int, # STFFT hop shifts window by so many samples
47
47
  "chunk_key": str, # maps to the corresponding chunk class
48
48
  "event_handler_key": str, # maps to the event handler used in post processing
49
+ "watch_extension": str # event handlers watch for files with this extension
49
50
  },
50
51
  "tagged-staircase": {
51
52
  "samp_rate": int, # [Hz]
@@ -63,6 +64,7 @@ class Receiver(SPECTREReceiver):
63
64
  "hop": int, # keyword arguments for scipy STFFT class
64
65
  "chunk_key": str, # maps to the corresponding chunk class
65
66
  "event_handler_key": str, # maps to the event handler used in post processing
67
+ "watch_extension": str # event handlers watch for files with this extension
66
68
  }
67
69
  }
68
70
 
@@ -93,27 +95,35 @@ class Receiver(SPECTREReceiver):
93
95
  event_handler_key = capture_config["event_handler_key"]
94
96
  time_resolution = capture_config["time_resolution"]
95
97
  frequency_resolution = capture_config["frequency_resolution"]
98
+ watch_extension = capture_config["watch_extension"]
96
99
 
97
100
  validators.samp_rate_strictly_positive(samp_rate)
98
101
  validators.chunk_size_strictly_positive(chunk_size)
99
102
  validators.time_resolution(time_resolution, chunk_size)
100
103
  validators.window(window_type,
101
- {},
102
- window_size,
103
- chunk_size,
104
- samp_rate)
104
+ {},
105
+ window_size,
106
+ chunk_size,
107
+ samp_rate)
105
108
  validators.hop(hop)
106
- validators.chunk_key(chunk_key, "fixed")
107
- validators.event_handler_key(event_handler_key, "fixed")
109
+ validators.chunk_key(chunk_key,
110
+ "fixed")
111
+ validators.event_handler_key(event_handler_key,
112
+ "fixed")
113
+ validators.watch_extension(watch_extension,
114
+ "bin")
108
115
 
109
116
  if samp_rate < self.specifications.get("samp_rate_lower_bound"):
110
- raise ValueError(f"Sample rate must be greater than or equal to {self.specifications.get('samp_rate_lower_bound')}")
117
+ raise ValueError((f"Sample rate must be greater than or equal to "
118
+ f"{self.specifications.get('samp_rate_lower_bound')}"))
111
119
 
112
120
  if time_resolution != 0:
113
- raise ValueError(f"Time resolution must be zero. Received: {time_resolution}")
121
+ raise ValueError(f"Time resolution must be zero. "
122
+ f"Got {time_resolution} [s]")
114
123
 
115
124
  if frequency_resolution != 0:
116
- raise ValueError(f"Frequency resolution must be zero. Received {frequency_resolution}")
125
+ raise ValueError((f"Frequency resolution must be zero. "
126
+ f"Got {frequency_resolution}"))
117
127
 
118
128
  # check that the sample rate is an integer multiple of the underlying signal frequency
119
129
  if samp_rate % frequency != 0:
@@ -121,22 +131,26 @@ class Receiver(SPECTREReceiver):
121
131
 
122
132
  a = samp_rate/frequency
123
133
  if a < 2:
124
- raise ValueError(f"The ratio of sampling rate over frequency must be a natural number greater than two. Received: {a}")
134
+ raise ValueError((f"The ratio of sampling rate over frequency must be a natural number greater than two. "
135
+ f"Got {a}"))
125
136
 
126
137
  # ensuring the window type is rectangular
127
138
  if window_type != "boxcar":
128
- raise ValueError(f"The window type must be \"boxcar\". Received: {window_type}")
139
+ raise ValueError((f"The window type must be 'boxcar'. "
140
+ f"Got {window_type}"))
129
141
 
130
142
  # analytical requirement
131
143
  # if p is the number of sampled cycles, we can find that p = window_size / a
132
144
  # the number of sampled cycles must be a positive natural number.
133
145
  p = window_size / a
134
146
  if window_size % a != 0:
135
- raise ValueError(f"The number of sampled cycles must be a positive natural number. Computed that p={p}")
147
+ raise ValueError((f"The number of sampled cycles must be a positive natural number. "
148
+ f"Computed that p={p}"))
136
149
 
137
150
 
138
151
  if amplitude <= 0:
139
- raise ValueError(f"The amplitude must be strictly positive. Received: {amplitude}")
152
+ raise ValueError((f"The amplitude must be strictly positive. "
153
+ f"Got {amplitude}"))
140
154
 
141
155
 
142
156
  def __tagged_staircase_validator(self, capture_config: CaptureConfig) -> None:
@@ -153,26 +167,37 @@ class Receiver(SPECTREReceiver):
153
167
  chunk_key = capture_config["chunk_key"]
154
168
  event_handler_key = capture_config["event_handler_key"]
155
169
  time_resolution = capture_config["time_resolution"]
156
-
170
+ watch_extension = capture_config["watch_extension"]
171
+
157
172
  validators.samp_rate_strictly_positive(samp_rate)
158
173
  validators.chunk_size_strictly_positive(chunk_size)
159
174
  validators.time_resolution(time_resolution, chunk_size)
160
- validators.window(window_type, window_kwargs, window_size, chunk_size, samp_rate)
175
+ validators.window(window_type,
176
+ window_kwargs,
177
+ window_size,
178
+ chunk_size,
179
+ samp_rate)
161
180
  validators.hop(hop)
162
181
  validators.chunk_key(chunk_key, "sweep")
163
182
  validators.event_handler_key(event_handler_key, "sweep")
164
-
183
+ validators.watch_extension(watch_extension,
184
+ "bin")
185
+
165
186
  if freq_step != samp_rate:
166
187
  raise ValueError(f"The frequency step must be equal to the sampling rate")
167
188
 
168
189
  if min_samples_per_step <= 0:
169
- raise ValueError(f"Minimum samples per step must be strictly positive. Received: {min_samples_per_step}")
190
+ raise ValueError((f"Minimum samples per step must be strictly positive. "
191
+ f"Got {min_samples_per_step}"))
170
192
 
171
193
  if max_samples_per_step <= 0:
172
- raise ValueError(f"Maximum samples per step must be strictly positive. Received: {max_samples_per_step}")
194
+ raise ValueError((f"Maximum samples per step must be strictly positive. "
195
+ f"Got {max_samples_per_step}"))
173
196
 
174
197
  if step_increment <= 0:
175
- raise ValueError(f"Step increment must be strictly positive. Received: {step_increment}")
198
+ raise ValueError((f"Step increment must be strictly positive. "
199
+ f"Got {step_increment}"))
176
200
 
177
201
  if min_samples_per_step > max_samples_per_step:
178
- raise ValueError(f"Minimum samples per step cannot be greater than the maximum samples per step. Received: {min_samples_per_step} > {max_samples_per_step}")
202
+ raise ValueError((f"Minimum samples per step cannot be greater than the maximum samples per step. "
203
+ f"Got {min_samples_per_step}, which is greater than {max_samples_per_step}"))
@@ -14,34 +14,39 @@ from scipy.signal import get_window
14
14
  def closed_upper_bound_RF_gain(RF_gain: float,
15
15
  RF_gain_upper_bound: float) -> None:
16
16
  if not (RF_gain <= RF_gain_upper_bound):
17
- raise ValueError(f"RF gain must be strictly less than or equal to {RF_gain_upper_bound} [dB]. Got {RF_gain} [dB]")
17
+ raise ValueError((f"RF gain must be less than or equal to {RF_gain_upper_bound} [dB]. "
18
+ f"Got {RF_gain} [dB]"))
18
19
 
19
20
 
20
21
  def closed_upper_bound_IF_gain(IF_gain: float,
21
22
  IF_gain_upper_bound: float) -> None:
22
23
  if not (IF_gain <= IF_gain_upper_bound):
23
- raise ValueError(f"IF gain must be strictly less than or equal to {IF_gain_upper_bound} [dB]. Got {IF_gain} [dB]")
24
+ raise ValueError((f"IF gain must be less than or equal to {IF_gain_upper_bound} [dB]. "
25
+ f"Got {IF_gain} [dB]"))
24
26
 
25
27
 
26
28
  def closed_confine_center_freq(center_freq: float,
27
29
  center_freq_lower_bound: float,
28
30
  center_freq_upper_bound: float) -> None:
29
31
  if not (center_freq_lower_bound <= center_freq <= center_freq_upper_bound):
30
- raise ValueError(f"Center frequency must be between {center_freq_lower_bound*1e-3} [kHz] and {center_freq_upper_bound*1e-9} [GHz]. Received {center_freq*1e-6} [MHz]")
32
+ raise ValueError((f"Center frequency must be between {center_freq_lower_bound*1e-3} [kHz] and {center_freq_upper_bound*1e-9} [GHz]. "
33
+ f"Got {center_freq*1e-6} [MHz]"))
31
34
 
32
35
 
33
36
  def closed_confine_samp_rate(samp_rate: int,
34
37
  samp_rate_lower_bound: int,
35
38
  samp_rate_upper_bound: int) -> None:
36
39
  if not (samp_rate_lower_bound <= samp_rate <= samp_rate_upper_bound):
37
- raise ValueError(f"Sampling rate must be between {samp_rate_lower_bound*1e-6} [MHz] and {samp_rate_upper_bound*1e-6} [MHz]. Received {samp_rate*1e-6} [MHz]")
40
+ raise ValueError((f"Sampling rate must be between {samp_rate_lower_bound*1e-6} [MHz] and {samp_rate_upper_bound*1e-6} [MHz]. "
41
+ f"Got {samp_rate*1e-6} [MHz]"))
38
42
 
39
43
 
40
44
  def closed_confine_bandwidth(bandwidth: float,
41
45
  bandwidth_lower_bound: float,
42
46
  bandwidth_upper_bound: float) -> None:
43
47
  if not (bandwidth_lower_bound <= bandwidth <= bandwidth_upper_bound):
44
- raise ValueError(f"Bandwidth must be between {bandwidth_lower_bound*1e-3} [kHz] and {bandwidth_upper_bound*1e-6} [MHz]. Received {bandwidth*1e-6} [MHz]")
48
+ raise ValueError((f"Bandwidth must be between {bandwidth_lower_bound*1e-3} [kHz] and {bandwidth_upper_bound*1e-6} [MHz]. "
49
+ f"Got {bandwidth*1e-6} [MHz]"))
45
50
 
46
51
 
47
52
  def is_power_of_two(n):
@@ -55,84 +60,103 @@ def window(window_type: str,
55
60
  samp_rate: float) -> None:
56
61
 
57
62
  if not is_power_of_two(window_size):
58
- raise ValueError(f"Window size must be some power of two. Received: {window_size}")
63
+ raise ValueError((f"Window size must be some power of two. "
64
+ f"Got {window_size} [samples]"))
59
65
 
60
-
61
- if window_size*(1/samp_rate) > chunk_size:
62
- raise ValueError("Windowing interval must be strictly less than the chunk size")
66
+ window_interval = window_size*(1/samp_rate)
67
+ if window_interval > chunk_size:
68
+ raise ValueError((f"The windowing interval must be strictly less than the chunk size. "
69
+ f"Computed the windowing interval to be {window_interval} [s], "
70
+ f"but the chunk size is {chunk_size} [s]"))
63
71
 
64
72
  try:
65
73
  window_params = (window_type,
66
74
  *window_kwargs.values())
67
75
  _ = get_window(window_params, window_size)
68
76
  except Exception as e:
69
- raise Exception(f"An error has occurred while validating the window. Received: {str(e)}")
77
+ raise Exception((f"An error has occurred while validating the window. "
78
+ f"Got {str(e)}"))
70
79
 
71
80
 
72
81
  def hop(hop: int):
73
82
  if hop < 0:
74
- raise ValueError(f"\"hop\" must be strictly positive. Received: {hop_value}")
83
+ raise ValueError((f"Window hop must be strictly positive. "
84
+ f"Got {hop} [samples]"))
75
85
 
76
86
 
77
87
  def center_freq_strictly_positive(center_freq: float):
78
88
  if center_freq <= 0:
79
- raise ValueError(f"Center frequency must be strictly positive. Received: {center_freq*1e-6} [MHz]")
89
+ raise ValueError((f"Center frequency must be strictly positive. "
90
+ f"Got {center_freq*1e-6} [MHz]"))
80
91
 
81
92
 
82
93
  def bandwidth_strictly_positive(bandwidth: float) -> None:
83
94
  if bandwidth < 0:
84
- raise ValueError(f"Bandwidth must be non-negative. Received: {bandwidth*1e-6} [MHz]")
95
+ raise ValueError((f"Bandwidth must be non-negative. "
96
+ f"Got {bandwidth*1e-6} [MHz]"))
85
97
 
86
98
 
87
99
  def nyquist_criterion(samp_rate: int,
88
100
  bandwidth: float) -> None:
89
101
  if samp_rate < bandwidth:
90
- raise ValueError("Sample rate must be greater than or equal to the bandwidth")
102
+ raise ValueError((f"Sample rate must be greater than or equal to the bandwidth. "
103
+ f"Got sample rate {samp_rate} [Hz], and bandwidth {bandwidth} [Hz]"))
91
104
 
92
105
 
93
106
  def samp_rate_strictly_positive(samp_rate: int) -> None:
94
- if samp_rate < 0:
95
- raise ValueError(f"Sample rate must be strictly positive. Received: {samp_rate} [Hz]")
107
+ if samp_rate <= 0:
108
+ raise ValueError((f"Sample rate must be strictly positive. "
109
+ f"Got {samp_rate} [Hz]"))
96
110
 
97
111
 
98
112
  def chunk_size_strictly_positive(chunk_size: int) -> None:
99
113
  if chunk_size <= 0:
100
- raise ValueError(f"Chunk size must be strictly positive. Received: {chunk_size} [s]")
114
+ raise ValueError((f"Chunk size must be strictly positive. "
115
+ f"Got {chunk_size} [s]"))
101
116
 
102
117
 
103
118
  def time_resolution(time_resolution: float,
104
119
  chunk_size: int) -> None:
105
120
  if time_resolution < 0:
106
- raise ValueError(f"Time resolution must be non-negative. Received: {time_resolution} [s]")
121
+ raise ValueError((f"Time resolution must be non-negative. "
122
+ f"Got {time_resolution} [s]"))
107
123
 
108
124
  if time_resolution > chunk_size:
109
- raise ValueError("Time resolution must be less than or equal to chunk size")
125
+ raise ValueError(f"Time resolution must be less than or equal to chunk size. "
126
+ f"Got time resolution {time_resolution} [s], "
127
+ f"and chunk size {chunk_size} [s]")
110
128
 
111
129
 
112
130
  def frequency_resolution(frequency_resolution: float,
113
131
  bandwidth: float = None) -> None:
114
132
  if frequency_resolution < 0:
115
- raise ValueError(f"Frequency resolution must be non-negative. Received {frequency_resolution} [Hz]")
133
+ raise ValueError((f"Frequency resolution must be non-negative. "
134
+ f"Got {frequency_resolution} [Hz]"))
116
135
 
117
136
  if bandwidth is not None and frequency_resolution >= bandwidth:
118
- raise ValueError(f"Frequency resolution must be less than the bandwidth. Received frequency resolution to be {frequency_resolution} [Hz], with bandwidth {bandwidth} [Hz]")
137
+ raise ValueError((f"Frequency resolution must be less than the bandwidth. "
138
+ f"Got frequency resolution to be {frequency_resolution} [Hz], "
139
+ f"with bandwidth {bandwidth} [Hz]"))
119
140
 
120
141
 
121
142
  def chunk_key(chunk_key: str,
122
143
  expected_chunk_key: str) -> None:
123
144
  if chunk_key != expected_chunk_key:
124
- raise ValueError(f"Expected \"{expected_chunk_key}\" for the chunk_key, received: {chunk_key}")
145
+ raise ValueError((f"Expected {expected_chunk_key} for the chunk key. "
146
+ f"Got {chunk_key}"))
125
147
 
126
148
 
127
149
  def event_handler_key(event_handler_key: str,
128
150
  expected_event_handler_key: str) -> None:
129
151
  if event_handler_key != expected_event_handler_key:
130
- raise ValueError(f"Expected \"{expected_event_handler_key}\" for the event_handler_key, received: {event_handler_key}")
152
+ raise ValueError((f"Expected {expected_event_handler_key} for the event handler key. "
153
+ f"Got {event_handler_key}"))
131
154
 
132
155
 
133
156
  def gain_is_negative(gain: float) -> None:
134
157
  if gain > 0:
135
- raise ValueError(f"Gain must be non-positive. Received {gain} [dB]")
158
+ raise ValueError(f"Gain must be non-positive. "
159
+ f"Got {gain} [dB]")
136
160
 
137
161
 
138
162
  def _compute_num_steps_per_sweep(min_freq: float,
@@ -151,7 +175,8 @@ def num_steps_per_sweep(min_freq: float,
151
175
  samp_rate,
152
176
  freq_step)
153
177
  if num_steps_per_sweep <= 1:
154
- raise ValueError(f"We need strictly greater than one sample per step. Computed: {num_steps_per_sweep}")
178
+ raise ValueError((f"We need strictly greater than one step per sweep. "
179
+ f"Computed {num_steps_per_sweep} step per sweep"))
155
180
 
156
181
 
157
182
  def sweep_interval(min_freq: float,
@@ -167,19 +192,25 @@ def sweep_interval(min_freq: float,
167
192
  num_samples_per_sweep = num_steps_per_sweep * samples_per_step
168
193
  sweep_interval = num_samples_per_sweep * 1/samp_rate
169
194
  if sweep_interval > chunk_size:
170
- raise ValueError(f"Sweep interval must be less than the chunk size. Computed sweep interval: {sweep_interval} [s] is greater than the given chunk size {chunk_size} [s]")
195
+ raise ValueError((f"Sweep interval must be less than the chunk size. "
196
+ f"The computed sweep interval is {sweep_interval} [s], "
197
+ f"but the given chunk size is {chunk_size} [s]"))
171
198
 
172
199
 
173
200
  def num_samples_per_step(samples_per_step: int,
174
201
  window_size: int) -> None:
175
202
  if window_size >= samples_per_step:
176
- raise ValueError(f"Window size must be strictly less than the number of samples per step. Received window size {window_size} [samples], which is more than or equal to the number of samples per step {samples_per_step}")
203
+ raise ValueError((f"Window size must be strictly less than the number of samples per step. "
204
+ f"Got window size {window_size} [samples], which is more than or equal "
205
+ f"to the number of samples per step {samples_per_step}"))
177
206
 
178
207
 
179
208
  def non_overlapping_steps(freq_step: float,
180
209
  samp_rate: int) -> None:
181
210
  if freq_step < samp_rate:
182
- raise NotImplementedError(f"SPECTRE does not yet support spectral steps overlapping in frequency. Received frequency step {freq_step/1e6} [MHz] which is less than the sample rate {samp_rate/1e6} [MHz]")
211
+ raise NotImplementedError(f"SPECTRE does not yet support spectral steps overlapping in frequency. "
212
+ f"Got frequency step {freq_step/1e6} [MHz] which is less than the sample "
213
+ f"rate {samp_rate/1e6} [MHz]")
183
214
 
184
215
 
185
216
  def step_interval(samples_per_step: int,
@@ -187,7 +218,14 @@ def step_interval(samples_per_step: int,
187
218
  api_latency: float) -> None:
188
219
  step_interval = samples_per_step * 1/samp_rate # [s]
189
220
  if step_interval < api_latency:
190
- warning_message = f"The computed step interval of {step_interval} [s] is of the order of empirically derived api latency {api_latency} [s]; you may experience undefined behaviour!"
221
+ warning_message = (f"The computed step interval of {step_interval} [s] is of the order of empirically "
222
+ f"derived api latency {api_latency} [s]; you may experience undefined behaviour!")
191
223
  warnings.warn(warning_message)
192
224
  _LOGGER.warning(warning_message)
193
225
 
226
+
227
+ def watch_extension(watch_extension: str,
228
+ target_extension: str) -> None:
229
+ if watch_extension != target_extension:
230
+ raise ValueError((f"Expected {target_extension} for the watch extension. "
231
+ f"Got {watch_extension}"))
@@ -94,7 +94,7 @@ def fetch_chunks(instrument_code: Optional[str],
94
94
  os.mkdir(temp_dir)
95
95
 
96
96
  if instrument_code not in CALLISTO_INSTRUMENT_CODES:
97
- raise ValueError(f"No match found for \"{instrument_code}\". Expected one of {CALLISTO_INSTRUMENT_CODES}")
97
+ raise ValueError(f"No match found for '{instrument_code}'. Expected one of {CALLISTO_INSTRUMENT_CODES}")
98
98
 
99
99
  download_callisto_data(instrument_code, year, month, day)
100
100
  unzip_to_chunks()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spectre-core
3
- Version: 0.0.7
3
+ Version: 0.0.9
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
@@ -698,7 +698,7 @@ Requires-Dist: watchdog==4.0.0
698
698
 
699
699
  :loudspeaker: **This project is under active development. Contributors welcome.** :loudspeaker:
700
700
 
701
- ```spectre-core``` provides a SDR receiver-agnostic digital signal processing framework. It is the core Python package used by the [`spectre`](https://github.com/jcfitzpatrick12/spectre.git) program.
701
+ ```spectre-core``` provides a SDR receiver-agnostic digital signal processing framework. It is the core Python package used by the [`spectre`](https://github.com/jcfitzpatrick12/spectre.git) program.
702
702
 
703
703
 
704
704
  ## Installation
@@ -23,17 +23,26 @@ spectre_core/plotting/base.py,sha256=4HhPPP7BNe5_SUAl1Ee52_QP62Zzh3kmNJwLzCHKG3c
23
23
  spectre_core/plotting/factory.py,sha256=pYil7PbTs3e08ek6nq-y8mSEtNu7DI6uloiCwsuIoOw,940
24
24
  spectre_core/plotting/format.py,sha256=Et-uc4juDl_2spLRZOzqaUVBP8-J3LPnV5M6CllM74E,512
25
25
  spectre_core/plotting/panel_register.py,sha256=rsmG2hXnvVOy6vfWsEZlZQAXej7bRtqtvjnXObyUCg4,351
26
- spectre_core/plotting/panel_stack.py,sha256=PxtoXp9WzSe38gG6dILoM__X_BxwNdydXRpzs3knsF0,5191
26
+ spectre_core/plotting/panel_stack.py,sha256=fB1kz9NgsnMl4l41cncNPaVmg5DtYRRvO4lhX8OLOOo,5239
27
27
  spectre_core/plotting/library/__init__.py,sha256=lQhj4kme2eKb-JSrHvLWkAjI7eVqexOxxUguo8LUXno,270
28
28
  spectre_core/plotting/library/frequency_cuts/panel.py,sha256=LtCWNad4Z1gxBOxi_Ni1d6KZxsgoR5Mbm2tWXN7VDqI,2772
29
29
  spectre_core/plotting/library/integral_over_frequency/panel.py,sha256=tvro2MCtY4Q0q7WdMUz9eW5Cvrweeqqse20q3x4D4fM,1274
30
30
  spectre_core/plotting/library/spectrogram/panel.py,sha256=CAaPz7sDYoWZ3-4Jb1kVRu9bvJYaBRiXvoMkV7QXWqk,3556
31
31
  spectre_core/plotting/library/time_cuts/panel.py,sha256=u9Sbnwy6ex61y5Jl-D77HlYvuuXdK8_YB-o2gCovCTY,2947
32
+ spectre_core/post_processing/__init__.py,sha256=pRzy46C32j9sfQjbCJKuVw0tQjuOErfrievq2m1Vx8c,239
33
+ spectre_core/post_processing/base.py,sha256=h0qtoigD6ZBkHm0_wV1lJX55AwppKC5htfXVWUWMMLQ,5403
34
+ spectre_core/post_processing/event_handler_register.py,sha256=DwlkU92IFkZ1_qvGfhep_OfuqTOIR_efY5qFpn1perw,498
35
+ spectre_core/post_processing/factory.py,sha256=7KZBysPymsYhpW4KI4H1jvjssmZY1mIvhkln91joeUM,1133
36
+ spectre_core/post_processing/post_processor.py,sha256=dQX71pUh83i6mNedTk-EVAYnxjAUs6S2Q8HkCXkiwns,1428
37
+ spectre_core/post_processing/library/__init__.py,sha256=vEwAnAV-sv7WcNYOdnjr1JVqZYr29Wr2cv01eoxwdmg,282
38
+ spectre_core/post_processing/library/fixed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ spectre_core/post_processing/library/fixed/event_handler.py,sha256=wfOdahnY78Movu0ESbuUy-QmpBbXhNk6_DKzsXS9G0Y,1394
40
+ spectre_core/post_processing/library/sweep/event_handler.py,sha256=ap7K8hOxgb8wsX9uJfEDW7lYXCyNKFlsva7vinW_6Ak,2194
32
41
  spectre_core/receivers/__init__.py,sha256=kKfhqrGs9sSPGLbrpTqScv816iPZOvT3ry3zSMcqLkM,227
33
- spectre_core/receivers/base.py,sha256=UW2L28juLWyzI6t6Ub6Ws-K4J9U4HT4JSgdVtuiYP8k,16012
42
+ spectre_core/receivers/base.py,sha256=szW7BfJEQiZ7dEiD_Oyk2lG16Kns3zKmnl_lIqKHP1o,16478
34
43
  spectre_core/receivers/factory.py,sha256=aE-Yw_cnlkhRe5HxK0JqhDzd2AwZcKmB2QkAKwaq27Y,873
35
44
  spectre_core/receivers/receiver_register.py,sha256=xHcRnT-3NQxyIWL3nyT3P9qT14Wl5liM9HbflOvOUAM,617
36
- spectre_core/receivers/validators.py,sha256=aP8nbRWZxU5pOwBIXTojXuHd3i9yksHW_vIBn4cbKug,8481
45
+ spectre_core/receivers/validators.py,sha256=ZWnFsZJsbHBB-ZIrp3WK8BPqb4ev7TYm6VPQPbljTE4,9965
37
46
  spectre_core/receivers/library/__init__.py,sha256=xmtF5p3_ZkGfso_pKnxSgUcXXFLEBwERGPq1Pek7cOU,274
38
47
  spectre_core/receivers/library/rsp1a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
48
  spectre_core/receivers/library/rsp1a/receiver.py,sha256=xs_aNMhwIYD83KwutizjBziyu9XsfHqGqvQXOFcglz4,2224
@@ -41,12 +50,14 @@ spectre_core/receivers/library/rsp1a/gr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
41
50
  spectre_core/receivers/library/rsp1a/gr/fixed.py,sha256=P39JwuOe-ydk9m8BNOHFDpzaqjenRnTIA7tpECshfUM,3369
42
51
  spectre_core/receivers/library/rsp1a/gr/sweep.py,sha256=9ZAoI0u1814lOM_cVwJnLUNSgM3VMstfvWKP67dVEV4,4741
43
52
  spectre_core/receivers/library/rspduo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- spectre_core/receivers/library/rspduo/receiver.py,sha256=3pxWXKt9IS4tpUKjYKwnCePzT0Zu3XIShi-LRI8VAH4,2362
53
+ spectre_core/receivers/library/rspduo/receiver.py,sha256=EMBFWwFjoIP2buwwamyb3uaYqMQwQHMzNi-we7_OfcU,3377
45
54
  spectre_core/receivers/library/rspduo/gr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py,sha256=YaRS9oNRry-6eZPIsFjNlKcZqhNcQw3X9N8Dw877aeM,3603
47
- spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py,sha256=0WDfR1R6uOnIEb_4aPQN-O5Gh62utZrvRukPEF3xzDU,5113
55
+ spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py,sha256=xO9F_GBwgNnebrxoZYPwbXLL2HmH0CNLrVlpQ6Z7bx0,3887
56
+ spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py,sha256=gR5arbjvJBJjCdQnpAdwNd7bEtnvDbhL_ttUhKk0Zss,5066
57
+ spectre_core/receivers/library/rspduo/gr/tuner_2_fixed.py,sha256=O86R3tuJ-7cZXfVECuZkds-wCxKdBKJ8MhVACJFgzeo,4130
58
+ spectre_core/receivers/library/rspduo/gr/tuner_2_sweep.py,sha256=4JgJ2QRO5DFPfqUTHBojj8ERR_4QpHyTBqnAXhOzykE,4747
48
59
  spectre_core/receivers/library/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
- spectre_core/receivers/library/test/receiver.py,sha256=ihfmUFFFEcyjMnMrqP4ajgB1mdC0vWEe9LFkNYHZn_Y,8346
60
+ spectre_core/receivers/library/test/receiver.py,sha256=hr6t8jpUwVEP2zMpjORKpzqLaBllxmbqF1fQE1DLWQw,9329
50
61
  spectre_core/receivers/library/test/gr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
62
  spectre_core/receivers/library/test/gr/cosine_signal_1.py,sha256=6XgYjYMh-QNPs_UUGUQcU_VQFr6BG4OLdsW-M-RU5Ww,2943
52
63
  spectre_core/receivers/library/test/gr/tagged_staircase.py,sha256=5rJHbB-3vdXjqT8DrcAGUSebaAqZ5RQtYHBWgH9iU2E,3465
@@ -55,18 +66,9 @@ spectre_core/spectrograms/analytical.py,sha256=c5X40YgLlutP8sbz6dqODZaCmZ98sAjub
55
66
  spectre_core/spectrograms/array_operations.py,sha256=6qKd3y2z6Pmu_U8yxTR4FN4eMhS10KgZ8rH60B_IXqw,2577
56
67
  spectre_core/spectrograms/spectrogram.py,sha256=EqeQyvjzjoKaXou4vJbPbRx85BeMPB9iiJtFZcCyimI,19488
57
68
  spectre_core/spectrograms/transform.py,sha256=xo7ch2lrRkJ54cfIqbkaTHNo_AptBuK0zRELPf7SfIE,13860
58
- spectre_core/watchdog/__init__.py,sha256=gGjyC7MeCfarB9Yu0RycZs-Wh_of7Cbp_wIyovYvtOQ,232
59
- spectre_core/watchdog/base.py,sha256=xDvWtt7fXeBQoZpR-ZxopAAKNHRa8YNSmfR63bYsSlU,4178
60
- spectre_core/watchdog/event_handler_register.py,sha256=DwlkU92IFkZ1_qvGfhep_OfuqTOIR_efY5qFpn1perw,498
61
- spectre_core/watchdog/factory.py,sha256=Uqx4nIZPVRxx7hRgm1M-1p2Sc7m3ObkIKWIC_ru9ObU,1115
62
- spectre_core/watchdog/post_processor.py,sha256=rZwe4u_T-7F7_q485PtUUZMAxPPec35zXV81MyERIjg,1673
63
- spectre_core/watchdog/library/__init__.py,sha256=vEwAnAV-sv7WcNYOdnjr1JVqZYr29Wr2cv01eoxwdmg,282
64
- spectre_core/watchdog/library/fixed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
- spectre_core/watchdog/library/fixed/event_handler.py,sha256=yWWS80LukB-cTrKBsF4-pRvw2obkX2MzQ5ZGytOtmAg,1387
66
- spectre_core/watchdog/library/sweep/event_handler.py,sha256=wDISZiQXBeqLDPxgEMo0a2QAXqQVOO7fng3yhZWSR74,2188
67
- spectre_core/web_fetch/callisto.py,sha256=874osjbp61qFwRgV584fpSp7E-xz8g1FEelbNBKhCsw,3632
68
- spectre_core-0.0.7.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
69
- spectre_core-0.0.7.dist-info/METADATA,sha256=mSmKzIO9I8ps1yKp-jJjZ_D_OhDQH_lUKfgzeW2-VFc,42149
70
- spectre_core-0.0.7.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
71
- spectre_core-0.0.7.dist-info/top_level.txt,sha256=-UsyjpFohXgZpgcZ9QbVeXhsIyF3Am8RxNFNDV_Ta2Y,13
72
- spectre_core-0.0.7.dist-info/RECORD,,
69
+ spectre_core/web_fetch/callisto.py,sha256=yW0NkmHqfUf2uYgJjSRxou6e65_mYciPhHwQIvKLK_w,3630
70
+ spectre_core-0.0.9.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
71
+ spectre_core-0.0.9.dist-info/METADATA,sha256=15Uh828ojJg6w-HCrIv7nka9wT_eVhv9svE33hmHI7k,42150
72
+ spectre_core-0.0.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
73
+ spectre_core-0.0.9.dist-info/top_level.txt,sha256=-UsyjpFohXgZpgcZ9QbVeXhsIyF3Am8RxNFNDV_Ta2Y,13
74
+ spectre_core-0.0.9.dist-info/RECORD,,
@@ -1,105 +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 logging import getLogger
6
- _LOGGER = getLogger(__name__)
7
-
8
- import os
9
- import time
10
- from queue import Queue
11
- from typing import Any
12
- from abc import ABC, abstractmethod
13
- from math import floor
14
-
15
- from watchdog.events import FileSystemEventHandler
16
-
17
- from spectre_core.chunks.factory import get_chunk_from_tag
18
- from spectre_core.file_handlers.configs import CaptureConfig
19
- from spectre_core.spectrograms.spectrogram import Spectrogram
20
- from spectre_core.spectrograms.transform import join_spectrograms
21
- from spectre_core.spectrograms.transform import (
22
- time_average,
23
- frequency_average
24
- )
25
-
26
-
27
- class BaseEventHandler(ABC, FileSystemEventHandler):
28
- def __init__(self,
29
- tag: str,
30
- exception_queue: Queue,
31
- extension: str):
32
- self._tag = tag
33
- self._Chunk = get_chunk_from_tag(tag)
34
-
35
- self._capture_config = CaptureConfig(tag)
36
-
37
- self._extension = extension
38
- self._exception_queue = exception_queue # Queue to propagate exceptions
39
-
40
- self._spectrogram: Spectrogram = None # spectrogram cache
41
-
42
-
43
- @abstractmethod
44
- def process(self, file_path: str) -> None:
45
- pass
46
-
47
-
48
- def on_created(self, event):
49
- if not event.is_directory and event.src_path.endswith(self._extension):
50
- _LOGGER.info(f"Noticed: {event.src_path}")
51
- try:
52
- self._wait_until_stable(event.src_path)
53
- self.process(event.src_path)
54
- except Exception as e:
55
- _LOGGER.error(f"An error has occured while processing {event.src_path}",
56
- exc_info=True)
57
- self._flush_spectrogram() # flush the internally stored spectrogram
58
- # Capture the exception and propagate it through the queue
59
- self._exception_queue.put(e)
60
-
61
-
62
- def _wait_until_stable(self, file_path: str):
63
- _LOGGER.info(f"Waiting for file stability: {file_path}")
64
- size = -1
65
- while True:
66
- current_size = os.path.getsize(file_path)
67
- if current_size == size:
68
- _LOGGER.info(f"File is now stable: {file_path}")
69
- break # File is stable when the size hasn't changed
70
- size = current_size
71
- time.sleep(0.25)
72
-
73
-
74
- def _average_in_time(self, spectrogram: Spectrogram) -> Spectrogram:
75
- requested_time_resolution = self._capture_config.get('time_resolution') # [s]
76
- if requested_time_resolution is None:
77
- raise KeyError(f"Time resolution has not been specified in the capture config!")
78
- average_over = floor(requested_time_resolution/spectrogram.time_resolution) if requested_time_resolution > spectrogram.time_resolution else 1
79
- return time_average(spectrogram, average_over)
80
-
81
-
82
- def _average_in_frequency(self, spectrogram: Spectrogram) -> Spectrogram:
83
- frequency_resolution = self._capture_config.get('frequency_resolution') # [Hz]
84
- if frequency_resolution is None:
85
- raise KeyError(f"Frequency resolution has not been specified in the capture config!")
86
- average_over = floor(frequency_resolution/spectrogram.frequency_resolution) if frequency_resolution > spectrogram.frequency_resolution else 1
87
- return frequency_average(spectrogram, average_over)
88
-
89
-
90
- def _join_spectrogram(self, spectrogram: Spectrogram) -> None:
91
- if self._spectrogram is None:
92
- self._spectrogram = spectrogram
93
- else:
94
- self._spectrogram = join_spectrograms([self._spectrogram, spectrogram])
95
-
96
- if self._spectrogram.time_range >= self._capture_config.get("joining_time"):
97
- self._flush_spectrogram()
98
-
99
-
100
- def _flush_spectrogram(self) -> None:
101
- if self._spectrogram:
102
- _LOGGER.info(f"Flushing spectrogram to file with chunk start time {self._spectrogram.chunk_start_time}")
103
- self._spectrogram.save()
104
- _LOGGER.info("Flush successful, resetting spectrogram cache")
105
- self._spectrogram = None # reset the cache