Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
    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