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)
Exemple #2
0
    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)
Exemple #4
0
    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)
 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)
 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")
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