diff --git a/htdocs/lib/Js8Threads.js b/htdocs/lib/Js8Threads.js
index 4fa5595..4e9faa8 100644
--- a/htdocs/lib/Js8Threads.js
+++ b/htdocs/lib/Js8Threads.js
@@ -70,7 +70,7 @@ Js8Thread.prototype.getMessageDuration = function() {
Js8Thread.prototype.getMode = function() {
// we filter messages by mode, so the first one is as good as any
if (!this.messages.length) return;
- return this.messages[0].mode;
+ return this.messages[0].js8mode;
};
Js8Thread.prototype.acceptsMode = function(mode) {
@@ -117,6 +117,10 @@ Js8Threader = function(el){
Js8Threader.prototype = new MessagePanel();
+Js8Threader.prototype.supportsMessage = function(message) {
+ return message['mode'] === 'JS8';
+};
+
Js8Threader.prototype.render = function() {
$(this.el).append($(
'
' +
@@ -158,7 +162,7 @@ Js8Threader.prototype.pushMessage = function(message) {
var thread;
// only look for exising threads if the message is not a starting message
if ((message.thread_type & 1) === 0) {
- thread = this.findThread(message.freq, message.mode);
+ thread = this.findThread(message.freq, message.js8mode);
}
if (!thread) {
var line = $("
");
diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js
index 057177b..c9b659c 100644
--- a/htdocs/openwebrx.js
+++ b/htdocs/openwebrx.js
@@ -821,9 +821,6 @@ function on_ws_recv(evt) {
this.update(json['value']);
});
break;
- case "js8_message":
- $("#openwebrx-panel-js8-message").js8().pushMessage(json['value']);
- break;
case "dial_frequencies":
var as_bookmarks = json['value'].map(function (d) {
return {
@@ -849,7 +846,8 @@ function on_ws_recv(evt) {
var panels = [
$("#openwebrx-panel-wsjt-message").wsjtMessagePanel(),
$('#openwebrx-panel-packet-message').packetMessagePanel(),
- $('#openwebrx-panel-pocsag-message').pocsagMessagePanel()
+ $('#openwebrx-panel-pocsag-message').pocsagMessagePanel(),
+ $("#openwebrx-panel-js8-message").js8()
];
if (!panels.some(function(panel) {
if (!panel.supportsMessage(value)) return false;
diff --git a/owrx/connection.py b/owrx/connection.py
index 47d8aa5..62771e5 100644
--- a/owrx/connection.py
+++ b/owrx/connection.py
@@ -437,22 +437,6 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
def write_backoff_message(self, reason):
self.send({"type": "backoff", "reason": reason})
- def write_js8_message(self, frame: Js8Frame, freq: int):
- self.send(
- {
- "type": "js8_message",
- "value": {
- "msg": str(frame),
- "timestamp": frame.timestamp,
- "db": frame.db,
- "dt": frame.dt,
- "freq": freq + frame.freq,
- "thread_type": frame.thread_type,
- "mode": frame.mode,
- },
- }
- )
-
def write_modes(self, modes):
def to_json(m):
res = {
diff --git a/owrx/dsp.py b/owrx/dsp.py
index eb46a1a..71c94a6 100644
--- a/owrx/dsp.py
+++ b/owrx/dsp.py
@@ -281,7 +281,6 @@ class DspManager(Output, SdrSourceEventClient):
self.sdrSource = sdrSource
self.parsers = {
"meta": MetaParser(self.handler),
- "js8_demod": Js8Parser(self.handler),
}
self.props = PropertyStack()
@@ -364,18 +363,6 @@ class DspManager(Output, SdrSourceEventClient):
# TODO there's multiple outputs depending on the modulation right now
self.wireOutput("secondary_demod", buffer)
- def set_dial_freq(changes):
- if (
- "center_freq" not in self.props
- or self.props["center_freq"] is None
- or "offset_freq" not in self.props
- or self.props["offset_freq"] is None
- ):
- return
- freq = self.props["center_freq"] + self.props["offset_freq"]
- for parser in self.parsers.values():
- parser.setDialFrequency(freq)
-
if "start_mod" in self.props:
self.setDemodulator(self.props["start_mod"])
mode = Modes.findByModulation(self.props["start_mod"])
@@ -409,7 +396,6 @@ class DspManager(Output, SdrSourceEventClient):
# self.props.wireProperty("wfm_deemphasis_tau", self.dsp.set_wfm_deemphasis_tau),
# TODO
# self.props.wireProperty("digital_voice_codecserver", self.dsp.set_codecserver),
- self.props.filter("center_freq", "offset_freq").wire(set_dial_freq),
]
# TODO
@@ -489,6 +475,8 @@ class DspManager(Output, SdrSourceEventClient):
# TODO add remaining modes
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
return AudioChopperDemodulator(mod, WsjtParser())
+ elif mod == "js8":
+ return AudioChopperDemodulator(mod, Js8Parser())
elif mod == "packet":
return PacketDemodulator()
elif mod == "pocsag":
diff --git a/owrx/js8.py b/owrx/js8.py
index 97b1195..8edbb95 100644
--- a/owrx/js8.py
+++ b/owrx/js8.py
@@ -1,5 +1,4 @@
from owrx.audio import AudioChopperProfile, ConfigWiredProfileSource
-from owrx.parser import Parser
import re
from js8py import Js8
from js8py.frames import Js8FrameHeartbeat, Js8FrameCompound
@@ -8,6 +7,7 @@ from owrx.metrics import Metrics, CounterMetric
from owrx.config import Config
from abc import ABCMeta, abstractmethod
from owrx.reporting import ReportingEngine
+from owrx.bands import Bandplan
from typing import List
import logging
@@ -81,13 +81,15 @@ class Js8TurboProfile(Js8Profile):
return "C"
-class Js8Parser(Parser):
+class Js8Parser:
decoderRegex = re.compile(" ?")
- def parse(self, raw):
+ def parse(self, profile, freq, raw_msg):
try:
- profile, freq, raw_msg = raw
- self.setDialFrequency(freq)
+ band = None
+ if freq is not None:
+ band = Bandplan.getSharedInstance().findBand(freq)
+
msg = raw_msg.decode().rstrip()
if Js8Parser.decoderRegex.match(msg):
return
@@ -95,38 +97,48 @@ class Js8Parser(Parser):
return
frame = Js8().parse_message(msg)
- self.handler.write_js8_message(frame, self.dial_freq)
- self.pushDecode()
+ self.pushDecode(band)
if (isinstance(frame, Js8FrameHeartbeat) or isinstance(frame, Js8FrameCompound)) and frame.grid:
Map.getSharedInstance().updateLocation(
- frame.callsign, LocatorLocation(frame.grid), "JS8", self.band
+ frame.callsign, LocatorLocation(frame.grid), "JS8", band
)
ReportingEngine.getSharedInstance().spot(
{
"callsign": frame.callsign,
"mode": "JS8",
"locator": frame.grid,
- "freq": self.dial_freq + frame.freq,
+ "freq": freq + frame.freq,
"db": frame.db,
"timestamp": frame.timestamp,
"msg": str(frame),
}
)
+ out = {
+ "mode": "JS8",
+ "msg": str(frame),
+ "timestamp": frame.timestamp,
+ "db": frame.db,
+ "dt": frame.dt,
+ "freq": freq + frame.freq,
+ "thread_type": frame.thread_type,
+ "js8mode": frame.mode,
+ }
+
+ return out
+
except Exception:
logger.exception("error while parsing js8 message")
- def pushDecode(self):
+ def pushDecode(self, band):
metrics = Metrics.getSharedInstance()
- band = "unknown"
- if self.band is not None:
- band = self.band.getName()
- if band is None:
- band = "unknown"
+ bandName = "unknown"
+ if band is not None:
+ bandName = band.getName()
- name = "js8call.decodes.{band}.JS8".format(band=band)
+ name = "js8call.decodes.{band}.JS8".format(band=bandName)
metric = metrics.getMetric(name)
if metric is None:
metric = CounterMetric()
diff --git a/owrx/meta.py b/owrx/meta.py
index f8f43bc..b995910 100644
--- a/owrx/meta.py
+++ b/owrx/meta.py
@@ -5,7 +5,6 @@ from datetime import datetime, timedelta
import logging
import threading
from owrx.map import Map, LatLngLocation
-from owrx.parser import Parser
from owrx.aprs import AprsParser, AprsLocation
from abc import ABC, abstractmethod
@@ -159,9 +158,9 @@ class DStarEnricher(Enricher):
return meta
-class MetaParser(Parser):
+class MetaParser:
def __init__(self, handler):
- super().__init__(handler)
+ self.handler = handler
self.enrichers = {
"DMR": RadioIDEnricher("dmr", self),
"YSF": YsfMetaEnricher(self),
diff --git a/owrx/parser.py b/owrx/parser.py
deleted file mode 100644
index 2bb75e2..0000000
--- a/owrx/parser.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from abc import ABC, abstractmethod
-from owrx.bands import Bandplan
-
-
-class Parser(ABC):
- def __init__(self, handler):
- self.handler = handler
- self.dial_freq = None
- self.band = None
-
- @abstractmethod
- def parse(self, raw):
- pass
-
- def setDialFrequency(self, freq):
- self.dial_freq = freq
- self.band = Bandplan.getSharedInstance().findBand(freq)
-
- def getBand(self):
- return self.band
diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py
index 930dc39..3a7d2f1 100644
--- a/owrx/service/__init__.py
+++ b/owrx/service/__init__.py
@@ -3,10 +3,10 @@ from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass
from owrx.sdr import SdrService
from owrx.bands import Bandplan
from owrx.wsjt import WsjtParser
+from owrx.js8 import Js8Parser
from owrx.config import Config
from owrx.source.resampler import Resampler
from owrx.property import PropertyLayer, PropertyDeleted
-from js8py import Js8Frame
from owrx.service.schedule import ServiceScheduler
from owrx.service.chain import ServiceDemodulatorChain
from owrx.modes import Modes, DigitalMode
@@ -250,7 +250,7 @@ class ServiceHandler(SdrSourceEventClient):
logger.warning("mode is not a digimode: %s", mode)
return None
- demod = self._getDemodulator(modeObject.get_modulation(), source.getProps())
+ demod = self._getDemodulator(modeObject.get_modulation())
secondaryDemod = self._getSecondaryDemodulator(modeObject.modulation)
center_freq = source.getProps()["center_freq"]
sampleRate = source.getProps()["samp_rate"]
@@ -269,7 +269,7 @@ class ServiceHandler(SdrSourceEventClient):
return chain
# TODO move this elsewhere
- def _getDemodulator(self, demod: Union[str, BaseDemodulatorChain], props):
+ def _getDemodulator(self, demod: Union[str, BaseDemodulatorChain]):
if isinstance(demod, BaseDemodulatorChain):
return demod
# TODO: move this to Modes
@@ -288,6 +288,8 @@ class ServiceHandler(SdrSourceEventClient):
# TODO add remaining modes
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
return AudioChopperDemodulator(mod, WsjtParser())
+ elif mod == "js8":
+ return AudioChopperDemodulator(mod, Js8Parser())
elif mod == "packet":
return PacketDemodulator(service=True)
return None