Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web control updates, preliminary KA9Q server support (no spectrum) #842

Merged
merged 4 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion auto_rx/auto_rx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))

Expand Down
2 changes: 1 addition & 1 deletion auto_rx/autorx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# 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
Expand Down
4 changes: 1 addition & 3 deletions auto_rx/autorx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.")
Expand Down
6 changes: 5 additions & 1 deletion auto_rx/autorx/decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -1845,8 +1845,12 @@ def log_critical(self, line):
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):
Expand Down
129 changes: 129 additions & 0 deletions auto_rx/autorx/ka9q.py
Original file line number Diff line number Diff line change
@@ -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
8 changes: 6 additions & 2 deletions auto_rx/autorx/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
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:
Expand Down Expand Up @@ -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)
Expand All @@ -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)

Expand Down
107 changes: 96 additions & 11 deletions auto_rx/autorx/sdr_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import numpy as np

from .utils import rtlsdr_test, reset_rtlsdr_by_serial, reset_all_rtlsdrs, timeout_cmd
from .ka9q import *


def test_sdr(
Expand Down Expand Up @@ -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.

# 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


# 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}"
)

if not _ok:
logging.error(f"KA9Q Server {sdr_hostname}:{sdr_port} non-functional.")
logging.debug(f"KA9Q - Testing using command: {_cmd}")

return _ok
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.
Expand Down Expand Up @@ -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}"
Expand All @@ -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.
Expand All @@ -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}")
Expand Down Expand Up @@ -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}")
Expand Down Expand Up @@ -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__":

Expand Down
Loading
Loading