@@ -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
0 commit comments