first shot at decoding ISM data with rtl_433

This commit is contained in:
Jakob Ketterl 2023-09-01 23:37:40 +02:00
parent 687bf1c3d2
commit 0c6de7cf2a
10 changed files with 139 additions and 6 deletions

15
csdr/chain/rtl433.py Normal file
View file

@ -0,0 +1,15 @@
from owrx.ism.rtl433 import Rtl433Module, JsonParser
from csdr.chain.demodulator import ServiceDemodulator
class Rtl433(ServiceDemodulator):
def getFixedAudioRate(self) -> int:
return 250000
def __init__(self):
super().__init__(
[
Rtl433Module(),
JsonParser(),
]
)

View file

@ -1273,6 +1273,7 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="adsb"] #openwebrx-digimode-content-container, #openwebrx-panel-digimodes[data-mode="adsb"] #openwebrx-digimode-content-container,
#openwebrx-panel-digimodes[data-mode="ism"] #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,
@ -1285,7 +1286,8 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-select-channel, #openwebrx-panel-digimodes[data-mode="msk144"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="adsb"] #openwebrx-digimode-select-channel #openwebrx-panel-digimodes[data-mode="adsb"] #openwebrx-digimode-select-channel,
#openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-select-channel
{ {
display: none; display: none;
} }
@ -1302,7 +1304,8 @@ img.openwebrx-mirror-img
#openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="fst4w"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-canvas-container, #openwebrx-panel-digimodes[data-mode="q65"] #openwebrx-digimode-canvas-container,
#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="adsb"] #openwebrx-digimode-canvas-container #openwebrx-panel-digimodes[data-mode="adsb"] #openwebrx-digimode-canvas-container,
#openwebrx-panel-digimodes[data-mode="ism"] #openwebrx-digimode-canvas-container
{ {
height: 200px; height: 200px;
margin: -10px; margin: -10px;

View file

@ -75,6 +75,7 @@
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-packet-message" style="display: none; width: 619px;" data-panel-name="aprs-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-packet-message" style="display: none; width: 619px;" data-panel-name="aprs-message"></div>
<div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-pocsag-message" style="display: none; width: 619px;" data-panel-name="pocsag-message"></div> <div class="openwebrx-panel openwebrx-message-panel" id="openwebrx-panel-pocsag-message" style="display: none; width: 619px;" data-panel-name="pocsag-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-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">
<div class="openwebrx-meta-user-image"> <div class="openwebrx-meta-user-image">

View file

@ -165,10 +165,9 @@ DemodulatorPanel.prototype.updatePanels = function() {
var mode = Modes.findByModulation(modulation); var mode = Modes.findByModulation(modulation);
toggle_panel("openwebrx-panel-digimodes", modulation && (!mode || mode.secondaryFft)); toggle_panel("openwebrx-panel-digimodes", modulation && (!mode || mode.secondaryFft));
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'ft4', 'fst4', 'fst4w', "q65", "msk144"].indexOf(modulation) >= 0); toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'ft4', 'fst4', 'fst4w', "q65", "msk144"].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-js8-message", modulation === "js8"); ['js8', 'packet', 'pocsag', 'adsb', 'ism'].forEach(function(m) {
toggle_panel("openwebrx-panel-packet-message", modulation === "packet"); toggle_panel('openwebrx-panel-' + m + '-message', modulation === m);
toggle_panel("openwebrx-panel-pocsag-message", modulation === "pocsag"); });
toggle_panel('openwebrx-panel-adsb-message', modulation === 'adsb');
modulation = this.getDemodulator().get_modulation(); modulation = this.getDemodulator().get_modulation();
var showing = 'openwebrx-panel-metadata-' + modulation; var showing = 'openwebrx-panel-metadata-' + modulation;

View file

@ -393,4 +393,67 @@ $.fn.adsbMessagePanel = function () {
this.data('panel', new AdsbMessagePanel(this)); this.data('panel', new AdsbMessagePanel(this));
} }
return this.data('panel'); return this.data('panel');
};
IsmMessagePanel = function(el) {
MessagePanel.call(this, el);
this.initClearTimer();
};
IsmMessagePanel.prototype = new MessagePanel();
IsmMessagePanel.prototype.supportsMessage = function(message) {
return message['mode'] === 'ISM';
};
IsmMessagePanel.prototype.render = function() {
$(this.el).append($(
'<table>' +
'<thead><tr>' +
'<th class="model">Model</th>' +
'<th class="id">ID</th>' +
'<th class="channel">Channel</th>' +
'<th class="message">Message</th>' +
'</tr></thead>' +
'<tbody></tbody>' +
'</table>'
));
};
IsmMessagePanel.prototype.pushMessage = function(message) {
var $t = $(this.el).find('table');
var $b = $t.find('tbody');
var ifDefined = function(input, formatter) {
if (typeof(input) !== 'undefined') {
if (formatter) return formatter(input);
return input;
}
return "";
}
var mergeRemainingMessage = function(input, exclude) {
return Object.entries(input).map(function(entry) {
if (exclude.includes(entry[0])) return '';
return entry[0] + ': ' + entry[1] + ';';
}).join(' ');
}
$b.append($(
'<tr>' +
'<td class="model">' + ifDefined(message.model) + '</td>' +
'<td class="id">' + ifDefined(message.id) + '</td>' +
'<td class="channel">' + ifDefined(message.channel) + '</td>' +
'<td class="message">' + this.htmlEscape(mergeRemainingMessage(message, ['model', 'id', 'channel', 'mode', 'time'])) + '</td>' +
'</tr>'
));
$t.scrollTop($t[0].scrollHeight);
};
$.fn.ismMessagePanel = function() {
if (!this.data('panel')) {
this.data('panel', new IsmMessagePanel(this));
}
return this.data('panel');
}; };

