Default squelch to zero and track SDR usage

This commit is contained in:
Smittix
2026-02-04 11:59:06 +00:00
parent e6e47f2202
commit 97b123a016
2 changed files with 94 additions and 43 deletions
+92 -41
View File
@@ -14,11 +14,12 @@ import time
from datetime import datetime from datetime import datetime
from typing import Generator, Optional, List, Dict from typing import Generator, Optional, List, Dict
from flask import Blueprint, jsonify, request, Response from flask import Blueprint, jsonify, request, Response
from utils.logging import get_logger import app as app_module
from utils.sse import format_sse from utils.logging import get_logger
from utils.constants import ( from utils.sse import format_sse
from utils.constants import (
SSE_QUEUE_TIMEOUT, SSE_QUEUE_TIMEOUT,
SSE_KEEPALIVE_INTERVAL, SSE_KEEPALIVE_INTERVAL,
PROCESS_TERMINATE_TIMEOUT, PROCESS_TERMINATE_TIMEOUT,
@@ -42,17 +43,19 @@ audio_frequency = 0.0
audio_modulation = 'fm' audio_modulation = 'fm'
# Scanner state # Scanner state
scanner_thread: Optional[threading.Thread] = None scanner_thread: Optional[threading.Thread] = None
scanner_running = False scanner_running = False
scanner_lock = threading.Lock() scanner_lock = threading.Lock()
scanner_paused = False scanner_paused = False
scanner_current_freq = 0.0 scanner_current_freq = 0.0
scanner_active_device: Optional[int] = None
listening_active_device: Optional[int] = None
scanner_config = { scanner_config = {
'start_freq': 88.0, 'start_freq': 88.0,
'end_freq': 108.0, 'end_freq': 108.0,
'step': 0.1, 'step': 0.1,
'modulation': 'wfm', 'modulation': 'wfm',
'squelch': 20, 'squelch': 0,
'dwell_time': 10.0, # Seconds to stay on active frequency 'dwell_time': 10.0, # Seconds to stay on active frequency
'scan_delay': 0.1, # Seconds between frequency hops (keep low for fast scanning) 'scan_delay': 0.1, # Seconds between frequency hops (keep low for fast scanning)
'device': 0, 'device': 0,
@@ -60,6 +63,7 @@ scanner_config = {
'bias_t': False, # Bias-T power for external LNA 'bias_t': False, # Bias-T power for external LNA
'sdr_type': 'rtlsdr', # SDR type: rtlsdr, hackrf, airspy, limesdr, sdrplay 'sdr_type': 'rtlsdr', # SDR type: rtlsdr, hackrf, airspy, limesdr, sdrplay
'scan_method': 'power', # power (rtl_power) or classic (rtl_fm hop) 'scan_method': 'power', # power (rtl_power) or classic (rtl_fm hop)
'snr_threshold': 12,
} }
# Activity log # Activity log
@@ -775,7 +779,7 @@ def check_tools() -> Response:
@listening_post_bp.route('/scanner/start', methods=['POST']) @listening_post_bp.route('/scanner/start', methods=['POST'])
def start_scanner() -> Response: def start_scanner() -> Response:
"""Start the frequency scanner.""" """Start the frequency scanner."""
global scanner_thread, scanner_running, scanner_config global scanner_thread, scanner_running, scanner_config, scanner_active_device, listening_active_device
with scanner_lock: with scanner_lock:
if scanner_running: if scanner_running:
@@ -792,7 +796,7 @@ def start_scanner() -> Response:
scanner_config['end_freq'] = float(data.get('end_freq', 108.0)) scanner_config['end_freq'] = float(data.get('end_freq', 108.0))
scanner_config['step'] = float(data.get('step', 0.1)) scanner_config['step'] = float(data.get('step', 0.1))
scanner_config['modulation'] = str(data.get('modulation', 'wfm')).lower() scanner_config['modulation'] = str(data.get('modulation', 'wfm')).lower()
scanner_config['squelch'] = int(data.get('squelch', 20)) scanner_config['squelch'] = int(data.get('squelch', 0))
scanner_config['dwell_time'] = float(data.get('dwell_time', 3.0)) scanner_config['dwell_time'] = float(data.get('dwell_time', 3.0))
scanner_config['scan_delay'] = float(data.get('scan_delay', 0.5)) scanner_config['scan_delay'] = float(data.get('scan_delay', 0.5))
scanner_config['device'] = int(data.get('device', 0)) scanner_config['device'] = int(data.get('device', 0))
@@ -833,6 +837,19 @@ def start_scanner() -> Response:
'status': 'error', 'status': 'error',
'message': 'rtl_power not found. Install rtl-sdr tools.' 'message': 'rtl_power not found. Install rtl-sdr tools.'
}), 503 }), 503
# Release listening device if active
if listening_active_device is not None:
app_module.release_sdr_device(listening_active_device)
listening_active_device = None
# Claim device for scanner
error = app_module.claim_sdr_device(scanner_config['device'], 'scanner')
if error:
return jsonify({
'status': 'error',
'error_type': 'DEVICE_BUSY',
'message': error
}), 409
scanner_active_device = scanner_config['device']
scanner_running = True scanner_running = True
scanner_thread = threading.Thread(target=scanner_loop_power, daemon=True) scanner_thread = threading.Thread(target=scanner_loop_power, daemon=True)
scanner_thread.start() scanner_thread.start()
@@ -849,6 +866,17 @@ def start_scanner() -> Response:
'status': 'error', 'status': 'error',
'message': f'rx_fm not found. Install SoapySDR utilities for {sdr_type}.' 'message': f'rx_fm not found. Install SoapySDR utilities for {sdr_type}.'
}), 503 }), 503
if listening_active_device is not None:
app_module.release_sdr_device(listening_active_device)
listening_active_device = None
error = app_module.claim_sdr_device(scanner_config['device'], 'scanner')
if error:
return jsonify({
'status': 'error',
'error_type': 'DEVICE_BUSY',
'message': error
}), 409
scanner_active_device = scanner_config['device']
scanner_running = True scanner_running = True
scanner_thread = threading.Thread(target=scanner_loop, daemon=True) scanner_thread = threading.Thread(target=scanner_loop, daemon=True)
@@ -861,14 +889,17 @@ def start_scanner() -> Response:
@listening_post_bp.route('/scanner/stop', methods=['POST']) @listening_post_bp.route('/scanner/stop', methods=['POST'])
def stop_scanner() -> Response: def stop_scanner() -> Response:
"""Stop the frequency scanner.""" """Stop the frequency scanner."""
global scanner_running global scanner_running, scanner_active_device
scanner_running = False scanner_running = False
_stop_audio_stream() _stop_audio_stream()
if scanner_active_device is not None:
return jsonify({'status': 'stopped'}) app_module.release_sdr_device(scanner_active_device)
scanner_active_device = None
return jsonify({'status': 'stopped'})
@listening_post_bp.route('/scanner/pause', methods=['POST']) @listening_post_bp.route('/scanner/pause', methods=['POST'])
@@ -1025,14 +1056,17 @@ def get_presets() -> Response:
# ============================================ # ============================================
@listening_post_bp.route('/audio/start', methods=['POST']) @listening_post_bp.route('/audio/start', methods=['POST'])
def start_audio() -> Response: def start_audio() -> Response:
"""Start audio at specific frequency (manual mode).""" """Start audio at specific frequency (manual mode)."""
global scanner_running global scanner_running, scanner_active_device, listening_active_device
# Stop scanner if running # Stop scanner if running
if scanner_running: if scanner_running:
scanner_running = False scanner_running = False
time.sleep(0.5) if scanner_active_device is not None:
app_module.release_sdr_device(scanner_active_device)
scanner_active_device = None
time.sleep(0.5)
data = request.json or {} data = request.json or {}
@@ -1069,11 +1103,24 @@ def start_audio() -> Response:
'message': f'Invalid sdr_type. Use: {", ".join(valid_sdr_types)}' 'message': f'Invalid sdr_type. Use: {", ".join(valid_sdr_types)}'
}), 400 }), 400
# Update config for audio # Update config for audio
scanner_config['squelch'] = squelch scanner_config['squelch'] = squelch
scanner_config['gain'] = gain scanner_config['gain'] = gain
scanner_config['device'] = device scanner_config['device'] = device
scanner_config['sdr_type'] = sdr_type scanner_config['sdr_type'] = sdr_type
# Claim device for listening audio
if listening_active_device is None or listening_active_device != device:
if listening_active_device is not None:
app_module.release_sdr_device(listening_active_device)
error = app_module.claim_sdr_device(device, 'listening')
if error:
return jsonify({
'status': 'error',
'error_type': 'DEVICE_BUSY',
'message': error
}), 409
listening_active_device = device
_start_audio_stream(frequency, modulation) _start_audio_stream(frequency, modulation)
@@ -1091,10 +1138,14 @@ def start_audio() -> Response:
@listening_post_bp.route('/audio/stop', methods=['POST']) @listening_post_bp.route('/audio/stop', methods=['POST'])
def stop_audio() -> Response: def stop_audio() -> Response:
"""Stop audio.""" """Stop audio."""
_stop_audio_stream() global listening_active_device
return jsonify({'status': 'stopped'}) _stop_audio_stream()
if listening_active_device is not None:
app_module.release_sdr_device(listening_active_device)
listening_active_device = None
return jsonify({'status': 'stopped'})
@listening_post_bp.route('/audio/status') @listening_post_bp.route('/audio/status')
+2 -2
View File
@@ -1172,10 +1172,10 @@
<!-- SQL, Gain, Vol Knobs --> <!-- SQL, Gain, Vol Knobs -->
<div style="display: flex; gap: 15px;"> <div style="display: flex; gap: 15px;">
<div class="knob-container"> <div class="knob-container">
<div class="radio-knob" id="radioSquelchKnob" data-value="30" data-min="0" <div class="radio-knob" id="radioSquelchKnob" data-value="0" data-min="0"
data-max="100" data-step="1"></div> data-max="100" data-step="1"></div>
<div class="knob-label">SQL</div> <div class="knob-label">SQL</div>
<div class="knob-value" id="radioSquelchValue">30</div> <div class="knob-value" id="radioSquelchValue">0</div>
</div> </div>
<div class="knob-container"> <div class="knob-container">
<div class="radio-knob" id="radioGainKnob" data-value="40" data-min="0" <div class="radio-knob" id="radioGainKnob" data-value="40" data-min="0"