Source code for pypozyx.structures.device

#!/usr/bin/env python
"""
pypozyx.structures.device - contains various classes representing device data

Structures contained
--------------------
DeviceCoordinates
    consists of a device's ID, flag, and coordinates
DeviceRange
    consists of a range measurements timestamp, distance, and RSS
NetworkID
    container for a device's ID. Prints in 0xID format.
DeviceList
    container for a list of IDs. Can be initialized through size and/or IDs.
UWBSettings
    contains all of the UWB settings: channel, bitrate, prf, plen, and gain.
"""

from pypozyx.definitions.constants import PozyxConstants
from pypozyx.structures.byte_structure import ByteStructure
from pypozyx.structures.generic import Data
from pypozyx.structures.sensor_data import Coordinates


[docs]class DeviceCoordinates(ByteStructure): """ Container for both reading and writing device coordinates from and to Pozyx. The keyword arguments are at once its properties. Kwargs: network_id: Network ID of the device flag: Type of the device. Tag or anchor. pos: Coordinates of the device. Coordinates(). """ byte_size = 15 data_format = 'HBiii' def __init__(self, network_id=0, flag=0, pos=Coordinates()): """ Initializes the DeviceCoordinates object. Kwargs: network_id: Network ID of the device flag: Type of the device. Tag or anchor. pos: Coordinates of the device. Coordinates(). """ self.network_id = network_id self.flag = flag self.pos = pos self.data = [network_id, flag, int(pos.x), int(pos.y), int(pos.z)]
[docs] def load(self, data): self.data = data self.network_id = data[0] self.flag = data[1] self.pos = Coordinates(data[2], data[3], data[4])
[docs] def update_data(self): try: if self.data != [self.network_id, self.flag, self.pos.x, self.pos.y, self.pos.z]: self.data = [self.network_id, self.flag, self.pos.x, self.pos.y, self.pos.z] except: return
def __str__(self): return "ID: 0x{self.network_id:x}, flag: {self.flag}, ".format(self=self) + str(self.pos)
[docs]class DeviceRange(ByteStructure): """ Container for the device range data, resulting from a range measurement. The keyword arguments are at once its properties. Kwargs: timestamp: Timestamp of the range measurement distance: Distance measured by the device. RSS: Signal strength during the ranging measurement. """ byte_size = 10 data_format = 'IIh' # TODO should ideally be rss not RSS def __init__(self, timestamp=0, distance=0, RSS=0): """Initializes the DeviceRange object.""" self.timestamp = timestamp self.distance = distance self.RSS = RSS self.data = [timestamp, distance, RSS]
[docs] def load(self, data): self.data = data self.timestamp = data[0] self.distance = data[1] self.RSS = data[2]
[docs] def update_data(self): try: if self.data != [self.timestamp, self.distance, self.RSS]: self.data = [self.timestamp, self.distance, self.RSS] except: return
def __str__(self): return '{self.timestamp}ms, {self.distance}mm, {self.RSS}dBm'.format(self=self)
[docs]class NetworkID(Data): """ Container for a device's network ID. Kwargs: network_id: The network ID of the device. """ def __init__(self, network_id=0): """Initializes the NetworkID object.""" Data.__init__(self, [network_id], 'H') self.id = network_id
[docs] def load(self, data): self.data = data self.id = data[0]
[docs] def update_data(self): try: if self.data != [self.id]: self.data = [self.id] except: return
def __str__(self): return "0x%0.4x" % self.id
[docs]class DeviceList(Data): """ Container for a list of device IDs. Using list_size is recommended when having just used getDeviceListSize, while ids is recommended when one knows the IDs. When using one, the other automatically gets its respective value. Therefore, only use on of both. Note also that DeviceList(list_size=1) is the same as NetworkID(). Kwargs: ids: Array of known or unknown device IDs. Empty by default. list_size: Size of the device list. """ def __init__(self, ids=[], list_size=0): """Initializes the DeviceList object with either IDs or its size.""" if list_size != 0 and ids == []: Data.__init__(self, [0] * list_size, 'H' * list_size) else: Data.__init__(self, ids, 'H' * len(ids)) def __str__(self): s = 'IDs: ' for i in range(len(self)): if i > 0: s += ', ' s += '0x%0.4x' % self[i] return s
[docs] def load(self, data): for i in range(len(data)): self.data[i] = data[i]
[docs]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.remote_id = remote_id self.amount_of_bytes = amount_of_bytes self.data = [self.remote_id, self.amount_of_bytes]
[docs] def load(self, data): self.remote_id = data[0] self.amount_of_bytes = data[1] self.data = [self.remote_id, self.amount_of_bytes]
[docs] def update_data(self): try: if self.data != [self.remote_id, self.amount_of_bytes]: self.data = [self.remote_id, self.amount_of_bytes] except: return
[docs]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.remote_id = remote_id self.operation = operation self.data = [self.remote_id, self.operation]
[docs] def update_data(self): try: if self.data != [self.remote_id, self.operation]: self.data = [self.remote_id, self.operation] except: return
[docs]class UWBSettings(ByteStructure): """ Container for a device's UWB settings. Its keyword arguments are at once its properties. It also provides parsing functions for all its respective properties, which means this doesn't need to be done by users. These functions are parse_prf, parse_plen and parse_bitrate. You can also directly print the UWB settings, resulting in the following example output: "CH: 1, bitrate: 850kbit/s, prf: 16MHz, plen: 1024 symbols, gain: 15.0dB" Kwargs: channel: UWB channel of the device. See POZYX_UWB_CHANNEL. bitrate: Bitrate of the UWB commmunication. See POZYX_UWB_RATES. prf: Pulse repeat frequency of the UWB. See POZYX_UWB_RATES. plen: Preamble length of the UWB packets. See POZYX_UWB_PLEN. gain_db: Gain of the UWB transceiver, a float value. See POZYX_UWB_GAIN. """ byte_size = 7 data_format = 'BBBBf' def __init__(self, channel=0, bitrate=0, prf=0, plen=0, gain_db=0.0): """Initializes the UWB settings.""" self.channel = channel self.bitrate = bitrate self.prf = prf self.plen = plen self.gain_db = float(gain_db) self.data = [self.channel, self.bitrate, self.prf, self.plen, self.gain_db]
[docs] def load(self, data): self.channel = data[0] self.bitrate = data[1] & 0x3F self.prf = (data[1] & 0xC0) >> 6 self.plen = data[2] self.gain_db = float(data[3]) / 2 self.data = [self.channel, self.bitrate, self.prf, self.plen, self.gain_db]
[docs] def update_data(self): try: if self.data != [self.channel, self.bitrate, self.prf, self.plen, self.gain_db]: self.data = [self.channel, self.bitrate, self.prf, self.plen, self.gain_db] except: return
[docs] def parse_bitrate(self): """Parses the bitrate to be humanly readable.""" bitrates = {0: '110 kbit/s', 1: '850 kbit/s', 2: '6.81 Mbit/s'} try: return bitrates[self.bitrate] except: return 'invalid bitrate'
[docs] def parse_prf(self): """Parses the pulse repetition frequency to be humanly readable.""" prfs = {1: '16 MHz', 2: '64 MHz'} try: return prfs[self.prf] except: return 'invalid pulse repetitions frequency (PRF)'
[docs] def parse_plen(self): """Parses the preamble length to be humanly readable.""" plens = {0x0C: '4096 symbols', 0x28: '2048 symbols', 0x18: '1536 symbols', 0x08: '1024 symbols', 0x34: '512 symbols', 0x24: '256 symbols', 0x14: '128 symbols', 0x04: '64 symbols'} try: return plens[self.plen] except: return 'invalid preamble length'
def __str__(self): return "CH: {}, bitrate: {}, prf: {}, plen: {}, gain: {} dB".format(self.channel, self.parse_bitrate(), self.parse_prf(), self.parse_plen(), self.gain_db)
# TODO maybe change with properties one day?
[docs]class FilterData(ByteStructure): byte_size = 1 data_format = 'B' def __init__(self, filter_type=0, filter_strength=0): self.filter_type = filter_type self.filter_strength = filter_strength # TODO add type validation? self.value = self.filter_type + (self.filter_strength << 4) self.load([self.value])
[docs] def load(self, data=[0], convert=False): self.data = data self.value = data[0] self.update_data() self.filter_type = self.data[0] & 0xF self.filter_strength = self.data[0] >> 4
[docs] def update_data(self): try: if self.data != [self.value]: self.data = [self.value] except: return
[docs] def get_filter_name(self): filter_types = { PozyxConstants.FILTER_TYPE_NONE: "No filter", PozyxConstants.FILTER_TYPE_FIR: "FIR filter", PozyxConstants.FILTER_TYPE_MOVING_AVERAGE: "Moving average filter", PozyxConstants.FILTER_TYPE_MOVING_MEDIAN: "Moving median filter", } return filter_types.get(self.filter_type, "Unknown filter {}".format(self.filter_type))
def __str__(self): return "{} with strength {}".format(self.get_filter_name(), self.filter_strength)
[docs]class AlgorithmData(ByteStructure): byte_size = 1 data_format = 'B' def __init__(self, algorithm=0, dimension=0): self.algorithm = algorithm self.dimension = dimension # TODO add type validation? self.value = self.algorithm + self.dimension << 4 self.load([self.value])
[docs] def load(self, data=None, convert=False): self.data = [0] if data is None else data self.value = self.data[0] self.algorithm = self.data[0] & 0xF self.dimension = self.data[0] >> 4
[docs] def update_data(self): try: if self.data != [self.value]: self.data = [self.value] except: return
[docs] def get_algorithm_name(self): algorithms = { PozyxConstants.POSITIONING_ALGORITHM_UWB_ONLY: "UWB only", PozyxConstants.POSITIONING_ALGORITHM_TRACKING: "Tracking", PozyxConstants.POSITIONING_ALGORITHM_NONE: "None", } return algorithms.get(self.algorithm, "Unknown filter {}".format(self.algorithm))
[docs] def get_dimension_name(self): dimensions = { PozyxConstants.DIMENSION_2D: "2D", PozyxConstants.DIMENSION_2_5D: "2.5D", PozyxConstants.DIMENSION_3D: "3D", } return dimensions.get(self.dimension, "Unknown filter {}".format(self.algorithm))
def __str__(self): return "Algorithm {}, dimension {}".format(self.get_algorithm_name(), self.get_dimension_name())