Porównaj commity

...

5 Commity

Autor SHA1 Wiadomość Data
Mark Jessop 9dcb784f17
Merge pull request #842 from darksidelemm/testing
Web control updates, preliminary KA9Q server support (no spectrum)
2024-01-01 10:29:28 +10:30
Mark Jessop c65d7fc97f Merge remote-tracking branch 'upstream/testing' into testing 2024-01-01 10:27:04 +10:30
Mark Jessop 2807232f23 Add Temp Block button on web controls 2024-01-01 10:25:38 +10:30
Mark Jessop 1d90d0c4b6 bump testing version 2023-11-10 08:43:27 +10:30
Mark Jessop 24b6835331 Some intial tests of KA9Q-Radio support (no spectrum yet) 2023-11-10 08:42:53 +10:30
12 zmienionych plików z 308 dodań i 31 usunięć

Wyświetl plik

@ -445,7 +445,8 @@ def clean_task_list():
else:
# Shutdown the SDR, if required for the particular SDR type.
shutdown_sdr(config["sdr_type"], _task_sdr)
if _key != 'SCAN':
shutdown_sdr(config["sdr_type"], _task_sdr, sdr_hostname=config["sdr_hostname"], frequency=_key)
# Release its associated SDR.
autorx.sdr_list[_task_sdr]["in_use"] = False
autorx.sdr_list[_task_sdr]["task"] = None
@ -505,6 +506,12 @@ def stop_all():
for _task in autorx.task_list.keys():
try:
autorx.task_list[_task]["task"].stop()
# Release the SDR channel if necessary
_task_sdr = autorx.task_list[_task]["device_idx"]
if _task != 'SCAN':
shutdown_sdr(config["sdr_type"], _task_sdr, sdr_hostname=config["sdr_hostname"], frequency=_task)
except Exception as e:
logging.error("Error stopping task - %s" % str(e))

Wyświetl plik

@ -12,7 +12,7 @@ from queue import Queue
# MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus.
# PATCH - Small changes, or minor feature additions.
__version__ = "1.7.2-beta1"
__version__ = "1.7.2-beta3"
# Global Variables

Wyświetl plik

@ -867,7 +867,7 @@ def read_auto_rx_config(filename, no_sdr_test=False):
return None
for _n in range(1, auto_rx_config["sdr_quantity"] + 1):
_sdr_name = f"KA9Q{_n:02d}"
_sdr_name = f"KA9Q-{_n:02d}"
auto_rx_config["sdr_settings"][_sdr_name] = {
"ppm": 0,
"gain": 0,
@ -876,8 +876,6 @@ def read_auto_rx_config(filename, no_sdr_test=False):
"task": None,
}
logging.critical("Config - KA9Q SDR Support not implemented yet - exiting.")
return None
else:
logging.critical(f"Config - Unknown SDR Type {auto_rx_config['sdr_type']} - exiting.")

Wyświetl plik

@ -1845,8 +1845,12 @@ class SondeDecoder(object):
f"Decoder ({_sdr_name}) {self.sonde_type} {self.sonde_freq/1e6:.3f} - {line}"
)
def stop(self, nowait=False):
def stop(self, nowait=False, temporary_lockout=False):
""" Kill the currently running decoder subprocess """
if temporary_lockout:
self.exit_state = "TempBlock"
self.decoder_running = False
if self.decoder is not None and (not nowait):

Wyświetl plik

