Skip to content

Commit

Permalink
Merge pull request #842 from darksidelemm/testing
Browse files Browse the repository at this point in the history
Web control updates, preliminary KA9Q server support (no spectrum)
  • Loading branch information
darksidelemm authored Dec 31, 2023
2 parents 45a9a86 + c65d7fc commit 9dcb784
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 31 deletions.
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

0 comments on commit 9dcb784

Please sign in to comment.