From 7549e34d450882c08b11bb28cdbf4e8a9de92b69 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 30 Dec 2023 21:47:16 +0100 Subject: [PATCH] ARQ WIP - channel busy detection --- modem/arq_session_irs.py | 3 +++ modem/arq_session_iss.py | 10 ++++------ modem/audio.py | 11 +++++------ modem/codec2.py | 2 +- modem/demodulator.py | 13 +++---------- modem/modem.py | 6 ++---- modem/state_manager.py | 37 ++++++++++++++++++++++++------------- 7 files changed, 42 insertions(+), 40 deletions(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index bc7c918d..6de4332b 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -50,6 +50,9 @@ class ARQSessionIRS(arq_session.ARQSession): FRAME_TYPE.ARQ_BURST_FRAME.value: 'receive_data', FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' }, + IRS_State.ABORTED: { + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' + }, } def __init__(self, config: dict, modem, dxcall: str, session_id: int): diff --git a/modem/arq_session_iss.py b/modem/arq_session_iss.py index a9b4ed0f..91658d4a 100644 --- a/modem/arq_session_iss.py +++ b/modem/arq_session_iss.py @@ -24,14 +24,14 @@ class ARQSessionISS(arq_session.ARQSession): # DJ2LS: 3 seconds seems to be too small for radios with a too slow PTT toggle time # DJ2LS: 3.5 seconds is working well WITHOUT a channel busy detection delay - TIMEOUT_CONNECT_ACK = 3.5 - TIMEOUT_TRANSFER = 3.5 - TIMEOUT_STOP_ACK = 3.5 + TIMEOUT_CHANNEL_BUSY = 2 + TIMEOUT_CONNECT_ACK = 3.5 + TIMEOUT_CHANNEL_BUSY + TIMEOUT_TRANSFER = 3.5 + TIMEOUT_CHANNEL_BUSY + TIMEOUT_STOP_ACK = 3.5 + TIMEOUT_CHANNEL_BUSY STATE_TRANSITION = { ISS_State.OPEN_SENT: { FRAME_TYPE.ARQ_SESSION_OPEN_ACK.value: 'send_info', - }, ISS_State.INFO_SENT: { FRAME_TYPE.ARQ_SESSION_OPEN_ACK.value: 'send_info', @@ -167,8 +167,6 @@ class ARQSessionISS(arq_session.ARQSession): self.event_frame_received.set() # start with abort sequence - # TODO: We have to wait some time here for avoiding collisions with actual transmissions... - # This could be done by the channel busy detection, for example, if part of def transmit() in modem.py self.send_stop() def send_stop(self): diff --git a/modem/audio.py b/modem/audio.py index 209e61ca..321c9641 100644 --- a/modem/audio.py +++ b/modem/audio.py @@ -8,6 +8,7 @@ import sounddevice as sd import structlog import numpy as np import queue +import threading atexit.register(sd._terminate) @@ -313,19 +314,17 @@ def calculate_fft(data, fft_queue, states) -> None: if addDelay: # Limit delay counter to a maximum of 200. The higher this value, # the longer we will wait until releasing state - states.set("channel_busy", True) + states.set_channel_busy_condition_traffic(True) CHANNEL_BUSY_DELAY = min(CHANNEL_BUSY_DELAY + 10, 200) else: # Decrement channel busy counter if no signal has been detected. CHANNEL_BUSY_DELAY = max(CHANNEL_BUSY_DELAY - 1, 0) # When our channel busy counter reaches 0, toggle state to False if CHANNEL_BUSY_DELAY == 0: - states.set("channel_busy", False) - # erase queue if greater than 10 - if fft_queue.qsize() >= 10: + states.set_channel_busy_condition_traffic(False) + # erase queue if greater than 3 + if fft_queue.qsize() >= 1: fft_queue = queue.Queue() fft_queue.put(dfftlist[:315]) # 315 --> bandwidth 3200 except Exception as err: print(f"[MDM] calculate_fft: Exception: {err}") - print("[MDM] Setting fft=0") - fft_queue.put([0]) diff --git a/modem/codec2.py b/modem/codec2.py index fbf5c3a1..5e071f76 100644 --- a/modem/codec2.py +++ b/modem/codec2.py @@ -101,7 +101,7 @@ for file in files: #log.info("[C2 ] Libcodec2 loaded", path=file) break except OSError as err: - log.warning("[C2 ] Error: Libcodec2 found but not loaded", path=file, e=err) + log.info("[C2 ] Error: Libcodec2 found but not loaded", path=file, e=err) # Quit module if codec2 cant be loaded if api is None or "api" not in locals(): diff --git a/modem/demodulator.py b/modem/demodulator.py index 4392d0f5..5adc65f2 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -37,7 +37,7 @@ class Demodulator(): self.AUDIO_FRAMES_PER_BUFFER_RX = 4800 self.buffer_overflow_counter = [0, 0, 0, 0, 0, 0, 0, 0] self.is_codec2_traffic_counter = 0 - self.is_codec2_traffic_cooldown = 20 + self.is_codec2_traffic_cooldown = 10 self.audio_received_queue = audio_rx_q self.modem_received_queue = modem_rx_q @@ -224,25 +224,18 @@ class Demodulator(): if rx_status not in [0]: # we need to disable this if in testmode as its causing problems with FIFO it seems - self.states.set("is_codec2_traffic", True) self.is_codec2_traffic_counter = self.is_codec2_traffic_cooldown - if not self.states.channel_busy: - self.log.debug("[MDM] Setting channel_busy since codec2 data detected") - self.states.set("channel_busy", True) - #self.channel_busy_delay += 10 self.log.debug( "[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status, sync_flag=codec2.api.rx_sync_flags_to_text[rx_status] ) - else: - self.states.set("is_codec2_traffic", False) # decrement codec traffic counter for making state smoother if self.is_codec2_traffic_counter > 0: self.is_codec2_traffic_counter -= 1 - self.states.set("is_codec2_traffic", True) + self.states.set_channel_busy_condition_codec2(True) else: - self.states.set("is_codec2_traffic", False) + self.states.set_channel_busy_condition_codec2(False) if rx_status == 10: state_buffer.append(rx_status) diff --git a/modem/modem.py b/modem/modem.py index 45472e47..62ac063a 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -221,7 +221,7 @@ class RF: alc_level=str(self.radio_alc)) def transmit( - self, mode, repeats: int, repeat_delay: int, frames: bytearray + self, mode, repeats: int, repeat_delay: int, frames: bytearray, timeout_channel_busy=5 ) -> bool: """ @@ -256,8 +256,7 @@ class RF: # Wait for some other thread that might be transmitting self.states.waitForTransmission() self.states.setTransmitting(True) - # if we're transmitting FreeDATA signals, reset channel busy state - self.states.set("channel_busy", False) + self.states.channel_busy_event.wait(timeout_channel_busy) start_of_transmission = time.time() # TODO Moved ptt toggle some steps before audio is ready for testing @@ -307,7 +306,6 @@ class RF: # transmit audio self.transmit_audio(txbuffer_out) - self.states.set("channel_busy", False) self.radio.set_ptt(False) self.event_manager.send_ptt_change(False) self.states.setTransmitting(False) diff --git a/modem/state_manager.py b/modem/state_manager.py index 0d199039..a0334b03 100644 --- a/modem/state_manager.py +++ b/modem/state_manager.py @@ -13,9 +13,11 @@ class StateManager: # modem related states # not every state is needed to publish, yet # TODO can we reduce them? - self.channel_busy = False self.channel_busy_slot = [False, False, False, False, False] - self.is_codec2_traffic = False + self.channel_busy_event = threading.Event() + self.channel_busy_condition_traffic = threading.Event() + self.channel_busy_condition_codec2 = threading.Event() + self.is_modem_running = False self.is_modem_busy = False self.is_beacon_running = False @@ -35,16 +37,6 @@ class StateManager: self.arq_iss_sessions = {} self.arq_irs_sessions = {} - - self.arq_session_state = 'disconnected' - self.arq_speed_level = 0 - self.arq_total_bytes = 0 - self.arq_bits_per_second = 0 - self.arq_bytes_per_minute = 0 - self.arq_transmission_percent = 0 - self.arq_compression_factor = 0 - self.arq_speed_list = [] - self.arq_seconds_until_timeout = 0 self.mesh_routing_table = [] @@ -89,7 +81,6 @@ class StateManager: return { "freedata-message": msgtype, "channel_busy": self.channel_busy, - "is_codec2_traffic": self.is_codec2_traffic, "is_modem_running": self.is_modem_running, "is_beacon_running": self.is_beacon_running, "radio_status": self.radio_status, @@ -162,3 +153,23 @@ class StateManager: self.activities_list[activity_id] = activity_data self.sendStateUpdate() + + def calculate_channel_busy_state(self): + if self.channel_busy_condition_traffic.is_set() and self.channel_busy_condition_codec2.is_set(): + self.channel_busy_event.set() + else: + self.channel_busy_event = threading.Event() + + def set_channel_busy_condition_traffic(self, busy): + if not busy: + self.channel_busy_condition_traffic.set() + else: + self.channel_busy_condition_traffic = threading.Event() + self.calculate_channel_busy_state() + + def set_channel_busy_condition_codec2(self, traffic): + if not traffic: + self.channel_busy_condition_codec2.set() + else: + self.channel_busy_condition_codec2 = threading.Event() + self.calculate_channel_busy_state()