diff --git a/modem/arq_session.py b/modem/arq_session.py index 8bae69af..75334d6a 100644 --- a/modem/arq_session.py +++ b/modem/arq_session.py @@ -127,4 +127,13 @@ class ARQSession(): 'total_bytes': total_bytes, 'duration': duration, 'bytes_per_minute': bytes_per_minute - } \ No newline at end of file + } + + def get_appropriate_speed_level(self, snr): + # Start with the lowest speed level as default + # In case of a not fitting SNR, we return the lowest speed level + appropriate_speed_level = min(self.SPEED_LEVEL_DICT.keys()) + for level, details in self.SPEED_LEVEL_DICT.items(): + if snr >= details['min_snr'] and level > appropriate_speed_level: + appropriate_speed_level = level + return appropriate_speed_level \ No newline at end of file diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 1c853568..fbc460b8 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -76,13 +76,10 @@ class ARQSessionIRS(arq_session.ARQSession): self.received_bytes = 0 self.received_crc = None - self.transmitted_acks = 0 + self.speed_level = 0 self.abort = False - def set_decode_mode(self): - self.modem.demodulator.set_decode_mode(self.get_mode_by_speed_level(self.speed_level)) - def all_data_received(self): return self.total_length == self.received_bytes @@ -127,9 +124,6 @@ class ARQSessionIRS(arq_session.ARQSession): self.log(f"New transfer of {self.total_length} bytes") self.event_manager.send_arq_session_new(False, self.id, self.dxcall, self.total_length, self.state.name) - self.calibrate_speed_settings() - self.set_decode_mode() - 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, flag_abort=self.abort) @@ -163,17 +157,20 @@ class ARQSessionIRS(arq_session.ARQSession): def receive_data(self, burst_frame): self.process_incoming_data(burst_frame) - self.calibrate_speed_settings() if not self.all_data_received(): + downshift, upshift = self.calibrate_speed_settings() ack = self.frame_factory.build_arq_burst_ack( - self.id, self.received_bytes, - self.speed_level, self.frames_per_burst, self.snr[0], flag_abort=self.abort) + self.id, + self.received_bytes, + self.speed_level, + self.frames_per_burst, + self.snr[0], + flag_abort=self.abort, + flag_upshift=upshift, + flag_downshift=downshift + ) - self.set_decode_mode() - - # increase ack counter - # self.transmitted_acks += 1 self.set_state(IRS_State.BURST_REPLY_SENT) self.launch_transmit_and_wait(ack, self.TIMEOUT_DATA, mode=FREEDV_MODE.signalling) return None, None @@ -209,16 +206,46 @@ class ARQSessionIRS(arq_session.ARQSession): self.transmission_failed() def calibrate_speed_settings(self): - self.speed_level = 0 # for now stay at lowest speed level - return - # if we have two ACKS, then consider increasing speed level - if self.transmitted_acks >= 2: - self.transmitted_acks = 0 - new_speed_level = min(self.speed_level + 1, len(self.SPEED_LEVEL_DICT) - 1) + latest_snr = self.snr[-1] if self.snr else -10 + appropriate_speed_level = self.get_appropriate_speed_level(latest_snr) + modes_to_decode = {} - # check first if the next mode supports the actual snr - if self.snr[0] >= self.SPEED_LEVEL_DICT[new_speed_level]["min_snr"]: - self.speed_level = new_speed_level + # Log the latest SNR, current, and appropriate speed levels for clarity + self.log( + f"Latest SNR: {latest_snr}, Current Speed Level: {self.speed_level}, Appropriate Speed Level: {appropriate_speed_level}", + isWarning=True) + + # Always decode the current mode + current_mode = self.get_mode_by_speed_level(self.speed_level).value + modes_to_decode[current_mode] = True + + # Initialize shift flags + upshift = False + downshift = False + + # Check if a shift is needed + if appropriate_speed_level != self.speed_level: + # Determine the direction of the shift + upshift = appropriate_speed_level > self.speed_level + downshift = appropriate_speed_level < self.speed_level + + # Update the speed level + self.speed_level = appropriate_speed_level + self.log(f"Updated Speed Level: {self.speed_level}", isWarning=True) + + # Determine additional modes based on the direction of the shift + if upshift and self.speed_level < len(self.SPEED_LEVEL_DICT) - 1: + next_mode = self.get_mode_by_speed_level(self.speed_level).value + modes_to_decode[next_mode] = True + elif downshift and self.speed_level > 0: + prev_mode = self.get_mode_by_speed_level(self.speed_level).value + modes_to_decode[prev_mode] = True + + self.log(f"Modes to Decode: {modes_to_decode}", isWarning=True) + # Apply the new decode mode based on the updated speed level + self.modem.demodulator.set_decode_mode(modes_to_decode) + + return downshift, upshift def abort_transmission(self): self.log(f"Aborting transmission... setting abort flag") diff --git a/modem/arq_session_iss.py b/modem/arq_session_iss.py index 9c69522c..7ff769f3 100644 --- a/modem/arq_session_iss.py +++ b/modem/arq_session_iss.py @@ -105,11 +105,25 @@ class ARQSessionISS(arq_session.ARQSession): self.launch_twr(session_open_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.signalling) self.set_state(ISS_State.OPEN_SENT) - def set_speed_and_frames_per_burst(self, frame): - self.speed_level = frame['speed_level'] - self.log(f"Speed level set to {self.speed_level}") - self.frames_per_burst = frame['frames_per_burst'] - self.log(f"Frames per burst set to {self.frames_per_burst}") + def update_speed_level(self, frame): + self.log("---------------------------------------------------------", isWarning=True) + + # Log the received frame for debugging + self.log(f"Received frame: {frame}", isWarning=True) + + # Safely extract upshift and downshift flags with default to False if not present + upshift = frame['flag'].get('UPSHIFT', False) + downshift = frame['flag'].get('DOWNSHIFT', False) + + # Check for UPSHIFT frame and ensure speed level does not exceed max limit + if upshift and not downshift and self.speed_level < len(self.SPEED_LEVEL_DICT) - 1: + self.speed_level += 1 + self.log(f"Upshifting. New speed level: {self.speed_level}") + + # Check for DOWNSHIFT frame and ensure speed level does not go below 0 + elif downshift and not upshift and self.speed_level > 0: + self.speed_level -= 1 + self.log(f"Downshifting. New speed level: {self.speed_level}") def send_info(self, irs_frame): # check if we received an abort flag @@ -128,7 +142,7 @@ class ARQSessionISS(arq_session.ARQSession): def send_data(self, irs_frame): - self.set_speed_and_frames_per_burst(irs_frame) + self.update_speed_level(irs_frame) if 'offset' in irs_frame: self.confirmed_bytes = irs_frame['offset'] diff --git a/modem/data_frame_factory.py b/modem/data_frame_factory.py index b62ba11b..894e2d1c 100644 --- a/modem/data_frame_factory.py +++ b/modem/data_frame_factory.py @@ -15,6 +15,8 @@ class DataFrameFactory: 'FINAL': 0, # Bit-position for indicating the FINAL state 'ABORT': 1, # Bit-position for indicating the ABORT request 'CHECKSUM': 2, # Bit-position for indicating the CHECKSUM is correct or not + 'DOWNSHIFT': 3, # Bit-position for indicating a requested ARQ DOWNSHIFT + 'UPSHIFT': 4, # Bit-position for indicating a requested ARQ UPSHIFT } def __init__(self, config): @@ -404,7 +406,7 @@ class DataFrameFactory: return frame def build_arq_burst_ack(self, session_id: bytes, offset, speed_level: int, - frames_per_burst: int, snr: int, flag_final=False, flag_checksum=False, flag_abort=False): + frames_per_burst: int, snr: int, flag_final=False, flag_checksum=False, flag_abort=False, flag_downshift=False, flag_upshift=False): flag = 0b00000000 if flag_final: flag = helpers.set_flag(flag, 'FINAL', True, self.ARQ_FLAGS) @@ -415,6 +417,13 @@ class DataFrameFactory: if flag_abort: flag = helpers.set_flag(flag, 'ABORT', True, self.ARQ_FLAGS) + if flag_downshift: + flag = helpers.set_flag(flag, 'DOWNSHIFT', True, self.ARQ_FLAGS) + flag = helpers.set_flag(flag, 'UPSHIFT', False, self.ARQ_FLAGS) + + if flag_upshift: + flag = helpers.set_flag(flag, 'DOWNSHIFT', False, self.ARQ_FLAGS) + flag = helpers.set_flag(flag, 'UPSHIFT', True, self.ARQ_FLAGS) payload = { "session_id": session_id.to_bytes(1, 'big'), diff --git a/modem/demodulator.py b/modem/demodulator.py index b22e99c0..01358174 100644 --- a/modem/demodulator.py +++ b/modem/demodulator.py @@ -382,15 +382,15 @@ class Demodulator(): for mode in self.MODE_DICT: codec2.api.freedv_set_sync(self.MODE_DICT[mode]["instance"], 0) - def set_decode_mode(self, mode): + def set_decode_mode(self, modes_to_decode): + # Reset all modes to not decode + for m in self.MODE_DICT: + self.MODE_DICT[m]["decode"] = False - for m in self.MODE_DICT: self.MODE_DICT[m]["decode"] = False + # Enable specified modes + for mode, decode in modes_to_decode.items(): + if mode in self.MODE_DICT: + self.MODE_DICT[mode]["decode"] = decode - # signalling is always true - self.MODE_DICT[codec2.FREEDV_MODE.signalling.value]["decode"] = True - - # Enable mode based on speed_level - self.MODE_DICT[mode.value]["decode"] = True - self.log.info(f"[MDM] [demod_audio] set data mode: {mode.name}") - - return + enabled_modes = [mode.name for mode, details in self.MODE_DICT.items() if details["decode"]] + self.log.info(f"[MDM] [demod_audio] Enabled decode modes: {', '.join(enabled_modes)}") diff --git a/modem/frame_handler.py b/modem/frame_handler.py index 246d3084..46347d1e 100644 --- a/modem/frame_handler.py +++ b/modem/frame_handler.py @@ -91,7 +91,6 @@ class FrameHandler(): def add_to_heard_stations(self): frame = self.details['frame'] - print(frame) if 'origin' not in frame: return diff --git a/modem/server.py b/modem/server.py index 912e4206..f425b37f 100644 --- a/modem/server.py +++ b/modem/server.py @@ -29,7 +29,7 @@ app = Flask(__name__) CORS(app) CORS(app, resources={r"/*": {"origins": "*"}}) sock = Sock(app) -MODEM_VERSION = "0.13.7-alpha" +MODEM_VERSION = "0.14.0-alpha-exp" # set config file to use def set_config(): diff --git a/tests/test_arq_session.py b/tests/test_arq_session.py index 185e90e3..8309565d 100644 --- a/tests/test_arq_session.py +++ b/tests/test_arq_session.py @@ -144,7 +144,7 @@ class TestARQSession(unittest.TestCase): self.waitAndCloseChannels() del cmd - def DisabledtestARQSessionLargePayload(self): + def testARQSessionLargePayload(self): # set Packet Error Rate (PER) / frame loss probability self.loss_probability = 0