class PozyxBitmasks:
    # Pozyx firmware identifiers
    FIRMWARE_MAJOR = 0xF0
    FIRMWARE_MINOR = 0xF

    # Pozyx device identifier for hardware
    ANCHOR = 0x00
    TAG = 0x20

    # Bit mask for POZYX_ST_RESULT
    SELFTEST_RESULT_ACCELEROMETER = 0x01
    SELFTEST_RESULT_MAGNETOMETER = 0x02
    SELFTEST_RESULT_GYRO = 0x04
    SELFTEST_RESULT_MCU = 0x08
    SELFTEST_RESULT_PRESSURE = 0x10
    SELFTEST_RESULT_UWB = 0x20

    # Bit mask for POZYX_INT_STATUS
    INT_STATUS_ERR = 0x01
    INT_STATUS_POS = 0x02
    INT_STATUS_IMU = 0x04
    INT_STATUS_RX_DATA = 0x08
    INT_STATUS_FUNC = 0x10

    # Bit mask for POZYX_INT_MASK
    INT_MASK_ERR = 0x01
    INT_MASK_POS = 0x02
    INT_MASK_IMU = 0x04
    INT_MASK_RX_DATA = 0x08
    INT_MASK_FUNC = 0x10
    INT_MASK_TDMA = 0x40
    INT_MASK_PIN = 0x80
    INT_MASK_ALL = 0x1F

    # Bit mask for POZYX_LED_CTRL
    LED_CTRL_LED1 = 0x01
    LED_CTRL_LED2 = 0x02
    LED_CTRL_LED3 = 0x04
    LED_CTRL_LED4 = 0x08

    # Bit mask for device type
    DEVICE_TYPE = 0xE0 class PozyxConstants:
    # Pozyx serial buffer sizes
    MAX_BUF_SIZE = 100
    MAX_SERIAL_SIZE = 28

    # Remote operations
    REMOTE_READ = 0x02
    REMOTE_WRITE = 0x04
    REMOTE_DATA = 0x06
    REMOTE_FUNCTION = 0x08

    # Pozyx delay constants
    DELAY_POLLING = 0.002
    DELAY_POLLING_FAST = 0.0005
    DELAY_LOCAL_WRITE = 0.001
    DELAY_LOCAL_FUNCTION = 0.005
    DELAY_REMOTE_WRITE = 0.005
    DELAY_REMOTE_FUNCTION = 0.01
    DELAY_INTERRUPT = 0.1
    DELAY_RANGING = 0.2
    DELAY_MODE_CHANGE = 0.02
    DELAY_FLASH = 0.5

    # Pozyx timeout constants
    TIMEOUT_RANGING = 0.025
    TIMEOUT_REMOTE_RANGING = 0.1
    TIMEOUT_POSITIONING = 0.2
    TIMEOUT_REMOTE_POSITIONING = 0.4
    TIMEOUT_POSITIONING_DATA = 1.0
    TIMEOUT_REMOTE_POSITIONING_DATA = 1.0
    TIMEOUT_OPTIMAL_DISCOVERY = 0.1

    # Pozyx status returns
    STATUS_FAILURE = 0x0
    STATUS_SUCCESS = 0x1
    STATUS_TIMEOUT = 0x8

    # Pozyx interrupt pin
    INT_PIN0 = 0x0
    INT_PIN1 = 0x1

    # Pozyx led control indexes
    LED_CTRL_LED_RX = 0x10
    LED_CTRL_LED_TX = 0x20
    LED_ON = True
    LED_OFF = False

    # Pozyx device modes
    TAG_MODE = 0
    ANCHOR_MODE = 1
    ALOHA_MODE = 2

    # The GPIO modes
    GPIO_DIGITAL_INPUT = 0
    GPIO_PUSH_PULL = 1
    GPIO_OPEN_DRAIN = 1

    ALL_GPIO_MODES = [GPIO_DIGITAL_INPUT, GPIO_PUSH_PULL, GPIO_OPEN_DRAIN]

    # The GPIO pull resistor configuration
    GPIO_NO_PULL = 0
    GPIO_PULL_UP = 1
    GPIO_PULL_DOWN = 2

    ALL_GPIO_PULLS = [GPIO_NO_PULL, GPIO_PULL_UP, GPIO_PULL_DOWN]

    # anchor selection modes
    ANCHOR_SELECT_MANUAL = 0
    ANCHOR_SELECT_AUTO = 1

    # discovery options
    DISCOVERY_ANCHORS_ONLY = 0
    DISCOVERY_TAGS_ONLY = 1
    DISCOVERY_ALL_DEVICES = 2

    DISCOVERY_TYPES = [DISCOVERY_ALL_DEVICES, DISCOVERY_ANCHORS_ONLY, DISCOVERY_TAGS_ONLY]

    # Pozyx positioning dimensions
    DIMENSION_3D = 3
    DIMENSION_2D = 2
    DIMENSION_2_5D = 1

    DIMENSIONS = [DIMENSION_3D, DIMENSION_2D, DIMENSION_2_5D]

    # positioning algorithm options
    POSITIONING_ALGORITHM_UWB_ONLY = 0
    POSITIONING_ALGORITHM_TRACKING = 4
    POSITIONING_ALGORITHM_NONE = 3

    POSITIONING_ALGORITHMS = [POSITIONING_ALGORITHM_UWB_ONLY, POSITIONING_ALGORITHM_TRACKING, POSITIONING_ALGORITHM_NONE]

    # ranging protocol options
    RANGE_PROTOCOL_PRECISION = 0x00
    RANGE_PROTOCOL_FAST = 0x01
    RANGE_PROTOCOL_TEST = 0x02

    RANGING_PROTOCOLS = [RANGE_PROTOCOL_PRECISION, RANGE_PROTOCOL_FAST]

    # positioning filters
    FILTER_TYPE_NONE = 0
    FILTER_TYPE_FIR = 1
    FILTER_TYPE_MOVING_AVERAGE = 3
    FILTER_TYPE_MOVING_MEDIAN = 4

    FILTER_TYPES = [FILTER_TYPE_NONE, FILTER_TYPE_FIR, FILTER_TYPE_MOVING_AVERAGE, FILTER_TYPE_MOVING_MEDIAN]

    # how to intercept pozyx events: by polling or by interrupts
    MODE_POLLING = 0
    MODE_INTERRUPT = 1

    # Division factors for converting the raw register values to meaningful
    # physical quantities
    POSITION_DIV_MM = 1.0
    PRESSURE_DIV_PA = 1000.0
    MAX_LINEAR_ACCELERATION_DIV_MG = 1.0
    ACCELERATION_DIV_MG = 1.0
    GYRO_DIV_DPS = 16.0
    MAGNETOMETER_DIV_UT = 16.0
    EULER_ANGLES_DIV_DEG = 16.0
    QUATERNION_DIV = 16384.0
    TEMPERATURE_DIV_CELSIUS = 1.0

    # flash configuration types
    FLASH_SAVE_REGISTERS = 1
    FLASH_SAVE_ANCHOR_IDS = 2
    FLASH_SAVE_NETWORK = 3
    FLASH_SAVE_ALL = 4

    ALL_FLASH_SAVE_TYPES = [FLASH_SAVE_REGISTERS, FLASH_SAVE_ANCHOR_IDS, FLASH_SAVE_NETWORK, FLASH_SAVE_ALL]

    # possible pin configuration settings
    INTERRUPT_CONFIG = 0x24
    PIN_MODE_PUSH_PULL = 0
    PIN_MODE_OPEN_DRAIN = 1

    # Possible pin activity states
    PIN_ACTIVE_LOW = 0
    PIN_ACTIVE_HIGH = 1

    # Possible UWB settings

    UWB_BITRATE_110_KBPS = 0
    UWB_BITRATE_850_KBPS = 1
    UWB_BITRATE_6810_KBPS = 2

    UWB_PRF_16_MHZ = 1
    UWB_PRF_64_MHZ = 2

    UWB_PLEN_64 = 0x04
    UWB_PLEN_128 = 0x14
    UWB_PLEN_256 = 0x24
    UWB_PLEN_512 = 0x34
    UWB_PLEN_1024 = 0x08
    UWB_PLEN_1536 = 0x18
    UWB_PLEN_2048 = 0x28
    UWB_PLEN_4096 = 0x0C

    ALL_UWB_CHANNELS = [1, 2, 3, 4, 5, 7]

    ALL_UWB_BITRATES = [
        UWB_BITRATE_110_KBPS,
        UWB_BITRATE_850_KBPS,
        UWB_BITRATE_6810_KBPS,
    ]

    ALL_UWB_PRFS = [
        UWB_PRF_16_MHZ,
        UWB_PRF_64_MHZ,
    ]

    ALL_UWB_PLENS = [
        UWB_PLEN_64,
        UWB_PLEN_128,
        UWB_PLEN_256,
        UWB_PLEN_512,
        UWB_PLEN_1024,
        UWB_PLEN_1536,
        UWB_PLEN_2048,
        UWB_PLEN_4096,
    ]

    # ALOHA send types
    ALOHA_SEND_CUSTOM_QUEUED = 0
    ALOHA_SEND_CUSTOM_IMMEDIATE = 1
    ALOHA_SEND_IMMEDIATE = 2

    ALL_ALOHA_TYPES = [ALOHA_SEND_CUSTOM_QUEUED, ALOHA_SEND_CUSTOM_IMMEDIATE, ALOHA_SEND_IMMEDIATE] POZYX_SUCCESS = 0x1 POZYX_FAILURE = 0x0 POZYX_TIMEOUT = 0x8 class PozyxRegisters:
    WHO_AM_I = 0x0  # Returns the constant value 0x43.
    FIRMWARE_VERSION = 0x1  # Returns the POZYX firmware version.
    HARDWARE_VERSION = 0x2  # Returns the POZYX hardware version.
    SELFTEST_RESULT = 0x3  # Returns the self-test result
    ERROR_CODE = 0x4  # Describes a possibly system error.
    INTERRUPT_STATUS = 0x5  # Indicates the source of the interrupt.
    CALIBRATION_STATUS = 0x6  # Returns the calibration status.

    # Configuration registers
    INTERRUPT_MASK = 0x10  # Indicates which interrupts are enabled.
    INTERRUPT_PIN = 0x11  # Configure the interrupt pin
    POSITIONING_FILTER = 0x14  # Filter used for positioning
    LED_CONFIGURATION = 0x15  # Configure the LEDs
    POSITIONING_ALGORITHM = 0x16  # Algorithm used for positioning
    # Configure the number of anchors and selection procedure
    POSITIONING_NUMBER_OF_ANCHORS = 0x17
    ALL_POSITIONING_REGISTERS = [0x14, 0x16, 0x21, 0x38]
    # Defines the update interval in ms in continuous positioning.
    POSITIONING_INTERVAL = 0x18
    NETWORK_ID = 0x1A  # The network id.
    UWB_CHANNEL = 0x1C  # UWB channel number.
    # Configure the UWB datarate and pulse repetition frequency (PRF)
    UWB_RATES = 0x1D
    UWB_PLEN = 0x1E  # Configure the UWB preamble length.
    UWB_GAIN = 0x1F  # Configure the power gain for the UWB transmitter
    ALL_UWB_REGISTERS = [0x1C, 0x1D, 0x1E, 0x1F]
    UWB_CRYSTAL_TRIM = 0x20  # Trimming value for the uwb crystal.
    RANGING_PROTOCOL = 0x21  # The ranging protocol
    # Configure the mode of operation of the pozyx device
    OPERATION_MODE = 0x22
    SENSORS_MODE = 0x23  # Configure the mode of operation of the sensors
    CONFIG_BLINK_PAYLOAD = 0x24  # Configure payload that needs to be transmitted with ALOHA
    ALOHA_VARIATION = 0x26  # Configure the variation on the ALOHA
    CONFIG_GPIO_1 = 0x27  # Configure GPIO pin 1.
    CONFIG_GPIO_2 = 0x28  # Configure GPIO pin 2.
    CONFIG_GPIO_3 = 0x29  # Configure GPIO pin 3.
    CONFIG_GPIO_4 = 0x2A  # Configure GPIO pin 4.

    ALL_ALOHA_REGISTERS = [0x18, 0x19, 0x24, 0x25, 0x26]


    # Positioning data
    POSITION_X = 0x30  # x-coordinate of the device in mm.
    POSITION_Y = 0x34  # y-coordinate of the device in mm.
    POSITION_Z = 0x38  # z-coordinate of the device in mm.
    HEIGHT = 0x38  # z-coordinate of the device in mm.
    POSITIONING_ERROR_X = 0x3C  # estimated error covariance of x
    POSITIONING_ERROR_Y = 0x3E  # estimated error covariance of y
    POSITIONING_ERROR_Z = 0x40  # estimated error covariance of z
    POSITIONING_ERROR_XY = 0x42  # estimated covariance of xy
    POSITIONING_ERROR_XZ = 0x44  # estimated covariance of xz
    POSITIONING_ERROR_YZ = 0x46  # estimated covariance of yz

    # Sensor data
    MAX_LINEAR_ACCELERATION = 0x4E  # Return the max linear acceleration and reset it to 0
    PRESSURE = 0x50  # Pressure data in mPa
    ACCELERATION_X = 0x54  # Accelerometer data (in mg)
    ACCELERATION_Y = 0x56
    ACCELERATION_Z = 0x58
    MAGNETIC_X = 0x5A  # Magnemtometer data
    MAGNETIC_Y = 0x5C
    MAGNETIC_Z = 0x5E
    GYRO_X = 0x60  # Gyroscope data
    GYRO_Y = 0x62
    GYRO_Z = 0x64
    # Euler angles heading (or yaw)  (1 degree = 16 LSB )
    EULER_ANGLE_HEADING = 0x66
    EULER_ANGLE_YAW = 0x66
    EULER_ANGLE_ROLL = 0x68  # Euler angles roll ( 1 degree = 16 LSB )
    EULER_ANGLE_PITCH = 0x6A  # Euler angles pitch ( 1 degree = 16 LSB )
    QUATERNION_W = 0x6C  # Weight of quaternion.
    QUATERNION_X = 0x6E  # x of quaternion
    QUATERNION_Y = 0x70  # y of quaternion
    QUATERNION_Z = 0x72  # z of quaternion
    LINEAR_ACCELERATION_X = 0x74  # Linear acceleration in x-direction
    LINEAR_ACCELERATION_Y = 0x76  # Linear acceleration in y-direction
    LINEAR_ACCELERATION_Z = 0x78  # Linear acceleration in z-direction
    GRAVITY_VECTOR_X = 0x7A  # x-component of gravity vector
    GRAVITY_VECTOR_Y = 0x7C  # y-component of gravity vector
    GRAVITY_VECTOR_Z = 0x7E  # z-component of gravity vector
    TEMPERATURE = 0x80  # Temperature

    # General data
    # Returns the number of devices stored internally
    DEVICE_LIST_SIZE = 0x81
    RX_NETWORK_ID = 0x82  # The network id of the latest received message
    RX_DATA_LENGTH = 0x84  # The length of the latest received message
    GPIO_1 = 0x85  # Value of the GPIO pin 1
    GPIO_2 = 0x86  # Value of the GPIO pin 2
    GPIO_3 = 0x87  # Value of the GPIO pin 3
    GPIO_4 = 0x88  # Value of the GPIO pin 4
    BLINK_INDEX = 0x89  # The blink index of the ALOHA transmissions

    # Functions
    RESET_SYSTEM = 0xB0  # Reset the Pozyx device
    LED_CONTROL = 0xB1  # Control LEDS 1 to 4 on the board
    WRITE_TX_DATA = 0xB2  # Write data in the UWB transmit (TX) buffer
    SEND_TX_DATA = 0xB3  # Transmit the TX buffer to some other pozyx device
    READ_RX_DATA = 0xB4  # Read data from the UWB receive (RX) buffer
    DO_RANGING = 0xB5  # Initiate ranging measurement
    DO_POSITIONING = 0xB6  # Initiate the positioning process.
    # Set the list of anchor ID's used for positioning.
    SET_POSITIONING_ANCHOR_IDS = 0xB7
    # Read the list of anchor ID's used for positioning.
    GET_POSITIONING_ANCHOR_IDS = 0xB8
    RESET_FLASH_MEMORY = 0xB9  # Reset a portion of the configuration in flash memory
    SAVE_FLASH_MEMORY = 0xBA  # Store a portion of the configuration in flash memory
    GET_FLASH_DETAILS = 0xBB  # Return information on what is stored in flash
    DO_ALOHA = 0xBC  # Start / stop ALOHA mode

    # Device list functions
    # Get all the network IDs's of devices in the device list.
    GET_DEVICE_LIST_IDS = 0xC0
    # Obtain the network ID's of all pozyx devices within range.
    DO_DISCOVERY = 0xC1

    CLEAR_DEVICES = 0xC3  # Clear the list of all pozyx devices.
    ADD_DEVICE = 0xC4  # Add a pozyx device to the devices list
    # Get the stored device information for a given pozyx device
    GET_DEVICE_INFO = 0xC5
    # Get the stored coordinates of a given pozyx device
    GET_DEVICE_COORDINATES = 0xC6
    # Get the stored range information of a given pozyx device
    GET_DEVICE_RANGE_INFO = 0xC7
    CIR_DATA = 0xC8  # Get the channel impulse response (CIR) coefficients

    DO_POSITIONING_WITH_DATA = 0xCC class Data(ByteStructure):
    """Data allows the user to define arbitrary data structures to use with Pozyx.

    The Leatherman of ByteStructure-derived classes, Data allows you to create your own
    library-compatible packed data structures. Also for empty data, this is used.

    The use of Data:
    Data creates a packed data structure with size and format that is entirely the user's choice.
    The format follows the one used in struct, where b is a byte, h is a 2-byte int, and
    i is a default-sized integer, and f is a float. In capitals, these are unsigned.
    So, to create a custom construct consisting of 4 uint16 and a single int, the
    following code can be used.

      >>> d = Data([0] * 5, 'HHHHi')

    or

      >>> data_format = 'HHHHi'
      >>> d = Data([0] * len(data_format), data_format)

    Args:
        data (optional): Data contained in the data structure. When no data_format is passed, these are assumed UInt8 values.
        data_format (optional): Custom data format for the data passed.
    """

    def __init__(self, data=None, data_format=None):
        if data is None:
            data = []
        self.data = data
        if data_format is None:
            data_format = 'B' * len(data)
        self.data_format = data_format
        self.set_packed_size()
        self.byte_data = '00' * self.byte_size

    def load(self, data, convert=True):
        self.data = data class SingleRegister(Data):
    """ SingleRegister is container for the data from a single Pozyx register.

    By default, this represents a UInt8 register. Used for both reading and writing.
    The size and whether the data is a 'signed' integer are both changeable by the
    user using the size and signed keyword arguments.

    Args:
        value (optional): Value of the register.
        size (optional): Size of the register. 1, 2, or 4. Default 1.
        signed (optional): Whether the data is signed. unsigned by default.
        print_hex (optional): How to print the register output. Hex by default. Special options are 'hex' and 'bin'
            other things, such as 'dec', will return decimal output.
    """
    byte_size = 1
    data_format = 'B'

    def __init__(self, value=0, size=1, signed=False, print_style='hex'):
        self.print_style = print_style
        if size == 1:
            data_format = 'b'
        elif size == 2:
            data_format = 'h'
        elif size == 4:
            data_format = 'i'
        else:
            raise ValueError("Size should be 1, 2, or 4")
        if not signed:
            data_format = data_format.capitalize()
        Data.__init__(self, [value], data_format)

    def load(self, data, convert=True):
        self.data = data

    @property
    def value(self):
        return self.data[0]

    @value.setter
    def value(self, new_value):
        self.data[0] = new_value

    def __str__(self):
        if self.print_style is 'hex':
            return hex(self.value).capitalize()
        elif self.print_style is 'bin':
            return bin(self.value)
        else:
            return str(self.value)

    def __eq__(self, other):
        if type(other) == SingleRegister:
            return self.value == other.value
        elif type(other) == int:
            return self.value == other
        else:
            raise ValueError("Can't compare SingleRegister value with non-integer values or registers")

    def __le__(self, other):
        if type(other) == SingleRegister:
            return self.value <= other.value
        elif type(other) == int:
            return self.value <= other
        else:
            raise ValueError("Can't compare SingleRegister value with non-integer values or registers")

    def __lt__(self, other):
        if type(other) == SingleRegister:
            return self.value < other.value
        elif type(other) == int:
            return self.value < other
        else:
            raise ValueError("Can't compare SingleRegister value with non-integer values or registers")

    def __gt__(self, other):
        return not self.__le__(other)

    def __ge__(self, other):
        return not self.__lt__(other) def dataCheck(data):
    """Returns whether an object is part of the ByteStructure-derived classes or not.

    The function checks the base classes of the passed data object. This function enables
    many library functions to be passed along its data as either an int/list or the properly
    intended data structure. For example, the following code will result in the
    same behaviour::

      >>> p.setCoordinates([0, 0, 0])
      >>> # or
      >>> coords = Coordinates()
      >>> p.setCoordinates(coords)

    AND

      >>> p.setNetworkId(0x6000)
      >>> # or
      >>> n = NetworkID(0x6000)
      >>> p.setNetworkId(n)

    Note that this only works for functions where you change one of the Pozyx's
    settings. When reading data from the Pozyx, you have to pass along the correct
    data structure.

    Using dataCheck:
    You might want to use this in your own function, as it makes it more robust
    to whether an int or list gets sent as a parameter to your function, or a
    ByteStructure-like object. If so, you can perform::

      >>> if not dataCheck(sample): # assume a is an int but you want it to be a SingleRegister
      >>>     sample = SingleRegister(sample)

    """
    if not(Data in type(data).__bases__ or ByteStructure in type(data).__bases__ or Data is type(data) or XYZ in type(data).__bases__ or SingleRegister in type(data).__bases__):
        return False
    return True def is_functioncall(reg):
    """Returns whether a Pozyx register is a Pozyx function."""
    if (0xB0 <= reg <= 0xBC) or (0xC0 <= reg < 0xC9):
        return True
    return False def is_reg_readable(reg):
    """Returns whether a Pozyx register is readable."""
    if (0x00 <= reg < 0x07) or (0x10 <= reg < 0x12) or (0x14 <= reg < 0x22) or (0x22 <= reg <= 0x24) or (
            0x26 <= reg < 0x2B) or (0x30 <= reg < 0x48) or (0x4E <= reg < 0x89):
        return True
    return False def is_reg_writable(reg):
    """Returns whether a Pozyx register is writeable."""
    if (0x10 <= reg < 0x12) or (0x14 <= reg < 0x22) or (0x22 <= reg <= 0x24) or (0x26 <= reg < 0x2B) or (
            0x30 <= reg < 0x3C) or (0x85 <= reg < 0x89):
        return True
    return False class RXInfo(ByteStructure):
    byte_size = 3
    data_format = 'HB'

    def __init__(self, remote_id=0, amount_of_bytes=0):
        """Initialises the RX Info structure"""
        self.data = [remote_id, amount_of_bytes]

    def load(self, data):
        self.data = data

    @property
    def remote_id(self):
        return self.data[0]

    @remote_id.setter
    def remote_id(self, value):
        self.data[0] = value

    @property
    def amount_of_bytes(self):
        return self.data[1]

    @amount_of_bytes.setter
    def amount_of_bytes(self, value):
        self.data[1] = value class TXInfo(ByteStructure):
    """Container for data transmission meta information

    Args:
        remote_id: ID to transmit to
        operation: remote operation to execute
    """
    byte_size = 3
    data_format = 'HB'

    def __init__(self, remote_id, operation=PozyxConstants.REMOTE_DATA):
        self.data = [remote_id, operation]

    @property
    def remote_id(self):
        return self.data[0]

    @remote_id.setter
    def remote_id(self, value):
        self.data[0] = value

    @property
    def operation(self):
        return self.data[1]

    @operation.setter
    def operation(self, value):
        self.data[1] = value
