Merging the rest of owrx folder.

This commit is contained in:
Marat Fayzullin 2023-09-09 23:29:46 -04:00 committed by Marat Fayzullin
parent 5117e27129
commit c4afe1e594
8 changed files with 197 additions and 82 deletions

View File

@ -37,7 +37,7 @@ It has the following features:
- supports a wide range of [SDR hardware](https://github.com/jketterl/openwebrx/wiki/Supported-Hardware#sdr-devices)
- Multiple SDR devices can be used simultaneously
- [digiham](https://github.com/jketterl/digiham) based demodularors (DMR, YSF, Pocsag, D-Star, NXDN)
- [wsjt-x](https://physics.princeton.edu/pulsar/k1jt/wsjtx.html) based demodulators (FT8, FT4, WSPR, JT65, JT9, FST4,
- [wsjt-x](https://wsjt.sourceforge.io/) based demodulators (FT8, FT4, WSPR, JT65, JT9, FST4,
FST4W)
- [direwolf](https://github.com/wb2osz/direwolf) based demodulation of APRS packets
- [JS8Call](http://js8call.com/) support

View File

@ -393,31 +393,40 @@
"upper_bound": 446200000,
"tags": ["public"]
},
{
"name": "ADS-B Reporting",
"lower_bound": 960000000,
"upper_bound": 1215000000,
"tags": ["service"],
"frequencies": {
"adsb": 1090000000
}
},
{
"name": "LPD433",
"lower_bound": 433050000,
"upper_bound": 434790000,
"tags": ["public"],
"frequencies": {
"ism": 433920000
},
"tags": ["public"]
}
},
{
"name": "VHF Air",
"lower_bound": 108000000,
"upper_bound": 137000000,
"tags": ["service"],
"frequencies": {
"vdl2": 136975000
},
"tags": ["service"]
},
{
"name": "VHF Marine",
"lower_bound": 156000000,
"upper_bound": 174000000,
"tags": ["service"],
"frequencies": {
"ais": [161975000, 162025000]
},
"tags": ["service"]
}
}
]

View File

@ -5,7 +5,7 @@ from owrx.aprs.kiss import KissDeframer
from owrx.aprs import Ax25Parser, AprsParser
from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, JKRttyDecoder, BaudotDecoder, Lowpass, RttyDecoder, CwDecoder, SstvDecoder, FaxDecoder, Shift
from pycsdr.types import Format
from owrx.aprs.module import DirewolfModule
from owrx.aprs.direwolf import DirewolfModule
from owrx.sstv import SstvParser
from owrx.fax import FaxParser
from owrx.config import Config

View File

@ -1,6 +1,12 @@
import random
from pycsdr.types import Format
from pycsdr.modules import Writer, TcpSource, ExecModule, CallbackWriter
from csdr.module import LogWriter
from owrx.config.core import CoreConfig
from owrx.config import Config
from abc import ABC, abstractmethod
import time
import os
import random
import socket
import logging
@ -136,3 +142,71 @@ IGLOGIN {callsign} {password}
)
return config
class DirewolfModule(ExecModule, DirewolfConfigSubscriber):
def __init__(self, service: bool = False, ais: bool = False):
self.tcpSource = None
self.writer = None
self.service = service
self.ais = ais
self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format(
tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self)
)
self.direwolfConfig = DirewolfConfig()
self.direwolfConfig.wire(self)
self.__writeConfig()
# compose command line
cmdLine = ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"]
# for AIS mode, add -B AIS -A
if self.ais:
cmdLine += ["-B", "AIS", "-A"]
super().__init__(Format.SHORT, Format.CHAR, cmdLine)
# direwolf supplies the data via a socket which we tap into in start()
# the output on its STDOUT is informative, but we still want to log it
super().setWriter(LogWriter(__name__))
self.start()
def __writeConfig(self):
file = open(self.direwolfConfigPath, "w")
file.write(self.direwolfConfig.getConfig(self.service))
file.close()
def setWriter(self, writer: Writer) -> None:
self.writer = writer
if self.tcpSource is not None:
self.tcpSource.setWriter(writer)
def start(self):
delay = 0.5
retries = 0
while True:
try:
self.tcpSource = TcpSource(self.direwolfConfig.getPort(), Format.CHAR)
if self.writer:
self.tcpSource.setWriter(self.writer)
break
except ConnectionError:
if retries > 20:
logger.error("maximum number of connection attempts reached. did direwolf start up correctly?")
raise
retries += 1
time.sleep(delay)
def restart(self):
self.__writeConfig()
super().restart()
self.start()
def onConfigChanged(self):
self.restart()
def stop(self) -> None:
super().stop()
os.unlink(self.direwolfConfigPath)
self.direwolfConfig.unwire(self)
self.direwolfConfig = None

