You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DroneDetector/scripts_nn/data_saver_headless.py

237 lines
7.0 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import time
import signal
import argparse
import threading
import numpy as np
from gnuradio import gr
import osmosdr
class SimsiSink(gr.sync_block):
def __init__(self, save_dir="./signal", file_tag="fragment_", split_size=1000000, delay=0.0):
gr.sync_block.__init__(
self,
name="Simsi_Sink",
in_sig=[np.complex64],
out_sig=None,
)
self.save_dir = str(save_dir)
self.file_tag = str(file_tag)
self.split_size = int(split_size)
self.delay = float(delay)
os.makedirs(self.save_dir, exist_ok=True)
self.file_index = 0
self.current_len = 0
self.current_fd = None
self.in_progress_path = os.path.join(self.save_dir, "reading_in_progress")
self.in_progress_fd = None
self._open_next_file()
def _touch_in_progress(self):
if self.in_progress_fd is not None:
try:
self.in_progress_fd.close()
except Exception:
pass
self.in_progress_fd = open(self.in_progress_path, "wb")
def _remove_in_progress(self):
if self.in_progress_fd is not None:
try:
self.in_progress_fd.close()
except Exception:
pass
self.in_progress_fd = None
if os.path.exists(self.in_progress_path):
try:
os.remove(self.in_progress_path)
except Exception:
pass
def _current_file_path(self):
return os.path.join(self.save_dir, f"{self.file_tag}{self.file_index}")
def _open_next_file(self):
self._touch_in_progress()
path = self._current_file_path()
self.current_fd = open(path, "wb")
self.current_len = 0
print(f"Opened file: {path}", flush=True)
def _rotate_file(self):
path = self._current_file_path()
print(f"Saving file: {path}", flush=True)
if self.current_fd is not None:
self.current_fd.close()
self.current_fd = None
self._remove_in_progress()
if self.delay > 0:
time.sleep(self.delay)
self.file_index += 1
self._open_next_file()
def work(self, input_items, output_items):
data = input_items[0]
offset = 0
total = len(data)
while offset < total:
remaining = self.split_size - self.current_len
chunk = min(remaining, total - offset)
self.current_fd.write(data[offset:offset + chunk].copy())
self.current_len += chunk
offset += chunk
if self.current_len >= self.split_size:
self._rotate_file()
return len(data)
def stop(self):
try:
if self.current_fd is not None:
self.current_fd.close()
self.current_fd = None
finally:
self._remove_in_progress()
return True
class DataSaver(gr.top_block):
def __init__(
self,
serial: str,
freq: float,
save_dir: str,
file_tag: str,
samp_rate: float,
split_size: int,
delay: float,
rf_gain: float,
if_gain: float,
bb_gain: float,
):
super().__init__("data_saver_headless", catch_exceptions=True)
self.serial = serial
self.freq = float(freq)
self.save_dir = save_dir
self.file_tag = file_tag
self.samp_rate = float(samp_rate)
self.split_size = int(split_size)
self.delay = float(delay)
self.rf_gain = float(rf_gain)
self.if_gain = float(if_gain)
self.bb_gain = float(bb_gain)
dev_args = f"numchan=1 hackrf={self.serial}"
self.source = osmosdr.source(args=dev_args)
self.source.set_time_unknown_pps(osmosdr.time_spec_t())
self.source.set_sample_rate(self.samp_rate)
self.source.set_center_freq(self.freq, 0)
self.source.set_freq_corr(0, 0)
self.source.set_dc_offset_mode(0, 0)
self.source.set_iq_balance_mode(0, 0)
self.source.set_gain_mode(False, 0)
self.source.set_gain(self.rf_gain, 0)
self.source.set_if_gain(self.if_gain, 0)
self.source.set_bb_gain(self.bb_gain, 0)
self.source.set_antenna("", 0)
self.source.set_bandwidth(0, 0)
self.sink = SimsiSink(
save_dir=self.save_dir,
file_tag=self.file_tag,
split_size=self.split_size,
delay=self.delay,
)
self.connect((self.source, 0), (self.sink, 0))
def parse_args():
parser = argparse.ArgumentParser(description="Headless GNU Radio IQ saver for HackRF")
parser.add_argument("--serial", required=True, help="HackRF serial number")
parser.add_argument("--freq", type=float, required=True, help="Center frequency in Hz")
parser.add_argument("--save-dir", required=True, help="Directory for output IQ files")
parser.add_argument("--file-tag", default="fragment_", help="Prefix for output files")
parser.add_argument("--samp-rate", type=float, default=20e6, help="Sample rate in S/s")
parser.add_argument("--split-size", type=int, default=400000, help="Max complex samples per file")
parser.add_argument("--delay", type=float, default=0.1, help="Delay between files in seconds")
parser.add_argument("--rf-gain", type=float, default=12, help="HackRF RF gain")
parser.add_argument("--if-gain", type=float, default=30, help="HackRF IF gain")
parser.add_argument("--bb-gain", type=float, default=36, help="HackRF BB gain")
return parser.parse_args()
def main():
args = parse_args()
tb = DataSaver(
serial=args.serial,
freq=args.freq,
save_dir=args.save_dir,
file_tag=args.file_tag,
samp_rate=args.samp_rate,
split_size=args.split_size,
delay=args.delay,
rf_gain=args.rf_gain,
if_gain=args.if_gain,
bb_gain=args.bb_gain,
)
stop_event = threading.Event()
def handle_signal(sig, frame):
print(f"Received signal {sig}, stopping...", flush=True)
stop_event.set()
tb.stop()
tb.wait()
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
print("Starting flowgraph...", flush=True)
print(f" serial: {args.serial}", flush=True)
print(f" freq: {args.freq}", flush=True)
print(f" save_dir: {args.save_dir}", flush=True)
print(f" file_tag: {args.file_tag}", flush=True)
print(f" samp_rate: {args.samp_rate}", flush=True)
print(f" split_size: {args.split_size}", flush=True)
print(f" gains: rf={args.rf_gain} if={args.if_gain} bb={args.bb_gain}", flush=True)
tb.start()
try:
while not stop_event.is_set():
time.sleep(0.5)
except KeyboardInterrupt:
handle_signal(signal.SIGINT, None)
print("Stopped.", flush=True)
if __name__ == "__main__":
main()