@ -0,0 +1,129 @@
#!/usr/bin/env python
#
# radiosonde_auto_rx - SDR Abstraction - KA9Q-Radio
#
# Copyright (C) 2022 Mark Jessop <vk5qi@rfhead.net>
# Released under GNU GPL v3 or later
#
import logging
import os.path
import platform
import subprocess
from .utils import timeout_cmd
def ka9q_setup_channel(
sdr_hostname,
frequency,
sample_rate
):
# tune --samprate 48000 --frequency 404m09 --mode iq --ssrc 404090000 --radio sonde.local
_cmd = (
f"{timeout_cmd()} 5 " # Add a timeout, because connections to non-existing servers block for ages
f"tune "
f"--samprate {int(sample_rate)} "
f"--mode iq "
f"--frequency {int(frequency)} "
f"--ssrc {int(frequency)} "
f"--radio {sdr_hostname}"
)
logging.debug(f"KA9Q - Starting channel at {frequency} Hz, with command: {_cmd}")
try:
_output = subprocess.check_output(
_cmd, shell=True, stderr=subprocess.STDOUT
)
except subprocess.CalledProcessError as e:
# Something went wrong...
if e.returncode == 124:
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call failed while opening channel with a timeout. Is the server running?"
)
elif e.returncode == 127:
logging.critical(
f"KA9Q ({sdr_hostname}) - Could not find KA9Q-Radio 'tune' binary! This may need to be compiled and installed."
)
else:
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call failed while opening channel with return code {e.returncode}."
)
# Look at the error output in a bit more details.
#_output = e.output.decode("ascii")
# TODO - see if we can look in the output for any error messages.
return False
return True
def ka9q_close_channel(
sdr_hostname,
frequency
):
_cmd = (
f"{timeout_cmd()} 5 " # Add a timeout, because connections to non-existing servers block for ages
f"tune "
f"--samprate 48000 "
f"--mode iq "
f"--frequency 0 "
f"--ssrc {int(frequency)} "
f"--radio {sdr_hostname}"
)
logging.debug(f"KA9Q - Closing channel at {frequency} Hz, with command: {_cmd}")
try:
_output = subprocess.check_output(
_cmd, shell=True, stderr=subprocess.STDOUT
)
except subprocess.CalledProcessError as e:
# Something went wrong...
if e.returncode == 124:
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call failed while closing channel with a timeout. Is the server running?"
)
elif e.returncode == 127:
logging.critical(
f"KA9Q ({sdr_hostname}) - Could not find KA9Q-Radio 'tune' binary! This may need to be compiled and installed."
)
else:
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call failed while closing chanel with return code {e.returncode}."
)
# Look at the error output in a bit more details.
#_output = e.output.decode("ascii")
# TODO - see if we can look in the output for any error messages.
return False
return True
def ka9q_get_iq_cmd(
sdr_hostname,
frequency,
sample_rate
):
# We need to setup a channel before we can use it!
_setup_success = ka9q_setup_channel(sdr_hostname, frequency, sample_rate)
if not _setup_success:
logging.critical(f"KA9Q ({sdr_hostname}) - Could not setup rx channel! Decoder will likely timeout.")
# Get the 'PCM' version of the server name, where as assume -pcm is added to the first part of the hostname.
_pcm_host = sdr_hostname.split('.')[0] + "-pcm." + ".".join(sdr_hostname.split(".")[1:])
# pcmcat -2 -s 404090000 sonde-pcm.local
_cmd = (
f"pcmcat -2 "
f"-s {int(frequency)} "
f"{_pcm_host} |"
)
return _cmd

Wyświetl plik