View File

@ -36,7 +36,7 @@ class Bookmark(object):
}
class BookmakrSubscription(object):
class BookmarkSubscription(object):
def __init__(self, subscriptee, range, subscriber: callable):
self.subscriptee = subscriptee
self.range = range
@ -67,11 +67,7 @@ class Bookmarks(object):
self.bookmarks = []
self.subscriptions = []
# Known bookmark files, starting with the main file
self.fileList = [
Bookmarks._getBookmarksFile(),
"bookmarks.json",
"/etc/openwebrx/bookmarks.json",
]
self.fileList = [Bookmarks._getBookmarksFile(), "/etc/openwebrx/bookmarks.json", "bookmarks.json"]
# Find additional bookmark files in the bookmarks.d folder
try:
bookmarksDir = "/etc/openwebrx/bookmarks.d"
@ -166,9 +162,11 @@ class Bookmarks(object):
logger.exception("Error while calling bookmark subscriptions")
def subscribe(self, range, callback):
self.subscriptions.append(BookmakrSubscription(self, range, callback))
sub = BookmarkSubscription(self, range, callback)
self.subscriptions.append(BookmarkSubscription(self, range, callback))
return sub
def unsubscribe(self, subscriptions: BookmakrSubscription):
if subscriptions not in self.subscriptions:
def unsubscribe(self, subscription: BookmarkSubscription):
if subscription not in self.subscriptions:
return
self.subscriptions.remove(subscriptions)
self.subscriptions.remove(subscription)

View File

