def __init__(self, src_mac, udp_port_rx, ip_rx, udp_port_smartphone, comm_direction, interface_drone_comm,
              mode, communication_id, dronebridge_port):
     self.src_mac = src_mac
     self.comm_id = communication_id  # must be the same on drone and groundstation
     self.udp_port_rx = udp_port_rx  # 1604
     self.ip_rx = ip_rx
     self.udp_port_smartphone = udp_port_smartphone  # we bind to that locally
     self.comm_direction = comm_direction  # set to 0x01 if program runs on groundst. and to 0x02 if runs on drone
     self.interface = interface_drone_comm  # the long range interface
     self.mode = mode
     self.tag = ''
     if dronebridge_port == DB_PORT_TELEMETRY:
         self.tag = "DB_TEL: "
     elif dronebridge_port == DB_PORT_COMMUNICATION:
         self.tag = "DB_Comm: "
     if self.mode == 'wifi':
         self.short_mode = 'w'
     else:
         self.short_mode = 'm'
     self.fcf = b'\xb4\x00\x00\x00'  # RTS frames
     self.db_port = dronebridge_port
     self.comm_sock = self._open_comm_sock()
     if self.comm_direction == TO_DRONE:
         self.android_sock = self._open_android_udpsocket()
         self.ipgetter = DB_IP_GETTER()
     self.changed = False
     self.signal = 0  # signal quality that is measured [dBm]
     self.first_run = True
     self.seq_num = 0
 def __init__(self,
              udp_port_rx,
              ip_rx,
              udp_port_smartphone,
              comm_direction,
              interface_drone_comm,
              mode,
              communication_id,
              dronebridge_port,
              tag=''):
     if type(communication_id) is int:
         self.comm_id = bytes([
             communication_id
         ])  # must be the same on drone and groundstation
     else:
         self.comm_id = communication_id  # must be the same on drone and groundstation
     assert type(self.comm_id) is bytes
     self.udp_port_rx = udp_port_rx  # 1604
     self.ip_rx = ip_rx
     self.udp_port_smartphone = udp_port_smartphone  # we bind to that locally
     # direction is stored as DBDir
     self.comm_direction = comm_direction  # set to 0x01 if program runs on groundst. and to 0x03 if runs on drone
     assert type(self.comm_direction) is DBDir
     self.interface = interface_drone_comm  # the long range interface
     self.mode = mode
     self.tag = tag
     if self.mode == 'wifi':
         self.short_mode = 'w'
     else:
         self.short_mode = 'm'
     self.fcf = b'\xb4\x00\x00\x00'  # RTS frames
     # port is stored as byte value
     if type(dronebridge_port) is DBPort:
         self.db_port = dronebridge_port.value
     else:
         self.db_port = dronebridge_port
     assert type(self.db_port) is bytes
     self.comm_sock = self._open_comm_sock()
     # dirty fix till we do some proper code cleanup!
     if self.comm_direction == DBDir.DB_TO_UAV and (
             self.db_port == DBPort.DB_PORT_TELEMETRY.value
             or self.db_port == DBPort.DB_PORT_COMMUNICATION.value):
         self.android_sock = self._open_android_udpsocket()
         self.ipgetter = DB_IP_GETTER()
     self.changed = False
     self.signal = 0  # signal quality that is measured [dBm]
     self.first_run = True
     self.seq_num = 0
     init_cam_gpios()
Example #3
0
 def __init__(self, src_mac, dst_mac, udp_port_rx, ip_rx,
              udp_port_smartphone, comm_direction, interface_drone_comm,
              mode, communication_id, frame_type, dronebridge_port):
     self.src_mac = src_mac
     self.dst_mac = dst_mac  # not used in monitor mode
     self.comm_id = communication_id  # must be the same on drone and groundstation
     self.udp_port_rx = udp_port_rx  # 1604
     self.ip_rx = ip_rx
     self.udp_port_smartphone = udp_port_smartphone  # we bind to that locally
     # communication direction: the direction the packets will have when sent from the application
     self.comm_direction = comm_direction  # set to 0x01 if program runs on groundst. and to 0x02 if runs on drone
     self.interface = interface_drone_comm  # the long range interface
     self.mode = mode
     self.tag = ''
     if dronebridge_port == PORT_TELEMETRY:
         self.tag = "DB_TEL: "
     elif dronebridge_port == PORT_COMMUNICATION:
         self.tag = "DB_Comm: "
     if self.mode == 'wifi':
         self.short_mode = 'w'
     else:
         self.short_mode = 'm'
     if frame_type == '1':
         self.fcf = b'\x08\x00'
         self.driver = DRIVER_RALINK
     else:
         self.fcf = b'\x80\x00'
         self.driver = DRIVER_ATHEROS
     self.db_port = dronebridge_port
     self.comm_sock = self._open_comm_sock()
     if self.comm_direction == TO_DRONE:
         self.android_sock = self._open_android_udpsocket()
         self.ipgetter = DB_IP_GETTER()
     self.changed = False
     self.signal = 0  # signal quality that is measured [dBm]
     self.first_run = True