@ -26,7 +26,7 @@ from .utils import (
peak_decimation,
timeout_cmd
)
from .sdr_wrappers import test_sdr, reset_sdr, get_sdr_name, get_sdr_iq_cmd, get_sdr_fm_cmd, get_power_spectrum
from .sdr_wrappers import test_sdr, reset_sdr, get_sdr_name, get_sdr_iq_cmd, get_sdr_fm_cmd, get_power_spectrum, shutdown_sdr
try:
@ -434,6 +434,10 @@ def detect_sonde(
ret_output = subprocess.check_output(rx_test_command, shell=True, stderr=FNULL)
FNULL.close()
ret_output = ret_output.decode("utf8")
# Release the SDR channel if necessary
shutdown_sdr(sdr_type, rtl_device_idx, sdr_hostname, frequency)
except subprocess.CalledProcessError as e:
# dft_detect returns a code of 1 if no sonde is detected.
# logging.debug("Scanner - dfm_detect return code: %s" % e.returncode)
@ -452,7 +456,7 @@ def detect_sonde(
except Exception as e:
# Something broke when running the detection function.
logging.error(
f"Scanner ({_sdr_name}) - Error when running dft_detect - {sdr(e)}"
f"Scanner ({_sdr_name}) - Error when running dft_detect - {str(e)}"
)
return (None, 0.0)

Wyświetl plik

@ -12,6 +12,7 @@ import subprocess
import numpy as np
from .utils import rtlsdr_test, reset_rtlsdr_by_serial, reset_all_rtlsdrs, timeout_cmd
from .ka9q import *
def test_sdr(
@ -51,13 +52,86 @@ def test_sdr(
elif sdr_type == "KA9Q":
# To be implemented
_ok = False
# Test that a KA9Q server is working by attempting to start up a new narrowband channel on it.
if not _ok:
logging.error(f"KA9Q Server {sdr_hostname}:{sdr_port} non-functional.")
# Check for presence of KA9Q-radio binaries that we need
# if not os.path.isfile('tune'):
# logging.critical("Could not find KA9Q-Radio 'tune' binary! This may need to be compiled and installed.")
# return False
# if not os.path.isfile('pcmcat'):
# logging.critical("Could not find KA9Q-Radio 'pcmcat' binary! This may need to be compiled and installed.")
# return False
# TBD - whatever we need for spectrum use.
# if not os.path.isfile('TBD'):
# logging.critical("Could not find KA9Q-Radio 'tune' binary! This may need to be compiled and installed.")
# return False
return _ok
# Try and configure a channel at check_freq Hz
# tune --samprate 48000 --frequency 404m09 --mode iq --ssrc 404090000 --radio sonde.local
_cmd = (
f"{timeout_cmd()} 5 " # Add a timeout, because connections to non-existing servers block for ages
f"tune "
f"--samprate 48000 --mode iq "
f"--frequency {int(check_freq)} "
f"--ssrc {int(check_freq)} "
f"--radio {sdr_hostname}"
)
logging.debug(f"KA9Q - Testing using command: {_cmd}")
try:
_output = subprocess.check_output(
_cmd, shell=True, stderr=subprocess.STDOUT
)
except subprocess.CalledProcessError as e:
# Something went wrong...
if e.returncode == 124:
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call failed with a timeout. Is the server running?"
)
elif e.returncode == 127:
logging.critical(
f"KA9Q ({sdr_hostname}) - Could not find KA9Q-Radio 'tune' binary! This may need to be compiled and installed."
)
else:
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call failed with return code {e.returncode}."
)
# Look at the error output in a bit more details.
#_output = e.output.decode("ascii")
# TODO - see if we can look in the output for any error messages.
return False
# Now close the channel we just opened by setting the frequency to 0 Hz.
_cmd = (
f"{timeout_cmd()} 5 " # Add a timeout, because connections to non-existing servers block for ages
f"tune "
f"--samprate 48000 --mode iq "
f"--frequency 0 "
f"--ssrc {int(check_freq)} "
f"--radio {sdr_hostname}"
)
logging.debug(f"KA9Q - Closing testing channel using command: {_cmd}")
try:
_output = subprocess.check_output(
_cmd, shell=True, stderr=subprocess.STDOUT
)
except subprocess.CalledProcessError as e:
# Something went wrong...
logging.critical(
f"KA9Q ({sdr_hostname}) - tune call (closing channel) failed with return code {e.returncode}."
)
# Look at the error output in a bit more details.
#_output = e.output.decode("ascii")
# TODO - see if we can look in the output for any error messages.
return False
return True
elif sdr_type == "SpyServer":
# Test connectivity to a SpyServer by trying to grab some samples.
@ -156,7 +230,7 @@ def get_sdr_name(
return f"RTLSDR {rtl_device_idx}"
elif sdr_type == "KA9Q":
return f"KA9Q {sdr_hostname}:{sdr_port}"
return f"KA9Q {sdr_hostname}"
elif sdr_type == "SpyServer":
return f"SpyServer {sdr_hostname}:{sdr_port}"
@ -167,7 +241,9 @@ def get_sdr_name(
def shutdown_sdr(
sdr_type: str,
sdr_id: str
sdr_id: str,
sdr_hostname = "",
frequency: int = None
):
"""
Function to trigger shutdown/cleanup of some SDR types.
@ -178,8 +254,8 @@ def shutdown_sdr(
"""
if sdr_type == "KA9Q":
# TODO - KA9Q Server channel cleanup.
logging.debug(f"TODO - Cleanup for SDR type {sdr_type}")
logging.debug(f"KA9Q - Closing Channel for {sdr_hostname} @ {frequency} Hz.")
ka9q_close_channel(sdr_hostname, frequency)
pass
else:
logging.debug(f"No shutdown action required for SDR type {sdr_type}")
@ -278,6 +354,14 @@ def get_sdr_iq_cmd(
_cmd += _dc_remove
return _cmd
if sdr_type == "KA9Q":
_cmd = ka9q_get_iq_cmd(sdr_hostname, frequency, sample_rate)
if dc_block:
_cmd += _dc_remove
return _cmd
else:
logging.critical(f"IQ Source - Unsupported SDR type {sdr_type}")
@ -614,8 +698,9 @@ def get_power_spectrum(
else:
# Unsupported SDR Type
logging.critical(f"Get PSD - Unsupported SDR Type: {sdr_type}")
return (None, None, None)
logging.debug(f"Get PSD - Unsupported SDR Type: {sdr_type}")
return (np.array([0,1,2]),np.array([0,1,2]),1)
#return (None, None, None)
if __name__ == "__main__":

Wyświetl plik

@ -62,6 +62,7 @@ function disable_web_controls(){
$("#verify-password").prop('disabled', true);
$("#start-decoder").prop('disabled', true);
$("#stop-decoder").prop('disabled', true);
$("#stop-decoder-lockout").prop('disabled', true);
$("#enable-scanner").prop('disabled', true);
$("#disable-scanner").prop('disabled', true);
$("#frequency-input").prop('disabled', true);
@ -75,6 +76,7 @@ function pause_web_controls() {
$("#verify-password").prop('disabled', true);
$("#start-decoder").prop('disabled', true);
$("#stop-decoder").prop('disabled', true);
$("#stop-decoder-lockout").prop('disabled', true);
$("#enable-scanner").prop('disabled', true);
$("#disable-scanner").prop('disabled', true);
$("#frequency-input").prop('disabled', true);
@ -86,6 +88,7 @@ function resume_web_controls() {
$("#verify-password").prop('disabled', false);
$("#start-decoder").prop('disabled', false);
$("#stop-decoder").prop('disabled', false);
$("#stop-decoder-lockout").prop('disabled', false);
$("#enable-scanner").prop('disabled', false);
$("#disable-scanner").prop('disabled', false);
$("#frequency-input").prop('disabled', false);
@ -235,6 +238,41 @@ function stop_decoder(){
});
}
function stop_decoder_lockout(){
// Stop the decoder on the requested frequency, and lockout frequency
// Re-verify the password. This will occur async, so wont stop the main request from going ahead,
// but will at least present an error for the user.
verify_password();
// Grab the password
_api_password = getCookie("password");
// Grab the selected frequency
_decoder = $('#stop-frequency-select').val();
// Do the request
$.post(
"stop_decoder",
{password: _api_password, freq: _decoder, lockout: 1},
function(data){
//console.log(data);
pause_web_controls();
setTimeout(resume_web_controls,10000);
// Need to figure out where to put this data..
}
).fail(function(xhr, status, error){
console.log(error);
// Otherwise, we probably got a 403 error (forbidden) which indicates the password was bad.
if(error == "FORBIDDEN"){
$("#password-header").html("<h2>Incorrect Password</h2>");
} else if (error == "NOT FOUND"){
// Scanner isn't running. Don't do anything.
alert("Decoder on supplied frequency not running!");
}
});
}
function start_decoder(){
// Start a decoder on the requested frequency

Wyświetl plik

@ -1684,6 +1684,9 @@
<div style="display:inline;vertical-align:middle;">
<button id="stop-decoder" onclick="stop_decoder();">Stop</button>
</div>
<div style="display:inline;vertical-align:middle;">
<button id="stop-decoder-lockout" onclick="stop_decoder_lockout();">Temp Block</button>
</div>
<h2>Scanner Control</h2>
<p>Scanner</p>
<div style="display:inline;vertical-align:middle;">

Wyświetl plik

@ -464,7 +464,11 @@ def flask_start_decoder():
def flask_stop_decoder():
""" Request that a decoder process be halted.
Example:
curl -d "freq=403250000" -X POST http://localhost:5000/stop_decoder
curl -d "freq=403250000&password=foobar" -X POST http://localhost:5000/stop_decoder
Stop decoder and lockout for temporary_block_time
curl -d "freq=403250000&password=foobar&lockout=1" -X POST http://localhost:5000/stop_decoder
"""
if request.method == "POST" and autorx.config.global_config["web_control"]:
@ -476,10 +480,15 @@ def flask_stop_decoder():
):
_freq = float(request.form["freq"])
_lockout = False
if "lockout" in request.form:
if int(request.form["lockout"]) == 1:
_lockout = True
logging.info("Web - Got decoder stop request: %f" % (_freq))
if _freq in autorx.task_list:
autorx.task_list[_freq]["task"].stop(nowait=True)
autorx.task_list[_freq]["task"].stop(nowait=True, temporary_lockout=_lockout)
return "OK"
else:
# If we aren't running a decoder, 404.

Wyświetl plik

@ -21,8 +21,8 @@
#
# EXPERIMENTAL / NOT IMPLEMENTED options:
# SpyServer - Use an Airspy SpyServer
# KA9Q - Use a KA9Q SDR Server (Not yet implemented)
# WARNING: These are still under development and may not work.
# KA9Q - Use a KA9Q-Radio Server
# WARNING: These are still under development and may not work correctly.
#
sdr_type = RTLSDR
@ -43,9 +43,9 @@ sdr_quantity = 1
#
# Network SDR Connection Details
#
# If using either a KA9Q or SpyServer network server, the hostname and port
# of the server needs to be defined below. Usually this will be running on the
# same machine as auto_rx, so the defaults are usually fine.
# If using a spyserver, the hostname and port need to be defined below.
# Is using KA9Q-Radio, the hostname of the 'radio' server (e.g. sonde.local) needs to be
# defined, and the port number is unused.
#
sdr_hostname = localhost
sdr_port = 5555

Wyświetl plik

@ -22,8 +22,8 @@
#
# EXPERIMENTAL / NOT IMPLEMENTED options:
# SpyServer - Use an Airspy SpyServer
# KA9Q - Use a KA9Q SDR Server (Not yet implemented)
# WARNING: These are still under development and may not work.
# KA9Q - Use a KA9Q-Radio Server
# WARNING: These are still under development and may not work correctly.
#
sdr_type = SpyServer
@ -44,9 +44,9 @@ sdr_quantity = 5
#
# Network SDR Connection Details
#
# If using either a KA9Q or SpyServer network server, the hostname and port
# of the server needs to be defined below. Usually this will be running on the
# same machine as auto_rx, so the defaults are usually fine.
# If using a spyserver, the hostname and port need to be defined below.
# Is using KA9Q-Radio, the hostname of the 'radio' server (e.g. sonde.local) needs to be
# defined, and the port number is unused.
#
sdr_hostname = localhost
sdr_port = 5555