restore pocsag functionality
This commit is contained in:
parent
b9f43654cd
commit
6014ce8921
@ -5,6 +5,8 @@ from owrx.aprs import Ax25Parser, AprsParser
|
|||||||
from pycsdr.modules import Convert, FmDemod
|
from pycsdr.modules import Convert, FmDemod
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from owrx.aprs.module import DirewolfModule
|
from owrx.aprs.module import DirewolfModule
|
||||||
|
from digiham.modules import FskDemodulator, PocsagDecoder
|
||||||
|
from owrx.pocsag import PocsagParser
|
||||||
|
|
||||||
|
|
||||||
class AudioChopperDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver):
|
class AudioChopperDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver):
|
||||||
@ -42,3 +44,20 @@ class PacketDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequency
|
|||||||
|
|
||||||
def setDialFrequency(self, frequency: int) -> None:
|
def setDialFrequency(self, frequency: int) -> None:
|
||||||
self.parser.setDialFrequency(frequency)
|
self.parser.setDialFrequency(frequency)
|
||||||
|
|
||||||
|
|
||||||
|
class PocsagDemodulator(SecondaryDemodulator, FixedAudioRateChain):
|
||||||
|
def __init__(self):
|
||||||
|
workers = [
|
||||||
|
FmDemod(),
|
||||||
|
FskDemodulator(samplesPerSymbol=40, invert=True),
|
||||||
|
PocsagDecoder(),
|
||||||
|
PocsagParser(),
|
||||||
|
]
|
||||||
|
super().__init__(workers)
|
||||||
|
|
||||||
|
def supportsSquelch(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getFixedAudioRate(self) -> int:
|
||||||
|
return 48000
|
||||||
|
@ -2,6 +2,9 @@ from pycsdr.modules import Module as BaseModule
|
|||||||
from pycsdr.modules import Reader, Writer
|
from pycsdr.modules import Reader, Writer
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
from threading import Thread
|
||||||
|
from io import BytesIO
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
|
||||||
class Module(BaseModule, metaclass=ABCMeta):
|
class Module(BaseModule, metaclass=ABCMeta):
|
||||||
@ -23,3 +26,57 @@ class Module(BaseModule, metaclass=ABCMeta):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def getOutputFormat(self) -> Format:
|
def getOutputFormat(self) -> Format:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ThreadModule(Module, Thread, metaclass=ABCMeta):
|
||||||
|
def __init__(self):
|
||||||
|
self.doRun = True
|
||||||
|
super().__init__()
|
||||||
|
Thread.__init__(self)
|
||||||
|
|
||||||
|
def _checkStart(self) -> None:
|
||||||
|
if self.reader is not None and self.writer is not None:
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def setReader(self, reader: Reader) -> None:
|
||||||
|
super().setReader(reader)
|
||||||
|
self._checkStart()
|
||||||
|
|
||||||
|
def setWriter(self, writer: Writer) -> None:
|
||||||
|
super().setWriter(writer)
|
||||||
|
self._checkStart()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def run(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.doRun = False
|
||||||
|
self.reader.stop()
|
||||||
|
|
||||||
|
|
||||||
|
class PickleModule(ThreadModule):
|
||||||
|
def getInputFormat(self) -> Format:
|
||||||
|
return Format.CHAR
|
||||||
|
|
||||||
|
def getOutputFormat(self) -> Format:
|
||||||
|
return Format.CHAR
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self.doRun:
|
||||||
|
data = self.reader.read()
|
||||||
|
if data is None:
|
||||||
|
self.doRun = False
|
||||||
|
break
|
||||||
|
io = BytesIO(data.tobytes())
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
output = self.process(pickle.load(io))
|
||||||
|
if output is not None:
|
||||||
|
self.writer.write(pickle.dumps(output))
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def process(self, input):
|
||||||
|
pass
|
||||||
|
@ -221,7 +221,7 @@ PacketMessagePanel.prototype.pushMessage = function(msg) {
|
|||||||
$.fn.packetMessagePanel = function() {
|
$.fn.packetMessagePanel = function() {
|
||||||
if (!this.data('panel')) {
|
if (!this.data('panel')) {
|
||||||
this.data('panel', new PacketMessagePanel(this));
|
this.data('panel', new PacketMessagePanel(this));
|
||||||
};
|
}
|
||||||
return this.data('panel');
|
return this.data('panel');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -232,6 +232,10 @@ PocsagMessagePanel = function(el) {
|
|||||||
|
|
||||||
PocsagMessagePanel.prototype = new MessagePanel();
|
PocsagMessagePanel.prototype = new MessagePanel();
|
||||||
|
|
||||||
|
PocsagMessagePanel.prototype.supportsMessage = function(message) {
|
||||||
|
return message['mode'] === 'Pocsag';
|
||||||
|
};
|
||||||
|
|
||||||
PocsagMessagePanel.prototype.render = function() {
|
PocsagMessagePanel.prototype.render = function() {
|
||||||
$(this.el).append($(
|
$(this.el).append($(
|
||||||
'<table>' +
|
'<table>' +
|
||||||
|
@ -848,7 +848,8 @@ function on_ws_recv(evt) {
|
|||||||
var value = json['value'];
|
var value = json['value'];
|
||||||
var panels = [
|
var panels = [
|
||||||
$("#openwebrx-panel-wsjt-message").wsjtMessagePanel(),
|
$("#openwebrx-panel-wsjt-message").wsjtMessagePanel(),
|
||||||
$('#openwebrx-panel-packet-message').packetMessagePanel()
|
$('#openwebrx-panel-packet-message').packetMessagePanel(),
|
||||||
|
$('#openwebrx-panel-pocsag-message').pocsagMessagePanel()
|
||||||
];
|
];
|
||||||
if (!panels.some(function(panel) {
|
if (!panels.some(function(panel) {
|
||||||
if (!panel.supportsMessage(value)) return false;
|
if (!panel.supportsMessage(value)) return false;
|
||||||
@ -861,9 +862,6 @@ function on_ws_recv(evt) {
|
|||||||
case 'log_message':
|
case 'log_message':
|
||||||
divlog(json['value'], true);
|
divlog(json['value'], true);
|
||||||
break;
|
break;
|
||||||
case 'pocsag_data':
|
|
||||||
$('#openwebrx-panel-pocsag-message').pocsagMessagePanel().pushMessage(json['value']);
|
|
||||||
break;
|
|
||||||
case 'backoff':
|
case 'backoff':
|
||||||
divlog("Server is currently busy: " + json['reason'], true);
|
divlog("Server is currently busy: " + json['reason'], true);
|
||||||
var $overlay = $('#openwebrx-error-overlay');
|
var $overlay = $('#openwebrx-error-overlay');
|
||||||
|
@ -2,14 +2,9 @@ from owrx.map import Map, LatLngLocation
|
|||||||
from owrx.metrics import Metrics, CounterMetric
|
from owrx.metrics import Metrics, CounterMetric
|
||||||
from owrx.bands import Bandplan
|
from owrx.bands import Bandplan
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from csdr.module import Module
|
from csdr.module import PickleModule
|
||||||
from pycsdr.modules import Reader
|
|
||||||
from pycsdr.types import Format
|
|
||||||
from threading import Thread
|
|
||||||
from io import BytesIO
|
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import pickle
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -50,41 +45,8 @@ def getSymbolData(symbol, table):
|
|||||||
return {"symbol": symbol, "table": table, "index": ord(symbol) - 33, "tableindex": ord(table) - 33}
|
return {"symbol": symbol, "table": table, "index": ord(symbol) - 33, "tableindex": ord(table) - 33}
|
||||||
|
|
||||||
|
|
||||||
class Ax25Parser(Module, Thread):
|
class Ax25Parser(PickleModule):
|
||||||
def __init__(self):
|
def process(self, ax25frame):
|
||||||
self.doRun = True
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def getInputFormat(self) -> Format:
|
|
||||||
return Format.CHAR
|
|
||||||
|
|
||||||
def getOutputFormat(self) -> Format:
|
|
||||||
return Format.CHAR
|
|
||||||
|
|
||||||
def setReader(self, reader: Reader) -> None:
|
|
||||||
super().setReader(reader)
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.doRun = False
|
|
||||||
self.reader.stop()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while self.doRun:
|
|
||||||
data = self.reader.read()
|
|
||||||
if data is None:
|
|
||||||
self.doRun = False
|
|
||||||
break
|
|
||||||
io = BytesIO(data.tobytes())
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
frame = self.parse(pickle.load(io))
|
|
||||||
if frame is not None:
|
|
||||||
self.writer.write(pickle.dumps(frame))
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def parse(self, ax25frame):
|
|
||||||
control_pid = ax25frame.find(bytes([0x03, 0xF0]))
|
control_pid = ax25frame.find(bytes([0x03, 0xF0]))
|
||||||
if control_pid % 7 > 0:
|
if control_pid % 7 > 0:
|
||||||
logger.warning("aprs packet framing error: control/pid position not aligned with 7-octet callsign data")
|
logger.warning("aprs packet framing error: control/pid position not aligned with 7-octet callsign data")
|
||||||
@ -189,45 +151,15 @@ class AprsLocation(LatLngLocation):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class AprsParser(Module, Thread):
|
class AprsParser(PickleModule):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.metrics = {}
|
self.metrics = {}
|
||||||
self.doRun = True
|
|
||||||
self.band = None
|
self.band = None
|
||||||
|
|
||||||
def setDialFrequency(self, freq):
|
def setDialFrequency(self, freq):
|
||||||
self.band = Bandplan.getSharedInstance().findBand(freq)
|
self.band = Bandplan.getSharedInstance().findBand(freq)
|
||||||
|
|
||||||
def setReader(self, reader: Reader) -> None:
|
|
||||||
super().setReader(reader)
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def getInputFormat(self) -> Format:
|
|
||||||
return Format.CHAR
|
|
||||||
|
|
||||||
def getOutputFormat(self) -> Format:
|
|
||||||
return Format.CHAR
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while self.doRun:
|
|
||||||
data = self.reader.read()
|
|
||||||
if data is None:
|
|
||||||
self.doRun = False
|
|
||||||
break
|
|
||||||
io = BytesIO(data.tobytes())
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
frame = self.parse(pickle.load(io))
|
|
||||||
if frame is not None:
|
|
||||||
self.writer.write(pickle.dumps(frame))
|
|
||||||
except EOFError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.doRun = False
|
|
||||||
self.reader.stop()
|
|
||||||
|
|
||||||
def getMetric(self, category):
|
def getMetric(self, category):
|
||||||
if category not in self.metrics:
|
if category not in self.metrics:
|
||||||
band = "unknown"
|
band = "unknown"
|
||||||
@ -250,7 +182,7 @@ class AprsParser(Module, Thread):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def parse(self, data):
|
def process(self, data):
|
||||||
try:
|
try:
|
||||||
# TODO how can we tell if this is an APRS frame at all?
|
# TODO how can we tell if this is an APRS frame at all?
|
||||||
aprsData = self.parseAprsData(data)
|
aprsData = self.parseAprsData(data)
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
from pycsdr.modules import Reader
|
from pycsdr.modules import Reader
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from csdr.module import Module
|
from csdr.module import ThreadModule
|
||||||
from threading import Thread
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -16,11 +13,10 @@ TFEND = 0xDC
|
|||||||
TFESC = 0xDD
|
TFESC = 0xDD
|
||||||
|
|
||||||
|
|
||||||
class KissDeframer(Module, Thread):
|
class KissDeframer(ThreadModule):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.escaped = False
|
self.escaped = False
|
||||||
self.buf = bytearray()
|
self.buf = bytearray()
|
||||||
self.doRun = True
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def getInputFormat(self) -> Format:
|
def getInputFormat(self) -> Format:
|
||||||
@ -29,10 +25,6 @@ class KissDeframer(Module, Thread):
|
|||||||
def getOutputFormat(self) -> Format:
|
def getOutputFormat(self) -> Format:
|
||||||
return Format.CHAR
|
return Format.CHAR
|
||||||
|
|
||||||
def setReader(self, reader: Reader) -> None:
|
|
||||||
super().setReader(reader)
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while self.doRun:
|
while self.doRun:
|
||||||
data = self.reader.read()
|
data = self.reader.read()
|
||||||
@ -42,10 +34,6 @@ class KissDeframer(Module, Thread):
|
|||||||
for frame in self.parse(data):
|
for frame in self.parse(data):
|
||||||
self.writer.write(pickle.dumps(frame))
|
self.writer.write(pickle.dumps(frame))
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.doRun = False
|
|
||||||
self.reader.stop()
|
|
||||||
|
|
||||||
def parse(self, input):
|
def parse(self, input):
|
||||||
for b in input:
|
for b in input:
|
||||||
if b == FESC:
|
if b == FESC:
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
from owrx.modes import Modes, AudioChopperMode
|
from owrx.modes import Modes, AudioChopperMode
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
import threading
|
|
||||||
from owrx.audio import ProfileSourceSubscriber
|
from owrx.audio import ProfileSourceSubscriber
|
||||||
from owrx.audio.wav import AudioWriter
|
from owrx.audio.wav import AudioWriter
|
||||||
from owrx.audio.queue import QueueJob
|
from owrx.audio.queue import QueueJob
|
||||||
from csdr.module import Module
|
from csdr.module import ThreadModule
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ logger = logging.getLogger(__name__)
|
|||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
class AudioChopper(threading.Thread, Module, ProfileSourceSubscriber):
|
class AudioChopper(ThreadModule, ProfileSourceSubscriber):
|
||||||
# TODO parser typing
|
# TODO parser typing
|
||||||
def __init__(self, mode_str: str, parser):
|
def __init__(self, mode_str: str, parser):
|
||||||
self.parser = parser
|
self.parser = parser
|
||||||
@ -26,7 +25,6 @@ class AudioChopper(threading.Thread, Module, ProfileSourceSubscriber):
|
|||||||
raise ValueError("Mode {} is not an audio chopper mode".format(mode_str))
|
raise ValueError("Mode {} is not an audio chopper mode".format(mode_str))
|
||||||
self.profile_source = mode.get_profile_source()
|
self.profile_source = mode.get_profile_source()
|
||||||
super().__init__()
|
super().__init__()
|
||||||
Module.__init__(self)
|
|
||||||
|
|
||||||
def getInputFormat(self) -> Format:
|
def getInputFormat(self) -> Format:
|
||||||
return Format.SHORT
|
return Format.SHORT
|
||||||
@ -49,14 +47,6 @@ class AudioChopper(threading.Thread, Module, ProfileSourceSubscriber):
|
|||||||
w.start()
|
w.start()
|
||||||
self.writers = writers
|
self.writers = writers
|
||||||
|
|
||||||
def setReader(self, reader):
|
|
||||||
super().setReader(reader)
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.reader.stop()
|
|
||||||
super().stop()
|
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
logger.debug("Audio chopper starting up")
|
logger.debug("Audio chopper starting up")
|
||||||
self.setup_writers()
|
self.setup_writers()
|
||||||
|
@ -434,9 +434,6 @@ class OpenWebRxReceiverClient(OpenWebRxClient, SdrSourceEventClient):
|
|||||||
def write_sdr_error(self, message):
|
def write_sdr_error(self, message):
|
||||||
self.send({"type": "sdr_error", "value": message})
|
self.send({"type": "sdr_error", "value": message})
|
||||||
|
|
||||||
def write_pocsag_data(self, data):
|
|
||||||
self.send({"type": "pocsag_data", "value": data})
|
|
||||||
|
|
||||||
def write_backoff_message(self, reason):
|
def write_backoff_message(self, reason):
|
||||||
self.send({"type": "backoff", "reason": reason})
|
self.send({"type": "backoff", "reason": reason})
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
from owrx.meta import MetaParser
|
from owrx.meta import MetaParser
|
||||||
from owrx.wsjt import WsjtParser
|
from owrx.wsjt import WsjtParser
|
||||||
from owrx.js8 import Js8Parser
|
from owrx.js8 import Js8Parser
|
||||||
from owrx.aprs import AprsParser
|
|
||||||
from owrx.pocsag import PocsagParser
|
|
||||||
from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass
|
from owrx.source import SdrSourceEventClient, SdrSourceState, SdrClientClass
|
||||||
from owrx.property import PropertyStack, PropertyLayer, PropertyValidator
|
from owrx.property import PropertyStack, PropertyLayer, PropertyValidator
|
||||||
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
|
from owrx.property.validators import OrValidator, RegexValidator, BoolValidator
|
||||||
@ -15,7 +13,7 @@ from csdr.chain.clientaudio import ClientAudioChain
|
|||||||
from csdr.chain.analog import NFm, WFm, Am, Ssb
|
from csdr.chain.analog import NFm, WFm, Am, Ssb
|
||||||
from csdr.chain.digiham import DigihamChain, Dmr, Dstar, Nxdn, Ysf
|
from csdr.chain.digiham import DigihamChain, Dmr, Dstar, Nxdn, Ysf
|
||||||
from csdr.chain.fft import FftChain
|
from csdr.chain.fft import FftChain
|
||||||
from csdr.chain.digimodes import AudioChopperDemodulator, PacketDemodulator
|
from csdr.chain.digimodes import AudioChopperDemodulator, PacketDemodulator, PocsagDemodulator
|
||||||
from pycsdr.modules import Buffer, Writer
|
from pycsdr.modules import Buffer, Writer
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from typing import Union
|
from typing import Union
|
||||||
@ -283,7 +281,6 @@ class DspManager(Output, SdrSourceEventClient):
|
|||||||
self.sdrSource = sdrSource
|
self.sdrSource = sdrSource
|
||||||
self.parsers = {
|
self.parsers = {
|
||||||
"meta": MetaParser(self.handler),
|
"meta": MetaParser(self.handler),
|
||||||
"pocsag_demod": PocsagParser(self.handler),
|
|
||||||
"js8_demod": Js8Parser(self.handler),
|
"js8_demod": Js8Parser(self.handler),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,6 +491,8 @@ class DspManager(Output, SdrSourceEventClient):
|
|||||||
return AudioChopperDemodulator(mod, WsjtParser())
|
return AudioChopperDemodulator(mod, WsjtParser())
|
||||||
elif mod == "packet":
|
elif mod == "packet":
|
||||||
return PacketDemodulator()
|
return PacketDemodulator()
|
||||||
|
elif mod == "pocsag":
|
||||||
|
return PocsagDemodulator()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def setSecondaryDemodulator(self, mod):
|
def setSecondaryDemodulator(self, mod):
|
||||||
|
@ -1,17 +1,38 @@
|
|||||||
from owrx.parser import Parser
|
from csdr.module import ThreadModule
|
||||||
|
from pycsdr.types import Format
|
||||||
|
import pickle
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PocsagParser(Parser):
|
class PocsagParser(ThreadModule):
|
||||||
|
def getInputFormat(self) -> Format:
|
||||||
|
return Format.CHAR
|
||||||
|
|
||||||
|
def getOutputFormat(self) -> Format:
|
||||||
|
return Format.CHAR
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self.doRun:
|
||||||
|
data = self.reader.read()
|
||||||
|
if data is None:
|
||||||
|
self.doRun = False
|
||||||
|
else:
|
||||||
|
for frame in self.parse(data.tobytes()):
|
||||||
|
self.writer.write(pickle.dumps(frame))
|
||||||
|
|
||||||
def parse(self, raw):
|
def parse(self, raw):
|
||||||
|
for line in raw.split(b"\n"):
|
||||||
|
if not len(line):
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
fields = raw.decode("ascii", "replace").rstrip("\n").split(";")
|
fields = line.decode("ascii", "replace").split(";")
|
||||||
meta = {v[0]: "".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}
|
meta = {v[0]: "".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}
|
||||||
if "address" in meta:
|
if "address" in meta:
|
||||||
meta["address"] = int(meta["address"])
|
meta["address"] = int(meta["address"])
|
||||||
self.handler.write_pocsag_data(meta)
|
meta["mode"] = "Pocsag"
|
||||||
|
yield meta
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Exception while parsing Pocsag message")
|
logger.exception("Exception while parsing Pocsag message")
|
||||||
|
Loading…
Reference in New Issue
Block a user