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