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_cfgval(self, offset: int, **kwargs): """ Parse CFG-VALGET payload to set of configuration key value pairs. :param int offset: payload offset :param **kwargs: optional payload key/value pairs :raises: 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) = cfgkey2name(key) atts = attsiz(att) valb = self._payload[offset + KEYLEN:offset + KEYLEN + atts] val = bytes2val(valb, att) setattr(self, keyname, val) i = 0 offset += KEYLEN + atts else: i += 1
def testBytes2Val(self): # test conversion of bytes to value INPUTS = [ (b"\x29\x09", ubt.U2), (b"\x29\x09", ubt.E2), (b"\x01", ubt.L), (b"\xdb\x30\xdc\xff", ubt.I4), (b"\x44\x55", ubt.X2), (b"\xd7\xfc\xb8\x41", ubt.R4), (b"\x1f\xc1\x37\xdd\x9a\x1f\x37\xc0", ubt.R8), (b"\x01\x02\x03\x04\x05", "A005"), ] EXPECTED_RESULTS = [ 2345, 2345, 1, -2346789, b"\x44\x55", 23.12345678, -23.12345678912345, [1, 2, 3, 4, 5], ] for i, inp in enumerate(INPUTS): (valb, att) = inp res = bytes2val(valb, att) if att == ubt.R4: self.assertAlmostEqual(res, EXPECTED_RESULTS[i], 6) elif att == ubt.R8: self.assertAlmostEqual(res, EXPECTED_RESULTS[i], 14) else: self.assertEqual(res, EXPECTED_RESULTS[i])
def length(self) -> int: """ Payload length getter. :return: payload length as integer :rtype: int """ return bytes2val(self._length, ubt.U2)
def parse(message: bytes, **kwargs) -> object: """ Parse UBX byte stream to UBXMessage object. Includes option to validate incoming payload length and checksum (the UBXMessage constructor can calculate and assign its own values anyway). :param bytes message: binary message to parse :param int validate: (kwarg) validate cksum (VALCKSUM (1)=True (default), VALNONE (0)=False) :param int msgmode: (kwarg) message mode (0=GET (default), 1=SET, 2=POLL) :param bool parsebitfield: (kwarg) parse bitfields True (default)/False :return: UBXMessage object :rtype: UBXMessage :raises: UBXParseError (if data stream contains invalid data or unknown message type) """ msgmode = kwargs.get("msgmode", ubt.GET) validate = kwargs.get("validate", ubt.VALCKSUM) parsebf = kwargs.get("parsebitfield", True) if msgmode not in (0, 1, 2): raise ube.UBXParseError( f"Invalid message mode {msgmode} - must be 0, 1 or 2") lenm = len(message) hdr = message[0:2] clsid = message[2:3] msgid = message[3:4] lenb = message[4:6] if lenb == b"\x00\x00": payload = None leni = 0 else: payload = message[6:lenm - 2] leni = len(payload) ckm = message[lenm - 2:lenm] if payload is not None: ckv = calc_checksum(clsid + msgid + lenb + payload) else: ckv = calc_checksum(clsid + msgid + lenb) if validate & ubt.VALCKSUM: if hdr != ubt.UBX_HDR: raise ube.UBXParseError((f"Invalid message header {hdr}" f" - should be {ubt.UBX_HDR}")) if leni != bytes2val(lenb, ubt.U2): raise ube.UBXParseError( (f"Invalid payload length {lenb}" f" - should be {val2bytes(leni, ubt.U2)}")) if ckm != ckv: raise ube.UBXParseError((f"Message checksum {ckm}" f" invalid - should be {ckv}")) try: if payload is None: return UBXMessage(clsid, msgid, msgmode) return UBXMessage( clsid, msgid, msgmode, payload=payload, parsebitfield=parsebf, ) except KeyError as err: modestr = ["GET", "SET", "POLL"][msgmode] raise ube.UBXParseError(( f"Unknown message type clsid {clsid}, msgid {msgid}, mode {modestr}" )) from err
def testBytes2Val(self): # test invalid attribute type EXPECTED_ERROR = "Unknown attribute type Z001" with self.assertRaisesRegex(UBXTypeError, EXPECTED_ERROR): bytes2val(b"\x01", "Z001")