Merging the rest of owrx folder.
This commit is contained in:
parent
5117e27129
commit
c4afe1e594
|
|
@ -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)
|
- supports a wide range of [SDR hardware](https://github.com/jketterl/openwebrx/wiki/Supported-Hardware#sdr-devices)
|
||||||
- Multiple SDR devices can be used simultaneously
|
- Multiple SDR devices can be used simultaneously
|
||||||
- [digiham](https://github.com/jketterl/digiham) based demodularors (DMR, YSF, Pocsag, D-Star, NXDN)
|
- [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)
|
FST4W)
|
||||||
- [direwolf](https://github.com/wb2osz/direwolf) based demodulation of APRS packets
|
- [direwolf](https://github.com/wb2osz/direwolf) based demodulation of APRS packets
|
||||||
- [JS8Call](http://js8call.com/) support
|
- [JS8Call](http://js8call.com/) support
|
||||||
|
|
|
||||||
19
bands.json
19
bands.json
|
|
@ -393,31 +393,40 @@
|
||||||
"upper_bound": 446200000,
|
"upper_bound": 446200000,
|
||||||
"tags": ["public"]
|
"tags": ["public"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ADS-B Reporting",
|
||||||
|
"lower_bound": 960000000,
|
||||||
|
"upper_bound": 1215000000,
|
||||||
|
"tags": ["service"],
|
||||||
|
"frequencies": {
|
||||||
|
"adsb": 1090000000
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "LPD433",
|
"name": "LPD433",
|
||||||
"lower_bound": 433050000,
|
"lower_bound": 433050000,
|
||||||
"upper_bound": 434790000,
|
"upper_bound": 434790000,
|
||||||
|
"tags": ["public"],
|
||||||
"frequencies": {
|
"frequencies": {
|
||||||
"ism": 433920000
|
"ism": 433920000
|
||||||
},
|
}
|
||||||
"tags": ["public"]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "VHF Air",
|
"name": "VHF Air",
|
||||||
"lower_bound": 108000000,
|
"lower_bound": 108000000,
|
||||||
"upper_bound": 137000000,
|
"upper_bound": 137000000,
|
||||||
|
"tags": ["service"],
|
||||||
"frequencies": {
|
"frequencies": {
|
||||||
"vdl2": 136975000
|
"vdl2": 136975000
|
||||||
},
|
},
|
||||||
"tags": ["service"]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "VHF Marine",
|
"name": "VHF Marine",
|
||||||
"lower_bound": 156000000,
|
"lower_bound": 156000000,
|
||||||
"upper_bound": 174000000,
|
"upper_bound": 174000000,
|
||||||
|
"tags": ["service"],
|
||||||
"frequencies": {
|
"frequencies": {
|
||||||
"ais": [161975000, 162025000]
|
"ais": [161975000, 162025000]
|
||||||
},
|
}
|
||||||
"tags": ["service"]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from owrx.aprs.kiss import KissDeframer
|
||||||
from owrx.aprs import Ax25Parser, AprsParser
|
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.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder, JKRttyDecoder, BaudotDecoder, Lowpass, RttyDecoder, CwDecoder, SstvDecoder, FaxDecoder, Shift
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from owrx.aprs.module import DirewolfModule
|
from owrx.aprs.direwolf import DirewolfModule
|
||||||
from owrx.sstv import SstvParser
|
from owrx.sstv import SstvParser
|
||||||
from owrx.fax import FaxParser
|
from owrx.fax import FaxParser
|
||||||
from owrx.config import Config
|
from owrx.config import Config
|
||||||
|
|
|
||||||
|
|
@ -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 owrx.config import Config
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import random
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -136,3 +142,71 @@ IGLOGIN {callsign} {password}
|
||||||
)
|
)
|
||||||
|
|
||||||
return config
|
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
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class Bookmark(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BookmakrSubscription(object):
|
class BookmarkSubscription(object):
|
||||||
def __init__(self, subscriptee, range, subscriber: callable):
|
def __init__(self, subscriptee, range, subscriber: callable):
|
||||||
self.subscriptee = subscriptee
|
self.subscriptee = subscriptee
|
||||||
self.range = range
|
self.range = range
|
||||||
|
|
@ -67,11 +67,7 @@ class Bookmarks(object):
|
||||||
self.bookmarks = []
|
self.bookmarks = []
|
||||||
self.subscriptions = []
|
self.subscriptions = []
|
||||||
# Known bookmark files, starting with the main file
|
# Known bookmark files, starting with the main file
|
||||||
self.fileList = [
|
self.fileList = [Bookmarks._getBookmarksFile(), "/etc/openwebrx/bookmarks.json", "bookmarks.json"]
|
||||||
Bookmarks._getBookmarksFile(),
|
|
||||||
"bookmarks.json",
|
|
||||||
"/etc/openwebrx/bookmarks.json",
|
|
||||||
]
|
|
||||||
# Find additional bookmark files in the bookmarks.d folder
|
# Find additional bookmark files in the bookmarks.d folder
|
||||||
try:
|
try:
|
||||||
bookmarksDir = "/etc/openwebrx/bookmarks.d"
|
bookmarksDir = "/etc/openwebrx/bookmarks.d"
|
||||||
|
|
@ -166,9 +162,11 @@ class Bookmarks(object):
|
||||||
logger.exception("Error while calling bookmark subscriptions")
|
logger.exception("Error while calling bookmark subscriptions")
|
||||||
|
|
||||||
def subscribe(self, range, callback):
|
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):
|
def unsubscribe(self, subscription: BookmarkSubscription):
|
||||||
if subscriptions not in self.subscriptions:
|
if subscription not in self.subscriptions:
|
||||||
return
|
return
|
||||||
self.subscriptions.remove(subscriptions)
|
self.subscriptions.remove(subscription)
|
||||||
|
|
|
||||||
133
owrx/feature.py
133
owrx/feature.py
|
|
@ -13,7 +13,6 @@ from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownFeatureException(Exception):
|
class UnknownFeatureException(Exception):
|
||||||
|
|
@ -76,22 +75,22 @@ class FeatureDetector(object):
|
||||||
# optional features and their requirements
|
# optional features and their requirements
|
||||||
"digital_voice_digiham": ["digiham", "codecserver_ambe"],
|
"digital_voice_digiham": ["digiham", "codecserver_ambe"],
|
||||||
"digital_voice_freedv": ["freedv_rx"],
|
"digital_voice_freedv": ["freedv_rx"],
|
||||||
"digital_voice_m17": ["m17_demod", "digiham"],
|
"digital_voice_m17": ["m17_demod"],
|
||||||
"wsjt-x": ["wsjtx"],
|
"wsjt-x": ["wsjtx"],
|
||||||
"wsjt-x-2-3": ["wsjtx_2_3"],
|
"wsjt-x-2-3": ["wsjtx_2_3"],
|
||||||
"wsjt-x-2-4": ["wsjtx_2_4"],
|
"wsjt-x-2-4": ["wsjtx_2_4"],
|
||||||
"msk144": ["msk144decoder"],
|
"msk144": ["msk144decoder"],
|
||||||
"packet": ["direwolf"],
|
"packet": ["direwolf"],
|
||||||
"pocsag": ["digiham"],
|
"pocsag": ["digiham"],
|
||||||
"page": ["multimon"],
|
|
||||||
"selcall": ["multimon"],
|
|
||||||
"ism": ["rtl433"],
|
|
||||||
"hfdl": ["dumphfdl"],
|
|
||||||
"vdl2": ["dumpvdl2"],
|
|
||||||
"adsb": ["dump1090"],
|
|
||||||
"acars": ["acarsdec"],
|
|
||||||
"js8call": ["js8", "js8py"],
|
"js8call": ["js8", "js8py"],
|
||||||
"drm": ["dream"],
|
"drm": ["dream"],
|
||||||
|
"adsb": ["dump1090"],
|
||||||
|
"ism": ["rtl_433"],
|
||||||
|
"hfdl": ["dumphfdl"],
|
||||||
|
"vdl2": ["dumpvdl2"],
|
||||||
|
"acars": ["acarsdec"],
|
||||||
|
"page": ["multimon"],
|
||||||
|
"selcall": ["multimon"],
|
||||||
"png": ["imagemagick"],
|
"png": ["imagemagick"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,14 +144,12 @@ class FeatureDetector(object):
|
||||||
if cache.has(requirement):
|
if cache.has(requirement):
|
||||||
return cache.get(requirement)
|
return cache.get(requirement)
|
||||||
|
|
||||||
logger.debug("performing feature check for %s", requirement)
|
|
||||||
method = self._get_requirement_method(requirement)
|
method = self._get_requirement_method(requirement)
|
||||||
result = False
|
result = False
|
||||||
if method is not None:
|
if method is not None:
|
||||||
result = method()
|
result = method()
|
||||||
else:
|
else:
|
||||||
logger.error("detection of requirement {0} not implement. please fix in code!".format(requirement))
|
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)
|
cache.set(requirement, result)
|
||||||
return result
|
return result
|
||||||
|
|
@ -175,7 +172,14 @@ class FeatureDetector(object):
|
||||||
cwd=tmp_dir,
|
cwd=tmp_dir,
|
||||||
env=env,
|
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:
|
if expected_result is None:
|
||||||
return rc != 32512
|
return rc != 32512
|
||||||
else:
|
else:
|
||||||
|
|
@ -418,7 +422,7 @@ class FeatureDetector(object):
|
||||||
|
|
||||||
You can find more information [here](https://github.com/mobilinkd/m17-cxx-demod)
|
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):
|
def has_direwolf(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -437,7 +441,7 @@ class FeatureDetector(object):
|
||||||
def has_wsjtx(self):
|
def has_wsjtx(self):
|
||||||
"""
|
"""
|
||||||
To decode FT8 and other digimodes, you need to install the WSJT-X software suite. Please check the
|
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.
|
on how to build from source.
|
||||||
"""
|
"""
|
||||||
return reduce(and_, map(self.command_is_runnable, ["jt9", "wsprd"]), True)
|
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.
|
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).
|
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()
|
config = Config.get()
|
||||||
|
|
@ -576,6 +583,65 @@ class FeatureDetector(object):
|
||||||
return False
|
return False
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
return False
|
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):
|
def has_imagemagick(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -592,42 +658,3 @@ class FeatureDetector(object):
|
||||||
"""
|
"""
|
||||||
return self.command_is_runnable("multimon-ng --help")
|
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")
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ class Modes(object):
|
||||||
AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False),
|
AnalogMode("drm", "DRM", bandpass=Bandpass(-5000, 5000), requirements=["drm"], squelch=False),
|
||||||
DigitalMode("bpsk31", "BPSK31", underlying=["usb"]),
|
DigitalMode("bpsk31", "BPSK31", underlying=["usb"]),
|
||||||
DigitalMode("bpsk63", "BPSK63", 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("jkrtty170", "RTTY 45/170", underlying=["usb", "lsb"]),
|
||||||
DigitalMode("jkrtty450", "RTTY 50N/450", underlying=["lsb", "usb"]),
|
DigitalMode("jkrtty450", "RTTY 50N/450", underlying=["lsb", "usb"]),
|
||||||
DigitalMode("jkrtty85", "RTTY 50N/85", underlying=["lsb", "usb"]),
|
DigitalMode("jkrtty85", "RTTY 50N/85", underlying=["lsb", "usb"]),
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class PskReporter(Reporter):
|
||||||
Supports all valid MODE and SUBMODE values from the ADIF standard.
|
Supports all valid MODE and SUBMODE values from the ADIF standard.
|
||||||
|
|
||||||
Current version at the time of the last change:
|
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"]
|
return ["FT8", "FT4", "JT9", "JT65", "FST4", "JS8", "Q65", "WSPR", "FST4W", "MSK144"]
|
||||||
|
|
||||||
|
|
@ -105,27 +105,34 @@ class Uploader(object):
|
||||||
# filter out any erroneous encodes
|
# filter out any erroneous encodes
|
||||||
encoded = [e for e in encoded if e is not None]
|
encoded = [e for e in encoded if e is not None]
|
||||||
|
|
||||||
def chunks(l, n):
|
def chunks(block, max_size):
|
||||||
"""Yield successive n-sized chunks from l."""
|
size = 0
|
||||||
for i in range(0, len(l), n):
|
current = []
|
||||||
yield l[i : i + n]
|
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()
|
rHeader = self.getReceiverInformationHeader()
|
||||||
rInfo = self.getReceiverInformation()
|
rInfo = self.getReceiverInformation()
|
||||||
sHeader = self.getSenderInformationHeader()
|
sHeader = self.getSenderInformationHeader()
|
||||||
|
|
||||||
packets = []
|
packets = []
|
||||||
# 50 seems to be a safe bet
|
# 1200 bytes of sender data should keep the packet size below MTU for most cases
|
||||||
for chunk in chunks(encoded, 50):
|
for chunk in chunks(encoded, 1200):
|
||||||
sInfo = self.getSenderInformation(chunk)
|
sInfo = self.getSenderInformation(chunk)
|
||||||
length = 16 + len(rHeader) + len(sHeader) + len(rInfo) + len(sInfo)
|
length = 16 + len(rHeader) + len(sHeader) + len(rInfo) + len(sInfo)
|
||||||
header = self.getHeader(length)
|
header = self.getHeader(length)
|
||||||
packets.append(header + rHeader + sHeader + rInfo + sInfo)
|
packets.append(header + rHeader + sHeader + rInfo + sInfo)
|
||||||
|
self.sequence = (self.sequence + len(chunk)) % (1 << 32)
|
||||||
|
|
||||||
return packets
|
return packets
|
||||||
|
|
||||||
def getHeader(self, length):
|
def getHeader(self, length):
|
||||||
self.sequence += 1
|
|
||||||
return bytes(
|
return bytes(
|
||||||
# protocol version
|
# protocol version
|
||||||
[0x00, 0x0A]
|
[0x00, 0x0A]
|
||||||
|
|
@ -142,7 +149,7 @@ class Uploader(object):
|
||||||
try:
|
try:
|
||||||
return bytes(
|
return bytes(
|
||||||
self.encodeString(spot["callsign"])
|
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))
|
+ list(int(spot["db"]).to_bytes(1, "big", signed=True))
|
||||||
+ self.encodeString(spot["mode"])
|
+ self.encodeString(spot["mode"])
|
||||||
+ self.encodeString(spot["locator"])
|
+ self.encodeString(spot["locator"])
|
||||||
|
|
@ -208,7 +215,7 @@ class Uploader(object):
|
||||||
# senderCallsign
|
# senderCallsign
|
||||||
+ [0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F]
|
+ [0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x76, 0x8F]
|
||||||
# frequency
|
# frequency
|
||||||
+ [0x80, 0x05, 0x00, 0x04, 0x00, 0x00, 0x76, 0x8F]
|
+ [0x80, 0x05, 0x00, 0x05, 0x00, 0x00, 0x76, 0x8F]
|
||||||
# sNR
|
# sNR
|
||||||
+ [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x76, 0x8F]
|
+ [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x76, 0x8F]
|
||||||
# mode
|
# mode
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue