def test_create_packet_constant_bitrate(self): packet_size_bytes = 1200.0 bitrate_kbps = 1500.0 packet_source = PacketSource(packet_size_bytes) for i in range(1, 11): packet = packet_source.create_packet(bitrate_kbps) self.assertEqual(i, packet.id) self.assertNear(i*8*packet_size_bytes/bitrate_kbps, packet.send_time_ms, 0.001)
def test_create_packet_constant_bitrate(self): packet_size_bytes = 1200.0 bitrate_kbps = 1500.0 packet_source = PacketSource(packet_size_bytes) for i in range(1, 11): packet = packet_source.create_packet(bitrate_kbps) self.assertEqual(i, packet.id) self.assertNear(i * 8 * packet_size_bytes / bitrate_kbps, packet.send_time_ms, 0.001)
def test_create_packet_variable_bitrate(self): packet_size_bytes = 1200.0 packet_source = PacketSource(packet_size_bytes) time_ms = 0.0 for i in range(1, 11): bitrate_kbps = random.randint(150, 2500) packet = packet_source.create_packet(bitrate_kbps) time_ms += 8*packet_size_bytes/bitrate_kbps self.assertEqual(i, packet.id) self.assertNear(time_ms, packet.send_time_ms, 0.001)
def test_create_packet_variable_bitrate(self): packet_size_bytes = 1200.0 packet_source = PacketSource(packet_size_bytes) time_ms = 0.0 for i in range(1, 11): bitrate_kbps = random.randint(150, 2500) packet = packet_source.create_packet(bitrate_kbps) time_ms += 8 * packet_size_bytes / bitrate_kbps self.assertEqual(i, packet.id) self.assertNear(time_ms, packet.send_time_ms, 0.001)
def test_receiving_rate_regular_packets(self): for i in range(10): bitrate_kbps = random.uniform(150.0, 2500.0) packet_size_bytes = random.uniform(1.0, 8000.0) delay_ms = random.uniform(10.0, 300.0) packet_source = PacketSource(packet_size_bytes) packets = [] for j in range(1000): packet = packet_source.create_packet(bitrate_kbps) packet.arrival_time_ms = packet.send_time_ms + delay_ms packets.append(packet) time_window_ms = random.uniform(0.0, 1.0) * packets[-1].arrival_time_ms self.assertNear(receiving_rate_kbps(packets, time_window_ms), bitrate_kbps, 0.001)
class NadaSender(object): PAYLOAD_SIZE_BYTES = 1200.0 MIN_BITRATE_KBPS = 50.0 MAX_BITRATE_KBPS = 2500.0 QUEUING_DELAY_UPPER_BOUND_MS = 10.0 MAX_CONGESTION_SIGNAL_MS = 40.0 # Used only in modified mode. # NADA modified operation mode is an attempt to improve the original one. def __init__(self, original_mode_): self.bitrate_kbps = 300.0 self.packet_source = PacketSource(NadaSender.PAYLOAD_SIZE_BYTES) self.original_mode = original_mode_ if not self.original_mode: self.min_est_travel_time_ms = float("inf") def create_packet(self): return self.packet_source.create_packet(self.bitrate_kbps) # Use feedback from receiver to update the sender's bitrate. def receive_feedback(self, feedback): if self.__should_ramp_up(feedback): self.__accelerated_ramp_up(feedback) elif not self.original_mode and self.__should_ramp_down(feedback): self.__accelerated_ramp_down(feedback) else: self.__gradual_rate_update(feedback) # Bitrate should be kept between MIN and MAX. self.bitrate_kbps = max(NadaSender.MIN_BITRATE_KBPS, min(NadaSender.MAX_BITRATE_KBPS, self.bitrate_kbps)) def __should_ramp_up(self, feedback): derivative_upper_bound = 10.0 / feedback.interval_ms if self.original_mode: return feedback.loss_ratio == 0 and feedback.est_queuing_delay_ms < NadaSender.QUEUING_DELAY_UPPER_BOUND_MS \ and feedback.derivative < derivative_upper_bound else: # Stricter accelerated ramp up. extra_delay_ms = self.__estimate_extra_delay_ms() return feedback.loss_ratio == 0 and (feedback.exp_smoothed_delay_ms < NadaSender.QUEUING_DELAY_UPPER_BOUND_MS / 3.0 \ or feedback.exp_smoothed_delay_ms - extra_delay_ms < NadaSender.QUEUING_DELAY_UPPER_BOUND_MS / 3.0) \ and feedback.derivative < derivative_upper_bound and feedback.receiving_rate_kbps > NadaSender.MIN_BITRATE_KBPS def __should_ramp_down(self, feedback): return max(feedback.congestion_signal_ms, feedback.exp_smoothed_delay_ms) > NadaSender.MAX_CONGESTION_SIGNAL_MS def __accelerated_ramp_up(self, feedback): MAX_RAMP_UP_QUEUING_DELAY_MS = 50.0 # Referred as T_th. GAMMA_0 = 0.5 gamma = min(GAMMA_0, MAX_RAMP_UP_QUEUING_DELAY_MS/(feedback.baseline_delay_ms + feedback.interval_ms)) if not self.original_mode: gamma = gamma / 2 self.bitrate_kbps = (1.0 + gamma) * feedback.receiving_rate_kbps def __accelerated_ramp_down(self, feedback): GAMMA_0 = 0.9 gamma = 2.0 * NadaSender.MAX_CONGESTION_SIGNAL_MS / (feedback.congestion_signal_ms + feedback.exp_smoothed_delay_ms) gamma = min(gamma**0.5, GAMMA_0) self.bitrate_kbps = gamma * feedback.receiving_rate_kbps def __gradual_rate_update(self, feedback): TAU_O_MS = 500.0 ETA = 2.0 KAPPA = 1.0 REFERENCE_DELAY_MS = 10.0 # Referred as x_ref. PRIORITY_WEIGHT = 1.0 # Referred as w. if self.original_mode: x_hat = feedback.congestion_signal_ms + ETA * TAU_O_MS * feedback.derivative theta = PRIORITY_WEIGHT * (NadaSender.MAX_BITRATE_KBPS - NadaSender.MIN_BITRATE_KBPS) * REFERENCE_DELAY_MS increase_kbps = (theta - x_hat * (self.bitrate_kbps - NadaSender.MIN_BITRATE_KBPS)) \ * KAPPA * feedback.delta_ms / (TAU_O_MS ** 2) self.bitrate_kbps += increase_kbps else: # Smoother rate update. extra_delay_ms = self.__estimate_extra_delay_ms() new_congestion_signal_ms = max(0.0, feedback.congestion_signal_ms - extra_delay_ms) x_hat = new_congestion_signal_ms + ETA * TAU_O_MS * feedback.derivative theta = PRIORITY_WEIGHT * (NadaSender.MAX_BITRATE_KBPS - NadaSender.MIN_BITRATE_KBPS) * REFERENCE_DELAY_MS increase_kbps = (theta - x_hat * (self.bitrate_kbps - NadaSender.MIN_BITRATE_KBPS)) \ * KAPPA * feedback.delta_ms / (TAU_O_MS ** 2) bitrate_reference = 3.0*(self.bitrate_kbps- NadaSender.MIN_BITRATE_KBPS) \ / (NadaSender.MAX_BITRATE_KBPS - NadaSender.MIN_BITRATE_KBPS) smoothing_factor = min(bitrate_reference ** 2.0, 1.0) self.bitrate_kbps += increase_kbps * smoothing_factor def __estimate_extra_delay_ms(self): est_travel_time_ms = 8.0 * NadaSender.PAYLOAD_SIZE_BYTES / self.bitrate_kbps self.min_est_travel_time_ms = min(self.min_est_travel_time_ms, est_travel_time_ms) return est_travel_time_ms - self.min_est_travel_time_ms