From 63273b7f027686f70b9bca4394062507d3de590e Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 30 Nov 2023 00:15:16 +0100 Subject: [PATCH] Add PingFrameHandler --- modem/data_frame_factory.py | 20 +++++++++- modem/frame_dispatcher.py | 4 +- modem/frame_handler.py | 74 ++++++++++++++++++++++++++++--------- modem/frame_handler_ping.py | 42 +++++++++++++++++++++ 4 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 modem/frame_handler_ping.py diff --git a/modem/data_frame_factory.py b/modem/data_frame_factory.py index f4463a7e..0910f570 100644 --- a/modem/data_frame_factory.py +++ b/modem/data_frame_factory.py @@ -68,6 +68,15 @@ class DataFrameFactory: "origin": 6 } + # ping ack + self.template_list[FR_TYPE.PING_ACK.value] = { + "frame_length": self.LENGTH_SIG0_FRAME, + "destination_crc": 3, + "origin_crc": 3, + "gridsquare": 4, + "snr": 1, + } + def _load_fec_templates(self): # fec wakeup frame self.template_list[FR_TYPE.FEC_WAKEUP.value] = { @@ -207,7 +216,16 @@ class DataFrameFactory: "origin": helpers.callsign_to_bytes(self.myfullcall), } return self.construct(FR_TYPE.PING, payload) - + + def build_ping_ack(self, destination, snr): + payload = { + "destination_crc": helpers.get_crc_24(destination), + "origin_crc": helpers.get_crc_24(self.myfullcall), + "gridsquare": helpers.encode_grid(self.mygrid), + "snr": helpers.snr_to_bytes(snr) + } + return self.construct(FR_TYPE.PING_ACK, payload) + def build_cq(self): payload = { "origin": helpers.callsign_to_bytes(self.myfullcall), diff --git a/modem/frame_dispatcher.py b/modem/frame_dispatcher.py index 6fcd8001..0e8ad8f0 100644 --- a/modem/frame_dispatcher.py +++ b/modem/frame_dispatcher.py @@ -21,6 +21,7 @@ from protocol_arq import ARQ from protocol_arq_session import SESSION from frame_handler import FrameHandler +from frame_handler_ping import PingFrameHandler class DISPATCHER(): @@ -41,7 +42,7 @@ class DISPATCHER(): FR_TYPE.FR_NACK.value: {"class": FrameHandler, "name": "FRAME NACK"}, FR_TYPE.FR_REPEAT.value: {"class": FrameHandler, "name": "REPEAT REQUEST"}, FR_TYPE.PING_ACK.value: {"class": FrameHandler, "name": "PING ACK"}, - FR_TYPE.PING.value: {"class": FrameHandler, "name": "PING"}, + FR_TYPE.PING.value: {"class": PingFrameHandler, "name": "PING"}, FR_TYPE.QRV.value: {"class": FrameHandler, "name": "QRV"}, FR_TYPE.IS_WRITING.value: {"class": FrameHandler, "name": "IS_WRITING"}, FR_TYPE.FEC.value: {"class": FrameHandler, "name": "FEC"}, @@ -169,6 +170,7 @@ class DISPATCHER(): # instantiate handler handler_class = self.FRAME_HANDLER[frametype]['class'] handler = handler_class(self.FRAME_HANDLER[frametype]['name'], + self.config, self.states, self.event_manager, MODEM_TRANSMIT_QUEUE, diff --git a/modem/frame_handler.py b/modem/frame_handler.py index c8588994..f676af47 100644 --- a/modem/frame_handler.py +++ b/modem/frame_handler.py @@ -3,41 +3,75 @@ from event_manager import EventManager from state_manager import StateManager from queue import Queue import structlog +import time, uuid +from codec2 import FREEDV_MODE class FrameHandler(): - def __init__(self, name: str, states: StateManager, event_manager: EventManager, + def __init__(self, name: str, config, states: StateManager, event_manager: EventManager, tx_frame_queue: Queue, arq_sessions: list) -> None: self.name = name + self.config = config self.states = states self.event_manager = event_manager - self.tx_trame_queue = tx_frame_queue + self.tx_frame_queue = tx_frame_queue self.arq_sessions = arq_sessions self.logger = structlog.get_logger("Frame Handler") - def add_to_heard_stations(self): - pass - - def make_event(self, frame): - return { - "freedata": "generic frame handler", - "frame": frame, + self.details = { + 'frame' : None, + 'snr' : 0, + 'freq_offset': 0, + 'freedv_inst': None, + 'bytes_per_frame': 0 } - def emit_event(self, frame): - event_data = self.make_event(frame) + def add_to_heard_stations(self): + frame = self.details['frame'] + dxgrid = frame['gridsquare'] if 'gridsquare' in frame else "------" + helpers.add_to_heard_stations( + frame['origin'], + dxgrid, + self.name, + self.details['snr'], + self.details['freq_offset'], + self.states.radio_frequency, + self.states.heard_stations, + ) + + def make_event(self): + event = { + "freedata": "modem-message", + "uuid": str(uuid.uuid4()), + "timestamp": int(time.time()), + "mycallsign": self.config['STATION']['mycall'], + "snr": str(self.details['snr']), + } + if 'origin' in self.details['frame']: + event['dxcallsign'] = self.details['frame']['origin'] + return event + + def emit_event(self): + event_data = self.make_event() self.event_manager.broadcast(event_data) def make_modem_queue_item(self, mode, repeat, repeat_delay, frame): return { 'mode': self.get_tx_mode(), - 'repeat': 1, - 'repeat_delay': 0, + 'repeat': repeat, + 'repeat_delay': repeat_delay, 'frame': frame, } + def get_tx_mode(self): + return ( + FREEDV_MODE.fsk_ldpc_0.value + if self.config['MODEM']['enable_fsk'] + else FREEDV_MODE.sig0.value + ) + def transmit(self, frame): tx_queue_item = self.make_modem_queue_item(self.get_tx_mode(), 1, 0, frame) self.tx_frame_queue.put(tx_queue_item) @@ -45,12 +79,18 @@ class FrameHandler(): def follow_protocol(self): pass - def log(self, frame): - self.logger.info(f"[Frame Handler] Handling frame {frame}") + def log(self): + self.logger.info(f"[Frame Handler] Handling frame {self.details['frame']}") pass def handle(self, frame, snr, freq_offset, freedv_inst, bytes_per_frame): - self.log(frame) + self.details['frame'] = frame + self.details['snr'] = snr + self.details['freq_offset'] = freq_offset + self.details['freedv_inst'] = freedv_inst + self.details['bytes_per_frame'] = bytes_per_frame + + self.log() self.add_to_heard_stations() - self.emit_event(frame) + self.emit_event() self.follow_protocol() diff --git a/modem/frame_handler_ping.py b/modem/frame_handler_ping.py new file mode 100644 index 00000000..d9a98290 --- /dev/null +++ b/modem/frame_handler_ping.py @@ -0,0 +1,42 @@ +import frame_handler +import helpers +import data_frame_factory + +class PingFrameHandler(frame_handler.FrameHandler): + + def make_event(self): + event = super().make_event() + event['ping'] = "received" + return event + + def follow_protocol(self): + deconstructed_frame = self.details['frame'] + origin = deconstructed_frame["origin"] + + # check if callsign ssid override + valid, mycallsign = helpers.check_callsign( + self.config['STATION']['mycall'], + deconstructed_frame["destination_crc"], + self.config['STATION']['ssid_list']) + + if not valid: + # PING packet not for me. + self.logger.debug("[Modem] received_ping: ping not for this station.") + return + + self.dxcallsign_crc = deconstructed_frame["origin_crc"] + self.dxcallsign = origin + self.logger.info( + f"[Modem] PING REQ from [{origin}] to [{mycallsign}]", + snr=self.details['snr'], + ) + + self.send_ack() + + def send_ack(self): + factory = data_frame_factory.DataFrameFactory(self.config) + ping_ack_frame = factory.build_ping_ack( + self.details['frame']['origin_crc'], + self.details['snr'] + ) + self.transmit(ping_ack_frame) \ No newline at end of file