Skip to content

Commit a425d79

Browse files
committed
solves review comments
1 parent c4a136c commit a425d79

3 files changed

Lines changed: 25 additions & 14 deletions

File tree

radioshaq/radioshaq/radio/sdr_tx.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,13 @@ def __init__(
7272
audit_log_path: str | None = None,
7373
restricted_region: str = "FCC",
7474
band_plan_source: dict[str, BandPlan] | None = None,
75+
rig_or_sdr: str = "hackrf",
7576
) -> None:
7677
self.allow_bands_only = allow_bands_only
7778
self.audit_log_path = audit_log_path
7879
self.restricted_region = restricted_region
7980
self._band_plan_source = band_plan_source
81+
self._rig_or_sdr = rig_or_sdr
8082

8183
def _check_compliance(self, frequency_hz: float, occupied_bandwidth_hz: float | None = None) -> None:
8284
"""Raise ValueError if TX not allowed on this frequency."""
@@ -125,7 +127,7 @@ def _audit(
125127
frequency_hz=frequency_hz,
126128
duration_sec=duration_sec,
127129
mode=mode,
128-
rig_or_sdr="hackrf",
130+
rig_or_sdr=self._rig_or_sdr,
129131
operator_id=None,
130132
audit_log_path=self.audit_log_path,
131133
success=success,
@@ -304,6 +306,7 @@ def __init__(
304306
audit_log_path=audit_log_path,
305307
restricted_region=restricted_region,
306308
band_plan_source=band_plan_source,
309+
rig_or_sdr="hackrf_broker",
307310
)
308311
self._base_url = base_url.rstrip("/")
309312
self._auth_token = auth_token

radioshaq/radioshaq/remote_receiver/server.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -334,17 +334,18 @@ async def tx_tone(
334334
loop = asyncio.get_running_loop()
335335

336336
async def _fn(dev: Any) -> None:
337-
# Generate a simple tone in int8 interleaved I/Q, matching HackRF expectations.
338-
tone_hz = 1000.0
339-
num_samples = int(duration_sec * sample_rate)
340-
t = np.arange(num_samples, dtype=np.float64) / sample_rate
341-
i = (127 * 0.3 * np.cos(2 * np.pi * tone_hz * t)).astype(np.int8)
342-
q = (127 * 0.3 * np.sin(2 * np.pi * tone_hz * t)).astype(np.int8)
343-
iq = np.empty(2 * num_samples, dtype=np.int8)
344-
iq[0::2] = i
345-
iq[1::2] = q
346-
347337
def _blocking_tx() -> None:
338+
# Generate tone and run TX in thread pool to avoid blocking the event loop
339+
# (NumPy ops for up to ~60M samples can take hundreds of ms).
340+
tone_hz = 1000.0
341+
num_samples = int(duration_sec * sample_rate)
342+
t = np.arange(num_samples, dtype=np.float64) / sample_rate
343+
i = (127 * 0.3 * np.cos(2 * np.pi * tone_hz * t)).astype(np.int8)
344+
q = (127 * 0.3 * np.sin(2 * np.pi * tone_hz * t)).astype(np.int8)
345+
iq = np.empty(2 * num_samples, dtype=np.int8)
346+
iq[0::2] = i
347+
iq[1::2] = q
348+
buf = iq.tobytes()
348349
try:
349350
dev.center_freq = int(frequency_hz)
350351
dev.sample_rate = sample_rate
@@ -356,7 +357,6 @@ def _blocking_tx() -> None:
356357
# Older or stub objects may not expose these attributes.
357358
pass
358359
try:
359-
buf = iq.tobytes()
360360
stream_hackrf_iq_bytes(dev, buf, duration_sec)
361361
except (AttributeError, TypeError) as e:
362362
# Stub/test device or API mismatch — warn and skip (non-fatal for audit).
@@ -638,13 +638,15 @@ async def tx_tone(request: Request) -> dict[str, Any]:
638638
operator_id = receiver.station_id
639639
except Exception:
640640
operator_id = None
641+
audit_log_path = os.environ.get("TX_AUDIT_LOG_PATH") or None
641642
log_tx(
642643
frequency_hz=frequency_hz,
643644
duration_sec=duration_sec,
644645
mode="tone",
645646
rig_or_sdr="hackrf_broker",
646647
operator_id=operator_id,
647648
success=tx_succeeded,
649+
audit_log_path=audit_log_path,
648650
)
649651
return {"success": True, "notes": "HackRF tone transmitted via remote receiver"}
650652

@@ -764,6 +766,7 @@ async def tx_iq(request: Request) -> dict[str, Any]:
764766
operator_id = receiver.station_id
765767
except Exception:
766768
pass
769+
audit_log_path = os.environ.get("TX_AUDIT_LOG_PATH") or None
767770
log_tx(
768771
frequency_hz=frequency_hz,
769772
duration_sec=duration_sec,
@@ -772,6 +775,7 @@ async def tx_iq(request: Request) -> dict[str, Any]:
772775
operator_id=operator_id,
773776
occupied_bandwidth_hz=occupied_bandwidth_hz,
774777
success=tx_succeeded,
778+
audit_log_path=audit_log_path,
775779
)
776780
return {"success": True, "notes": "HackRF IQ transmitted via remote receiver"}
777781

radioshaq/radioshaq/specialized/radio_tx.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,14 @@ async def _transmit_voice(
152152
raise RuntimeError(
153153
"SDR voice TX from file requires optional deps: uv sync --extra voice_tx"
154154
) from e
155-
data, sr = sf.read(str(audio_path), dtype="float32", always_2d=False)
156-
# Modulate to 2 MHz (HackRF-friendly); run CPU-heavy scipy in executor to avoid blocking event loop.
157155
tx_rate = 2_000_000
158156
loop = asyncio.get_running_loop()
157+
# Run blocking file I/O in executor to avoid stalling the event loop.
158+
data, sr = await loop.run_in_executor(
159+
None,
160+
lambda: sf.read(str(audio_path), dtype="float32", always_2d=False),
161+
)
162+
# Modulate to 2 MHz (HackRF-friendly); run CPU-heavy scipy in executor to avoid blocking event loop.
159163
if mode_name.value in ("NFM",):
160164
iq = await loop.run_in_executor(
161165
None, lambda: nfm_modulate(data, int(sr), tx_rate, deviation_hz=2_500.0)

0 commit comments

Comments
 (0)