Skip to content

Commit 03fd0a6

Browse files
committed
[cr] add printable enclosure designs
1 parent 824ff10 commit 03fd0a6

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed
137 KB
Binary file not shown.

src/openflight/streaming/cfar.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ class CFARConfig:
3636
# This masks out stationary clutter and slow movements
3737
dc_mask_bins: int = 150 # Mask bins 0-150 and 3946-4095 (~15 mph)
3838

39+
# Nyquist masking - bins to ignore near Nyquist frequency (max detectable speed)
40+
# At 30ksps with 4096 FFT: bin 2047 = Nyquist-1 = ~208 mph
41+
# Body reflection artifacts appear at this edge when standing in front of radar
42+
nyquist_mask_bins: int = 10 # Mask bins 2038-2047 and 2048-2057
43+
44+
# Minimum magnitude threshold for detection
45+
# Real golf signals have magnitude 1-100+, noise artifacts are 0.05-0.2
46+
# This filters out low-energy edge artifacts that pass SNR threshold
47+
min_magnitude: float = 0.5
48+
3949
# Edge masking - don't detect near spectrogram edges
4050
edge_mask_time: int = 4 # Mask first/last N time windows
4151
edge_mask_freq: int = 50 # Mask first/last N frequency bins

src/openflight/streaming/processor.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,23 @@ def process_block(self, block: IQBlock) -> Optional[SpeedReading]:
108108

109109
half = cfg.fft_size // 2
110110
dc_mask = cfg.cfar.dc_mask_bins
111+
nyquist_mask = cfg.cfar.nyquist_mask_bins
111112

112-
# Analyze positive frequencies (outbound) above DC mask
113-
pos_region = magnitude[dc_mask:half]
113+
# Valid frequency ranges (excluding DC and Nyquist edges)
114+
# Positive frequencies: dc_mask to (half - nyquist_mask)
115+
# Negative frequencies: (half + nyquist_mask) to (fft_size - dc_mask)
116+
pos_end = half - nyquist_mask
117+
neg_start = half + nyquist_mask
118+
neg_end = cfg.fft_size - dc_mask
119+
120+
# Analyze positive frequencies (outbound) - excludes DC and Nyquist
121+
pos_region = magnitude[dc_mask:pos_end]
114122
pos_peak_idx = np.argmax(pos_region)
115123
pos_peak_bin = pos_peak_idx + dc_mask
116124
pos_peak_mag = magnitude[pos_peak_bin]
117125

118-
# Analyze negative frequencies (inbound) above DC mask
119-
neg_start = half + dc_mask
120-
neg_region = magnitude[neg_start:]
126+
# Analyze negative frequencies (inbound) - excludes DC and Nyquist
127+
neg_region = magnitude[neg_start:neg_end]
121128
neg_peak_idx = np.argmax(neg_region)
122129
neg_peak_bin = neg_peak_idx + neg_start
123130
neg_peak_mag = magnitude[neg_peak_bin]
@@ -153,6 +160,13 @@ def process_block(self, block: IQBlock) -> Optional[SpeedReading]:
153160
if snr < snr_threshold:
154161
return None # Signal not strong enough
155162

163+
# Minimum magnitude filter (rejects low-energy edge artifacts)
164+
min_mag = cfg.cfar.min_magnitude
165+
if peak_mag < min_mag:
166+
if self.debug:
167+
print(f" [LOW-MAG] mag={peak_mag:.4f} < {min_mag} speed={speed_mph:.1f}mph")
168+
return None
169+
156170
# Speed filter
157171
if not (cfg.min_speed_mph <= speed_mph <= cfg.max_speed_mph):
158172
if self.debug:

0 commit comments

Comments
 (0)