@ -13,7 +13,6 @@ from datetime import datetime, timedelta
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class UnknownFeatureException(Exception):
@ -76,22 +75,22 @@ class FeatureDetector(object):
# optional features and their requirements
"digital_voice_digiham": ["digiham", "codecserver_ambe"],
"digital_voice_freedv": ["freedv_rx"],
"digital_voice_m17": ["m17_demod", "digiham"],
"digital_voice_m17": ["m17_demod"],
"wsjt-x": ["wsjtx"],
"wsjt-x-2-3": ["wsjtx_2_3"],
"wsjt-x-2-4": ["wsjtx_2_4"],
"msk144": ["msk144decoder"],
"packet": ["direwolf"],
"pocsag": ["digiham"],
"page": ["multimon"],
"selcall": ["multimon"],
"ism": ["rtl433"],
"hfdl": ["dumphfdl"],
"vdl2": ["dumpvdl2"],
"adsb": ["dump1090"],
"acars": ["acarsdec"],
"js8call": ["js8", "js8py"],
"drm": ["dream"],
"adsb": ["dump1090"],
"ism": ["rtl_433"],
"hfdl": ["dumphfdl"],
"vdl2": ["dumpvdl2"],
"acars": ["acarsdec"],
"page": ["multimon"],
"selcall": ["multimon"],
"png": ["imagemagick"],
}
@ -145,14 +144,12 @@ class FeatureDetector(object):
if cache.has(requirement):
return cache.get(requirement)
logger.debug("performing feature check for %s", requirement)
method = self._get_requirement_method(requirement)
result = False
if method is not None:
result = method()
else:
logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement))
logger.debug("feature check for %s complete. result: %s", requirement, result)
cache.set(requirement, result)
return result
@ -175,7 +172,14 @@ class FeatureDetector(object):
cwd=tmp_dir,
env=env,
)
rc = process.wait()
while True:
try:
rc = process.wait(10)
break
except subprocess.TimeoutExpired:
logger.warning("feature check command \"%s\" did not return after 10 seconds!", command)
process.kill()
if expected_result is None:
return rc != 32512
else:
@ -418,7 +422,7 @@ class FeatureDetector(object):
You can find more information [here](https://github.com/mobilinkd/m17-cxx-demod)
"""
return self.command_is_runnable("m17-demod")
return self.command_is_runnable("m17-demod", 0)
def has_direwolf(self):
"""
@ -437,7 +441,7 @@ class FeatureDetector(object):
def has_wsjtx(self):
"""
To decode FT8 and other digimodes, you need to install the WSJT-X software suite. Please check the
[WSJT-X homepage](https://physics.princeton.edu/pulsar/k1jt/wsjtx.html) for ready-made packages or instructions
[WSJT-X homepage](https://wsjt.sourceforge.io/) for ready-made packages or instructions
on how to build from source.
"""
return reduce(and_, map(self.command_is_runnable, ["jt9", "wsprd"]), True)
@ -562,6 +566,9 @@ class FeatureDetector(object):
Codecserver is used to decode audio data from digital voice modes using the AMBE codec.
You can find more information [here](https://github.com/jketterl/codecserver).
NOTE: this feature flag checks both the availability of codecserver as well as the availability of the AMBE
codec in the configured codecserver instance.
"""
config = Config.get()
@ -576,6 +583,65 @@ class FeatureDetector(object):
return False
except ConnectionError:
return False
except RuntimeError as e:
logger.exception("Codecserver error while checking for AMBE support:")
return False
def has_dump1090(self):
"""
To be able to decode Mode-S and ADS-B traffic originating from airplanes, you need to install the dump1090
decoder. There is a number of forks available, any version that supports the `--ifile` and `--iformat` arguments
should work.
Recommended fork: [dump1090 by Flightaware](https://github.com/flightaware/dump1090)
If you are using the OpenWebRX Debian or Ubuntu repository, you should be able to install the package
`dump1090-fa-minimal`.
If you are running a different fork, please make sure that the command `dump1090` (without suffixes) runs the
version you would like to use. You can use symbolic links or the
[Debian alternatives system](https://wiki.debian.org/DebianAlternatives) to achieve this.
"""
return self.command_is_runnable("dump1090 --version")
def has_rtl_433(self):
"""
OpenWebRX can make use of the `rtl_433` software to decode various signals in the ISM bands.
You can find more information [here](https://github.com/merbanan/rtl_433).
Debian and Ubuntu based systems should be able to install the package `rtl-433` from the package manager.
"""
return self.command_is_runnable("rtl_433 -h")
def has_dumphfdl(self):
"""
OpenWebRX supports decoding HFDL airplane communications using the `dumphfdl` decoder.
You can find more information [here](https://github.com/szpajder/dumphfdl)
If you are using the OpenWebRX Debian or Ubuntu repository, you should be able to install the package
`dumphfdl`.
"""
return self.command_is_runnable("dumphfdl --version")
def has_dumpvdl2(self):
"""
OpenWebRX supports decoding VDL Mode 2 airplane communications using the `dumpvdl2` decoder.
You can find more information [here](https://github.com/szpajder/dumpvdl2)
If you are using the OpenWebRX Debian or Ubuntu repository, you should be able to install the package
`dumpvdl2`.
"""
return self.command_is_runnable("dumpvdl2 --version")
def has_acarsdec(self):
"""
OpenWebRX uses the [acarsdec](https://github.com/TLeconte/acarsdec) tool to decode ACARS
traffic. You will have to compile it from source.
"""
return self.command_is_runnable("acarsdec --help")
def has_imagemagick(self):
"""
@ -592,42 +658,3 @@ class FeatureDetector(object):
"""
return self.command_is_runnable("multimon-ng --help")
def has_rtl433(self):
"""
OpenWebRX uses the [rtl-433](https://github.com/merbanan/rtl_433) decoder suite to decode various
instrumentation signals. Rtl_433 is available from the package manager on many
distributions, or you can compile it from source.
"""
return self.command_is_runnable("rtl_433 --help")
def has_dumphfdl(self):
"""
OpenWebRX uses the [DumpHFDL](https://github.com/szpajder/dumphfdl) tool to decode HFDL
aircraft communications. The latest DumpHFDL package is available from the OpenWebRX
repository as "dumphfdl", or you can compile it from source.
"""
return self.command_is_runnable("dumphfdl --help")
def has_dumpvdl2(self):
"""
OpenWebRX uses the [DumpVDL2](https://github.com/szpajder/dumpvdl2) tool to decode VDL2
aircraft communications. The latest DumpVDL2 package is available from the OpenWebRX
repository as "dumpvdl2", or you can compile it from source.
"""
return self.command_is_runnable("dumpvdl2 --help")
def has_dump1090(self):
"""
OpenWebRX uses the [dump1090](https://github.com/antirez/dump1090) tool to decode ADSB
traffic. The latest Dump1090 package is available from the OpenWebRX repository as
"dump1090-fa-minimal", or you can compile it from source.
"""
return self.command_is_runnable("dump1090 --help")
def has_acarsdec(self):
"""
OpenWebRX uses the [acarsdec](https://github.com/TLeconte/acarsdec) tool to decode ACARS
traffic. You will have to compile it from source.
"""
return self.command_is_runnable("acarsdec --help")

View File

@ -134,7 +134,7 @@ class Modes(object):
AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False),
DigitalMode("bpsk31", "BPSK31", underlying=["usb"]),
DigitalMode("bpsk63", "BPSK63", underlying=["usb"]),
# Using current RTTY decoder for now
# Testing jketterl's RTTY decoder
DigitalMode("jkrtty170", "RTTY 45/170", underlying=["usb", "lsb"]),
DigitalMode("jkrtty450", "RTTY 50N/450", underlying=["lsb", "usb"]),
DigitalMode("jkrtty85", "RTTY 50N/85", underlying=["lsb", "usb"]),

View File

@ -27,7 +27,7 @@ class PskReporter(Reporter):
Supports all valid MODE and SUBMODE values from the ADIF standard.
Current version at the time of the last change:
https://www.adif.org/312/ADIF_312.htm#Mode_Enumeration
https://www.adif.org/314/ADIF_314.htm#Mode_Enumeration
"""
return ["FT8", "FT4", "JT9", "JT65", "FST4", "JS8", "Q65", "WSPR", "FST4W", "MSK144"]
@ -105,27 +105,34 @@ class Uploader(object):
# filter out any erroneous encodes
encoded = [e for e in encoded if e is not None]
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i : i + n]
def chunks(block, max_size):
size = 0
current = []
for r in block:
if size + len(r) > max_size:
yield current
current = []
size = 0
size += len(r)
current.append(r)
yield current
rHeader = self.getReceiverInformationHeader()
rInfo = self.getReceiverInformation()
sHeader = self.getSenderInformationHeader()
packets = []
# 50 seems to be a safe bet
for chunk in chunks(encoded, 50):
# 1200 bytes of sender data should keep the packet size below MTU for most cases
for chunk in chunks(encoded, 1200):
sInfo = self.getSenderInformation(chunk)
length = 16 + len(rHeader) + len(sHeader) + len(rInfo) + len(sInfo)
header = self.getHeader(length)
packets.append(header + rHeader + sHeader + rInfo + sInfo)
self.sequence = (self.sequence + len(chunk)) % (1 << 32)
return packets
def getHeader(self, length):
self.sequence += 1
return bytes(
# protocol version
[0x00, 0x0A]
@ -142,7 +149,7 @@ class Uploader(object):
try:
return bytes(
self.encodeString(spot["callsign"])
+ list(int(spot["freq"]).to_bytes(4, "big"))
+ list(int(spot["freq"]).to_bytes(5, "big"))
+ list(int(spot["db"]).to_bytes(1, "big", signed=True))
+ self.encodeString(spot["mode"])
+ self.encodeString(spot["locator"])
@ -208,7 +215,7 @@ class Uploader(object):
# senderCallsign
+ [0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F]
# frequency
+ [0x80, 0x05, 0x00, 0x04, 0x00, 0x00, 0x76, 0x8F]
+ [0x80, 0x05, 0x00, 0x05, 0x00, 0x00, 0x76, 0x8F]
# sNR
+ [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x76, 0x8F]
# mode