mirror of
https://github.com/smittix/intercept.git
synced 2026-06-14 16:43:38 -07:00
Add SDR device status panel and ADS-B Bias-T toggle
- Add /devices/status endpoint showing which SDR is in use and by what mode - Add real-time status panel on main dashboard with 5s auto-refresh - Add Bias-T toggle to ADS-B dashboard with localStorage persistence - Auto-detect correct dump1090 bias-t flag (--enable-biast vs unsupported) - Standardize SDR device labels across all pages Closes #102 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -347,6 +347,22 @@ def get_devices() -> Response:
|
||||
return jsonify([d.to_dict() for d in devices])
|
||||
|
||||
|
||||
@app.route('/devices/status')
|
||||
def get_devices_status() -> Response:
|
||||
"""Get all SDR devices with usage status."""
|
||||
devices = SDRFactory.detect_devices()
|
||||
registry = get_sdr_device_status()
|
||||
|
||||
result = []
|
||||
for device in devices:
|
||||
d = device.to_dict()
|
||||
d['in_use'] = device.index in registry
|
||||
d['used_by'] = registry.get(device.index)
|
||||
result.append(d)
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@app.route('/devices/debug')
|
||||
def get_devices_debug() -> Response:
|
||||
"""Get detailed SDR device detection diagnostics."""
|
||||
|
||||
@@ -814,6 +814,24 @@ body {
|
||||
color: var(--accent-green);
|
||||
}
|
||||
|
||||
/* Bias-T toggle styling */
|
||||
.bias-t-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 8px;
|
||||
background: linear-gradient(90deg, rgba(255, 100, 0, 0.15), rgba(255, 100, 0, 0.05));
|
||||
border: 1px solid var(--accent-orange, #ff6400);
|
||||
border-radius: 4px;
|
||||
color: var(--accent-orange, #ff6400);
|
||||
font-weight: 500;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.bias-t-label input[type="checkbox"] {
|
||||
accent-color: var(--accent-orange, #ff6400);
|
||||
}
|
||||
|
||||
.control-group.airband-group {
|
||||
background: rgba(245, 158, 11, 0.05);
|
||||
border-color: rgba(245, 158, 11, 0.2);
|
||||
|
||||
+111
-103
@@ -17,15 +17,15 @@
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/adsb_dashboard.css') }}">
|
||||
<script>
|
||||
window.INTERCEPT_SHARED_OBSERVER_LOCATION = {{ shared_observer_location | tojson }};
|
||||
window.INTERCEPT_ADSB_AUTO_START = {{ adsb_auto_start | tojson }};
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/core/observer-location.js') }}"></script>
|
||||
</head>
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/adsb_dashboard.css') }}">
|
||||
<script>
|
||||
window.INTERCEPT_SHARED_OBSERVER_LOCATION = {{ shared_observer_location | tojson }};
|
||||
window.INTERCEPT_ADSB_AUTO_START = {{ adsb_auto_start | tojson }};
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/core/observer-location.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="radar-bg"></div>
|
||||
<div class="scanline"></div>
|
||||
@@ -269,6 +269,7 @@
|
||||
<select id="adsbDeviceSelect" title="SDR device for ADS-B (1090 MHz)">
|
||||
<option value="0">SDR 0</option>
|
||||
</select>
|
||||
<label class="bias-t-label" title="Enable Bias-T power for external LNA/preamp"><input type="checkbox" id="adsbBiasT" onchange="saveAdsbBiasTSetting()"> Bias-T</label>
|
||||
<button class="start-btn" id="startBtn" onclick="toggleTracking()">START</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -322,10 +323,23 @@
|
||||
|
||||
<script>
|
||||
// ============================================
|
||||
// BIAS-T HELPER (reads from main dashboard localStorage)
|
||||
// BIAS-T HELPER
|
||||
// ============================================
|
||||
function getBiasTEnabled() {
|
||||
return localStorage.getItem('biasTEnabled') === 'true';
|
||||
return document.getElementById('adsbBiasT')?.checked || false;
|
||||
}
|
||||
|
||||
function saveAdsbBiasTSetting() {
|
||||
const enabled = document.getElementById('adsbBiasT')?.checked || false;
|
||||
localStorage.setItem('adsbBiasTEnabled', enabled);
|
||||
}
|
||||
|
||||
function loadAdsbBiasTSetting() {
|
||||
const saved = localStorage.getItem('adsbBiasTEnabled');
|
||||
if (saved === 'true') {
|
||||
const checkbox = document.getElementById('adsbBiasT');
|
||||
if (checkbox) checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -522,19 +536,19 @@
|
||||
}
|
||||
|
||||
// Observer location and range rings (load from localStorage or default to London)
|
||||
let observerLocation = (function() {
|
||||
if (window.ObserverLocation && ObserverLocation.getForModule) {
|
||||
return ObserverLocation.getForModule('observerLocation');
|
||||
}
|
||||
const saved = localStorage.getItem('observerLocation');
|
||||
if (saved) {
|
||||
try {
|
||||
const parsed = JSON.parse(saved);
|
||||
if (parsed.lat && parsed.lon) return parsed;
|
||||
} catch (e) {}
|
||||
}
|
||||
return { lat: 51.5074, lon: -0.1278 };
|
||||
})();
|
||||
let observerLocation = (function() {
|
||||
if (window.ObserverLocation && ObserverLocation.getForModule) {
|
||||
return ObserverLocation.getForModule('observerLocation');
|
||||
}
|
||||
const saved = localStorage.getItem('observerLocation');
|
||||
if (saved) {
|
||||
try {
|
||||
const parsed = JSON.parse(saved);
|
||||
if (parsed.lat && parsed.lon) return parsed;
|
||||
} catch (e) {}
|
||||
}
|
||||
return { lat: 51.5074, lon: -0.1278 };
|
||||
})();
|
||||
let rangeRingsLayer = null;
|
||||
let observerMarker = null;
|
||||
|
||||
@@ -1810,12 +1824,12 @@ ACARS: ${r.statistics.acarsMessages} messages`;
|
||||
observerLocation.lat = lat;
|
||||
observerLocation.lon = lon;
|
||||
|
||||
// Save to localStorage for persistence
|
||||
if (window.ObserverLocation) {
|
||||
ObserverLocation.setForModule('observerLocation', observerLocation);
|
||||
} else {
|
||||
localStorage.setItem('observerLocation', JSON.stringify(observerLocation));
|
||||
}
|
||||
// Save to localStorage for persistence
|
||||
if (window.ObserverLocation) {
|
||||
ObserverLocation.setForModule('observerLocation', observerLocation);
|
||||
} else {
|
||||
localStorage.setItem('observerLocation', JSON.stringify(observerLocation));
|
||||
}
|
||||
|
||||
if (radarMap) {
|
||||
radarMap.setView([lat, lon], radarMap.getZoom());
|
||||
@@ -1842,12 +1856,12 @@ ACARS: ${r.statistics.acarsMessages} messages`;
|
||||
observerLocation.lat = position.coords.latitude;
|
||||
observerLocation.lon = position.coords.longitude;
|
||||
|
||||
// Save to localStorage for persistence
|
||||
if (window.ObserverLocation) {
|
||||
ObserverLocation.setForModule('observerLocation', observerLocation);
|
||||
} else {
|
||||
localStorage.setItem('observerLocation', JSON.stringify(observerLocation));
|
||||
}
|
||||
// Save to localStorage for persistence
|
||||
if (window.ObserverLocation) {
|
||||
ObserverLocation.setForModule('observerLocation', observerLocation);
|
||||
} else {
|
||||
localStorage.setItem('observerLocation', JSON.stringify(observerLocation));
|
||||
}
|
||||
|
||||
document.getElementById('obsLat').value = observerLocation.lat.toFixed(4);
|
||||
document.getElementById('obsLon').value = observerLocation.lon.toFixed(4);
|
||||
@@ -1936,17 +1950,17 @@ ACARS: ${r.statistics.acarsMessages} messages`;
|
||||
}
|
||||
});
|
||||
|
||||
function updateLocationFromGps(position) {
|
||||
observerLocation.lat = position.latitude;
|
||||
observerLocation.lon = position.longitude;
|
||||
document.getElementById('obsLat').value = position.latitude.toFixed(4);
|
||||
document.getElementById('obsLon').value = position.longitude.toFixed(4);
|
||||
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
|
||||
ObserverLocation.setShared({ lat: position.latitude, lon: position.longitude });
|
||||
}
|
||||
|
||||
// Center map on GPS location (on first fix)
|
||||
if (radarMap && !radarMap._gpsInitialized) {
|
||||
function updateLocationFromGps(position) {
|
||||
observerLocation.lat = position.latitude;
|
||||
observerLocation.lon = position.longitude;
|
||||
document.getElementById('obsLat').value = position.latitude.toFixed(4);
|
||||
document.getElementById('obsLon').value = position.longitude.toFixed(4);
|
||||
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
|
||||
ObserverLocation.setShared({ lat: position.latitude, lon: position.longitude });
|
||||
}
|
||||
|
||||
// Center map on GPS location (on first fix)
|
||||
if (radarMap && !radarMap._gpsInitialized) {
|
||||
radarMap.setView([position.latitude, position.longitude], radarMap.getZoom());
|
||||
radarMap._gpsInitialized = true;
|
||||
// Draw range rings immediately after centering
|
||||
@@ -2008,6 +2022,9 @@ ACARS: ${r.statistics.acarsMessages} messages`;
|
||||
const detectionToggle = document.getElementById('detectionSoundToggle');
|
||||
if (detectionToggle) detectionToggle.checked = detectionSoundEnabled;
|
||||
|
||||
// Load Bias-T setting from localStorage
|
||||
loadAdsbBiasTSetting();
|
||||
|
||||
initMap();
|
||||
initDeviceSelectors();
|
||||
updateClock();
|
||||
@@ -2046,27 +2063,18 @@ ACARS: ${r.statistics.acarsMessages} messages`;
|
||||
} else {
|
||||
devices.forEach((dev, i) => {
|
||||
const idx = dev.index !== undefined ? dev.index : i;
|
||||
|
||||
// Build descriptive label
|
||||
const type = dev.sdr_type || dev.driver || 'RTL-SDR';
|
||||
const typeName = type.toUpperCase().replace('RTLSDR', 'RTL-SDR');
|
||||
const shortSerial = dev.serial ? ` (${dev.serial.slice(-4)})` : '';
|
||||
const displayName = `${typeName} #${idx}${shortSerial}`;
|
||||
const fullName = dev.name || `${typeName} Device ${idx}`;
|
||||
const tooltip = `${fullName}${dev.serial ? ' - Serial: ' + dev.serial : ''}`;
|
||||
const displayName = `SDR ${idx}: ${dev.name}`;
|
||||
|
||||
// Add to ADS-B selector
|
||||
const adsbOpt = document.createElement('option');
|
||||
adsbOpt.value = idx;
|
||||
adsbOpt.textContent = displayName;
|
||||
adsbOpt.title = tooltip;
|
||||
adsbSelect.appendChild(adsbOpt);
|
||||
|
||||
// Add to Airband selector
|
||||
const airbandOpt = document.createElement('option');
|
||||
airbandOpt.value = idx;
|
||||
airbandOpt.textContent = displayName;
|
||||
airbandOpt.title = tooltip;
|
||||
airbandSelect.appendChild(airbandOpt);
|
||||
});
|
||||
|
||||
@@ -2542,32 +2550,32 @@ sudo make install</code>
|
||||
}
|
||||
}
|
||||
|
||||
async function syncTrackingStatus() {
|
||||
// This function checks LOCAL tracking status on page load
|
||||
// For local mode: auto-start if session is already running OR SDR is available
|
||||
// For agent mode: don't auto-start (user controls agent tracking)
|
||||
|
||||
const useAgent = typeof adsbCurrentAgent !== 'undefined' && adsbCurrentAgent !== 'local';
|
||||
if (useAgent) {
|
||||
console.log('[ADS-B] Agent mode on page load - not auto-starting local');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/adsb/session');
|
||||
if (!response.ok) {
|
||||
// No session info - only auto-start if enabled
|
||||
if (window.INTERCEPT_ADSB_AUTO_START) {
|
||||
console.log('[ADS-B] No session found, attempting auto-start...');
|
||||
await tryAutoStartLocal();
|
||||
} else {
|
||||
console.log('[ADS-B] No session found; auto-start disabled');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
if (data.tracking_active) {
|
||||
async function syncTrackingStatus() {
|
||||
// This function checks LOCAL tracking status on page load
|
||||
// For local mode: auto-start if session is already running OR SDR is available
|
||||
// For agent mode: don't auto-start (user controls agent tracking)
|
||||
|
||||
const useAgent = typeof adsbCurrentAgent !== 'undefined' && adsbCurrentAgent !== 'local';
|
||||
if (useAgent) {
|
||||
console.log('[ADS-B] Agent mode on page load - not auto-starting local');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/adsb/session');
|
||||
if (!response.ok) {
|
||||
// No session info - only auto-start if enabled
|
||||
if (window.INTERCEPT_ADSB_AUTO_START) {
|
||||
console.log('[ADS-B] No session found, attempting auto-start...');
|
||||
await tryAutoStartLocal();
|
||||
} else {
|
||||
console.log('[ADS-B] No session found; auto-start disabled');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
if (data.tracking_active) {
|
||||
// Session is running - auto-connect to stream
|
||||
console.log('[ADS-B] Local session already active - auto-connecting to stream');
|
||||
|
||||
@@ -2603,24 +2611,24 @@ sudo make install</code>
|
||||
document.getElementById('trackingDot').classList.add('active');
|
||||
const statusEl = document.getElementById('trackingStatus');
|
||||
statusEl.textContent = 'TRACKING';
|
||||
} else {
|
||||
// Session not active - only auto-start if enabled
|
||||
if (window.INTERCEPT_ADSB_AUTO_START) {
|
||||
console.log('[ADS-B] No active session, attempting auto-start...');
|
||||
await tryAutoStartLocal();
|
||||
} else {
|
||||
console.log('[ADS-B] No active session; auto-start disabled');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.warn('[ADS-B] Failed to sync tracking status:', err);
|
||||
// Try auto-start only if enabled
|
||||
if (window.INTERCEPT_ADSB_AUTO_START) {
|
||||
await tryAutoStartLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Session not active - only auto-start if enabled
|
||||
if (window.INTERCEPT_ADSB_AUTO_START) {
|
||||
console.log('[ADS-B] No active session, attempting auto-start...');
|
||||
await tryAutoStartLocal();
|
||||
} else {
|
||||
console.log('[ADS-B] No active session; auto-start disabled');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.warn('[ADS-B] Failed to sync tracking status:', err);
|
||||
// Try auto-start only if enabled
|
||||
if (window.INTERCEPT_ADSB_AUTO_START) {
|
||||
await tryAutoStartLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function tryAutoStartLocal() {
|
||||
// Try to auto-start local ADS-B tracking if SDR is available
|
||||
@@ -4004,7 +4012,7 @@ sudo make install</code>
|
||||
devices.forEach((d, i) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = d.index || i;
|
||||
opt.textContent = `Device ${d.index || i}: ${d.name || d.type || 'SDR'}`;
|
||||
opt.textContent = `SDR ${d.index || i}: ${d.name || d.type || 'SDR'}`;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
// Default to device 1 if available (device 0 likely used for ADS-B)
|
||||
@@ -4774,7 +4782,7 @@ sudo make install</code>
|
||||
devices.forEach(device => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = device.index;
|
||||
opt.textContent = `Device ${device.index}: ${device.name || device.type || 'SDR'}`;
|
||||
opt.textContent = `SDR ${device.index}: ${device.name || device.type || 'SDR'}`;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -558,11 +558,9 @@
|
||||
}
|
||||
devices.forEach((dev, idx) => {
|
||||
const index = dev.index !== undefined ? dev.index : idx;
|
||||
const type = (dev.sdr_type || dev.driver || 'RTL-SDR').toUpperCase();
|
||||
const serial = dev.serial ? ` (${dev.serial.slice(-4)})` : '';
|
||||
const opt = document.createElement('option');
|
||||
opt.value = index;
|
||||
opt.textContent = `${type} #${index}${serial}`;
|
||||
opt.textContent = `SDR ${index}: ${dev.name}`;
|
||||
sessionDeviceSelect.appendChild(opt);
|
||||
});
|
||||
sessionDeviceSelect.disabled = false;
|
||||
|
||||
@@ -537,6 +537,14 @@
|
||||
Refresh Devices
|
||||
</button>
|
||||
|
||||
<!-- SDR Device Status -->
|
||||
<div id="sdrStatusPanel" style="margin-top: 10px; border: 1px solid var(--border-color); border-radius: 4px;">
|
||||
<div id="sdrStatusList" style="max-height: 150px; overflow-y: auto;"></div>
|
||||
<div style="padding: 6px 8px; background: var(--bg-tertiary); border-top: 1px solid var(--border-color); font-size: 10px; color: #666;">
|
||||
Auto-refreshes every 5s
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Remote SDR (rtl_tcp) -->
|
||||
<div class="form-group" style="margin-top: 10px;">
|
||||
<label class="inline-checkbox">
|
||||
@@ -2445,6 +2453,9 @@
|
||||
|
||||
// Initialize dropdown nav active state
|
||||
updateDropdownActiveState();
|
||||
|
||||
// Start SDR device status polling
|
||||
startSdrStatusPolling();
|
||||
});
|
||||
|
||||
// Toggle section collapse
|
||||
@@ -3717,6 +3728,9 @@
|
||||
|
||||
// Trigger filter update
|
||||
onSDRTypeChanged();
|
||||
|
||||
// Also refresh SDR status panel
|
||||
fetchSdrStatus();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to refresh devices:', err);
|
||||
@@ -3725,6 +3739,71 @@
|
||||
});
|
||||
}
|
||||
|
||||
// SDR Device Status Panel
|
||||
let sdrStatusPollingInterval = null;
|
||||
|
||||
function renderSdrStatus(devices) {
|
||||
const container = document.getElementById('sdrStatusList');
|
||||
if (!container) return;
|
||||
|
||||
if (!devices || devices.length === 0) {
|
||||
container.innerHTML = '<div style="padding: 8px; color: #888; font-size: 11px; text-align: center;">No SDR devices detected</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const html = devices.map(d => {
|
||||
const isActive = d.in_use;
|
||||
const statusDot = isActive
|
||||
? '<span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #00ff88; box-shadow: 0 0 6px #00ff88; margin-right: 6px;"></span>'
|
||||
: '<span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #555; margin-right: 6px;"></span>';
|
||||
const modeName = d.used_by ? d.used_by.toUpperCase() : 'IDLE';
|
||||
const modeColor = isActive ? '#00ff88' : '#666';
|
||||
const sdrType = (d.sdr_type || 'RTL').toUpperCase().replace('RTLSDR', 'RTL');
|
||||
|
||||
return `<div style="display: flex; align-items: center; justify-content: space-between; padding: 6px 8px; border-bottom: 1px solid var(--border-color);">
|
||||
<div style="display: flex; align-items: center;">
|
||||
${statusDot}
|
||||
<span style="font-size: 11px;">#${d.index} ${d.name || 'Unknown'}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 6px;">
|
||||
<span style="font-size: 10px; color: ${modeColor}; font-weight: bold;">${modeName}</span>
|
||||
<span style="font-size: 9px; padding: 1px 4px; background: var(--bg-tertiary); border-radius: 3px; color: #888;">${sdrType}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function fetchSdrStatus() {
|
||||
fetch('/devices/status')
|
||||
.then(r => r.json())
|
||||
.then(devices => {
|
||||
renderSdrStatus(devices);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to fetch SDR status:', err);
|
||||
const container = document.getElementById('sdrStatusList');
|
||||
if (container) {
|
||||
container.innerHTML = '<div style="padding: 8px; color: #ff6666; font-size: 11px; text-align: center;">Error loading status</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startSdrStatusPolling() {
|
||||
// Initial fetch
|
||||
fetchSdrStatus();
|
||||
// Poll every 5 seconds
|
||||
sdrStatusPollingInterval = setInterval(fetchSdrStatus, 5000);
|
||||
}
|
||||
|
||||
function stopSdrStatusPolling() {
|
||||
if (sdrStatusPollingInterval) {
|
||||
clearInterval(sdrStatusPollingInterval);
|
||||
sdrStatusPollingInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedDevice() {
|
||||
return document.getElementById('deviceSelect').value;
|
||||
}
|
||||
|
||||
+41
-1
@@ -7,11 +7,44 @@ with existing RTL-SDR installations. No SoapySDR dependency required.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
|
||||
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
||||
from utils.dependencies import get_tool_path
|
||||
|
||||
logger = logging.getLogger('intercept.sdr.rtlsdr')
|
||||
|
||||
|
||||
def _get_dump1090_bias_t_flag(dump1090_path: str) -> Optional[str]:
|
||||
"""Detect the correct bias-t flag for the installed dump1090 variant.
|
||||
|
||||
Different dump1090 forks use different flags:
|
||||
- dump1090-fa, readsb: --enable-biast (no hyphen before 't')
|
||||
- dump1090-mutability, original dump1090: no bias-t support
|
||||
|
||||
Returns the correct flag string or None if bias-t is not supported.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[dump1090_path, '--help'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
help_text = result.stdout + result.stderr
|
||||
|
||||
# Check for dump1090-fa/readsb style flag (no hyphen)
|
||||
if '--enable-biast' in help_text:
|
||||
return '--enable-biast'
|
||||
|
||||
# No bias-t support found
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not detect dump1090 bias-t support: {e}")
|
||||
return None
|
||||
|
||||
|
||||
class RTLSDRCommandBuilder(CommandBuilder):
|
||||
"""RTL-SDR command builder using native rtl_* tools."""
|
||||
@@ -113,7 +146,14 @@ class RTLSDRCommandBuilder(CommandBuilder):
|
||||
cmd.extend(['--gain', str(int(gain))])
|
||||
|
||||
if bias_t:
|
||||
cmd.extend(['--enable-bias-t'])
|
||||
bias_t_flag = _get_dump1090_bias_t_flag(dump1090_path)
|
||||
if bias_t_flag:
|
||||
cmd.append(bias_t_flag)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Bias-t requested but {dump1090_path} does not support it. "
|
||||
"Consider using dump1090-fa or readsb for bias-t support."
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
Reference in New Issue
Block a user