2018-01-07 14:09:40 +01:00
|
|
|
|
#!/usr/bin/python
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
2023-09-19 16:13:23 +02:00
|
|
|
|
r"""!
|
2018-01-07 14:09:40 +01:00
|
|
|
|
____ ____ ______ __ __ __ _____
|
|
|
|
|
|
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
|
|
|
|
|
|
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
|
|
|
|
|
|
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
|
|
|
|
|
|
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
|
|
|
|
|
|
German BOS Information Script
|
|
|
|
|
|
by Bastian Schroll
|
|
|
|
|
|
|
2025-10-21 15:58:17 +02:00
|
|
|
|
@file: pocsagDecoder.py
|
|
|
|
|
|
@date: 15.10.2025
|
2018-01-07 14:09:40 +01:00
|
|
|
|
@author: Bastian Schroll
|
|
|
|
|
|
@description: Decoder class for pocsag
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
2019-03-01 12:16:06 +01:00
|
|
|
|
from boswatch.packet import Packet
|
2018-01-07 14:09:40 +01:00
|
|
|
|
|
|
|
|
|
|
logging.debug("- %s loaded", __name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-09-09 16:17:49 +02:00
|
|
|
|
class PocsagDecoder:
|
2023-09-19 16:13:23 +02:00
|
|
|
|
r"""!POCSAG decoder class
|
2018-01-07 14:09:40 +01:00
|
|
|
|
|
|
|
|
|
|
This class decodes POCSAG data.
|
2018-02-03 22:32:28 +01:00
|
|
|
|
First step is to validate the data and _check if the format is correct.
|
2018-01-07 14:09:40 +01:00
|
|
|
|
In the last step a valid BOSWatch packet is created and returned"""
|
|
|
|
|
|
|
2018-01-08 07:56:10 +01:00
|
|
|
|
@staticmethod
|
2018-01-08 07:58:33 +01:00
|
|
|
|
def decode(data):
|
2023-09-19 16:13:23 +02:00
|
|
|
|
r"""!Decodes POCSAG
|
2018-01-07 14:09:40 +01:00
|
|
|
|
|
|
|
|
|
|
@param data: POCSAG for decoding
|
|
|
|
|
|
@return BOSWatch POCSAG packet or None"""
|
2018-09-09 16:17:49 +02:00
|
|
|
|
bitrate, ric, subric = PocsagDecoder._getBitrateRicSubric(data)
|
2018-01-07 14:09:40 +01:00
|
|
|
|
|
2025-10-21 15:58:17 +02:00
|
|
|
|
# If no valid SubRIC (Function 0–3) detected → abort
|
|
|
|
|
|
if subric is None:
|
|
|
|
|
|
logging.warning("Invalid POCSAG function (not 0–3)")
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
if ric and len(ric) == 7:
|
2018-01-07 14:09:40 +01:00
|
|
|
|
if "Alpha:" in data:
|
2025-06-11 10:33:09 +02:00
|
|
|
|
message = data.split('Alpha:')[1].strip()
|
2025-10-21 15:58:17 +02:00
|
|
|
|
message = re.sub(r'<\s*(?:NUL|EOT)\s*>?', '', message).strip()
|
2018-01-07 14:09:40 +01:00
|
|
|
|
else:
|
|
|
|
|
|
message = ""
|
|
|
|
|
|
subricText = subric.replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d")
|
|
|
|
|
|
|
|
|
|
|
|
logging.debug("found valid POCSAG")
|
|
|
|
|
|
|
2019-03-01 12:16:06 +01:00
|
|
|
|
bwPacket = Packet()
|
2018-01-09 11:33:23 +01:00
|
|
|
|
bwPacket.set("mode", "pocsag")
|
|
|
|
|
|
bwPacket.set("bitrate", bitrate)
|
|
|
|
|
|
bwPacket.set("ric", ric)
|
|
|
|
|
|
bwPacket.set("subric", subric)
|
|
|
|
|
|
bwPacket.set("subricText", subricText)
|
|
|
|
|
|
bwPacket.set("message", message)
|
2018-01-07 14:09:40 +01:00
|
|
|
|
|
|
|
|
|
|
return bwPacket
|
|
|
|
|
|
|
2019-10-26 18:56:14 +02:00
|
|
|
|
logging.warning("no valid POCSAG")
|
2018-01-07 14:09:40 +01:00
|
|
|
|
return None
|
2018-01-08 07:56:10 +01:00
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def _getBitrateRicSubric(data):
|
2025-10-21 15:58:17 +02:00
|
|
|
|
"""Gets the Bitrate, Ric and Subric from data using robust regex parsing."""
|
|
|
|
|
|
bitrate = "0"
|
|
|
|
|
|
ric = None
|
|
|
|
|
|
subric = None
|
2018-01-08 07:56:10 +01:00
|
|
|
|
|
2025-10-21 15:58:17 +02:00
|
|
|
|
# determine bitrate
|
2018-01-08 07:56:10 +01:00
|
|
|
|
if "POCSAG512:" in data:
|
2019-09-20 17:38:33 +02:00
|
|
|
|
bitrate = "512"
|
2018-01-08 07:56:10 +01:00
|
|
|
|
elif "POCSAG1200:" in data:
|
2019-09-20 17:38:33 +02:00
|
|
|
|
bitrate = "1200"
|
2018-01-08 07:56:10 +01:00
|
|
|
|
elif "POCSAG2400:" in data:
|
2019-09-20 17:38:33 +02:00
|
|
|
|
bitrate = "2400"
|
2025-10-21 15:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
# extract RIC (address)
|
|
|
|
|
|
m_ric = re.search(r'Address:\s*(\d{1,7})(?=\s|$)', data)
|
|
|
|
|
|
if m_ric:
|
|
|
|
|
|
ric = m_ric.group(1).zfill(7)
|
|
|
|
|
|
|
|
|
|
|
|
# extract SubRIC (function)
|
|
|
|
|
|
m_sub = re.search(r'Function:\s*([0-3])', data)
|
|
|
|
|
|
if m_sub:
|
|
|
|
|
|
subric = str(int(m_sub.group(1)) + 1)
|
2018-01-08 07:56:10 +01:00
|
|
|
|
|
|
|
|
|
|
return bitrate, ric, subric
|