def _calc_num_repeats( self, attd: dict, payload: bytes, offset: int, offsetend: int = 0 ) -> int: """ Deduce number of items in 'variable by size' repeating group by dividing length of remaining payload by length of group. This is predicated on there being only one such repeating group per message payload, which is true for all currently supported types. :param dict attd: grouped attribute dictionary :param bytes payload : raw payload :param int offset: number of bytes in payload before repeating group :param int offsetend: number of bytes in payload after repeating group :return: number of repeats :rtype: int """ # pylint: disable=no-self-use lenpayload = len(payload) - offset - offsetend lengroup = 0 for _, val in attd.items(): lengroup += attsiz(val) return int(lenpayload / lengroup)
def val2bytes(val, att: str) -> bytes: """ Convert value to bytes for given UBX attribute type. :param object val: attribute value e.g. 25 :param str att: attribute type e.g. 'U004' :return: attribute value as bytes :rtype: bytes :raises: UBXTypeError """ if att == ubt.CH: # single variable-length string (e.g. INF-NOTICE) return val.encode("utf-8", "backslashreplace") atts = attsiz(att) if atttyp(att) in ("C", "X"): # byte or char valb = val elif atttyp(att) in ("E", "L", "U"): # unsigned integer valb = val.to_bytes(atts, byteorder="little", signed=False) elif atttyp(att) == "I": # signed integer valb = val.to_bytes(atts, byteorder="little", signed=True) elif att == ubt.R4: # single precision floating point valb = struct.pack("<f", val) elif att == ubt.R8: # double precision floating point valb = struct.pack("<d", val) else: raise ube.UBXTypeError(f"Unknown attribute type {att}") return valb
def bytes2val(valb: bytes, att: str) -> object: """ Convert bytes to value for given UBX attribute type. :param bytes valb: attribute value in byte format e.g. b'\\\\x19\\\\x00\\\\x00\\\\x00' :param str att: attribute type e.g. 'U004' :return: attribute value as int, float, str or bytes :rtype: object :raises: UBXTypeError """ if att == ubt.CH: # single variable-length string (e.g. INF-NOTICE) val = valb.decode("utf-8", "backslashreplace") elif atttyp(att) in ("X", "C"): val = valb elif atttyp(att) in ("E", "L", "U"): # unsigned integer val = int.from_bytes(valb, "little", signed=False) elif atttyp(att) == "I": # signed integer val = int.from_bytes(valb, "little", signed=True) elif att == ubt.R4: # single precision floating point val = struct.unpack("<f", valb)[0] elif att == ubt.R8: # double precision floating point val = struct.unpack("<d", valb)[0] elif atttyp(att) == "A": # array of bytes atts = attsiz(att) val = [] for i in range(atts): val.append(valb[i]) else: raise ube.UBXTypeError(f"Unknown attribute type {att}") return val
def _set_cfgval_attributes(self, offset: int, **kwargs): """ Parse CFG-VALGET payload to set of configuration key value pairs. :param int offset: payload offset :param **kwargs: payload key value pairs :raise UBXMessageError """ KEYLEN = 4 if "payload" in kwargs: self._payload = kwargs["payload"] else: raise ube.UBXMessageError( "CFG-VALGET message definitions must include payload keyword") cfglen = len(self._payload[offset:]) i = 0 while offset < cfglen: if i == KEYLEN: key = int.from_bytes(self._payload[offset:offset + KEYLEN], "little", signed=False) (keyname, att) = self.cfgkey2name(key) atts = attsiz(att) valb = self._payload[offset + KEYLEN:offset + KEYLEN + atts] val = self.bytes2val(valb, att) setattr(self, keyname, val) i = 0 offset += KEYLEN + atts else: i += 1
def _set_attribute_navdata( self, att: tuple, offset: int, key: str, index: list ) -> tuple: """ Parse and decode RXM-SFRBX nav data dwrds. TODO WORK IN PROGRESS :param tuple att: attribute group - tuple of (num repeats, attribute dict) :param int offset: payload offset in bytes :param str key: group keyword :param list index: repeating group index array :return: (offset, index[]) :rtype: tuple """ numr, attd = att # number of repeats, attribute dictionary gnssId = getattr(self, "gnssId") rng = getattr(self, numr) dwrds = [] for i in range(rng): index[-1] = i + 1 att = attd["dwrd"] # "U004" atts = attsiz(att) # 4 bytes valb = self._payload[offset : offset + atts] val = self.bytes2val(valb, att) dwrds.append(val) offset += atts attd = nav_decode(gnssId, dwrds) for (key, val) in attd.items(): setattr(self, key, val) return (offset, index)
def _set_attribute_single(self, att: object, offset: int, key: str, index: list, **kwargs) -> int: """ Set individual attribute value, applying scaling where appropriate. EITHER :param str att: attribute type string e.g. 'U002' OR :param list att: if scaled, list of [attribute type string, scaling factor float] :param int offset: payload offset in bytes :param str key: attribute keyword :param list index: repeating group index array :param kwargs: optional payload key/value pairs :return: offset :rtype: int """ # pylint: disable=no-member # if attribute is scaled scale = 1 if isinstance(att, list): scale = att[1] att = att[0] # if attribute is part of a (nested) repeating group, suffix name with index keyr = key for i in index: # one index for each nested level if i > 0: keyr += f"_{i:02d}" # determine attribute size (bytes) if att == ubt.CH: # variable length string atts = len(self._payload) else: atts = attsiz(att) # if payload keyword has been provided, # use the appropriate offset of the payload if "payload" in kwargs: valb = self._payload[offset:offset + atts] if scale == 1: val = bytes2val(valb, att) else: val = round(bytes2val(valb, att) * scale, ubt.SCALROUND) else: # if individual keyword has been provided, # set to provided value, else set to # nominal value val = kwargs.get(keyr, nomval(att)) if scale == 1: valb = val2bytes(val, att) else: valb = val2bytes(int(val / scale), att) self._payload += valb setattr(self, keyr, val) offset += atts return offset
def _set_attribute_single(self, att: str, offset: int, key: str, index: list, **kwargs) -> int: """ Set individual attribute value. :param str att: attribute type e.g. 'U002' :param int offset: payload offset :param str key: attribute keyword :param list index: repeating group index array :param kwargs: optional payload key/value pairs :return: offset :rtype: int """ # pylint: disable=no-member # if attribute is part of a (nested) repeating group, suffix name with index keyr = key for i in index: # one index for each nested level if i > 0: keyr = keyr + "_{0:0=2d}".format(i) # determine attribute size (bytes) if att == ubt.CH: # variable length string atts = len(self._payload) else: atts = attsiz(att) # if payload keyword has been provided, # use the appropriate offset of the payload if "payload" in kwargs: valb = self._payload[offset:offset + atts] val = self.bytes2val(valb, att) else: # if individual attribute keyword has been provided if keyr in kwargs: val = kwargs[keyr] # else set attribute to nominal value (0) else: if atttyp(att) in ("X", "C"): # byte or char val = b"\x00" * atts else: val = 0 valb = self.val2bytes(val, att) self._payload += valb setattr(self, keyr, val) offset += atts return offset
def _set_attribute_bits( self, bitfield: int, bfoffset: int, key: str, keyt: str, index: list, **kwargs, ) -> tuple: """ Set individual bit flag from bitfield. :param int bitfield: bitfield :param int bfoffset: bitfield offset in bits :param str key: attribute key name :param str keyt: key type e.g. 'U001' :param list index: repeating group index array :param kwargs: optional payload key/value pairs :return: (bitfield, bfoffset) :rtype: tuple """ # pylint: disable=no-member # if attribute is part of a (nested) repeating group, suffix name with index keyr = key for i in index: # one index for each nested level if i > 0: keyr += f"_{i:02d}" atts = attsiz(keyt) # determine flag size in bits if "payload" in kwargs: mask = pow(2, atts) - 1 val = (bitfield >> bfoffset) & mask else: val = kwargs.get(keyr, 0) bitfield = bitfield | (val << bfoffset) if key[0:8] != "reserved": # don't bother to set reserved bits setattr(self, keyr, val) bfoffset += atts return (bitfield, bfoffset)
def _set_attribute_bitfield( self, att: str, offset: int, index: list, **kwargs ) -> tuple: """ Parse bitfield attribute (type 'X'). :param str att: attribute type e.g. 'X002' :param int offset: payload offset in bytes :param str key: attribute key name :param list index: repeating group index array :param kwargs: optional payload key/value pairs :return: (offset, index[]) :rtype: tuple """ # pylint: disable=no-member bft, bfd = att # type of bitfield, bitfield dictionary bfs = attsiz(bft) # size of bitfield in bytes bfoffset = 0 # if payload keyword has been provided, # use the appropriate offset of the payload if "payload" in kwargs: bitfield = int.from_bytes(self._payload[offset : offset + bfs], "little") else: bitfield = 0 # process each flag in bitfield for key, keyt in bfd.items(): (bitfield, bfoffset) = self._set_attribute_bits( bitfield, bfoffset, key, keyt, index, **kwargs ) # update payload offset += bfs if "payload" not in kwargs: self._payload += bitfield.to_bytes(bfs, "little") return (offset, index)
def nomval(att: str) -> object: """ Get nominal value for given UBX attribute type. :param str att: attribute type e.g. 'U004' :return: attribute value as int, float, str or bytes :rtype: object :raises: UBXTypeError """ if att == "CH": val = "" elif atttyp(att) in ("X", "C"): val = b"\x00" * attsiz(att) elif atttyp(att) == "R": val = 0.0 elif atttyp(att) in ("E", "I", "L", "U"): val = 0 else: raise ube.UBXTypeError(f"Unknown attribute type {att}") return val
def _do_valset(self): """ Send a CFG-VALSET message. :return: valid entry flag :rtype: bool """ valid_entry = True att = atttyp(self._cfgatt.get()) atts = attsiz(self._cfgatt.get()) val = self._cfgval.get() layers = self._cfglayer.get() if layers == "BBR": layers = 2 elif layers == "FLASH": layers = 4 else: layers = 1 try: if att in ("C", "X"): # byte or char if len(val) == atts * 2: # 2 hex chars per byte val = bytearray.fromhex(val) else: valid_entry = False elif att in ("E", "U"): # unsigned integer val = int(val) if val < 0: valid_entry = False elif att == "L": # bool val = int(val) if val not in (0, 1): valid_entry = False elif att == "I": # signed integer val = int(val) elif att == "R": # floating point val = float(val) transaction = 0 cfgData = [ (self._cfgval_keyname, val), ] except ValueError: valid_entry = False if valid_entry: msg = UBXMessage.config_set(layers, transaction, cfgData) self.__app.serial_handler.serial_write(msg.serialize()) self._ent_val.configure(bg=ENTCOL) self._lbl_send_command.config(image=self._img_pending) self.__container.set_status("CFG-VALSET SET message sent", "blue") self.__container.set_pending(UBX_CFGVAL, ("ACK-ACK", "ACK-NAK")) else: self._ent_val.configure(bg=ERRCOL) self._lbl_send_command.config(image=self._img_warn) typ = ATTDICT[att] self.__container.set_status( ("INVALID ENTRY - must conform to parameter " f"type {att} ({typ}) and size {atts} bytes"), "red", ) return valid_entry