def __init__(self, arguments, thread_manager, location_callback,
                 receive_callback, valid_callback, usb_manager=None):
        """
        Initialize the physical RF sensor.

        The `usb_manager` must be a `USB_Manager` object. It is used for
        connecting to physical RF sensors.

        Classes that inherit this base class may extend this method.
        """

        super(RF_Sensor_Physical, self).__init__(arguments, thread_manager,
                                                 location_callback, receive_callback,
                                                 valid_callback)

        if not isinstance(usb_manager, USB_Manager):
            raise TypeError("The USB manager must be a `USB_Manager` object")

        self._usb_manager = usb_manager

        self._synchronized = False
        self._discovery_callback = None

        self._ntp = NTP(self)
        self._ntp_delay = self._settings.get("ntp_delay")
    def __init__(
        self, arguments, thread_manager, location_callback, receive_callback, valid_callback, usb_manager=None
    ):
        """
        Initialize the physical RF sensor.

        The `usb_manager` must be a `USB_Manager` object. It is used for
        connecting to physical RF sensors.

        Classes that inherit this base class may extend this method.
        """

        super(RF_Sensor_Physical, self).__init__(
            arguments, thread_manager, location_callback, receive_callback, valid_callback
        )

        if not isinstance(usb_manager, USB_Manager):
            raise TypeError("The USB manager must be a `USB_Manager` object")

        self._usb_manager = usb_manager

        self._synchronized = False
        self._discovery_callback = None

        self._ntp = NTP(self)
        self._ntp_delay = self._settings.get("ntp_delay")
class RF_Sensor_Physical(RF_Sensor):
    """
    Base class for all physical RF sensors.

    In addition to the `RF_Sensor` base class, this class takes care of
    working with the USB manager and other physical RF sensor characteristics.
    """

    def __init__(
        self, arguments, thread_manager, location_callback, receive_callback, valid_callback, usb_manager=None
    ):
        """
        Initialize the physical RF sensor.

        The `usb_manager` must be a `USB_Manager` object. It is used for
        connecting to physical RF sensors.

        Classes that inherit this base class may extend this method.
        """

        super(RF_Sensor_Physical, self).__init__(
            arguments, thread_manager, location_callback, receive_callback, valid_callback
        )

        if not isinstance(usb_manager, USB_Manager):
            raise TypeError("The USB manager must be a `USB_Manager` object")

        self._usb_manager = usb_manager

        self._synchronized = False
        self._discovery_callback = None

        self._ntp = NTP(self)
        self._ntp_delay = self._settings.get("ntp_delay")

    def discover(self, callback, required_sensors=None):
        """
        Discover RF sensors in the network. The `callback` callable function is
        called when an RF sensor reports its identity. The `required_sensors`
        set indicates which sensors should be discovered; if it is not
        provided, then all RF sensors are discovered.

        Classes that inherit this base class must extend this method.
        """

        super(RF_Sensor_Physical, self).discover(callback, required_sensors=required_sensors)

        self._discovery_callback = callback

    def _synchronize(self):
        """
        Synchronize the clock with the ground station's clock before
        sending messages. This avoids clock skew caused by the fact that
        the Raspberry Pi devices do not have an onboard real time clock.
        """

        if self._id > 0 and self._settings.get("synchronize"):
            while not self._synchronized:
                self._ntp.start()
                time.sleep(self._ntp_delay)

    def _process(self, packet, **kwargs):
        """
        Process a `Packet` object `packet`. Returns whether or not `packet` is
        an RSSI broadcast packet for further processing.

        Classes that inherit this base class must extend this method.
        """

        # Check if the packet is public and pass it along to the receive callback.
        if not packet.is_private():
            self._receive_callback(packet)
            return False

        specification = packet.get("specification")
        error_message = "Received packet is of unexpected type '{}', expected '{}'"

        # Handle an NTP synchronization packet.
        if specification == "ntp":
            self._ntp.process(packet)
            return False

        if self._id == 0:
            # Handle an RSSI ground station packet.
            if specification == "rssi_ground_station":
                if self._buffer is not None:
                    self._buffer.put(packet)

                return False

            raise ValueError(error_message.format(specification, "rssi_ground_station"))

        # Handle an RSSI broadcast packet.
        if specification != "rssi_broadcast":
            raise ValueError(error_message.format(specification, "rssi_broadcast"))

        return True

    def _process_rssi_broadcast_packet(self, packet, **kwargs):
        """
        Process a `Packet` object `packet` that has been created according to
        the "rssi_broadcast" specification.

        Classes that inherit this base class must extend this method.
        """

        if self._started:
            # Synchronize the scheduler using the timestamp in the packet.
            self._scheduler.synchronize(packet)

        # Create the packet for the ground station.
        return self._create_rssi_ground_station_packet(packet)
