From ad6f913aab0299421fac538c2001058dcbbc9d19 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 10:28:30 +0100 Subject: [PATCH 01/14] WIP ARQ - cleanup and fixes --- modem/arq_session_irs.py | 4 ++-- modem/arq_session_iss.py | 4 ++-- modem/codec2.py | 3 +-- modem/command.py | 6 +----- modem/modem.py | 26 +++++++++----------------- modem/server.py | 3 --- modem/service_manager.py | 10 ---------- 7 files changed, 15 insertions(+), 41 deletions(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 4d928126..92d97809 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -90,7 +90,7 @@ class ARQSessionIRS(arq_session.ARQSession): self.dxcall, self.version, self.snr[0]) - self.launch_transmit_and_wait(ack_frame, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.datac13) + self.launch_transmit_and_wait(ack_frame, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling) self.set_state(self.STATE_OPEN_ACK_SENT) def send_info_ack(self, info_frame): @@ -104,7 +104,7 @@ class ARQSessionIRS(arq_session.ARQSession): info_ack = self.frame_factory.build_arq_session_info_ack( self.id, self.total_crc, self.snr[0], self.speed_level, self.frames_per_burst) - self.launch_transmit_and_wait(info_ack, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.datac13) + self.launch_transmit_and_wait(info_ack, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling) self.set_state(self.STATE_INFO_ACK_SENT) def send_burst_nack(self): diff --git a/modem/arq_session_iss.py b/modem/arq_session_iss.py index d8f0aae1..65644d41 100644 --- a/modem/arq_session_iss.py +++ b/modem/arq_session_iss.py @@ -71,7 +71,7 @@ class ARQSessionISS(arq_session.ARQSession): def start(self): session_open_frame = self.frame_factory.build_arq_session_open(self.dxcall, self.id) - self.launch_twr(session_open_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.datac13) + self.launch_twr(session_open_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.signalling) self.set_state(self.STATE_OPEN_SENT) def set_speed_and_frames_per_burst(self, frame): @@ -84,7 +84,7 @@ class ARQSessionISS(arq_session.ARQSession): info_frame = self.frame_factory.build_arq_session_info(self.id, len(self.data), helpers.get_crc_32(self.data), self.snr[0]) - self.launch_twr(info_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.datac13) + self.launch_twr(info_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.signalling) self.set_state(self.STATE_INFO_SENT) def send_data(self, irs_frame): diff --git a/modem/codec2.py b/modem/codec2.py index d3585ad1..56610e68 100644 --- a/modem/codec2.py +++ b/modem/codec2.py @@ -24,8 +24,7 @@ class FREEDV_MODE(Enum): """ Enumeration for codec2 modes and names """ - sig0 = 19 - sig1 = 19 + signalling = 19 datac0 = 14 datac1 = 10 datac3 = 12 diff --git a/modem/command.py b/modem/command.py index 337f107d..89bf692d 100644 --- a/modem/command.py +++ b/modem/command.py @@ -30,11 +30,7 @@ class TxCommand(): pass def get_tx_mode(self): - return ( - FREEDV_MODE.fsk_ldpc_0.value - if self.config['MODEM']['enable_fsk'] - else FREEDV_MODE.sig0.value - ) + return FREEDV_MODE.signalling def make_modem_queue_item(self, mode, repeat, repeat_delay, frame): return { diff --git a/modem/modem.py b/modem/modem.py index ffae9dbe..6f55a13b 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -14,7 +14,6 @@ import ctypes import queue import threading import time -from collections import deque import codec2 import numpy as np import sounddevice as sd @@ -47,8 +46,6 @@ class RF: self.tx_audio_level = config['AUDIO']['tx_audio_level'] self.enable_audio_auto_tune = config['AUDIO']['enable_auto_tune'] - #Dynamically enable FFT data stream when a client connects to FFT web socket - self.enable_fft_stream = False self.tx_delay = config['MODEM']['tx_delay'] self.radiocontrol = config['RADIO']['control'] @@ -272,11 +269,12 @@ class RF: self.demodulator.reset_data_sync() # get freedv instance by mode mode_transition = { - codec2.FREEDV_MODE.datac0.value: self.freedv_datac0_tx, - codec2.FREEDV_MODE.datac1.value: self.freedv_datac1_tx, - codec2.FREEDV_MODE.datac3.value: self.freedv_datac3_tx, - codec2.FREEDV_MODE.datac4.value: self.freedv_datac4_tx, - codec2.FREEDV_MODE.datac13.value: self.freedv_datac13_tx, + codec2.FREEDV_MODE.signalling: self.freedv_datac13_tx, + codec2.FREEDV_MODE.datac0: self.freedv_datac0_tx, + codec2.FREEDV_MODE.datac1: self.freedv_datac1_tx, + codec2.FREEDV_MODE.datac3: self.freedv_datac3_tx, + codec2.FREEDV_MODE.datac4: self.freedv_datac4_tx, + codec2.FREEDV_MODE.datac13: self.freedv_datac13_tx, } if mode in mode_transition: freedv = mode_transition[mode] @@ -711,18 +709,12 @@ class RF: # When our channel busy counter reaches 0, toggle state to False if self.channel_busy_delay == 0: self.states.set("channel_busy", False) - if (self.enable_fft_stream): # erase queue if greater than 10 - if self.fft_queue.qsize() >= 10: - self.fft_queue = queue.Queue() - self.fft_queue.put(dfftlist[:315]) # 315 --> bandwidth 3200 + if self.fft_queue.qsize() >= 10: + self.fft_queue = queue.Queue() + self.fft_queue.put(dfftlist[:315]) # 315 --> bandwidth 3200 except Exception as err: self.log.error(f"[MDM] calculate_fft: Exception: {err}") self.log.debug("[MDM] Setting fft=0") # else 0 self.fft_queue.put([0]) - - def set_FFT_stream(self, enable: bool): - # Set config boolean regarding wheter it should sent FFT data to queue - self.enable_fft_stream = enable - diff --git a/modem/server.py b/modem/server.py index 0caf5a18..c83ac98e 100644 --- a/modem/server.py +++ b/modem/server.py @@ -240,9 +240,6 @@ def sock_events(sock): @sock.route('/fft') def sock_fft(sock): - if len(wsm.fft_client_list) == 0: - app.modem_service.put("fft:true") - print("Streaming data to FFT socket since a client is connected") wsm.handle_connection(sock, wsm.fft_client_list, app.modem_fft) @sock.route('/states') diff --git a/modem/service_manager.py b/modem/service_manager.py index 02485543..642a142f 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -17,7 +17,6 @@ class SM: self.config = self.app.config_manager.read() self.modem_events = app.modem_events self.modem_fft = app.modem_fft - self.enable_fft_stream = False self.modem_service = app.modem_service self.states = app.state_manager @@ -49,14 +48,6 @@ class SM: threading.Event().wait(0.5) if self.start_modem(): self.modem_events.put(json.dumps({"freedata": "modem-event", "event": "restart"})) - elif cmd in ['fft:true']: - # Tell modem it should put FFT data in the queue - self.modem.set_FFT_stream(True) - self.enable_fft_stream=True - elif cmd in ['fft:false']: - # Tell modem it should not put FFT data in the queue - self.modem.set_FFT_stream(False) - self.enable_fft_stream=False else: self.log.warning("[SVC] modem command processing failed", cmd=cmd, state=self.states.is_modem_running) @@ -88,7 +79,6 @@ class SM: self.frame_dispatcher.start() self.states.set("is_modem_running", True) - self.modem.set_FFT_stream(self.enable_fft_stream) self.modem.start_modem() return True From b3519eef56e2a4fc80621cf4e84f8c79a798b6de Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 10:32:07 +0100 Subject: [PATCH 02/14] WIP ARQ - removed sig0/1 --- modem/demodulator.py | 105 +++++++++++++------------------------------ 1 file changed, 32 insertions(+), 73 deletions(-) diff --git a/modem/demodulator.py b/modem/demodulator.py index 20cb7c4b..057f0a32 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -24,8 +24,7 @@ class Demodulator(): self.is_codec2_traffic_cooldown = 20 # Receive only specific modes to reduce CPU load - self.RECEIVE_SIG0 = True - self.RECEIVE_SIG1 = False + self.RECEIVE_SIGNALLING = True self.RECEIVE_DATAC1 = False self.RECEIVE_DATAC3 = False self.RECEIVE_DATAC4 = False @@ -49,7 +48,7 @@ class Demodulator(): def init_state_buffers(self): # state buffer - self.SIG0_DATAC13_STATE = [] + self.SIGNALLING_DATAC13_STATE = [] self.SIG1_DATAC13_STATE = [] self.DAT0_DATAC1_STATE = [] self.DAT0_DATAC3_STATE = [] @@ -63,20 +62,11 @@ class Demodulator(): # DATAC13 # SIGNALLING MODE 0 - Used for Connecting - Payload 14 Bytes - self.sig0_datac13_freedv, \ - self.sig0_datac13_bytes_per_frame, \ - self.sig0_datac13_bytes_out, \ - self.sig0_datac13_buffer, \ - self.sig0_datac13_nin = \ - self.init_codec2_mode(codec2.FREEDV_MODE.datac13.value, None) - - # DATAC13 - # SIGNALLING MODE 1 - Used for ACK/NACK - Payload 5 Bytes - self.sig1_datac13_freedv, \ - self.sig1_datac13_bytes_per_frame, \ - self.sig1_datac13_bytes_out, \ - self.sig1_datac13_buffer, \ - self.sig1_datac13_nin = \ + self.signalling_datac13_freedv, \ + self.signalling_datac13_bytes_per_frame, \ + self.signalling_datac13_bytes_out, \ + self.signalling_datac13_buffer, \ + self.signalling_datac13_nin = \ self.init_codec2_mode(codec2.FREEDV_MODE.datac13.value, None) # DATAC1 @@ -179,18 +169,18 @@ class Demodulator(): nin = codec2.api.freedv_nin(c2instance) # Additional Datac0-specific information - these are not referenced anywhere else. - # self.sig0_datac0_payload_per_frame = self.sig0_datac0_bytes_per_frame - 2 - # self.sig0_datac0_n_nom_modem_samples = codec2.api.freedv_get_n_nom_modem_samples( - # self.sig0_datac0_freedv + # self.signalling_datac0_payload_per_frame = self.signalling_datac0_bytes_per_frame - 2 + # self.signalling_datac0_n_nom_modem_samples = codec2.api.freedv_get_n_nom_modem_samples( + # self.signalling_datac0_freedv # ) - # self.sig0_datac0_n_tx_modem_samples = codec2.api.freedv_get_n_tx_modem_samples( - # self.sig0_datac0_freedv + # self.signalling_datac0_n_tx_modem_samples = codec2.api.freedv_get_n_tx_modem_samples( + # self.signalling_datac0_freedv # ) - # self.sig0_datac0_n_tx_preamble_modem_samples = ( - # codec2.api.freedv_get_n_tx_preamble_modem_samples(self.sig0_datac0_freedv) + # self.signalling_datac0_n_tx_preamble_modem_samples = ( + # codec2.api.freedv_get_n_tx_preamble_modem_samples(self.signalling_datac0_freedv) # ) - # self.sig0_datac0_n_tx_postamble_modem_samples = ( - # codec2.api.freedv_get_n_tx_postamble_modem_samples(self.sig0_datac0_freedv) + # self.signalling_datac0_n_tx_postamble_modem_samples = ( + # codec2.api.freedv_get_n_tx_postamble_modem_samples(self.signalling_datac0_freedv) # ) # return values @@ -213,15 +203,10 @@ class Demodulator(): audio_thread_fsk_ldpc1.start() else: - audio_thread_sig0_datac13 = threading.Thread( - target=self.audio_sig0_datac13, name="AUDIO_THREAD DATAC13 - 0", daemon=True + audio_thread_signalling_datac13 = threading.Thread( + target=self.audio_signalling_datac13, name="AUDIO_THREAD DATAC13 - 0", daemon=True ) - audio_thread_sig0_datac13.start() - - audio_thread_sig1_datac13 = threading.Thread( - target=self.audio_sig1_datac13, name="AUDIO_THREAD DATAC13 - 1", daemon=True - ) - audio_thread_sig1_datac13.start() + audio_thread_signalling_datac13.start() audio_thread_dat0_datac1 = threading.Thread( target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True @@ -238,28 +223,16 @@ class Demodulator(): ) audio_thread_dat0_datac4.start() - def audio_sig0_datac13(self) -> None: + def audio_signalling_datac13(self) -> None: """Receive data encoded with datac13 - 0""" - self.sig0_datac13_nin = self.demodulate_audio( - self.sig0_datac13_buffer, - self.sig0_datac13_nin, - self.sig0_datac13_freedv, - self.sig0_datac13_bytes_out, - self.sig0_datac13_bytes_per_frame, - self.SIG0_DATAC13_STATE, - "sig0-datac13" - ) - - def audio_sig1_datac13(self) -> None: - """Receive data encoded with datac13 - 1""" - self.sig1_datac13_nin = self.demodulate_audio( - self.sig1_datac13_buffer, - self.sig1_datac13_nin, - self.sig1_datac13_freedv, - self.sig1_datac13_bytes_out, - self.sig1_datac13_bytes_per_frame, - self.SIG1_DATAC13_STATE, - "sig1-datac13" + self.signalling_datac13_nin = self.demodulate_audio( + self.signalling_datac13_buffer, + self.signalling_datac13_nin, + self.signalling_datac13_freedv, + self.signalling_datac13_bytes_out, + self.signalling_datac13_bytes_per_frame, + self.SIGNALLING_DATAC13_STATE, + "signalling-datac13" ) def audio_dat0_datac4(self) -> None: @@ -339,8 +312,7 @@ class Demodulator(): # Avoid buffer overflow by filling only if buffer for # selected datachannel mode is not full for audiobuffer, receive, index in [ - (self.sig0_datac13_buffer, self.RECEIVE_SIG0, 0), - (self.sig1_datac13_buffer, self.RECEIVE_SIG1, 1), + (self.signalling_datac13_buffer, self.RECEIVE_SIGNALLING, 0), (self.dat0_datac1_buffer, self.RECEIVE_DATAC1, 2), (self.dat0_datac3_buffer, self.RECEIVE_DATAC3, 3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4, 4), @@ -469,18 +441,7 @@ class Demodulator(): if nbytes == bytes_per_frame: print(bytes(bytes_out)) - # ignore data channel opener frames for avoiding toggle states - # use case: opener already received, but ack got lost and we are receiving - # an opener again - if mode_name in ["sig1-datac13"] and int.from_bytes(bytes(bytes_out[:1]), "big") in [ - FRAME_TYPE.ARQ_SESSION_OPEN.value, - FRAME_TYPE.ARQ_DC_OPEN_W.value, - FRAME_TYPE.ARQ_DC_OPEN_ACK_W.value, - FRAME_TYPE.ARQ_DC_OPEN_N.value, - FRAME_TYPE.ARQ_DC_OPEN_ACK_N.value - ]: - print("dropp") - elif int.from_bytes(bytes(bytes_out[:1]), "big") in [ + if int.from_bytes(bytes(bytes_out[:1]), "big") in [ FRAME_TYPE.MESH_BROADCAST.value, FRAME_TYPE.MESH_SIGNALLING_PING.value, FRAME_TYPE.MESH_SIGNALLING_PING_ACK.value, @@ -522,8 +483,7 @@ class Demodulator(): length_x = len(x) for data_buffer, receive in [ - (self.sig0_datac13_buffer, self.RECEIVE_SIG0), - (self.sig1_datac13_buffer, self.RECEIVE_SIG1), + (self.signalling_datac13_buffer, self.RECEIVE_SIGNALLING), (self.dat0_datac1_buffer, self.RECEIVE_DATAC1), (self.dat0_datac3_buffer, self.RECEIVE_DATAC3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4), @@ -556,8 +516,7 @@ class Demodulator(): length_x = len(x) for data_buffer, receive in [ - (self.sig0_datac13_buffer, self.RECEIVE_SIG0), - (self.sig1_datac13_buffer, self.RECEIVE_SIG1), + (self.signalling_datac13_buffer, self.RECEIVE_SIGNALLING), (self.dat0_datac1_buffer, self.RECEIVE_DATAC1), (self.dat0_datac3_buffer, self.RECEIVE_DATAC3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4), From 7394e8b31b2d5c37484b1a64b8dac2709c14d449 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 10:35:06 +0100 Subject: [PATCH 03/14] WIP ARQ - removed fsk --- modem/demodulator.py | 116 ++++++++----------------------------------- modem/modem.py | 28 +++-------- 2 files changed, 27 insertions(+), 117 deletions(-) diff --git a/modem/demodulator.py b/modem/demodulator.py index 057f0a32..8df57622 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -93,29 +93,6 @@ class Demodulator(): self.dat0_datac4_nin = \ self.init_codec2_mode(codec2.FREEDV_MODE.datac4.value, None) - - # FSK LDPC - 0 - self.fsk_ldpc_freedv_0, \ - self.fsk_ldpc_bytes_per_frame_0, \ - self.fsk_ldpc_bytes_out_0, \ - self.fsk_ldpc_buffer_0, \ - self.fsk_ldpc_nin_0 = \ - self.init_codec2_mode( - codec2.FREEDV_MODE.fsk_ldpc.value, - codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV - ) - - # FSK LDPC - 1 - self.fsk_ldpc_freedv_1, \ - self.fsk_ldpc_bytes_per_frame_1, \ - self.fsk_ldpc_bytes_out_1, \ - self.fsk_ldpc_buffer_1, \ - self.fsk_ldpc_nin_1 = \ - self.init_codec2_mode( - codec2.FREEDV_MODE.fsk_ldpc.value, - codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV - ) - def init_codec2_mode(self, mode, adv): """ Init codec2 and return some important parameters @@ -128,21 +105,11 @@ class Demodulator(): Returns: c2instance, bytes_per_frame, bytes_out, audio_buffer, nin """ - if adv: - # FSK Long-distance Parity Code 1 - data frames - c2instance = ctypes.cast( - codec2.api.freedv_open_advanced( - codec2.FREEDV_MODE.fsk_ldpc.value, - ctypes.byref(adv), - ), - ctypes.c_void_p, - ) - else: - # create codec2 instance - c2instance = ctypes.cast( - codec2.api.freedv_open(mode), ctypes.c_void_p - ) + # create codec2 instance + c2instance = ctypes.cast( + codec2.api.freedv_open(mode), ctypes.c_void_p + ) # set tuning range codec2.api.freedv_set_tuning_range( @@ -191,37 +158,25 @@ class Demodulator(): self.stream = stream # Start decoder threads - if self.enable_fsk: - audio_thread_fsk_ldpc0 = threading.Thread( - target=self.audio_fsk_ldpc_0, name="AUDIO_THREAD FSK LDPC0", daemon=True - ) - audio_thread_fsk_ldpc0.start() + audio_thread_signalling_datac13 = threading.Thread( + target=self.audio_signalling_datac13, name="AUDIO_THREAD DATAC13 - 0", daemon=True + ) + audio_thread_signalling_datac13.start() - audio_thread_fsk_ldpc1 = threading.Thread( - target=self.audio_fsk_ldpc_1, name="AUDIO_THREAD FSK LDPC1", daemon=True - ) - audio_thread_fsk_ldpc1.start() + audio_thread_dat0_datac1 = threading.Thread( + target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True + ) + audio_thread_dat0_datac1.start() - else: - audio_thread_signalling_datac13 = threading.Thread( - target=self.audio_signalling_datac13, name="AUDIO_THREAD DATAC13 - 0", daemon=True - ) - audio_thread_signalling_datac13.start() + audio_thread_dat0_datac3 = threading.Thread( + target=self.audio_dat0_datac3, name="AUDIO_THREAD DATAC3", daemon=True + ) + audio_thread_dat0_datac3.start() - audio_thread_dat0_datac1 = threading.Thread( - target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True - ) - audio_thread_dat0_datac1.start() - - audio_thread_dat0_datac3 = threading.Thread( - target=self.audio_dat0_datac3, name="AUDIO_THREAD DATAC3", daemon=True - ) - audio_thread_dat0_datac3.start() - - audio_thread_dat0_datac4 = threading.Thread( - target=self.audio_dat0_datac4, name="AUDIO_THREAD DATAC4", daemon=True - ) - audio_thread_dat0_datac4.start() + audio_thread_dat0_datac4 = threading.Thread( + target=self.audio_dat0_datac4, name="AUDIO_THREAD DATAC4", daemon=True + ) + audio_thread_dat0_datac4.start() def audio_signalling_datac13(self) -> None: """Receive data encoded with datac13 - 0""" @@ -271,29 +226,6 @@ class Demodulator(): "dat0-datac3" ) - def audio_fsk_ldpc_0(self) -> None: - """Receive data encoded with FSK + LDPC0""" - self.fsk_ldpc_nin_0 = self.demodulate_audio( - self.fsk_ldpc_buffer_0, - self.fsk_ldpc_nin_0, - self.fsk_ldpc_freedv_0, - self.fsk_ldpc_bytes_out_0, - self.fsk_ldpc_bytes_per_frame_0, - self.FSK_LDPC0_STATE, - "fsk_ldpc0", - ) - - def audio_fsk_ldpc_1(self) -> None: - """Receive data encoded with FSK + LDPC1""" - self.fsk_ldpc_nin_1 = self.demodulate_audio( - self.fsk_ldpc_buffer_1, - self.fsk_ldpc_nin_1, - self.fsk_ldpc_freedv_1, - self.fsk_ldpc_bytes_out_1, - self.fsk_ldpc_bytes_per_frame_1, - self.FSK_LDPC1_STATE, - "fsk_ldpc1", - ) def sd_input_audio_callback(self, indata: np.ndarray, frames: int, time, status) -> None: x = np.frombuffer(indata, dtype=np.int16) @@ -316,8 +248,6 @@ class Demodulator(): (self.dat0_datac1_buffer, self.RECEIVE_DATAC1, 2), (self.dat0_datac3_buffer, self.RECEIVE_DATAC3, 3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4, 4), - (self.fsk_ldpc_buffer_0, self.enable_fsk, 5), - (self.fsk_ldpc_buffer_1, self.enable_fsk, 6), ]: if (audiobuffer.nbuffer + length_x) > audiobuffer.size: self.buffer_overflow_counter[index] += 1 @@ -487,8 +417,6 @@ class Demodulator(): (self.dat0_datac1_buffer, self.RECEIVE_DATAC1), (self.dat0_datac3_buffer, self.RECEIVE_DATAC3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4), - (self.fsk_ldpc_buffer_0, self.enable_fsk), - (self.fsk_ldpc_buffer_1, self.enable_fsk), ]: if ( not (data_buffer.nbuffer + length_x) > data_buffer.size @@ -520,8 +448,6 @@ class Demodulator(): (self.dat0_datac1_buffer, self.RECEIVE_DATAC1), (self.dat0_datac3_buffer, self.RECEIVE_DATAC3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4), - (self.fsk_ldpc_buffer_0, self.enable_fsk), - (self.fsk_ldpc_buffer_1, self.enable_fsk), ]: if ( not (data_buffer.nbuffer + length_x) > data_buffer.size @@ -545,7 +471,6 @@ class Demodulator(): codec2.api.freedv_set_frames_per_burst(self.dat0_datac1_freedv, frames_per_burst) codec2.api.freedv_set_frames_per_burst(self.dat0_datac3_freedv, frames_per_burst) codec2.api.freedv_set_frames_per_burst(self.dat0_datac4_freedv, frames_per_burst) - codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, frames_per_burst) def calculate_snr(self, freedv: ctypes.c_void_p) -> float: """ @@ -627,4 +552,3 @@ class Demodulator(): codec2.api.freedv_set_sync(self.dat0_datac1_freedv, 0) codec2.api.freedv_set_sync(self.dat0_datac3_freedv, 0) codec2.api.freedv_set_sync(self.dat0_datac4_freedv, 0) - codec2.api.freedv_set_sync(self.fsk_ldpc_freedv_0, 0) diff --git a/modem/modem.py b/modem/modem.py index 6f55a13b..5e04bd65 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -339,16 +339,10 @@ class RF: # Create modulation for all frames in the list for frame in frames: + # Write preamble to txbuffer - # codec2 fsk preamble may be broken - - # at least it sounds like that, so we are disabling it for testing - if self.MODE not in [ - codec2.FREEDV_MODE.fsk_ldpc_0.value, - codec2.FREEDV_MODE.fsk_ldpc_1.value, - ]: - # Write preamble to txbuffer - codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) - txbuffer += bytes(mod_out_preamble) + codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) + txbuffer += bytes(mod_out_preamble) # Create buffer for data # Use this if CRC16 checksum is required (DATAc1-3) @@ -373,16 +367,10 @@ class RF: codec2.api.freedv_rawdatatx(freedv, mod_out, data) txbuffer += bytes(mod_out) - # codec2 fsk postamble may be broken - - # at least it sounds like that, so we are disabling it for testing - if self.MODE not in [ - codec2.FREEDV_MODE.fsk_ldpc_0.value, - codec2.FREEDV_MODE.fsk_ldpc_1.value, - ]: - # Write postamble to txbuffer - codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble) - # Append postamble to txbuffer - txbuffer += bytes(mod_out_postamble) + # Write postamble to txbuffer + codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble) + # Append postamble to txbuffer + txbuffer += bytes(mod_out_postamble) # Add delay to end of frames samples_delay = int(self.MODEM_SAMPLE_RATE * (repeat_delay / 1000)) # type: ignore @@ -514,8 +502,6 @@ class RF: self.freedv_datac3_tx = codec2.open_instance(codec2.FREEDV_MODE.datac3.value) self.freedv_datac4_tx = codec2.open_instance(codec2.FREEDV_MODE.datac4.value) self.freedv_datac13_tx = codec2.open_instance(codec2.FREEDV_MODE.datac13.value) - self.freedv_ldpc0_tx = codec2.open_instance(codec2.FREEDV_MODE.fsk_ldpc_0.value) - self.freedv_ldpc1_tx = codec2.open_instance(codec2.FREEDV_MODE.fsk_ldpc_1.value) def init_data_threads(self): worker_received = threading.Thread( From 681bc5703f7c894691d9030a8ab19111a0b74a63 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 11:05:53 +0100 Subject: [PATCH 04/14] WIP ARQ - moved calculate_fft to audio --- modem/audio.py | 124 ++++++++++++++++++++++++++++++++++++++++++- modem/demodulator.py | 60 +++++---------------- modem/modem.py | 120 ++--------------------------------------- 3 files changed, 138 insertions(+), 166 deletions(-) diff --git a/modem/audio.py b/modem/audio.py index bcf03b93..209e61ca 100644 --- a/modem/audio.py +++ b/modem/audio.py @@ -7,6 +7,7 @@ import crcengine import sounddevice as sd import structlog import numpy as np +import queue atexit.register(sd._terminate) @@ -206,4 +207,125 @@ def set_audio_volume(datalist: np.ndarray, dB: float) -> np.ndarray: scaled_data = datalist * scale_factor # Clip values to int16 range and convert data type - return np.clip(scaled_data, -32768, 32767).astype(np.int16) \ No newline at end of file + return np.clip(scaled_data, -32768, 32767).astype(np.int16) + + +RMS_COUNTER = 0 +CHANNEL_BUSY_DELAY = 0 + +def calculate_fft(data, fft_queue, states) -> None: + """ + Calculate an average signal strength of the channel to assess + whether the channel is "busy." + """ + # Initialize dbfs counter + # rms_counter = 0 + + # https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c + # Fast Fourier Transform, 10*log10(abs) is to scale it to dB + # and make sure it's not imaginary + + global RMS_COUNTER, CHANNEL_BUSY_DELAY + + try: + fftarray = np.fft.rfft(data) + + # Set value 0 to 1 to avoid division by zero + fftarray[fftarray == 0] = 1 + dfft = 10.0 * np.log10(abs(fftarray)) + + # get average of dfft + avg = np.mean(dfft) + + # Detect signals which are higher than the + # average + 10 (+10 smoothes the output). + # Data higher than the average must be a signal. + # Therefore we are setting it to 100 so it will be highlighted + # Have to do this when we are not transmitting so our + # own sending data will not affect this too much + if not states.isTransmitting(): + dfft[dfft > avg + 15] = 100 + + # Calculate audio dbfs + # https://stackoverflow.com/a/9763652 + # calculate dbfs every 50 cycles for reducing CPU load + RMS_COUNTER += 1 + if RMS_COUNTER > 5: + d = np.frombuffer(data, np.int16).astype(np.float32) + # calculate RMS and then dBFS + # https://dsp.stackexchange.com/questions/8785/how-to-compute-dbfs + # try except for avoiding runtime errors by division/0 + try: + rms = int(np.sqrt(np.max(d ** 2))) + if rms == 0: + raise ZeroDivisionError + audio_dbfs = 20 * np.log10(rms / 32768) + states.set("audio_dbfs", audio_dbfs) + except Exception as e: + states.set("audio_dbfs", -100) + + RMS_COUNTER = 0 + + # Convert data to int to decrease size + dfft = dfft.astype(int) + + # Create list of dfft + dfftlist = dfft.tolist() + + # Reduce area where the busy detection is enabled + # We want to have this in correlation with mode bandwidth + # TODO This is not correctly and needs to be checked for correct maths + # dfftlist[0:1] = 10,15Hz + # Bandwidth[Hz] / 10,15 + # narrowband = 563Hz = 56 + # wideband = 1700Hz = 167 + # 1500Hz = 148 + # 2700Hz = 266 + # 3200Hz = 315 + + # slot + slot = 0 + slot1 = [0, 65] + slot2 = [65,120] + slot3 = [120, 176] + slot4 = [176, 231] + slot5 = [231, len(dfftlist)] + slotbusy = [False,False,False,False,False] + + # Set to true if we should increment delay count; else false to decrement + addDelay=False + for range in [slot1, slot2, slot3, slot4, slot5]: + + range_start = range[0] + range_end = range[1] + # define the area, we are detecting busy state + slotdfft = dfft[range_start:range_end] + # Check for signals higher than average by checking for "100" + # If we have a signal, increment our channel_busy delay counter + # so we have a smoother state toggle + if np.sum(slotdfft[slotdfft > avg + 15]) >= 200 and not states.isTransmitting(): + addDelay=True + slotbusy[slot]=True + #states.channel_busy_slot[slot] = True + # increment slot + slot += 1 + states.set_channel_slot_busy(slotbusy) + 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) + 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: + 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/demodulator.py b/modem/demodulator.py index 8df57622..50b9b0e5 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -12,7 +12,7 @@ TESTMODE = False class Demodulator(): - def __init__(self, config, audio_rx_q, modem_rx_q, data_q_rx, states, event_manager): + def __init__(self, config, audio_rx_q, modem_rx_q, data_q_rx, states, event_manager, fft_queue): self.tuning_range_fmin = config['MODEM']['tuning_range_fmin'] self.tuning_range_fmax = config['MODEM']['tuning_range_fmax'] self.enable_fsk = config['MODEM']['enable_fsk'] @@ -40,6 +40,8 @@ class Demodulator(): self.states = states self.event_manager = event_manager + self.fft_queue = fft_queue + # init codec2 resampler self.resampler = codec2.resampler() @@ -226,21 +228,14 @@ class Demodulator(): "dat0-datac3" ) - def sd_input_audio_callback(self, indata: np.ndarray, frames: int, time, status) -> None: - x = np.frombuffer(indata, dtype=np.int16) - x = self.resampler.resample48_to_8(x) - x = audio.set_audio_volume(x, self.rx_audio_level) + audio_48k = np.frombuffer(indata, dtype=np.int16) + audio_8k = self.resampler.resample48_to_8(audio_48k) + audio.calculate_fft(audio_8k, self.fft_queue, self.states) - # audio recording for debugging purposes - # TODO Find a nice place for this - #if AudioParam.audio_record: - # AudioParam.audio_record_file.writeframes(x) + audio_8k_level_adjusted = audio.set_audio_volume(audio_8k, self.rx_audio_level) - # Avoid decoding when transmitting to reduce CPU - # TODO Overriding this for testing purposes - # if not self.states.is_transmitting: - length_x = len(x) + length_audio_8k_level_adjusted = len(audio_8k_level_adjusted) # Avoid buffer overflow by filling only if buffer for # selected datachannel mode is not full for audiobuffer, receive, index in [ @@ -249,12 +244,12 @@ class Demodulator(): (self.dat0_datac3_buffer, self.RECEIVE_DATAC3, 3), (self.dat0_datac4_buffer, self.RECEIVE_DATAC4, 4), ]: - if (audiobuffer.nbuffer + length_x) > audiobuffer.size: + if (audiobuffer.nbuffer + length_audio_8k_level_adjusted) > audiobuffer.size: self.buffer_overflow_counter[index] += 1 self.event_manager.send_buffer_overflow(self.buffer_overflow_counter) elif receive: - audiobuffer.push(x) - return x + audiobuffer.push(audio_8k_level_adjusted) + return audio_8k_level_adjusted def worker_received(self) -> None: """Worker for FIFO queue for processing received frames""" @@ -409,7 +404,7 @@ class Demodulator(): x = np.frombuffer(x, dtype=np.int16) # x = self.resampler.resample48_to_8(x) - self.calculate_fft(x) + audio.calculate_fft(x, self.fft_queue, self.states) length_x = len(x) for data_buffer, receive in [ @@ -424,37 +419,6 @@ class Demodulator(): ): data_buffer.push(x) - def mkfifo_read_callback(self) -> None: - """ - Support testing by reading the audio data from a pipe and - depositing the data into the codec data buffers. - """ - while True: - threading.Event().wait(0.01) - # -----read - data_in48k = bytes() - with open("", "rb") as fifo: - for line in fifo: - data_in48k += line - - while len(data_in48k) >= 48: - x = np.frombuffer(data_in48k[:48], dtype=np.int16) - x = self.resampler.resample48_to_8(x) - data_in48k = data_in48k[48:] - - length_x = len(x) - for data_buffer, receive in [ - (self.signalling_datac13_buffer, self.RECEIVE_SIGNALLING), - (self.dat0_datac1_buffer, self.RECEIVE_DATAC1), - (self.dat0_datac3_buffer, self.RECEIVE_DATAC3), - (self.dat0_datac4_buffer, self.RECEIVE_DATAC4), - ]: - if ( - not (data_buffer.nbuffer + length_x) > data_buffer.size - and receive - ): - data_buffer.push(x) - def set_frames_per_burst(self, frames_per_burst: int) -> None: """ Configure codec2 to send the configured number of frames per burst. diff --git a/modem/modem.py b/modem/modem.py index 5e04bd65..4f52b951 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -60,7 +60,6 @@ class RF: self.tci_ip = config['TCI']['tci_ip'] self.tci_port = config['TCI']['tci_port'] - self.channel_busy_delay = 0 self.AUDIO_SAMPLE_RATE = 48000 self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000 @@ -96,7 +95,9 @@ class RF: self.modem_received_queue, self.data_queue_received, self.states, - self.event_manager) + self.event_manager, + self.fft_queue + ) self.beacon = beacon.Beacon(self.config, self.states, event_queue, self.log, self.modem_transmit_queue) @@ -513,7 +514,6 @@ class RF: def transmit_audio(self, audio_48k) -> None: self.radio.set_ptt(True) self.event_manager.send_ptt_change(True) - self.calculate_fft(audio_48k) if self.radiocontrol in ["tci"]: self.tci_tx_callback(audio_48k) @@ -590,117 +590,3 @@ class RF: ) threading.Event().wait(1) - def calculate_fft(self, data) -> None: - """ - Calculate an average signal strength of the channel to assess - whether the channel is "busy." - """ - # Initialize dbfs counter - # rms_counter = 0 - - # https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c - # Fast Fourier Transform, 10*log10(abs) is to scale it to dB - # and make sure it's not imaginary - try: - fftarray = np.fft.rfft(data) - - # Set value 0 to 1 to avoid division by zero - fftarray[fftarray == 0] = 1 - dfft = 10.0 * np.log10(abs(fftarray)) - - # get average of dfft - avg = np.mean(dfft) - - # Detect signals which are higher than the - # average + 10 (+10 smoothes the output). - # Data higher than the average must be a signal. - # Therefore we are setting it to 100 so it will be highlighted - # Have to do this when we are not transmitting so our - # own sending data will not affect this too much - if not self.states.isTransmitting(): - dfft[dfft > avg + 15] = 100 - - # Calculate audio dbfs - # https://stackoverflow.com/a/9763652 - # calculate dbfs every 50 cycles for reducing CPU load - self.rms_counter += 1 - if self.rms_counter > 5: - d = np.frombuffer(data, np.int16).astype(np.float32) - # calculate RMS and then dBFS - # https://dsp.stackexchange.com/questions/8785/how-to-compute-dbfs - # try except for avoiding runtime errors by division/0 - try: - rms = int(np.sqrt(np.max(d ** 2))) - if rms == 0: - raise ZeroDivisionError - audio_dbfs = 20 * np.log10(rms / 32768) - self.states.set("audio_dbfs", audio_dbfs) - except Exception as e: - self.states.set("audio_dbfs", -100) - - self.rms_counter = 0 - - # Convert data to int to decrease size - dfft = dfft.astype(int) - - # Create list of dfft - dfftlist = dfft.tolist() - - # Reduce area where the busy detection is enabled - # We want to have this in correlation with mode bandwidth - # TODO This is not correctly and needs to be checked for correct maths - # dfftlist[0:1] = 10,15Hz - # Bandwidth[Hz] / 10,15 - # narrowband = 563Hz = 56 - # wideband = 1700Hz = 167 - # 1500Hz = 148 - # 2700Hz = 266 - # 3200Hz = 315 - - # slot - slot = 0 - slot1 = [0, 65] - slot2 = [65,120] - slot3 = [120, 176] - slot4 = [176, 231] - slot5 = [231, len(dfftlist)] - slotbusy = [False,False,False,False,False] - - # Set to true if we should increment delay count; else false to decrement - addDelay=False - for range in [slot1, slot2, slot3, slot4, slot5]: - - range_start = range[0] - range_end = range[1] - # define the area, we are detecting busy state - slotdfft = dfft[range_start:range_end] - # Check for signals higher than average by checking for "100" - # If we have a signal, increment our channel_busy delay counter - # so we have a smoother state toggle - if np.sum(slotdfft[slotdfft > avg + 15]) >= 200 and not self.states.isTransmitting(): - addDelay=True - slotbusy[slot]=True - #self.states.channel_busy_slot[slot] = True - # increment slot - slot += 1 - self.states.set_channel_slot_busy(slotbusy) - if addDelay: - # Limit delay counter to a maximum of 200. The higher this value, - # the longer we will wait until releasing state - self.states.set("channel_busy", True) - self.channel_busy_delay = min(self.channel_busy_delay + 10, 200) - else: - # Decrement channel busy counter if no signal has been detected. - self.channel_busy_delay = max(self.channel_busy_delay - 1, 0) - # When our channel busy counter reaches 0, toggle state to False - if self.channel_busy_delay == 0: - self.states.set("channel_busy", False) - # erase queue if greater than 10 - if self.fft_queue.qsize() >= 10: - self.fft_queue = queue.Queue() - self.fft_queue.put(dfftlist[:315]) # 315 --> bandwidth 3200 - except Exception as err: - self.log.error(f"[MDM] calculate_fft: Exception: {err}") - self.log.debug("[MDM] Setting fft=0") - # else 0 - self.fft_queue.put([0]) From 6d80d1585939c07717ece0c539d4d894324de158 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 11:33:10 +0100 Subject: [PATCH 05/14] WIP ARQ - attempt fixing some tests --- tests/test_data_frame_factory.py | 4 ++-- tests/test_protocols.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_data_frame_factory.py b/tests/test_data_frame_factory.py index 16004275..6cb4a974 100755 --- a/tests/test_data_frame_factory.py +++ b/tests/test_data_frame_factory.py @@ -61,8 +61,8 @@ class TestDataFrameFactory(unittest.TestCase): FREEDV_MODE.datac3, session_id, offset, payload) def testAvailablePayload(self): - avail = self.factory.get_available_data_payload_for_mode(FRAME_TYPE.BURST_FRAME, FREEDV_MODE.datac3) - self.assertEqual(avail, 123) # 128 bytes datac3 frame payload - BURST frame overhead + avail = self.factory.get_available_data_payload_for_mode(FRAME_TYPE.ARQ_BURST_FRAME, FREEDV_MODE.datac3) + self.assertEqual(avail, 120) # 128 bytes datac3 frame payload - BURST frame overhead if __name__ == '__main__': unittest.main() diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 1ef444f0..5e3a7308 100755 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -9,6 +9,7 @@ import queue from state_manager import StateManager from command_ping import PingCommand from command_cq import CQCommand +import modem class TestProtocols(unittest.TestCase): @@ -22,14 +23,14 @@ class TestProtocols(unittest.TestCase): cls.event_queue = queue.Queue() - cls.data_queue_received = queue.Queue() cls.modem_transmit_queue = queue.Queue() + cls.modem = modem.RF(cls.config, cls.event_queue, queue.Queue(), queue.Queue(), cls.state_manager) cls.frame_dispatcher = DISPATCHER(cls.config, cls.event_queue, cls.state_manager, - cls.data_queue_received, - cls.modem_transmit_queue) + cls.modem) + def shortcutTransmission(self): transmission_item = self.modem_transmit_queue.get() From 6f7166201fc8e1f401fe3ae3ba567172f041fda5 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 12:54:16 +0100 Subject: [PATCH 06/14] WIP ARQ - fixed tests --- modem/command.py | 6 ++++++ modem/frame_dispatcher.py | 1 - modem/frame_handler.py | 13 +++++++------ modem/helpers.py | 3 ++- modem/modem.py | 20 +++++++++++++++----- tests/test_protocols.py | 37 +++++++++++++++++++++++-------------- 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/modem/command.py b/modem/command.py index 89bf692d..4f91a373 100644 --- a/modem/command.py +++ b/modem/command.py @@ -48,3 +48,9 @@ class TxCommand(): self.emit_event(event_queue) self.logger.info(self.log_message()) self.transmit(modem) + + def test(self, event_queue: queue.Queue): + self.emit_event(event_queue) + self.logger.info(self.log_message()) + frame = self.build_frame() + return frame diff --git a/modem/frame_dispatcher.py b/modem/frame_dispatcher.py index a93aa819..224baa0b 100644 --- a/modem/frame_dispatcher.py +++ b/modem/frame_dispatcher.py @@ -93,7 +93,6 @@ class DISPATCHER(): self.states, self.event_manager, self.modem) - handler.handle(deconstructed_frame, snr, frequency_offset, freedv, bytes_per_frame) def get_id_from_frame(self, data): diff --git a/modem/frame_handler.py b/modem/frame_handler.py index b8547b4c..7b1ef00b 100644 --- a/modem/frame_handler.py +++ b/modem/frame_handler.py @@ -6,6 +6,8 @@ import structlog import time, uuid from codec2 import FREEDV_MODE +TESTMODE = False + class FrameHandler(): def __init__(self, name: str, config, states: StateManager, event_manager: EventManager, @@ -87,14 +89,13 @@ class FrameHandler(): self.event_manager.broadcast(event_data) def get_tx_mode(self): - return ( - FREEDV_MODE.fsk_ldpc_0.value - if self.config['MODEM']['enable_fsk'] - else FREEDV_MODE.sig0.value - ) + return FREEDV_MODE.signalling.value def transmit(self, frame): - self.modem.transmit(self.get_tx_mode(), 1, 0, frame) + if not TESTMODE: + self.modem.transmit(self.get_tx_mode(), 1, 0, frame) + else: + self.event_manager.broadcast(frame) def follow_protocol(self): pass diff --git a/modem/helpers.py b/modem/helpers.py index 85af20d7..cee713cd 100644 --- a/modem/helpers.py +++ b/modem/helpers.py @@ -322,9 +322,10 @@ def check_callsign(callsign: str, crc_to_check: bytes, ssid_list): #call_with_ssid.extend(str(ssid).encode("utf-8")) callsign_crc = get_crc_24(call_with_ssid) + callsign_crc = callsign_crc.hex() if callsign_crc == crc_to_check: - log.debug("[HLP] check_callsign matched:", call_with_ssid=call_with_ssid) + log.debug("[HLP] check_callsign matched:", call_with_ssid=call_with_ssid, checksum=crc_to_check) return [True, call_with_ssid.decode()] return [False, b''] diff --git a/modem/modem.py b/modem/modem.py index 4f52b951..3ac854ef 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -26,6 +26,8 @@ import event_manager import beacon import demodulator +TESTMODE = False + class RF: """Class to encapsulate interactions between the audio device and codec2""" @@ -108,13 +110,14 @@ class RF: self.tci_module.push_audio(audio_48k) def start_modem(self): - result = False - + # testmode: We need to call the modem without audio parts for running protocol tests + if self.radiocontrol not in ["tci"]: - result = self.init_audio() + result = self.init_audio() if not TESTMODE else True if not result: raise RuntimeError("Unable to init audio devices") - self.demodulator.start(self.sd_input_stream) + if not TESTMODE: + self.demodulator.start(self.sd_input_stream) else: result = self.init_tci() @@ -128,7 +131,8 @@ class RF: # init data thread self.init_data_threads() - atexit.register(self.sd_input_stream.stop) + if not TESTMODE: + atexit.register(self.sd_input_stream.stop) # init beacon self.beacon.start() @@ -225,6 +229,7 @@ class RF: daemon=True, ) tci_tx_callback_thread.start() + return True def audio_auto_tune(self): # enable / disable AUDIO TUNE Feature / ALC correction @@ -267,6 +272,10 @@ class RF: frames: """ + if TESTMODE: + return + + self.demodulator.reset_data_sync() # get freedv instance by mode mode_transition = { @@ -438,6 +447,7 @@ class RF: end_of_transmission = time.time() transmission_time = end_of_transmission - start_of_transmission self.log.debug("[MDM] ON AIR TIME", time=transmission_time) + return True def transmit_morse(self, repeats, repeat_delay, frames): self.states.waitForTransmission() diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 5e3a7308..c148a2cd 100755 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -10,6 +10,8 @@ from state_manager import StateManager from command_ping import PingCommand from command_cq import CQCommand import modem +import frame_handler + class TestProtocols(unittest.TestCase): @@ -26,15 +28,16 @@ class TestProtocols(unittest.TestCase): cls.modem_transmit_queue = queue.Queue() cls.modem = modem.RF(cls.config, cls.event_queue, queue.Queue(), queue.Queue(), cls.state_manager) + modem.TESTMODE = True + frame_handler.TESTMODE = True + + #cls.modem.start_modem() cls.frame_dispatcher = DISPATCHER(cls.config, cls.event_queue, - cls.state_manager, + cls.state_manager, cls.modem) - - def shortcutTransmission(self): - transmission_item = self.modem_transmit_queue.get() - frame_bytes = bytes(transmission_item['frame']) + def shortcutTransmission(self, frame_bytes): self.frame_dispatcher.new_process_data(frame_bytes, None, len(frame_bytes), 0, 0) def assertEventReceivedType(self, event_type): @@ -45,30 +48,36 @@ class TestProtocols(unittest.TestCase): def testPingWithAck(self): # Run ping command - api_params = { "dxcall": "XX1XXX-7" } + api_params = { "dxcall": "XX1XXX-7"} ping_cmd = PingCommand(self.config, self.state_manager, self.event_queue, api_params) - ping_cmd.run(self.event_queue, self.modem_transmit_queue) - + #ping_cmd.run(self.event_queue, self.modem) + frame = ping_cmd.test(self.event_queue) # Shortcut the transmit queue directly to the frame dispatcher - self.shortcutTransmission() + self.shortcutTransmission(frame) self.assertEventReceivedType('PING') + event_frame = self.event_queue.get() # Check ACK - self.shortcutTransmission() - self.assertEventReceivedType('PING_ACK') + self.shortcutTransmission(event_frame) + self.assertEventReceivedType('PING_ACK') + print("PING/PING ACK CHECK SUCCESSFULLY") def testCQWithQRV(self): self.config['MODEM']['respond_to_cq'] = True api_params = {} cmd = CQCommand(self.config, self.state_manager, self.event_queue, api_params) - cmd.run(self.event_queue, self.modem_transmit_queue) + #cmd.run(self.event_queue, self.modem) + frame = cmd.test(self.event_queue) - self.shortcutTransmission() + self.shortcutTransmission(frame) self.assertEventReceivedType('CQ') - self.shortcutTransmission() + event_frame = self.event_queue.get() + # Check QRV + self.shortcutTransmission(event_frame) self.assertEventReceivedType('QRV') + print("CQ/QRV CHECK SUCCESSFULLY") if __name__ == '__main__': unittest.main() From 00e46c02efc17b4848cfb633d19149a06cfe3c23 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:29:47 +0100 Subject: [PATCH 07/14] WIP ARQ - modem stuff ... a bit weird --- modem/arq_session_irs.py | 23 ++-- modem/codec2.py | 39 +++--- modem/demodulator.py | 287 +++++++++++---------------------------- 3 files changed, 115 insertions(+), 234 deletions(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 92d97809..995a84d2 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -56,9 +56,6 @@ class ARQSessionIRS(arq_session.ARQSession): self.transmitted_acks = 0 - def set_modem_decode_modes(self, modes): - pass - def all_data_received(self): return self.received_bytes == len(self.received_data) @@ -100,7 +97,7 @@ class ARQSessionIRS(arq_session.ARQSession): self.dx_snr.append(info_frame['snr']) self.calibrate_speed_settings() - self.set_modem_listening_modes(self.speed_level) + self.set_modem_decode_modes(self.speed_level) info_ack = self.frame_factory.build_arq_session_info_ack( self.id, self.total_crc, self.snr[0], self.speed_level, self.frames_per_burst) @@ -109,14 +106,24 @@ class ARQSessionIRS(arq_session.ARQSession): def send_burst_nack(self): self.calibrate_speed_settings() - self.set_modem_listening_modes(self.speed_level) + self.set_modem_decode_modes(self.speed_level) nack = self.frame_factory.build_arq_burst_ack(self.id, self.received_bytes, self.speed_level, self.frames_per_burst, self.snr[0]) self.transmit_and_wait(nack) - def set_modem_listening_modes(self, speed_level): - # TODO - # We want to set the modems listening modes somehow... + def set_modem_decode_modes(self, speed_level): + # decoding signalling is always on + self.modem.demodulator.RECEIVE_SIGNALLING = True + self.modem.demodulator.RECEIVE_DATAC4 = False + self.modem.demodulator.RECEIVE_DATAC3 = False + self.modem.demodulator.RECEIVE_DATAC1 = False + + # Enable mode based on speed_level + self.modem.demodulator.MODE_DICT[ + self.SPEED_LEVEL_DICT[self.speed_level["mode"].value] + ]["decode"] = True + self.log(f"Modem set to speed level {speed_level}") + return diff --git a/modem/codec2.py b/modem/codec2.py index 56610e68..fbf5c3a1 100644 --- a/modem/codec2.py +++ b/modem/codec2.py @@ -30,9 +30,6 @@ class FREEDV_MODE(Enum): datac3 = 12 datac4 = 18 datac13 = 19 - fsk_ldpc = 9 - fsk_ldpc_0 = 200 - fsk_ldpc_1 = 201 class FREEDV_MODE_USED_SLOTS(Enum): @@ -434,24 +431,24 @@ def open_instance(mode: int) -> ctypes.c_void_p: :return: C-function of the requested codec2 instance :rtype: ctypes.c_void_p """ - if mode in [FREEDV_MODE.fsk_ldpc_0.value]: - return ctypes.cast( - api.freedv_open_advanced( - FREEDV_MODE.fsk_ldpc.value, - ctypes.byref(api.FREEDV_MODE_FSK_LDPC_0_ADV), - ), - ctypes.c_void_p, - ) - - if mode in [FREEDV_MODE.fsk_ldpc_1.value]: - return ctypes.cast( - api.freedv_open_advanced( - FREEDV_MODE.fsk_ldpc.value, - ctypes.byref(api.FREEDV_MODE_FSK_LDPC_1_ADV), - ), - ctypes.c_void_p, - ) - + # if mode in [FREEDV_MODE.fsk_ldpc_0.value]: + # return ctypes.cast( + # api.freedv_open_advanced( + # FREEDV_MODE.fsk_ldpc.value, + # ctypes.byref(api.FREEDV_MODE_FSK_LDPC_0_ADV), + # ), + # ctypes.c_void_p, + # ) + # + # if mode in [FREEDV_MODE.fsk_ldpc_1.value]: + # return ctypes.cast( + # api.freedv_open_advanced( + # FREEDV_MODE.fsk_ldpc.value, + # ctypes.byref(api.FREEDV_MODE_FSK_LDPC_1_ADV), + # ), + # ctypes.c_void_p, + # ) + # return ctypes.cast(api.freedv_open(mode), ctypes.c_void_p) def get_bytes_per_frame(mode: int) -> int: diff --git a/modem/demodulator.py b/modem/demodulator.py index 50b9b0e5..eb8ecd0b 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -12,26 +12,33 @@ TESTMODE = False class Demodulator(): + MODE_DICT = {} + # Iterate over the FREEDV_MODE enum members + for mode in codec2.FREEDV_MODE: + MODE_DICT[mode.value] = { + 'decode': False, + 'bytes_per_frame': None, + 'bytes_out': None, + 'audio_buffer': None, + 'nin': None, + 'instance': None, + 'state_buffer': None, + 'name': mode.name.upper(), + 'decoding_thread': None + } + def __init__(self, config, audio_rx_q, modem_rx_q, data_q_rx, states, event_manager, fft_queue): + self.log = structlog.get_logger("Demodulator") + self.tuning_range_fmin = config['MODEM']['tuning_range_fmin'] self.tuning_range_fmax = config['MODEM']['tuning_range_fmax'] - self.enable_fsk = config['MODEM']['enable_fsk'] self.rx_audio_level = config['AUDIO']['rx_audio_level'] - self.AUDIO_FRAMES_PER_BUFFER_RX = 2400 * 2 # 8192 + 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 - # Receive only specific modes to reduce CPU load - self.RECEIVE_SIGNALLING = True - self.RECEIVE_DATAC1 = False - self.RECEIVE_DATAC3 = False - self.RECEIVE_DATAC4 = False - - self.RXCHANNEL = "" - - self.log = structlog.get_logger("Demodulator") self.audio_received_queue = audio_rx_q self.modem_received_queue = modem_rx_q @@ -45,67 +52,18 @@ class Demodulator(): # init codec2 resampler self.resampler = codec2.resampler() - self.init_state_buffers() self.init_codec2() - def init_state_buffers(self): - # state buffer - self.SIGNALLING_DATAC13_STATE = [] - self.SIG1_DATAC13_STATE = [] - self.DAT0_DATAC1_STATE = [] - self.DAT0_DATAC3_STATE = [] - self.DAT0_DATAC4_STATE = [] - - self.FSK_LDPC0_STATE = [] - self.FSK_LDPC1_STATE = [] def init_codec2(self): # Open codec2 instances + for mode in codec2.FREEDV_MODE: + self.init_codec2_mode(mode.value) - # DATAC13 - # SIGNALLING MODE 0 - Used for Connecting - Payload 14 Bytes - self.signalling_datac13_freedv, \ - self.signalling_datac13_bytes_per_frame, \ - self.signalling_datac13_bytes_out, \ - self.signalling_datac13_buffer, \ - self.signalling_datac13_nin = \ - self.init_codec2_mode(codec2.FREEDV_MODE.datac13.value, None) - # DATAC1 - self.dat0_datac1_freedv, \ - self.dat0_datac1_bytes_per_frame, \ - self.dat0_datac1_bytes_out, \ - self.dat0_datac1_buffer, \ - self.dat0_datac1_nin = \ - self.init_codec2_mode(codec2.FREEDV_MODE.datac1.value, None) - - # DATAC3 - self.dat0_datac3_freedv, \ - self.dat0_datac3_bytes_per_frame, \ - self.dat0_datac3_bytes_out, \ - self.dat0_datac3_buffer, \ - self.dat0_datac3_nin = \ - self.init_codec2_mode(codec2.FREEDV_MODE.datac3.value, None) - - # DATAC4 - self.dat0_datac4_freedv, \ - self.dat0_datac4_bytes_per_frame, \ - self.dat0_datac4_bytes_out, \ - self.dat0_datac4_buffer, \ - self.dat0_datac4_nin = \ - self.init_codec2_mode(codec2.FREEDV_MODE.datac4.value, None) - - def init_codec2_mode(self, mode, adv): + def init_codec2_mode(self, mode): """ Init codec2 and return some important parameters - - Args: - self: - mode: - adv: - - Returns: - c2instance, bytes_per_frame, bytes_out, audio_buffer, nin """ # create codec2 instance @@ -152,81 +110,23 @@ class Demodulator(): # codec2.api.freedv_get_n_tx_postamble_modem_samples(self.signalling_datac0_freedv) # ) - # return values - return c2instance, bytes_per_frame, bytes_out, audio_buffer, nin + self.MODE_DICT[mode]["instance"] = c2instance + self.MODE_DICT[mode]["bytes_per_frame"] = bytes_per_frame + self.MODE_DICT[mode]["bytes_out"] = bytes_out + self.MODE_DICT[mode]["audio_buffer"] = audio_buffer + self.MODE_DICT[mode]["nin"] = nin def start(self, stream): self.stream = stream - # Start decoder threads - audio_thread_signalling_datac13 = threading.Thread( - target=self.audio_signalling_datac13, name="AUDIO_THREAD DATAC13 - 0", daemon=True - ) - audio_thread_signalling_datac13.start() + for mode in self.MODE_DICT: + # Start decoder threads + self.MODE_DICT[mode]['decoding_thread'] = threading.Thread( + target=self.demodulate_audio,args=[mode], name=self.MODE_DICT[mode]['name'], daemon=True + ) + self.MODE_DICT[mode]['decoding_thread'].start() - audio_thread_dat0_datac1 = threading.Thread( - target=self.audio_dat0_datac1, name="AUDIO_THREAD DATAC1", daemon=True - ) - audio_thread_dat0_datac1.start() - - audio_thread_dat0_datac3 = threading.Thread( - target=self.audio_dat0_datac3, name="AUDIO_THREAD DATAC3", daemon=True - ) - audio_thread_dat0_datac3.start() - - audio_thread_dat0_datac4 = threading.Thread( - target=self.audio_dat0_datac4, name="AUDIO_THREAD DATAC4", daemon=True - ) - audio_thread_dat0_datac4.start() - - def audio_signalling_datac13(self) -> None: - """Receive data encoded with datac13 - 0""" - self.signalling_datac13_nin = self.demodulate_audio( - self.signalling_datac13_buffer, - self.signalling_datac13_nin, - self.signalling_datac13_freedv, - self.signalling_datac13_bytes_out, - self.signalling_datac13_bytes_per_frame, - self.SIGNALLING_DATAC13_STATE, - "signalling-datac13" - ) - - def audio_dat0_datac4(self) -> None: - """Receive data encoded with datac4""" - self.dat0_datac4_nin = self.demodulate_audio( - self.dat0_datac4_buffer, - self.dat0_datac4_nin, - self.dat0_datac4_freedv, - self.dat0_datac4_bytes_out, - self.dat0_datac4_bytes_per_frame, - self.DAT0_DATAC4_STATE, - "dat0-datac4" - ) - - def audio_dat0_datac1(self) -> None: - """Receive data encoded with datac1""" - self.dat0_datac1_nin = self.demodulate_audio( - self.dat0_datac1_buffer, - self.dat0_datac1_nin, - self.dat0_datac1_freedv, - self.dat0_datac1_bytes_out, - self.dat0_datac1_bytes_per_frame, - self.DAT0_DATAC1_STATE, - "dat0-datac1" - ) - - def audio_dat0_datac3(self) -> None: - """Receive data encoded with datac3""" - self.dat0_datac3_nin = self.demodulate_audio( - self.dat0_datac3_buffer, - self.dat0_datac3_nin, - self.dat0_datac3_freedv, - self.dat0_datac3_bytes_out, - self.dat0_datac3_bytes_per_frame, - self.DAT0_DATAC3_STATE, - "dat0-datac3" - ) def sd_input_audio_callback(self, indata: np.ndarray, frames: int, time, status) -> None: audio_48k = np.frombuffer(indata, dtype=np.int16) @@ -238,17 +138,19 @@ class Demodulator(): length_audio_8k_level_adjusted = len(audio_8k_level_adjusted) # Avoid buffer overflow by filling only if buffer for # selected datachannel mode is not full - for audiobuffer, receive, index in [ - (self.signalling_datac13_buffer, self.RECEIVE_SIGNALLING, 0), - (self.dat0_datac1_buffer, self.RECEIVE_DATAC1, 2), - (self.dat0_datac3_buffer, self.RECEIVE_DATAC3, 3), - (self.dat0_datac4_buffer, self.RECEIVE_DATAC4, 4), - ]: - if (audiobuffer.nbuffer + length_audio_8k_level_adjusted) > audiobuffer.size: - self.buffer_overflow_counter[index] += 1 - self.event_manager.send_buffer_overflow(self.buffer_overflow_counter) - elif receive: - audiobuffer.push(audio_8k_level_adjusted) + index = 0 + for mode in self.MODE_DICT: + mode_data = self.MODE_DICT[mode] + audiobuffer = mode_data['audio_buffer'] + decode = mode_data['decode'] + index += 1 + if audiobuffer: + if (audiobuffer.nbuffer + length_audio_8k_level_adjusted) > audiobuffer.size: + self.buffer_overflow_counter[index] += 1 + self.event_manager.send_buffer_overflow(self.buffer_overflow_counter) + elif decode: + audiobuffer.push(audio_8k_level_adjusted) + return audio_8k_level_adjusted def worker_received(self) -> None: @@ -286,40 +188,20 @@ class Demodulator(): offset = round(modemStats.foff) * (-1) return offset - def demodulate_audio( - self, - audiobuffer: codec2.audio_buffer, - nin: int, - freedv: ctypes.c_void_p, - bytes_out, - bytes_per_frame, - state_buffer, - mode_name, - ) -> int: + def demodulate_audio(self, mode) -> int: """ De-modulate supplied audio stream with supplied codec2 instance. Decoded audio is placed into `bytes_out`. - - :param audiobuffer: Incoming audio - :type audiobuffer: codec2.audio_buffer - :param nin: Number of frames codec2 is expecting - :type nin: int - :param freedv: codec2 instance - :type freedv: ctypes.c_void_p - :param bytes_out: Demodulated audio - :type bytes_out: _type_ - :param bytes_per_frame: Number of bytes per frame - :type bytes_per_frame: int - :param state_buffer: modem states - :type state_buffer: int - :param mode_name: mode name - :type mode_name: str - :return: NIN from freedv instance - :rtype: int """ - nbytes = 0 - #try: + audiobuffer = self.MODE_DICT[mode]["audio_buffer"] + nin = self.MODE_DICT[mode]["nin"] + freedv = self.MODE_DICT[mode]["instance"] + bytes_out = self.MODE_DICT[mode]["bytes_out"] + bytes_per_frame= self.MODE_DICT[mode]["bytes_per_frame"] + state_buffer = self.MODE_DICT[mode]["state_buffer"] + mode_name = self.MODE_DICT[mode]["name"] + while self.stream.active: threading.Event().wait(0.01) while audiobuffer.nbuffer >= nin: @@ -337,13 +219,12 @@ class Demodulator(): if rx_status not in [0]: # we need to disable this if in testmode as its causing problems with FIFO it seems - if not TESTMODE: - 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.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] @@ -385,11 +266,6 @@ class Demodulator(): self.get_scatter(freedv) state_buffer = [] - #except Exception as e: - # self.log.warning("[MDM] [demod_audio] Stream not active anymore", e=e) - - return nin - def tci_rx_callback(self) -> None: """ Callback for TCI RX @@ -400,24 +276,26 @@ class Demodulator(): while True: - x = self.audio_received_queue.get() - x = np.frombuffer(x, dtype=np.int16) - # x = self.resampler.resample48_to_8(x) - - audio.calculate_fft(x, self.fft_queue, self.states) + audio_48k = self.audio_received_queue.get() + audio_48k = np.frombuffer(audio_48k, dtype=np.int16) + + audio.calculate_fft(audio_48k, self.fft_queue, self.states) + + length_audio_48k = len(audio_48k) + index = 0 + for mode in self.MODE_DICT: + mode_data = self.MODE_DICT[mode] + audiobuffer = mode_data['audio_buffer'] + decode = mode_data['decode'] + index += 1 + if audiobuffer: + if (audiobuffer.nbuffer + length_audio_48k) > audiobuffer.size: + self.buffer_overflow_counter[index] += 1 + self.event_manager.send_buffer_overflow(self.buffer_overflow_counter) + elif decode: + audiobuffer.push(audio_48k) + - length_x = len(x) - for data_buffer, receive in [ - (self.signalling_datac13_buffer, self.RECEIVE_SIGNALLING), - (self.dat0_datac1_buffer, self.RECEIVE_DATAC1), - (self.dat0_datac3_buffer, self.RECEIVE_DATAC3), - (self.dat0_datac4_buffer, self.RECEIVE_DATAC4), - ]: - if ( - not (data_buffer.nbuffer + length_x) > data_buffer.size - and receive - ): - data_buffer.push(x) def set_frames_per_burst(self, frames_per_burst: int) -> None: """ @@ -507,12 +385,11 @@ class Demodulator(): def reset_data_sync(self) -> None: """ - reset sync state for data modes + reset sync state for modes :param frames_per_burst: Number of frames per burst requested :type frames_per_burst: int """ + for mode in self.MODE_DICT: + codec2.api.freedv_set_sync(self.MODE_DICT[mode]["instance"], 0) - codec2.api.freedv_set_sync(self.dat0_datac1_freedv, 0) - codec2.api.freedv_set_sync(self.dat0_datac3_freedv, 0) - codec2.api.freedv_set_sync(self.dat0_datac4_freedv, 0) From a7b02a400f2d68967cb671188cf6cb600e18f33b Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:31:57 +0100 Subject: [PATCH 08/14] WIP ARQ - audio fix --- modem/demodulator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/modem/demodulator.py b/modem/demodulator.py index eb8ecd0b..9fa13a4b 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -151,8 +151,6 @@ class Demodulator(): elif decode: audiobuffer.push(audio_8k_level_adjusted) - return audio_8k_level_adjusted - def worker_received(self) -> None: """Worker for FIFO queue for processing received frames""" while True: From 0d3ec7aa4b813ccfbbd50ffaffabd4413ba4e966 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:36:14 +0100 Subject: [PATCH 09/14] WIP ARQ - enable audio decoding of signalling --- modem/demodulator.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modem/demodulator.py b/modem/demodulator.py index 9fa13a4b..9a1ef602 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -54,6 +54,9 @@ class Demodulator(): self.init_codec2() + # enable decoding of signalling modes + self.MODE_DICT[codec2.FREEDV_MODE.signalling.value]["decode"] = True + def init_codec2(self): # Open codec2 instances From 2de4d32cbb3bb5eaca0d79754858895840feb02e Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:38:58 +0100 Subject: [PATCH 10/14] WIP ARQ - speed level fix --- modem/arq_session_irs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 995a84d2..d3f0610a 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -120,7 +120,7 @@ class ARQSessionIRS(arq_session.ARQSession): # Enable mode based on speed_level self.modem.demodulator.MODE_DICT[ - self.SPEED_LEVEL_DICT[self.speed_level["mode"].value] + self.SPEED_LEVEL_DICT[self.speed_level] ]["decode"] = True self.log(f"Modem set to speed level {speed_level}") From 045729720059479d63db7f9ae2980a0c83e99530 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:40:02 +0100 Subject: [PATCH 11/14] WIP ARQ - speed level fix --- modem/arq_session_irs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index d3f0610a..defa65f5 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -120,7 +120,7 @@ class ARQSessionIRS(arq_session.ARQSession): # Enable mode based on speed_level self.modem.demodulator.MODE_DICT[ - self.SPEED_LEVEL_DICT[self.speed_level] + self.SPEED_LEVEL_DICT[self.speed_level]["mode"] ]["decode"] = True self.log(f"Modem set to speed level {speed_level}") From 31c833135e611ede6a9616b748617f2b06ab49b7 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:44:06 +0100 Subject: [PATCH 12/14] WIP ARQ - speed level fix --- modem/arq_session_irs.py | 2 +- modem/demodulator.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index defa65f5..0988c787 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -120,7 +120,7 @@ class ARQSessionIRS(arq_session.ARQSession): # Enable mode based on speed_level self.modem.demodulator.MODE_DICT[ - self.SPEED_LEVEL_DICT[self.speed_level]["mode"] + self.SPEED_LEVEL_DICT[self.speed_level]["mode"].value ]["decode"] = True self.log(f"Modem set to speed level {speed_level}") diff --git a/modem/demodulator.py b/modem/demodulator.py index 9a1ef602..4b87d951 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -22,7 +22,7 @@ class Demodulator(): 'audio_buffer': None, 'nin': None, 'instance': None, - 'state_buffer': None, + 'state_buffer': [], 'name': mode.name.upper(), 'decoding_thread': None } From 2f2f9d956ad1cf4647f243e910f41060f62884f7 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:51:50 +0100 Subject: [PATCH 13/14] WIP ARQ - speed level fix --- modem/arq_session_irs.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 0988c787..5bfe4943 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -112,16 +112,16 @@ class ARQSessionIRS(arq_session.ARQSession): def set_modem_decode_modes(self, speed_level): - # decoding signalling is always on - self.modem.demodulator.RECEIVE_SIGNALLING = True - self.modem.demodulator.RECEIVE_DATAC4 = False - self.modem.demodulator.RECEIVE_DATAC3 = False - self.modem.demodulator.RECEIVE_DATAC1 = False + for mode in self.modem.demodulator.MODE_DICT: + self.modem.demodulator.MODE_DICT[mode]["decode"] = False + + # signalling is always true + self.modem.demodulator.MODE_DICT[FREEDV_MODE.signalling]["decode"] = True + + mode = self.get_mode_by_speed_level(self.speed_level) # Enable mode based on speed_level - self.modem.demodulator.MODE_DICT[ - self.SPEED_LEVEL_DICT[self.speed_level]["mode"].value - ]["decode"] = True + self.modem.demodulator.MODE_DICT[mode]["decode"] = True self.log(f"Modem set to speed level {speed_level}") return From 32317b373fc81e1d55c3dfc91e50b2726eab1389 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Sat, 16 Dec 2023 14:54:34 +0100 Subject: [PATCH 14/14] WIP ARQ - speed level fix --- modem/arq_session_irs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 5bfe4943..68a0e67f 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -117,11 +117,11 @@ class ARQSessionIRS(arq_session.ARQSession): self.modem.demodulator.MODE_DICT[mode]["decode"] = False # signalling is always true - self.modem.demodulator.MODE_DICT[FREEDV_MODE.signalling]["decode"] = True + self.modem.demodulator.MODE_DICT[FREEDV_MODE.signalling.value]["decode"] = True mode = self.get_mode_by_speed_level(self.speed_level) # Enable mode based on speed_level - self.modem.demodulator.MODE_DICT[mode]["decode"] = True + self.modem.demodulator.MODE_DICT[mode.value]["decode"] = True self.log(f"Modem set to speed level {speed_level}") return