Adding UI for the DSC decoder.

This commit is contained in:
Marat Fayzullin 2024-02-28 22:35:44 -05:00
parent 1b9a525449
commit 8fdbf90303
8 changed files with 142 additions and 10 deletions

View File

@ -8,6 +8,7 @@ from pycsdr.types import Format
from owrx.aprs.direwolf 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.dsc import DscParser
from owrx.config import Config from owrx.config import Config
@ -258,10 +259,11 @@ class SitorBDemodulator(SecondaryDemodulator, SecondarySelectorChain):
class DscDemodulator(SecondaryDemodulator, SecondarySelectorChain): class DscDemodulator(SecondaryDemodulator, SecondarySelectorChain):
def __init__(self, baudRate=100, bandWidth=170, invert=False): def __init__(self, baudRate=100, bandWidth=170, invert=False, service=False):
self.baudRate = baudRate self.baudRate = baudRate
self.bandWidth = bandWidth self.bandWidth = bandWidth
self.invert = invert self.invert = invert
self.parser = DscParser(service=service)
# this is an assumption, we will adjust in setSampleRate # this is an assumption, we will adjust in setSampleRate
self.sampleRate = 12000 self.sampleRate = 12000
secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate)) secondary_samples_per_bit = int(round(self.sampleRate / self.baudRate))
@ -274,6 +276,7 @@ class DscDemodulator(SecondaryDemodulator, SecondarySelectorChain):
TimingRecovery(Format.FLOAT, secondary_samples_per_bit, loop_gain, 10), TimingRecovery(Format.FLOAT, secondary_samples_per_bit, loop_gain, 10),
Ccir493Decoder(invert=invert), Ccir493Decoder(invert=invert),
DscDecoder(), DscDecoder(),
self.parser
] ]
super().__init__(workers) super().__init__(workers)
@ -289,3 +292,4 @@ class DscDemodulator(SecondaryDemodulator, SecondarySelectorChain):
loop_gain = self.sampleRate / self.getBandwidth() / 5 loop_gain = self.sampleRate / self.getBandwidth() / 5
self.replace(2, Lowpass(Format.FLOAT, cutoff)) self.replace(2, Lowpass(Format.FLOAT, cutoff))
self.replace(3, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, loop_gain, 10)) self.replace(3, TimingRecovery(Format.FLOAT, secondary_samples_per_bit, loop_gain, 10))

View File