Example #4
0
class DBProtocol:
    ip_smartp = "192.168.42.129"
    APP_PORT_TEL = 1604
    APP_PORT_COMM = 1603

    def __init__(self,
                 udp_port_rx,
                 ip_rx,
                 udp_port_smartphone,
                 comm_direction,
                 interface_drone_comm,
                 mode,
                 communication_id,
                 dronebridge_port,
                 tag=''):
        if type(communication_id) is int:
            self.comm_id = bytes([
                communication_id
            ])  # must be the same on drone and groundstation
        else:
            self.comm_id = communication_id  # must be the same on drone and groundstation
        assert type(self.comm_id) is bytes
        self.udp_port_rx = udp_port_rx  # 1604
        self.ip_rx = ip_rx
        self.udp_port_smartphone = udp_port_smartphone  # we bind to that locally
        # direction is stored as DBDir
        self.comm_direction = comm_direction  # set to 0x01 if program runs on groundst. and to 0x03 if runs on drone
        assert type(self.comm_direction) is DBDir
        self.interface = interface_drone_comm  # the long range interface
        self.mode = mode
        self.tag = tag
        if self.mode == 'wifi':
            self.short_mode = 'w'
        else:
            self.short_mode = 'm'
        self.fcf = b'\xb4\x00\x00\x00'  # RTS frames
        # port is stored as byte value
        if type(dronebridge_port) is DBPort:
            self.db_port = dronebridge_port.value
        else:
            self.db_port = dronebridge_port
        assert type(self.db_port) is bytes
        self.comm_sock = self._open_comm_sock()
        # dirty fix till we do some proper code cleanup!
        if self.comm_direction == DBDir.DB_TO_UAV and (
                self.db_port == DBPort.DB_PORT_TELEMETRY.value
                or self.db_port == DBPort.DB_PORT_COMMUNICATION.value):
            self.android_sock = self._open_android_udpsocket()
            self.ipgetter = DB_IP_GETTER()
        self.changed = False
        self.signal = 0  # signal quality that is measured [dBm]
        self.first_run = True
        self.seq_num = 0

    def receive_from_db(self, custom_timeout=1.5):
        """Check if new data from the drone arrived and return packet payload. Default timeout is 1.5s"""
        if self.mode == 'wifi':
            try:
                data, addr = self.comm_sock.recvfrom(UDP_BUFFERSIZE)
                return data
            except Exception as e:
                print(
                    self.tag + str(e) +
                    ": Drone is not ready or has wrong IP address of groundstation. Sending hello"
                )
                self._send_hello()
                return False
        else:
            try:
                readable, writable, exceptional = select.select(
                    [self.comm_sock], [], [], custom_timeout)
                if readable:
                    data = self.parse_packet(
                        bytearray(
                            self.comm_sock.recv(MONITOR_BUFFERSIZE_COMM)))
                    if data != False:
                        return data
            except timeout as t:
                print(
                    self.tag + str(t) +
                    "Socket timed out. No response received from drone (monitor mode)"
                )
                return False
            except Exception as e:
                print(self.tag + str(e) +
                      ": Error receiving data form drone (monitor mode)")
                return False

    def receive_telemetryfromdrone(self):
        """Receive telemetry message from drone on groundstation and return the payload."""
        if self.mode == 'wifi':
            try:
                data, addr = self.comm_sock.recvfrom(UDP_BUFFERSIZE)
                return data
            except Exception as e:
                print(
                    self.tag + str(e) +
                    ": Drone is not ready or has wrong IP address of groundstation. Sending hello"
                )
                self._send_hello()
                return False
        else:
            try:
                while True:
                    data = self.parse_packet(
                        bytearray(self.comm_sock.recv(MONITOR_BUFFERSIZE)))
                    if data != False:
                        return data
            except Exception as e:
                print(self.tag + str(e) +
                      ": Error receiving telemetry form drone (monitor mode)")
                return False

    def receive_process_datafromgroundstation(self):
        """Check if new data from the groundstation arrived and process the packet - do not use for custom data!"""
        # check if the socket received something and process data
        if self.mode == "wifi":
            readable, writable, exceptional = select.select([self.comm_sock],
                                                            [], [], 0)
            if readable:
                data, addr = self.comm_sock.recvfrom(UDP_BUFFERSIZE)
                if data.decode() == "tx_hello_packet":
                    self.ip_rx = addr[0]
                    print(self.tag + "Updated goundstation IP-address to: " +
                          str(self.ip_rx))
                else:
                    print(self.tag + "New data from groundstation: " +
                          data.decode())
        else:
            if self.db_port == DBPort.DB_PORT_TELEMETRY.value:
                # socket is non-blocking - return if nothing there and keep sending telemetry
                readable, writable, exceptional = select.select(
                    [self.comm_sock], [], [], 0)
                if readable:
                    # just get RSSI of radiotap header
                    self.parse_packet(
                        bytes(self.comm_sock.recv(MONITOR_BUFFERSIZE)))
            else:
                if self.first_run:
                    self._clear_monitor_comm_socket_buffer()
                    self.first_run = False
                db_comm_prot_request = self.parse_packet(
                    bytes(self.comm_sock.recv(MONITOR_BUFFERSIZE_COMM)))
                if db_comm_prot_request != False:
                    try:
                        if not self._route_db_comm_protocol(
                                db_comm_prot_request):
                            print(
                                self.tag +
                                "smartphone request could not be processed correctly"
                            )
                    except (UnicodeDecodeError, ValueError):
                        print(
                            self.tag +
                            "Received message from groundstation with error. Not UTF error or ValueError"
                        )

    def process_smartphonerequests(self, last_keepalive):
        """See if smartphone told the groundstation to do something. Returns recent keep-alive time"""
        r, w, e = select.select([self.android_sock], [], [], 0)
        if r:
            smartph_data, android_addr = self.android_sock.recvfrom(
                UDP_BUFFERSIZE)
            return self._process_smartphone_command(smartph_data,
                                                    last_keepalive)
        return last_keepalive

    def sendto_smartphone(self, raw_data, port):
        """Sends data to smartphone. Socket is nonblocking so we need to wait till it becomes"""
        self.ip_smartp = self.ipgetter.return_smartphone_ip()
        while True:
            r, w, e = select.select([], [self.android_sock], [], 0)
            if w:
                try:
                    return self.android_sock.sendto(raw_data,
                                                    (self.ip_smartp, port))
                except:
                    print(self.tag + "Could not send to smartphone (" +
                          self.ip_smartp + "). Make sure it is connected.")
                    return 0

    def sendto_groundstation(self, data_bytes, db_port):
        """Call this function to send stuff to the groundstation"""
        if type(db_port) is DBPort:
            db_port = db_port.value
        if self.mode == "wifi":
            num = self._sendto_tx_wifi(data_bytes)
        else:
            num = self._send_monitor(data_bytes, db_port,
                                     DBDir.DB_TO_GND.value)
        return num

    def sendto_uav(self, data_bytes, db_port):
        """Call this function to send stuff to the drone!"""
        if type(db_port) is DBPort:
            db_port = db_port.value
        if self.mode == "wifi":
            num = self._sendto_rx_wifi(data_bytes, db_port)
        else:
            num = self._send_monitor(data_bytes, db_port,
                                     DBDir.DB_TO_UAV.value)
        return num

    def send_beacon(self):
        self.sendto_uav('groundstation_beacon'.encode(),
                        DBPort.DB_PORT_TELEMETRY.value)

    def update_routing_gopro(self):
        print(self.tag + "Update iptables to send GoPro stream to " +
              str(self.ip_rx))
        if self.changed:
            call(
                "iptables -t nat -R PREROUTING 1 -p udp --dport 8554 -j DNAT --to "
                + str(self.ip_rx))
        else:
            call(
                "iptables -t nat -I PREROUTING 1 -p udp --dport 8554 -j DNAT --to "
                + str(self.ip_rx))
            self.changed = True

    def set_raw_sock_blocking(self, is_blocking):
        self.comm_sock.setblocking(is_blocking)

    def getsmartphonesocket(self):
        return self.android_sock

    def getcommsocket(self):
        return self.comm_sock

    @staticmethod
    def parse_packet(packet):
        """Pars DroneBridge raw protocol v2. Returns False if not OK or return packet payload if it is!"""
        rth_length = packet[2]
        db_v2_payload_length = int.from_bytes(
            packet[(rth_length + 7):(rth_length + 8)] +
            packet[(rth_length + 8):(rth_length + 9)],
            byteorder='little',
            signed=False)
        payload_start = rth_length + DB_V2_HEADER_LENGTH
        return packet[payload_start:(payload_start + db_v2_payload_length)]

    def _process_smartphone_command(self, raw_data, thelast_keepalive):
        """We received something from the smartphone. Most likely a communication message. Do something with it."""
        try:
            raw_data_decoded = bytes.decode(raw_data)
            print(self.tag + "Received from SP: " + raw_data_decoded)
        except UnicodeDecodeError:
            pass
        if not self._route_db_comm_protocol(raw_data):
            print(self.tag +
                  "smartphone command could not be processed correctly!")
        return thelast_keepalive

    def _route_db_comm_protocol(self, raw_data_encoded):
        """Routing of the DroneBridge communication protocol packets"""
        status = False
        extracted_info = comm_message_extract_info(
            raw_data_encoded)  # returns json bytes [0] and crc bytes [1]
        try:
            loaded_json = json.loads(extracted_info[0].decode())
        except UnicodeDecodeError:
            print(self.tag + "Invalid command: Could not decode json message")
            return False
        except ValueError:
            print(self.tag + "ValueError on decoding extracted_info[0]")
            return False

        if loaded_json[
                'destination'] == 1 and self.comm_direction == DBDir.DB_TO_UAV and check_package_good(
                    extracted_info):
            message = self._process_db_comm_protocol_type(loaded_json)
            if message != "":
                status = self.sendto_smartphone(message, self.APP_PORT_COMM)
            else:
                status = True
        elif loaded_json['destination'] == 2 and check_package_good(
                extracted_info):
            if self.comm_direction == DBDir.DB_TO_UAV:
                response_drone = self._redirect_comm_to_drone(raw_data_encoded)
                if response_drone != False and response_drone != None:
                    message = self._process_db_comm_protocol_type(loaded_json)
                    self.sendto_smartphone(message, self.APP_PORT_COMM)
                    status = self.sendto_smartphone(response_drone,
                                                    self.APP_PORT_COMM)
            else:
                message = self._process_db_comm_protocol_type(loaded_json)
                sentbytes = self.sendto_groundstation(
                    message, DBPort.DB_PORT_COMMUNICATION.value)
                if sentbytes == None:
                    status = True
        elif loaded_json['destination'] == 3:
            if self.comm_direction == DBDir.DB_TO_UAV:
                status = self.sendto_uav(raw_data_encoded,
                                         DBPort.DB_PORT_COMMUNICATION.value)
            else:
                change_settings_gopro(loaded_json)
        elif loaded_json['destination'] == 4:
            if self.comm_direction == DBDir.DB_TO_UAV:
                status = self.sendto_smartphone(raw_data_encoded,
                                                self.APP_PORT_COMM)
        else:
            print(self.tag + "DB_COMM_PROTO: Unknown message destination")
        return status

    def _process_db_comm_protocol_type(self, loaded_json):
        """Execute the command given in the DroneBridge communication packet"""
        message = ""
        if loaded_json['type'] == DBCommProt.DB_TYPE_MSP.value:
            # deprecated
            self.sendto_uav(base64.b64decode(loaded_json['MSP']),
                            DBPort.DB_PORT_CONTROLLER.value)
        elif loaded_json['type'] == DBCommProt.DB_TYPE_SETTINGS_REQUEST.value:
            if self.comm_direction == DBDir.DB_TO_UAV:
                message = new_settingsresponse_message(
                    loaded_json, DBCommProt.DB_ORIGIN_GND.value)
            else:
                message = new_settingsresponse_message(
                    loaded_json, DBCommProt.DB_ORIGIN_UAV.value)
        elif loaded_json['type'] == DBCommProt.DB_TYPE_SETTINGS_CHANGE.value:
            if self.comm_direction == DBDir.DB_TO_UAV:
                message = change_settings(loaded_json,
                                          DBCommProt.DB_ORIGIN_GND.value)
            else:
                message = change_settings(loaded_json,
                                          DBCommProt.DB_ORIGIN_UAV.value)
        elif loaded_json['type'] == DBCommProt.DB_TYPE_SYS_IDENT_REQUEST.value:
            if self.comm_direction == DBDir.DB_TO_UAV:
                message = create_sys_ident_response(
                    loaded_json, DBCommProt.DB_ORIGIN_GND.value)
            else:
                message = create_sys_ident_response(
                    loaded_json, DBCommProt.DB_ORIGIN_UAV.value)
        else:
            print(self.tag + "DB_COMM_PROTO: Unknown message type")
        return message

    def _redirect_comm_to_drone(self, raw_data_encoded):
        """This one will forward communication message to drone. Response is returned or False"""
        if self.first_run:
            self._clear_monitor_comm_socket_buffer()
            self.first_run = False
        self.sendto_uav(raw_data_encoded, DBPort.DB_PORT_COMMUNICATION.value)
        response = self.receive_from_db()
        print(self.tag + "Parsed packet received from drone:")
        print(response)
        return response

    def _send_hello(self):
        """Send this in wifi mode to let the drone know about IP of groundstation"""
        self.comm_sock.sendto("tx_hello_packet".encode(),
                              (self.ip_rx, self.udp_port_rx))

    def _sendto_tx_wifi(self, data_bytes):
        """Sends LTM and other stuff to groundstation/smartphone in wifi mode"""
        while True:
            r, w, e = select.select([], [self.comm_sock], [], 0)
            if w:
                num = self.comm_sock.sendto(data_bytes,
                                            (self.ip_rx, self.udp_port_rx))
                return num

    def _sendto_rx_wifi(self, raw_data_bytes, port_bytes):
        """
        Send a packet to drone in wifi mode
        depending on message type different ports/programmes aka front ends on the drone need to be addressed
        """
        if port_bytes == DBPort.DB_PORT_CONTROLLER.value:
            print(self.tag + "Sending MSP command to RX Controller (wifi)")
            try:
                # TODO
                num = 0
                pass
            except Exception:
                return False
            print(self.tag + "Sent it!")
        else:
            print(self.tag +
                  "Sending a message to telemetry frontend on drone")
            num = self.comm_sock.sendto(raw_data_bytes,
                                        (self.ip_rx, self.udp_port_rx))
        return num

    def _send_monitor(self, data_bytes, port_bytes, direction):
        """Send a packet in monitor mode using DroneBridge raw protocol v2. Return None on success"""
        payload_length_bytes = bytes(
            len(data_bytes).to_bytes(2, byteorder='little', signed=False))
        if self.seq_num == 255:
            self.seq_num = 0
        else:
            self.seq_num += 1
        db_v2_raw_header = bytes(
            bytearray(self.fcf + direction + self.comm_id + port_bytes +
                      payload_length_bytes + bytes([self.seq_num])))
        while True:
            r, w, e = select.select([], [self.comm_sock], [], 0)
            if w:
                num = self.comm_sock.sendall(RADIOTAP_HEADER +
                                             db_v2_raw_header + data_bytes)
                break
        return num

    def _open_comm_sock(self):
        """Opens a socket that talks to drone (on tx side) or groundstation (on rx side)"""
        if self.mode == "wifi":
            return self._open_comm_udpsocket()
        else:
            return self._open_comm_monitorsocket()

    def _open_comm_udpsocket(self):
        print(self.tag + "Opening UDP-Socket for DroneBridge communication")
        sock = socket(AF_INET, SOCK_DGRAM)
        server_address = ('', self.udp_port_rx)
        sock.bind(server_address)
        if self.comm_direction.value == b'\x00':
            sock.settimeout(1)
        else:
            sock.setblocking(False)
        return sock

    def _open_comm_monitorsocket(self):
        print(self.tag + "Opening socket for monitor mode")
        raw_socket = socket(AF_PACKET, SOCK_RAW, htons(0x0004))
        raw_socket.bind((self.interface, 0))
        raw_socket = self._set_comm_socket_behavior(raw_socket)
        if self.comm_direction == DBDir.DB_TO_GND:
            raw_socket = attach_filter(
                raw_socket,
                byte_comm_id=self.comm_id,
                byte_direction=DBDir.DB_TO_UAV.value,
                byte_port=self.db_port)  # filter for packets TO_DRONE
        else:
            raw_socket = attach_filter(
                raw_socket,
                byte_comm_id=self.comm_id,
                byte_direction=DBDir.DB_TO_GND.value,
                byte_port=self.db_port)  # filter for packets TO_GROUND
        return raw_socket

    def _set_comm_socket_behavior(self, thesocket):
        """Set to blocking or non-blocking depending on Module (Telemetry, Communication) and if on drone or ground"""
        adjusted_socket = thesocket
        # On drone side in telemetry module
        if self.comm_direction == DBDir.DB_TO_GND and self.db_port == DBPort.DB_PORT_TELEMETRY.value:
            adjusted_socket.setblocking(False)
        # On ground side in comm module
        elif self.comm_direction == DBDir.DB_TO_UAV and self.db_port == DBPort.DB_PORT_COMMUNICATION.value:
            adjusted_socket.setblocking(False)
        return adjusted_socket

    def _clear_monitor_comm_socket_buffer(self):
        self.comm_sock.setblocking(False)
        while True:
            readable, writable, exceptional = select.select([self.comm_sock],
                                                            [], [], 1)
            if readable:
                self.comm_sock.recv(8192)
            else:
                break
        self.comm_sock.setblocking(True)

    def _open_android_udpsocket(self):
        print(self.tag + "Opening UDP-Socket to smartphone on port: " +
              str(self.udp_port_smartphone))
        sock = socket(AF_INET, SOCK_DGRAM)
        sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        if self.db_port == DBPort.DB_PORT_COMMUNICATION.value:
            address = ('', self.udp_port_smartphone)
            sock.bind(address)
        sock.setblocking(False)
        return sock
