def _do_attributes(self, **kwargs): """Populate UBXMessage from named attribute keywords. Where a named attribute is absent, set to a nominal value (zeros or blanks). :param **kwargs: """ offset = 0 try: if len(kwargs) == 0: # if no kwargs, assume null payload self._payload = None else: pdict = self._get_dict() # get appropriate payload dict for key in pdict.keys(): # set each attribute in dict (offset, att) = self._set_attribute(offset, pdict, key, **kwargs) self._do_len_checksum() except (AttributeError, OverflowError, struct.error, TypeError, ValueError) as err: raise ube.UBXTypeError((f"Incorrect type for attribute '{key}' " f"in {self.mode2str(self._mode)} message " f"class {self.identity}")) from err except ube.UBXTypeError as err: raise ube.UBXTypeError((f"Undefined attribute type '{att}' " f"in {self.mode2str(self._mode)} message " f"class {self.identity}")) from err except KeyError as err: raise ube.UBXMessageError( (f"Undefined {self.mode2str(self._mode)} " f"message class={self._ubxClass}, " f"id={self._ubxID}")) from err
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] else: raise ube.UBXTypeError(f"Unknown attribute type {att}") return val
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 _do_attributes(self, **kwargs): """ Populate UBXMessage from named attribute keywords. Where a named attribute is absent, set to a nominal value (zeros or blanks). :param kwargs: optional payload key/value pairs :raises: UBXTypeError """ offset = 0 # payload offset in bytes index = [] # array of (nested) group indices try: if len(kwargs) == 0: # if no kwargs, assume null payload self._payload = None else: self._payload = kwargs.get("payload", b"") pdict = self._get_dict(** kwargs) # get appropriate payload dict for key in pdict: # process each attribute in dict (offset, index) = self._set_attribute(offset, pdict, key, index, **kwargs) self._do_len_checksum() except ( AttributeError, struct.error, TypeError, ValueError, ) as err: raise ube.UBXTypeError( (f"Incorrect type for attribute '{key}' " f"in {['GET', 'SET', 'POLL'][self._mode]} message " f"class {self.identity}")) from err except (OverflowError, ) as err: raise ube.UBXTypeError( (f"Overflow error for attribute '{key}' " f"in {['GET', 'SET', 'POLL'][self._mode]} message " f"class {self.identity}")) from err
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 _set_attribute(self, offset: int, pdict: dict, key: str, **kwargs) -> (int, str): """Recursive routine to populate individual payload attributes :param offset: int: :param pdict: dict: :param key: str: :param **kwargs: """ # pylint: disable=no-member # if repeating group, suffix keyword with index if self._index > 0: keyr = key + "_{0:0=2d}".format(self._index) else: keyr = key att = pdict[key] # get attribute type if isinstance( att, tuple): # attribute is a tuple i.e. a nested repeating group numr, attd = att if numr == "None": rng = self._calc_num_repeats(attd, self._payload, offset) else: rng = getattr(self, numr) for i in range(rng): self._index = i + 1 for key1 in attd.keys(): (offset, _) = self._set_attribute(offset, attd, key1, **kwargs) else: if att == ubt.CH: # INF message payload atts = len(self._payload) else: atts = int(att[1:3]) # if the entire payload has been provided, # use the appropriate section of the payload if "payload" in kwargs: self._payload = kwargs["payload"] val = self._payload[offset:offset + atts] if ( att == ubt.CH ): # attribute is a single variable-length string (e.g. INF-NOTICE) val = self._payload.decode("utf-8", "backslashreplace") elif att[0:1] in ("X", "C"): pass elif att[0:1] == "U": # unsigned integer val = int.from_bytes(val, "little", signed=False) elif att[0:1] == "I": # signed integer val = int.from_bytes(val, "little", signed=True) elif att == ubt.R4: # single precision floating point val = self.bytes_to_float(val) elif att == ubt.R8: # double precision floating point val = self.bytes_to_double(val) else: raise ube.UBXTypeError( f"Unknown attribute type {att} for key {key}") # else if individual attribute has been provided elif keyr in kwargs: val = kwargs[keyr] if att[0:1] in ("X", "C"): # byte or char valb = val elif att[0:1] == "U": # unsigned integer valb = val.to_bytes(atts, byteorder="little", signed=False) elif att[0:1] == "I": # signed integer valb = val.to_bytes(atts, byteorder="little", signed=True) elif att == ubt.R4: # single precision floating point valb = self.float_to_bytes(val) elif att == ubt.R8: # double precision floating point valb = self.double_to_bytes(val) else: raise ube.UBXTypeError( f"Unknown attribute type {att} for key {key}") self._payload += valb # else set individual attribute to nominal value else: if att[0:1] in ("X", "C"): # byte or char valb = b"\x00" * atts val = valb elif att[0:1] == "U": # unsigned integer val = 0 valb = val.to_bytes(atts, byteorder="little", signed=False) elif att[0:1] == "I": # signed integer val = 0 valb = val.to_bytes(atts, byteorder="little", signed=True) elif att == ubt.R4: # single precision floating point val = 0.0 valb = self.float_to_bytes(val) elif att == ubt.R8: # double precision floating point val = 0.0 valb = self.double_to_bytes(val) else: raise ube.UBXTypeError( f"Unknown attribute type {att} for key {key}") self._payload += valb if not isinstance(att, tuple): if self._index > 0: # add 2-digit suffix to repeating attribute names key = key + "_{0:0=2d}".format(self._index) setattr(self, key, val) offset += atts return (offset, att)