implement aprs config changes
This commit is contained in:
parent
c0ca216e4d
commit
fcbaa4f22a
34
csdr/csdr.py
34
csdr/csdr.py
@ -28,7 +28,7 @@ import threading
|
|||||||
import math
|
import math
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from owrx.kiss import KissClient, DirewolfConfig
|
from owrx.kiss import KissClient, DirewolfConfig, DirewolfConfigSubscriber
|
||||||
from owrx.wsjt import (
|
from owrx.wsjt import (
|
||||||
Ft8Profile,
|
Ft8Profile,
|
||||||
WsprProfile,
|
WsprProfile,
|
||||||
@ -81,7 +81,7 @@ class output(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class dsp(object):
|
class dsp(DirewolfConfigSubscriber):
|
||||||
def __init__(self, output):
|
def __init__(self, output):
|
||||||
self.samp_rate = 250000
|
self.samp_rate = 250000
|
||||||
self.output_rate = 11025
|
self.output_rate = 11025
|
||||||
@ -136,7 +136,7 @@ class dsp(object):
|
|||||||
|
|
||||||
self.is_service = False
|
self.is_service = False
|
||||||
self.direwolf_config = None
|
self.direwolf_config = None
|
||||||
self.direwolf_port = None
|
self.direwolf_config_path = None
|
||||||
self.process = None
|
self.process = None
|
||||||
|
|
||||||
def set_service(self, flag=True):
|
def set_service(self, flag=True):
|
||||||
@ -382,7 +382,7 @@ class dsp(object):
|
|||||||
if_samp_rate=self.if_samp_rate(),
|
if_samp_rate=self.if_samp_rate(),
|
||||||
last_decimation=self.last_decimation,
|
last_decimation=self.last_decimation,
|
||||||
audio_rate=self.get_audio_rate(),
|
audio_rate=self.get_audio_rate(),
|
||||||
direwolf_config=self.direwolf_config,
|
direwolf_config=self.direwolf_config_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("secondary command (demod) = %s", secondary_command_demod)
|
logger.debug("secondary command (demod) = %s", secondary_command_demod)
|
||||||
@ -443,7 +443,7 @@ class dsp(object):
|
|||||||
self.output.send_output("js8_demod", chopper.read)
|
self.output.send_output("js8_demod", chopper.read)
|
||||||
elif self.isPacket():
|
elif self.isPacket():
|
||||||
# we best get the ax25 packets from the kiss socket
|
# we best get the ax25 packets from the kiss socket
|
||||||
kiss = KissClient(self.direwolf_port)
|
kiss = KissClient(self.direwolf_config.getPort())
|
||||||
self.output.send_output("packet_demod", kiss.read)
|
self.output.send_output("packet_demod", kiss.read)
|
||||||
elif self.isPocsag():
|
elif self.isPocsag():
|
||||||
self.output.send_output("pocsag_demod", self.secondary_process_demod.stdout.readline)
|
self.output.send_output("pocsag_demod", self.secondary_process_demod.stdout.readline)
|
||||||
@ -750,27 +750,34 @@ class dsp(object):
|
|||||||
|
|
||||||
def try_create_configs(self, command):
|
def try_create_configs(self, command):
|
||||||
if "{direwolf_config}" in command:
|
if "{direwolf_config}" in command:
|
||||||
self.direwolf_config = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
|
self.direwolf_config_path = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
|
||||||
tmp_dir=self.temporary_directory, myid=id(self)
|
tmp_dir=self.temporary_directory, myid=id(self)
|
||||||
)
|
)
|
||||||
self.direwolf_port = KissClient.getFreePort()
|
self.direwolf_config = DirewolfConfig()
|
||||||
file = open(self.direwolf_config, "w")
|
self.direwolf_config.wire(self)
|
||||||
file.write(DirewolfConfig().getConfig(self.direwolf_port, self.is_service))
|
file = open(self.direwolf_config_path, "w")
|
||||||
|
file.write(self.direwolf_config.getConfig(self.is_service))
|
||||||
file.close()
|
file.close()
|
||||||
else:
|
else:
|
||||||
self.direwolf_config = None
|
self.direwolf_config = None
|
||||||
self.direwolf_port = None
|
self.direwolf_config_path = None
|
||||||
|
|
||||||
def try_delete_configs(self):
|
def try_delete_configs(self):
|
||||||
if self.direwolf_config:
|
if self.direwolf_config is not None:
|
||||||
|
self.direwolf_config.unwire(self)
|
||||||
|
self.direwolf_config = None
|
||||||
|
if self.direwolf_config_path is not None:
|
||||||
try:
|
try:
|
||||||
os.unlink(self.direwolf_config)
|
os.unlink(self.direwolf_config_path)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# result suits our expectations. fine :)
|
# result suits our expectations. fine :)
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("try_delete_configs()")
|
logger.exception("try_delete_configs()")
|
||||||
self.direwolf_config = None
|
self.direwolf_config_path = None
|
||||||
|
|
||||||
|
def onConfigChanged(self):
|
||||||
|
self.restart()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
with self.modification_lock:
|
with self.modification_lock:
|
||||||
@ -882,6 +889,7 @@ class dsp(object):
|
|||||||
self.stop_secondary_demodulator()
|
self.stop_secondary_demodulator()
|
||||||
|
|
||||||
self.try_delete_pipes(self.pipe_names)
|
self.try_delete_pipes(self.pipe_names)
|
||||||
|
self.try_delete_configs()
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
if not self.running:
|
if not self.running:
|
||||||
|
@ -166,12 +166,12 @@ defaultConfig = PropertyLayer(
|
|||||||
aprs_igate_beacon=False,
|
aprs_igate_beacon=False,
|
||||||
aprs_igate_symbol="R&",
|
aprs_igate_symbol="R&",
|
||||||
aprs_igate_comment="OpenWebRX APRS gateway",
|
aprs_igate_comment="OpenWebRX APRS gateway",
|
||||||
aprs_igate_height=None,
|
# aprs_igate_height=None,
|
||||||
aprs_igate_gain=None,
|
# aprs_igate_gain=None,
|
||||||
aprs_igate_dir=None,
|
# aprs_igate_dir=None,
|
||||||
pskreporter_enabled=False,
|
pskreporter_enabled=False,
|
||||||
pskreporter_callsign="N0CALL",
|
pskreporter_callsign="N0CALL",
|
||||||
pskreporter_antenna_information=None,
|
# pskreporter_antenna_information=None,
|
||||||
wsprnet_enabled=False,
|
wsprnet_enabled=False,
|
||||||
wsprnet_callsign="N0CALL",
|
wsprnet_callsign="N0CALL",
|
||||||
).readonly()
|
).readonly()
|
||||||
|
117
owrx/kiss.py
117
owrx/kiss.py
@ -3,6 +3,7 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
from owrx.config import Config
|
from owrx.config import Config
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -14,8 +15,66 @@ TFESC = 0xDD
|
|||||||
FEET_PER_METER = 3.28084
|
FEET_PER_METER = 3.28084
|
||||||
|
|
||||||
|
|
||||||
|
class DirewolfConfigSubscriber(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def onConfigChanged(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DirewolfConfig(object):
|
class DirewolfConfig(object):
|
||||||
def getConfig(self, port, is_service):
|
config_keys = [
|
||||||
|
"aprs_callsign",
|
||||||
|
"aprs_igate_enabled",
|
||||||
|
"aprs_igate_server",
|
||||||
|
"aprs_igate_password",
|
||||||
|
"receiver_gps",
|
||||||
|
"aprs_igate_symbol",
|
||||||
|
"aprs_igate_beacon",
|
||||||
|
"aprs_igate_gain",
|
||||||
|
"aprs_igate_dir",
|
||||||
|
"aprs_igate_comment",
|
||||||
|
"aprs_igate_height",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.subscribers = []
|
||||||
|
self.configSub = None
|
||||||
|
self.port = None
|
||||||
|
|
||||||
|
def wire(self, subscriber: DirewolfConfigSubscriber):
|
||||||
|
self.subscribers.append(subscriber)
|
||||||
|
if self.configSub is None:
|
||||||
|
pm = Config.get()
|
||||||
|
self.configSub = pm.filter(*DirewolfConfig.config_keys).wire(self._fireChanged)
|
||||||
|
|
||||||
|
def unwire(self, subscriber: DirewolfConfigSubscriber):
|
||||||
|
self.subscribers.remove(subscriber)
|
||||||
|
if not self.subscribers and self.configSub is not None:
|
||||||
|
self.configSub.cancel()
|
||||||
|
|
||||||
|
def _fireChanged(self, changes):
|
||||||
|
for sub in self.subscribers:
|
||||||
|
try:
|
||||||
|
sub.onConfigChanged()
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Error while notifying Direwolf subscribers")
|
||||||
|
|
||||||
|
def getPort(self):
|
||||||
|
# direwolf has some strange hardcoded port ranges
|
||||||
|
while self.port is None:
|
||||||
|
try:
|
||||||
|
port = random.randrange(1024, 49151)
|
||||||
|
# test if port is available for use
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.bind(("localhost", port))
|
||||||
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
s.close()
|
||||||
|
self.port = port
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return self.port
|
||||||
|
|
||||||
|
def getConfig(self, is_service):
|
||||||
pm = Config.get()
|
pm = Config.get()
|
||||||
|
|
||||||
config = """
|
config = """
|
||||||
@ -29,16 +88,11 @@ MODEM 1200
|
|||||||
KISSPORT {port}
|
KISSPORT {port}
|
||||||
AGWPORT off
|
AGWPORT off
|
||||||
""".format(
|
""".format(
|
||||||
port=port, callsign=pm["aprs_callsign"]
|
port=self.getPort(), callsign=pm["aprs_callsign"]
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_service and pm["aprs_igate_enabled"]:
|
if is_service and pm["aprs_igate_enabled"]:
|
||||||
config += """
|
pbeacon = ""
|
||||||
IGSERVER {server}
|
|
||||||
IGLOGIN {callsign} {password}
|
|
||||||
""".format(
|
|
||||||
server=pm["aprs_igate_server"], callsign=pm["aprs_callsign"], password=pm["aprs_igate_password"]
|
|
||||||
)
|
|
||||||
|
|
||||||
if pm["aprs_igate_beacon"]:
|
if pm["aprs_igate_beacon"]:
|
||||||
# Format beacon lat/lon
|
# Format beacon lat/lon
|
||||||
@ -51,12 +105,6 @@ IGLOGIN {callsign} {password}
|
|||||||
lat = "{0:02d}^{1:05.2f}{2}".format(int(lat), (lat - int(lat)) * 60, direction_ns)
|
lat = "{0:02d}^{1:05.2f}{2}".format(int(lat), (lat - int(lat)) * 60, direction_ns)
|
||||||
lon = "{0:03d}^{1:05.2f}{2}".format(int(lon), (lon - int(lon)) * 60, direction_we)
|
lon = "{0:03d}^{1:05.2f}{2}".format(int(lon), (lon - int(lon)) * 60, direction_we)
|
||||||
|
|
||||||
# Format beacon details
|
|
||||||
symbol = str(pm["aprs_igate_symbol"]) if "aprs_igate_symbol" in pm else "R&"
|
|
||||||
gain = "GAIN=" + str(pm["aprs_igate_gain"]) if "aprs_igate_gain" in pm else ""
|
|
||||||
adir = "DIR=" + str(pm["aprs_igate_dir"]) if "aprs_igate_dir" in pm else ""
|
|
||||||
comment = str(pm["aprs_igate_comment"]) if "aprs_igate_comment" in pm else '"OpenWebRX APRS gateway"'
|
|
||||||
|
|
||||||
# Convert height from meters to feet if specified
|
# Convert height from meters to feet if specified
|
||||||
height = ""
|
height = ""
|
||||||
if "aprs_igate_height" in pm:
|
if "aprs_igate_height" in pm:
|
||||||
@ -69,38 +117,33 @@ IGLOGIN {callsign} {password}
|
|||||||
"Cannot parse 'aprs_igate_height', expected float: " + str(pm["aprs_igate_height"])
|
"Cannot parse 'aprs_igate_height', expected float: " + str(pm["aprs_igate_height"])
|
||||||
)
|
)
|
||||||
|
|
||||||
if (len(comment) > 0) and ((comment[0] != '"') or (comment[len(comment) - 1] != '"')):
|
pbeacon = 'PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} {height} {gain} {adir} comment="{comment}"'.format(
|
||||||
comment = '"' + comment + '"'
|
symbol=pm["aprs_igate_symbol"],
|
||||||
elif len(comment) == 0:
|
lat=lat,
|
||||||
comment = '""'
|
lon=lon,
|
||||||
|
height=height,
|
||||||
pbeacon = "PBEACON sendto=IG delay=0:30 every=60:00 symbol={symbol} lat={lat} long={lon} {height} {gain} {adir} comment={comment}".format(
|
gain="GAIN=" + str(pm["aprs_igate_gain"]) if "aprs_igate_gain" in pm else "",
|
||||||
symbol=symbol, lat=lat, lon=lon, height=height, gain=gain, adir=adir, comment=comment
|
adir="DIR=" + str(pm["aprs_igate_dir"]) if "aprs_igate_dir" in pm else "",
|
||||||
|
comment=pm["aprs_igate_comment"],
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("APRS PBEACON String: " + pbeacon)
|
logger.info("APRS PBEACON String: " + pbeacon)
|
||||||
|
|
||||||
config += "\n" + pbeacon + "\n"
|
config += """
|
||||||
|
IGSERVER {server}
|
||||||
|
IGLOGIN {callsign} {password}
|
||||||
|
{pbeacon}
|
||||||
|
""".format(
|
||||||
|
server=pm["aprs_igate_server"],
|
||||||
|
callsign=pm["aprs_callsign"],
|
||||||
|
password=pm["aprs_igate_password"],
|
||||||
|
pbeacon=pbeacon,
|
||||||
|
)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
class KissClient(object):
|
class KissClient(object):
|
||||||
@staticmethod
|
|
||||||
def getFreePort():
|
|
||||||
# direwolf has some strange hardcoded port ranges
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
port = random.randrange(1024, 49151)
|
|
||||||
# test if port is available for use
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
s.bind(("localhost", port))
|
|
||||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
s.close()
|
|
||||||
return port
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __init__(self, port):
|
def __init__(self, port):
|
||||||
delay = 0.5
|
delay = 0.5
|
||||||
retries = 0
|
retries = 0
|
||||||
|
@ -103,14 +103,15 @@ class PropertyManager(ABC):
|
|||||||
def _fireCallbacks(self, changes):
|
def _fireCallbacks(self, changes):
|
||||||
if not changes:
|
if not changes:
|
||||||
return
|
return
|
||||||
for c in self.subscribers:
|
subscribers = self.subscribers.copy()
|
||||||
|
for c in subscribers:
|
||||||
try:
|
try:
|
||||||
if c.getName() is None:
|
if c.getName() is None:
|
||||||
c.call(changes)
|
c.call(changes)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("exception while firing changes")
|
logger.exception("exception while firing changes")
|
||||||
for name in changes:
|
for name in changes:
|
||||||
for c in self.subscribers:
|
for c in subscribers:
|
||||||
try:
|
try:
|
||||||
if c.getName() == name:
|
if c.getName() == name:
|
||||||
c.call(changes[name])
|
c.call(changes[name])
|
||||||
|
Loading…
Reference in New Issue
Block a user