Example #5
0
class DBProtocol:
    ip_smartp = "192.168.42.129"
    LTM_PORT_SMARTPHONE = 1604
    COMM_PORT_SMARTPHONE = 1603

    def __init__(self, src_mac, dst_mac, udp_port_rx, ip_rx,
                 udp_port_smartphone, comm_direction, interface_drone_comm,
                 mode, communication_id, frame_type, dronebridge_port):
        self.src_mac = src_mac
        self.dst_mac = dst_mac  # not used in monitor mode
        self.comm_id = communication_id  # must be the same on drone and groundstation
        self.udp_port_rx = udp_port_rx  # 1604
        self.ip_rx = ip_rx
        self.udp_port_smartphone = udp_port_smartphone  # we bind to that locally
        # communication direction: the direction the packets will have when sent from the application
        self.comm_direction = comm_direction  # set to 0x01 if program runs on groundst. and to 0x02 if runs on drone
        self.interface = interface_drone_comm  # the long range interface
        self.mode = mode
        self.tag = ''
        if dronebridge_port == PORT_TELEMETRY:
            self.tag = "DB_TEL: "
        elif dronebridge_port == PORT_COMMUNICATION:
            self.tag = "DB_Comm: "
        if self.mode == 'wifi':
            self.short_mode = 'w'
        else:
            self.short_mode = 'm'
        if frame_type == '1':
            self.fcf = b'\x08\x00'
            self.driver = DRIVER_RALINK
        else:
            self.fcf = b'\x80\x00'
            self.driver = DRIVER_ATHEROS
        self.db_port = dronebridge_port
        self.comm_sock = self._open_comm_sock()
        if self.comm_direction == TO_DRONE:
            self.android_sock = self._open_android_udpsocket()
            self.ipgetter = DB_IP_GETTER()
        self.changed = False
        self.signal = 0  # signal quality that is measured [dBm]
        self.first_run = True

    def receive_datafromdrone(self):
        """Used by db_comm_protocol - want non-blocking socket to be able to set timeout in this case"""
        if self.mode == 'wifi':
            try:
                data, addr = self.comm_sock.recvfrom(UDP_BUFFERSIZE)
                return data
            except Exception as e:
                print(
                    self.tag + str(e) +
                    ": Drone is not ready or has wrong IP address of groundstation. Sending hello-packet"
                )
                self._send_hello()
                return False
        else:
            try:
                readable, writable, exceptional = select.select(
                    [self.comm_sock], [], [], 1.5)
                if readable:
                    data = self._pars_packet(
                        bytearray(
                            self.comm_sock.recv(MONITOR_BUFFERSIZE_COMM)))
                    if data != False:
                        return data
            except timeout as t:
                print(
                    self.tag + str(t) +
                    "Socket timed out. No response received from drone (monitor mode)"
                )
                return False
            except Exception as e:
                print(self.tag + str(e) +
                      ": Error receiving data form drone (monitor mode)")
                return False

    def receive_telemetryfromdrone(self):
        """Used by db_telemetry_tx - want blocking socket in this case"""
        if self.mode == 'wifi':
            try:
                data, addr = self.comm_sock.recvfrom(UDP_BUFFERSIZE)
                return data
            except Exception as e:
                print(
                    self.tag + str(e) +
                    ": Drone is not ready or has wrong IP address of groundstation. Sending hello-packet"
                )
                self._send_hello()
                return False
        else:
            try:
                while True:
                    data = self._pars_packet(
                        bytearray(self.comm_sock.recv(MONITOR_BUFFERSIZE)))
                    if data != False:
                        return data
            except Exception as e:
                print(self.tag + str(e) +
                      ": Error receiving telemetry form drone (monitor mode)")
                return False

    def receive_process_datafromgroundstation(self):
        # check if the socket received something and process data
        if self.mode == "wifi":
            readable, writable, exceptional = select.select([self.comm_sock],
                                                            [], [], 0)
            if readable:
                data, addr = self.comm_sock.recvfrom(UDP_BUFFERSIZE)
                if data.decode() == "tx_hello_packet":
                    self.ip_rx = addr[0]
                    self.updateRouting()
                    print(self.tag + "Updated goundstation IP-address to: " +
                          str(self.ip_rx))
                else:
                    print(self.tag + "New data from groundstation: " +
                          data.decode())
        else:
            if self.db_port == PORT_TELEMETRY:
                # socket is non-blocking - return if nothing there and keep sending telemetry
                readable, writable, exceptional = select.select(
                    [self.comm_sock], [], [], 0)
                if readable:
                    # just get RSSI of radiotap header
                    self._pars_packet(
                        bytes(self.comm_sock.recv(MONITOR_BUFFERSIZE)))
            else:
                if self.first_run:
                    self._clear_monitor_comm_socket_buffer()
                    self.first_run = False
                db_comm_prot_request = self._pars_packet(
                    bytes(self.comm_sock.recv(MONITOR_BUFFERSIZE_COMM)))
                if db_comm_prot_request != False:
                    try:
                        print("Received from groundstation")
                        print(db_comm_prot_request)
                        if not self._route_db_comm_protocol(
                                db_comm_prot_request):
                            print(
                                self.tag +
                                "smartphone request could not be processed correctly"
                            )
                    except UnicodeDecodeError as e:
                        print(
                            self.tag +
                            "Received message not UTF-8 conform. Maybe a invalid packet in the buffer."
                        )

    def process_smartphonerequests(self, last_keepalive):
        """See if smartphone told the groundstation to do something. Returns recent keep-alive time"""
        r, w, e = select.select([self.android_sock], [], [], 0)
        if r:
            smartph_data, android_addr = self.android_sock.recvfrom(
                UDP_BUFFERSIZE)
            return self._process_smartphonecommand(smartph_data,
                                                   last_keepalive)
        return last_keepalive

    def check_smartphone_ready(self):
        """Checks if smartphone app is ready for data. Returns IP of smartphone"""
        sock_status = select.select([self.android_sock], [], [], 0.05)
        if sock_status[0]:
            new_data, new_addr = self.android_sock.recvfrom(UDP_BUFFERSIZE)
            if new_data.decode() == "smartphone_is_still_here":
                print(self.tag + "Smartphone is ready")
                self.ip_smartp = new_addr[0]
                print(self.tag +
                      "(IGNORED) Sending future data to smartphone - " +
                      self.ip_smartp + ":" + str(self.udp_port_smartphone))
                return True
        return False

    def finish_dronebridge_ltmframe(self, frame):
        """Adds information to custom LTM-Frame on groundstation side"""
        if self.mode == 'wifi':
            with open('/proc/net/wireless') as fp:
                for line in fp:
                    if line.startswith(self.interface, 1,
                                       len(self.interface) + 1):
                        result = line.split(" ", 8)
                        frame[5] = int(result[5][:-1])
                        frame[6] = int(result[7][1:-1])
                        fp.close()
                        return bytes(frame)
            return frame
        else:
            # frame[5] = int((int(self.datarate)*500)/1000)
            frame[6] = self.signal
            # TODO add wbc information and crc
            return bytes(frame)

    def sendto_smartphone(self, raw_data, port):
        """Sends data to smartphone. Socket is nonblocking so we need to wait till it becomes"""
        self.ip_smartp = self.ipgetter.return_smartphone_ip()
        while True:
            r, w, e = select.select([], [self.android_sock], [], 0)
            if w:
                try:
                    return self.android_sock.sendto(raw_data,
                                                    (self.ip_smartp, port))
                except:
                    print(self.tag + "Could not send to smartphone (" +
                          self.ip_smartp + "). Make sure it is connected.")
                    return 0

    def sendto_groundstation(self, data_bytes, port_bytes):
        """Call this function to send stuff to the groundstation or directly to smartphone"""
        if self.mode == "wifi":
            num = self._sendto_tx_wifi(data_bytes)
        else:
            num = self._send_monitor(data_bytes, port_bytes, TO_GROUND)
        return num

    def send_dronebridge_frame(self):
        DroneBridgeFrame = b'$TY' + self.short_mode.encode() + chr(int(psutil.cpu_percent(interval=None))).encode() + \
                           bytes([self.signal]) + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        self.sendto_groundstation(DroneBridgeFrame, PORT_TELEMETRY)

    def send_beacon(self):
        self._sendto_drone('groundstation_beacon'.encode(), PORT_TELEMETRY)

    def updateRouting(self):
        print(self.tag + "Update iptables to send GoPro stream to " +
              str(self.ip_rx))
        if self.changed:
            call(
                "iptables -t nat -R PREROUTING 1 -p udp --dport 8554 -j DNAT --to "
                + str(self.ip_rx))
        else:
            call(
                "iptables -t nat -I PREROUTING 1 -p udp --dport 8554 -j DNAT --to "
                + str(self.ip_rx))
            self.changed = True

    def getsmartphonesocket(self):
        return self.android_sock

    def getcommsocket(self):
        return self.comm_sock

    def _pars_packet(self, packet):
        """Check if packet is OK and get RSSI. Returns False if not OK or return packet payload if it is"""
        rth_length = packet[2]
        payload_length = int.from_bytes(
            packet[(rth_length + 19):(rth_length + 20)] +
            packet[(rth_length + 20):(rth_length + 21)],
            byteorder='little',
            signed=False)
        if self._frameis_ok(packet, rth_length, payload_length):
            if self.driver == DRIVER_RALINK:
                self.signal = packet[14]
                # self.datarate = packet[9]
            else:
                self.signal = packet[30]
            payload_start = rth_length + DB_80211_HEADER_LENGTH
            return packet[payload_start:(payload_start + payload_length)]
        else:
            return False

    def _frameis_ok(self, packet, radiotap_header_length, payload_length):
        # TODO: check crc8 of header or something; currently: dump unusual large frames (ones much larger than payload)
        if (radiotap_header_length + payload_length + DB_80211_HEADER_LENGTH +
                20) > len(packet):
            return True
        else:
            print(self.tag +
                  "Found a DroneBridge Frame that is not OK - ignoring it")
            return False

    def _process_smartphonecommand(self, raw_data, thelast_keepalive):
        try:
            raw_data_decoded = bytes.decode(raw_data)
            print("Received from SP: " + raw_data_decoded)
            if raw_data == "smartphone_is_still_here":
                return time.time()
        except UnicodeDecodeError as unicodeerr:
            pass
        if not self._route_db_comm_protocol(raw_data):
            print(self.tag +
                  "smartphone command could not be processed correctly")
        return thelast_keepalive

    def _route_db_comm_protocol(self, raw_data_encoded):
        status = False
        extracted_info = comm_message_extract_info(
            raw_data_encoded)  # returns json bytes and crc bytes
        loaded_json = json.loads(extracted_info[0].decode())

        if loaded_json[
                'destination'] == 1 and self.comm_direction == TO_DRONE and check_package_good(
                    extracted_info):
            message = self._process_db_comm_protocol_type(loaded_json)
            if message != "":
                status = self.sendto_smartphone(message,
                                                self.COMM_PORT_SMARTPHONE)
            else:
                status = True
        elif loaded_json['destination'] == 2 and check_package_good(
                extracted_info):
            if self.comm_direction == TO_DRONE:
                response_drone = self._redirect_comm_to_drone(raw_data_encoded)
                if response_drone != False and response_drone != None:
                    message = self._process_db_comm_protocol_type(loaded_json)
                    self.sendto_smartphone(message, self.COMM_PORT_SMARTPHONE)
                    status = self.sendto_smartphone(response_drone,
                                                    self.COMM_PORT_SMARTPHONE)
            else:
                message = self._process_db_comm_protocol_type(loaded_json)
                sentbytes = self.sendto_groundstation(message,
                                                      PORT_COMMUNICATION)
                if sentbytes == None:
                    status = True
        elif loaded_json['destination'] == 3:
            if self.comm_direction == TO_DRONE:
                status = self._sendto_drone(raw_data_encoded,
                                            PORT_COMMUNICATION)
            else:
                change_settings_gopro(loaded_json)
        elif loaded_json['destination'] == 4:
            if self.comm_direction == TO_DRONE:
                status = self.sendto_smartphone(raw_data_encoded,
                                                self.COMM_PORT_SMARTPHONE)
        else:
            print(self.tag + "DB_COMM_PROTO: Unknown message destination")
        return status

    def _process_db_comm_protocol_type(self, loaded_json):
        message = ""
        if loaded_json['type'] == 'mspcommand':
            self._sendto_drone(base64.b64decode(loaded_json['MSP']),
                               PORT_CONTROLLER)
        elif loaded_json['type'] == 'settingsrequest':
            if self.comm_direction == TO_DRONE:
                message = new_settingsresponse_message(loaded_json,
                                                       'groundstation')
            else:
                message = new_settingsresponse_message(loaded_json, 'drone')
        elif loaded_json['type'] == 'settingschange':
            if self.comm_direction == TO_DRONE:
                message = change_settings(loaded_json, 'groundstation')
            else:
                message = change_settings(loaded_json, 'drone')
        else:
            print(self.tag + "DB_COMM_PROTO: Unknown message type")
        return message

    def _redirect_comm_to_drone(self, raw_data_encoded):
        """This one will send to drone till it receives a valid response. Response is returned or False"""
        if self.first_run:
            self._clear_monitor_comm_socket_buffer()
            self.first_run = False
        self._sendto_drone(raw_data_encoded, PORT_COMMUNICATION)
        print("Forwarding to drone:")
        print(raw_data_encoded)
        response = self.receive_datafromdrone()
        print("Parsed packet received from drone:")
        print(response)
        return response

    def _send_hello(self):
        """Send this in wifi mode to let the drone know about IP of groundstation"""
        self.comm_sock.sendto("tx_hello_packet".encode(),
                              (self.ip_rx, self.udp_port_rx))

    def _sendto_drone(self, data_bytes, port_bytes):
        """Call this function to send stuff to the drone!"""
        if self.mode == "wifi":
            num = self._sendto_rx_wifi(data_bytes, port_bytes)
        else:
            num = self._send_monitor(data_bytes, port_bytes, TO_DRONE)
        return num

    def _sendto_tx_wifi(self, data_bytes):
        """Sends LTM and other stuff to groundstation/smartphone in wifi mode"""
        while True:
            r, w, e = select.select([], [self.comm_sock], [], 0)
            if w:
                num = self.comm_sock.sendto(data_bytes,
                                            (self.ip_rx, self.udp_port_rx))
                return num

    def _sendto_rx_wifi(self, raw_data_bytes, port_bytes):
        """
        Send a packet to drone in wifi mode
        depending on message type different ports/programmes aka frontends on the drone need to be addressed
        """
        if port_bytes == PORT_CONTROLLER:
            print(self.tag + "Sending MSP command to RX Controller (wifi)")
            try:
                raw_socket = socket(AF_PACKET, SOCK_RAW)
                raw_socket.bind((self.interface, 0))
                num = raw_socket.send(self.dst_mac + self.src_mac + ETH_TYPE +
                                      raw_data_bytes)
                raw_socket.close()
            except Exception as e:
                print(self.tag + str(e) +
                      ": Are you sure this program was run as superuser?")
                return False
            print(self.tag + "Sent it! " + str(num))
        else:
            print(self.tag +
                  "Sending a message to telemetry frontend on drone")
            num = self.comm_sock.sendto(raw_data_bytes,
                                        (self.ip_rx, self.udp_port_rx))
        return num

    def _send_monitor(self, data_bytes, port_bytes, direction):
        """Send a packet in monitor mode"""
        payload_length_bytes = bytes(
            len(data_bytes).to_bytes(2, byteorder='little', signed=False))
        crc_content = bytes(
            bytearray(DB_FRAME_VERSION + port_bytes + direction +
                      payload_length_bytes))
        crc = crc8.crc8()
        crc.update(crc_content)
        ieee_min_header_mod = bytes(
            bytearray(self.fcf + b'\x00\x00' + self.comm_id + self.src_mac +
                      crc_content + crc.digest() + b'\x00\x00'))
        while True:
            r, w, e = select.select([], [self.comm_sock], [], 0)
            if w:
                num = self.comm_sock.sendall(RADIOTAP_HEADER +
                                             ieee_min_header_mod + data_bytes)
                break
        return num

    def _open_comm_sock(self):
        """Opens a socket that talks to drone (on tx side) or groundstation (on rx side)"""
        if self.mode == "wifi":
            return self._open_comm_udpsocket()
        else:
            return self._open_comm_monitorsocket()

    def _open_comm_udpsocket(self):
        print(self.tag + "Opening UDP-Socket for DroneBridge communication")
        sock = socket(AF_INET, SOCK_DGRAM)
        server_address = ('', self.udp_port_rx)
        sock.bind(server_address)
        if self.comm_direction == b'\x00':
            sock.settimeout(1)
        else:
            sock.setblocking(False)
        return sock

    def _open_comm_monitorsocket(self):
        print(self.tag + "Opening socket for monitor mode")
        raw_socket = socket(AF_PACKET, SOCK_RAW, htons(0x0004))
        raw_socket.bind((self.interface, 0))
        raw_socket = self._set_comm_socket_behavior(raw_socket)
        if self.comm_direction == TO_GROUND:
            raw_socket = attach_filter(
                raw_socket, TO_DRONE, self.comm_id,
                self.db_port)  # filter for packets TO_DRONE
        else:
            raw_socket = attach_filter(
                raw_socket, TO_GROUND, self.comm_id,
                self.db_port)  # filter for packets TO_GROUND
        return raw_socket

    def _set_comm_socket_behavior(self, thesocket):
        """Set to blocking or non-blocking depending on Module (Telemetry, Communication) and if on drone or ground"""
        adjusted_socket = thesocket
        if self.comm_direction == TO_GROUND and self.db_port == PORT_TELEMETRY:  # On drone side in telemetry module
            adjusted_socket.setblocking(False)
        elif self.comm_direction == TO_DRONE and self.db_port == PORT_COMMUNICATION:  # On ground side in comm module
            adjusted_socket.setblocking(False)
        return adjusted_socket

    def _clear_monitor_comm_socket_buffer(self):
        self.comm_sock.setblocking(False)
        while True:
            readable, writable, exceptional = select.select([self.comm_sock],
                                                            [], [], 1)
            if readable:
                self.comm_sock.recv(8192)
            else:
                break
        self.comm_sock.setblocking(True)

    def _open_android_udpsocket(self):
        print(self.tag + "Opening UDP-Socket to smartphone on port: " +
              str(self.udp_port_smartphone) + ")")
        sock = socket(AF_INET, SOCK_DGRAM)
        sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        if self.db_port == PORT_COMMUNICATION:
            address = ('', self.udp_port_smartphone)
            sock.bind(address)
        sock.setblocking(False)
        print("Done")
        return sock