@ -1360,6 +1360,45 @@ img.openwebrx-mirror-img
text-align: right; text-align: right;
} }
#openwebrx-panel-dsc-message {
height: 310px;
}
#openwebrx-panel-dsc-message a {
color: inherit;
}
#openwebrx-panel-dsc-message tbody {
height: 280px;
}
#openwebrx-panel-dsc-message .timestamp {
width: 80px;
text-align: center;
}
#openwebrx-panel-dsc-message .src {
width: 80px;
text-align: center;
}
#openwebrx-panel-dsc-message .dst {
width: 80px;
text-align: center;
}
#openwebrx-panel-dsc-message .data {
width: 326px;
text-align: center;
}
#openwebrx-panel-dsc-message .message {
width: 566px;
max-width: 566px;
column-span: all;
word-wrap: break-word;
}
#openwebrx-panel-ism-message { #openwebrx-panel-ism-message {
height: 310px; height: 310px;
} }
@ -1533,6 +1572,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="dsc"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="ft8"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="wspr"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="jt65"] #openwebrx-digimode-select-channel,
@ -1580,7 +1620,8 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="sstv"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="fax"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-canvas-container #openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="dsc"] #openwebrx-digimode-canvas-container
{ {
height: 200px; height: 200px;
margin: -10px; margin: -10px;

View File

@ -92,6 +92,7 @@
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-hfdl-message" style="display: none; width: 619px;" data-panel-name="hfdl-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-hfdl-message" style="display: none; width: 619px;" data-panel-name="hfdl-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-adsb-message" style="display: none; width: 619px;" data-panel-name="adsb-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-adsb-message" style="display: none; width: 619px;" data-panel-name="adsb-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-ism-message" style="display: none; width: 619px;" data-panel-name="ism-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-ism-message" style="display: none; width: 619px;" data-panel-name="ism-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-dsc-message" style="display: none; width: 619px;" data-panel-name="dsc-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-rds-message" style="display: none; width: 92%; max-width: 450px;" data-panel-name="rds-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-rds-message" style="display: none; width: 92%; max-width: 450px;" data-panel-name="rds-message"></div>
<div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-m17" style="display: none;" data-panel-name="metadata-m17"> <div class="openwebrx-panel openwebrx-meta-panel" id="openwebrx-panel-metadata-m17" style="display: none;" data-panel-name="metadata-m17">
<div class="openwebrx-meta-slot"> <div class="openwebrx-meta-slot">

View File

@ -175,7 +175,7 @@ DemodulatorPanel.prototype.updatePanels = function() {
// Packet modes share the same panel // Packet modes share the same panel
toggle_panel("openwebrx-panel-packet-message", ['packet', 'ais'].indexOf(modulation) >= 0); toggle_panel("openwebrx-panel-packet-message", ['packet', 'ais'].indexOf(modulation) >= 0);
// these modes come with their own // these modes come with their own
['js8', 'page', 'pocsag', 'sstv', 'fax', 'ism', 'rds'].forEach(function(m) { ['js8', 'page', 'pocsag', 'sstv', 'fax', 'ism', 'dsc', 'rds'].forEach(function(m) {
toggle_panel('openwebrx-panel-' + m + '-message', modulation === m); toggle_panel('openwebrx-panel-' + m + '-message', modulation === m);
}); });

View File

@ -562,6 +562,66 @@ $.fn.adsbMessagePanel = function() {
return this.data('panel'); return this.data('panel');
}; };
DscMessagePanel = function(el) {
MessagePanel.call(this, el);
this.initClearTimer();
}
DscMessagePanel.prototype = Object.create(MessagePanel.prototype);
DscMessagePanel.prototype.supportsMessage = function(message) {
return message['mode'] === 'DSC';
};
DscMessagePanel.prototype.render = function() {
$(this.el).append($(
'<table>' +
'<thead><tr>' +
'<th class="timestamp">Time</th>' +
'<th class="src">From</th>' +
'<th class="dst">To</th>' +
'<th class="data">Data</th>' +
'</tr></thead>' +
'<tbody></tbody>' +
'</table>'
));
};
DscMessagePanel.prototype.pushMessage = function(msg) {
var tstamp = 0;
var src = '*';
var dst = '*';
var data = '';
// Append report
var $b = $(this.el).find('tbody');
$b.append($(
'<tr>' +
'<td class="timestamp">' + tstamp + '</td>' +
'<td class="src">' + src + '</td>' +
'<td class="dst">' + dst + '</td>' +
'<td class="data" style="text-align:left;">' + data + '</td>' +
'</tr>'
));
// Append messsage if present
if (msg.message) {
$b.append($(
'<tr><td class="message" colspan="4">' + Utils.htmlEscape(msg.message) + '</td></tr>'
))
}
// Jump list to the last received message
this.scrollToBottom();
};
$.fn.dscMessagePanel = function() {
if (!this.data('panel')) {
this.data('panel', new DscMessagePanel(this));
}
return this.data('panel');
};
IsmMessagePanel = function(el) { IsmMessagePanel = function(el) {
MessagePanel.call(this, el); MessagePanel.call(this, el);
this.initClearTimer(); this.initClearTimer();

View File

@ -1169,7 +1169,7 @@ function on_ws_recv(evt) {
break; break;
case 'secondary_demod': case 'secondary_demod':
var value = json['value']; var value = json['value'];
var panels = ['wsjt', 'packet', 'pocsag', 'page', 'sstv', 'fax', 'ism', 'hfdl', 'adsb'].map(function(id) { var panels = ['wsjt', 'packet', 'pocsag', 'page', 'sstv', 'fax', 'ism', 'hfdl', 'adsb', 'dsc'].map(function(id) {
return $('#openwebrx-panel-' + id + '-message')[id + 'MessagePanel'](); return $('#openwebrx-panel-' + id + '-message')[id + 'MessagePanel']();
}); });
panels.push($('#openwebrx-panel-js8-message').js8()); panels.push($('#openwebrx-panel-js8-message').js8());
@ -1846,7 +1846,7 @@ function secondary_demod_init() {
.mousedown(secondary_demod_canvas_container_mousedown) .mousedown(secondary_demod_canvas_container_mousedown)
.mouseenter(secondary_demod_canvas_container_mousein) .mouseenter(secondary_demod_canvas_container_mousein)
.mouseleave(secondary_demod_canvas_container_mouseleave); .mouseleave(secondary_demod_canvas_container_mouseleave);
['wsjt', 'packet', 'pocsag', 'page', 'sstv', 'fax', 'ism', 'hfdl', 'adsb'].forEach(function(id){ ['wsjt', 'packet', 'pocsag', 'page', 'sstv', 'fax', 'ism', 'hfdl', 'adsb', 'dsc'].forEach(function(id){
$('#openwebrx-panel-' + id + '-message')[id + 'MessagePanel'](); $('#openwebrx-panel-' + id + '-message')[id + 'MessagePanel']();
}) })
$('#openwebrx-panel-js8-message').js8(); $('#openwebrx-panel-js8-message').js8();

27
owrx/dsc.py Normal file
View File

@ -0,0 +1,27 @@
from owrx.toolbox import TextParser
from owrx.color import ColorCache
import json
import logging
logger = logging.getLogger(__name__)
class DscParser(TextParser):
def __init__(self, service: bool = False):
# Colors will be assigned via this cache
self.colors = ColorCache()
# Construct parent object
super().__init__(filePrefix="DSC", service=service)
def parse(self, msg: bytes):
# Do not parse in service mode
if self.service:
return None
# Expect JSON data in text form
out = json.loads(msg)
# Add mode name and a color to identify the sender
out["mode"] = "DSC"
out["color"] = self.colors.getColor(out["src"])
logger.debug("{0}".format(out))
return out

View File

@ -142,8 +142,7 @@ class Modes(object):
DigitalMode("rtty450", "RTTY-450 (50N)", underlying=["usb", "lsb"]), DigitalMode("rtty450", "RTTY-450 (50N)", underlying=["usb", "lsb"]),
DigitalMode("rtty85", "RTTY-85 (50N)", underlying=["usb", "lsb"]), DigitalMode("rtty85", "RTTY-85 (50N)", underlying=["usb", "lsb"]),
DigitalMode("sitorb", "SITOR-B", underlying=["usb"]), DigitalMode("sitorb", "SITOR-B", underlying=["usb"]),
# Currently in development DigitalMode("dsc", "DSC", underlying=["usb"]),
# DigitalMode("dsc", "DSC", underlying=["usb"]),
WsjtMode("ft8", "FT8"), WsjtMode("ft8", "FT8"),
WsjtMode("ft4", "FT4"), WsjtMode("ft4", "FT4"),
WsjtMode("jt65", "JT65"), WsjtMode("jt65", "JT65"),