View file

@ -864,6 +864,7 @@ function on_ws_recv(evt) {
$('#openwebrx-panel-packet-message').packetMessagePanel(), $('#openwebrx-panel-packet-message').packetMessagePanel(),
$('#openwebrx-panel-pocsag-message').pocsagMessagePanel(), $('#openwebrx-panel-pocsag-message').pocsagMessagePanel(),
$('#openwebrx-panel-adsb-message').adsbMessagePanel(), $('#openwebrx-panel-adsb-message').adsbMessagePanel(),
$('#openwebrx-panel-ism-message').ismMessagePanel(),
$("#openwebrx-panel-js8-message").js8() $("#openwebrx-panel-js8-message").js8()
]; ];
if (!panels.some(function(panel) { if (!panels.some(function(panel) {
@ -1470,6 +1471,7 @@ function secondary_demod_init() {
$('#openwebrx-panel-packet-message').packetMessagePanel(); $('#openwebrx-panel-packet-message').packetMessagePanel();
$('#openwebrx-panel-pocsag-message').pocsagMessagePanel(); $('#openwebrx-panel-pocsag-message').pocsagMessagePanel();
$('#openwebrx-panel-adsb-message').adsbMessagePanel(); $('#openwebrx-panel-adsb-message').adsbMessagePanel();
$('#openwebrx-panel-ism-message').ismMessagePanel();
$('#openwebrx-panel-js8-message').js8(); $('#openwebrx-panel-js8-message').js8();
} }

View file

@ -620,6 +620,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient)
elif mod == "adsb": elif mod == "adsb":
from csdr.chain.dump1090 import Dump1090 from csdr.chain.dump1090 import Dump1090
return Dump1090() return Dump1090()
elif mod == "ism":
from csdr.chain.rtl433 import Rtl433
return Rtl433()
def setSecondaryDemodulator(self, mod): def setSecondaryDemodulator(self, mod):
demodulator = self._getSecondaryDemodulator(mod) demodulator = self._getSecondaryDemodulator(mod)

View file

@ -85,6 +85,7 @@ class FeatureDetector(object):
"js8call": ["js8", "js8py"], "js8call": ["js8", "js8py"],
"drm": ["dream"], "drm": ["dream"],
"dump1090": ["dump1090"], "dump1090": ["dump1090"],
"ism": ["rtl_433"],
} }
def feature_availability(self): def feature_availability(self):
@ -591,3 +592,13 @@ class FeatureDetector(object):
[Debian alternatives system](https://wiki.debian.org/DebianAlternatives) to achieve this. [Debian alternatives system](https://wiki.debian.org/DebianAlternatives) to achieve this.
""" """
return self.command_is_runnable("dump1090 --version") 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")

28
owrx/ism/rtl433.py Normal file
View file

@ -0,0 +1,28 @@
from pycsdr.modules import ExecModule
from pycsdr.types import Format
from csdr.module import LineBasedModule
import json
import logging
logger = logging.getLogger(__name__)
class Rtl433Module(ExecModule):
def __init__(self):
super().__init__(
Format.COMPLEX_FLOAT,
Format.CHAR,
["rtl_433", "-r", "cf32:-", "-F", "json", "-M", "time:unix", "-C", "si"]
)
class JsonParser(LineBasedModule):
def process(self, line):
try:
msg = json.loads(line.decode())
msg["mode"] = "ISM"
logger.debug(msg)
return msg
except json.JSONDecodeError:
logger.exception("error parsing rtl433 json")

View file

@ -174,6 +174,14 @@ class Modes(object):
squelch=False, squelch=False,
secondaryFft=False, secondaryFft=False,
), ),
DigitalMode(
"ism",
"ISM",
underlying=["empty"],
bandpass=None,
requirements=["ism"],
squelch=False,
)
] ]
@staticmethod @staticmethod