from time import sleep, time
from pypozyx.definitions.bitmasks import PozyxBitmasks
from pypozyx.definitions.constants import PozyxConstants, POZYX_SUCCESS, POZYX_FAILURE, POZYX_TIMEOUT
from pypozyx.definitions.registers import PozyxRegisters
from pypozyx.structures.generic import (Data, SingleRegister, dataCheck, is_functioncall,
                                        is_reg_readable, is_reg_writable)
from pypozyx.structures.device import RXInfo, TXInfo
from warnings import warn
            remote_id: Remote ID for remote read.

        Returns:
            POZYX_SUCCESS, POZYX_FAILURE, POZYX_TIMEOUT

        Example:
            >>> who_am_i = SingleRegister()
            >>> self.getRead(PozyxRegisters.WHO_AM_I, who_am_i)
            >>> print(who_am_i)
            67
        """
        if not is_reg_readable(address):
            if not self.suppress_warnings:
                warn("Register 0x%0.02x isn't readable" % address, stacklevel=3)
        if remote_id is None:
            return self.regRead(address, data)
        else:
            return self.remoteRegRead(remote_id, address, data)

    def useFunction(self, function, params=None, data=None, remote_id=None):
        """Activates a Pozyx register function either locally or remotely.

        Args:
            address: The function address

        Kwargs:
            params: A ByteStructure - derived object that contains the parameters for the function.
            data: A ByteStructure - derived object that is the container of the read data.
            remote_id: Remote ID for remote read.

        Returns:
            POZYX_SUCCESS, POZYX_FAILURE, POZYX_TIMEOUT

        Example:
            >>> self.useFunction(PozyxRegisters.CLEAR_DEVICES)
        """
        if not is_functioncall(function):
            if not self.suppress_warnings:
                warn("Register 0x%0.02x isn't a function register" % function, stacklevel=3)
        params = Data([]) if params is None else params
        data = Data([]) if data is None else data
        if remote_id is None:
            return self.regFunction(function, params, data)
        else:
            return self.remoteRegFunction(remote_id, function, params, data)

    # wait for flag functions

    def checkForFlag(self, interrupt_flag, timeout_s, interrupt=None):
        """Performs waitForFlag_safe and checks against errors or timeouts.

        This abstracts the waitForFlag status check routine commonly encountered
        in more complex library functions and checks the given flag against
        the error flag.

        Args:
            interrupt_flag: Flag of interrupt type to check the interrupt register against.
            timeout_s: duration to wait for the interrupt in seconds
            interrupt (optional): Container for the interrupt status register data.

        Returns:
            POZYX_SUCCESS, POZYX_FAILURE, POZYX_TIMEOUT
        """
        if interrupt is None:
            interrupt = SingleRegister()
        error_interrupt_mask = PozyxBitmasks.INT_MASK_ERR
        if self.waitForFlagSafe(interrupt_flag | error_interrupt_mask, timeout_s, interrupt):
            if (interrupt[0] & error_interrupt_mask) == error_interrupt_mask:
                return POZYX_FAILURE
            else:
                return POZYX_SUCCESS
        else:
            return POZYX_TIMEOUT

    ## @}

    ## \addtogroup communication_functions
    # @{

    def readRXBufferData(self, data, offset=0):
        """Reads the device's receive buffer's data completely.

        Args:
            data: Container for the data to be read from the receiver buffer.
            offset (optional): Offset of where in the RX buffer to start read

        Returns:
            POZYX_SUCCESS, POZYX_FAILURE
        """
        if data.byte_size + offset > PozyxConstants.MAX_BUF_SIZE:
            return POZYX_FAILURE

        _MAX_SERIAL_SIZE = PozyxConstants.MAX_SERIAL_SIZE

        status = POZYX_SUCCESS
        runs = int(data.byte_size / _MAX_SERIAL_SIZE)
        total_byte_data = ""
        for i in range(runs):
            partial_data = Data([0] * _MAX_SERIAL_SIZE)
            status &= self.regFunction(PozyxRegisters.READ_RX_DATA, Data([offset + i * _MAX_SERIAL_SIZE,
                                                            partial_data.byte_size]), partial_data)
            total_byte_data += partial_data.byte_data
        partial_data = Data([0] * (data.byte_size - runs * _MAX_SERIAL_SIZE))
        status &= self.regFunction(PozyxRegisters.READ_RX_DATA, Data([offset + runs * _MAX_SERIAL_SIZE,
                                                        partial_data.byte_size]), partial_data)
        total_byte_data += partial_data.byte_data
        data.load_bytes(total_byte_data)
        return status

    # TODO: here it's max serial size but this should be set in PozyxSerial and used as a max size here.
    def writeTXBufferData(self, data, offset=0):
        """Writes data to the device's transmit buffer at the offset address.

        Args:
            data: Data to write to the Pozyx buffer. Has to be a ByteStructure derived object.
            offset (optional): Offset in buffer to start writing data

        Returns:
            POZYX_SUCCESS, POZYX_FAILURE
        """

        if not dataCheck(data):