class RF_Sensor_Physical(RF_Sensor):
    """
    Base class for all physical RF sensors.

    In addition to the `RF_Sensor` base class, this class takes care of
    working with the USB manager and other physical RF sensor characteristics.
    """

    def __init__(self, arguments, thread_manager, location_callback,
                 receive_callback, valid_callback, usb_manager=None):
        """
        Initialize the physical RF sensor.

        The `usb_manager` must be a `USB_Manager` object. It is used for
        connecting to physical RF sensors.

        Classes that inherit this base class may extend this method.
        """

        super(RF_Sensor_Physical, self).__init__(arguments, thread_manager,
                                                 location_callback, receive_callback,
                                                 valid_callback)

        if not isinstance(usb_manager, USB_Manager):
            raise TypeError("The USB manager must be a `USB_Manager` object")

        self._usb_manager = usb_manager

        self._synchronized = False
        self._discovery_callback = None

        self._ntp = NTP(self)
        self._ntp_delay = self._settings.get("ntp_delay")

    def discover(self, callback, required_sensors=None):
        """
        Discover RF sensors in the network. The `callback` callable function is
        called when an RF sensor reports its identity. The `required_sensors`
        set indicates which sensors should be discovered; if it is not
        provided, then all RF sensors are discovered.

        Classes that inherit this base class must extend this method.
        """

        super(RF_Sensor_Physical, self).discover(callback,
                                                 required_sensors=required_sensors)

        self._discovery_callback = callback

    def _synchronize(self):
        """
        Synchronize the clock with the ground station's clock before
        sending messages. This avoids clock skew caused by the fact that
        the Raspberry Pi devices do not have an onboard real time clock.
        """

        if self._id > 0 and self._settings.get("synchronize"):
            while not self._synchronized:
                self._ntp.start()
                time.sleep(self._ntp_delay)

    def _process(self, packet, **kwargs):
        """
        Process a `Packet` object `packet`. Returns whether or not `packet` is
        an RSSI broadcast packet for further processing.

        Classes that inherit this base class must extend this method.
        """

        # Check if the packet is public and pass it along to the receive callback.
        if not packet.is_private():
            self._receive_callback(packet)
            return False

        specification = packet.get("specification")
        error_message = "Received packet is of unexpected type '{}', expected '{}'"

        # Handle an NTP synchronization packet.
        if specification == "ntp":
            self._ntp.process(packet)
            return False

        if self._id == 0:
            # Handle an RSSI ground station packet.
            if specification == "rssi_ground_station":
                if self._buffer is not None:
                    self._buffer.put(packet)

                return False

            raise ValueError(error_message.format(specification, "rssi_ground_station"))

        # Handle an RSSI broadcast packet.
        if specification != "rssi_broadcast":
            raise ValueError(error_message.format(specification, "rssi_broadcast"))

        return True

    def _process_rssi_broadcast_packet(self, packet, **kwargs):
        """
        Process a `Packet` object `packet` that has been created according to
        the "rssi_broadcast" specification.

        Classes that inherit this base class must extend this method.
        """

        if self._started:
            # Synchronize the scheduler using the timestamp in the packet.
            self._scheduler.synchronize(packet)

        # Create the packet for the ground station.
        return self._create_rssi_ground_station_packet(packet)