diff --git a/sw/ext/pprzlink b/sw/ext/pprzlink index 02cabd56e0..8b8d80fe12 160000 --- a/sw/ext/pprzlink +++ b/sw/ext/pprzlink @@ -1 +1 @@ -Subproject commit 02cabd56e026323bf8540db6166e38a6019af185 +Subproject commit 8b8d80fe12126b9fa62069db981701c4eed39274 diff --git a/sw/ground_segment/python/dashboard/radiowatchframe.py b/sw/ground_segment/python/dashboard/radiowatchframe.py index 8b3578c2b2..274f88ee52 100644 --- a/sw/ground_segment/python/dashboard/radiowatchframe.py +++ b/sw/ground_segment/python/dashboard/radiowatchframe.py @@ -8,7 +8,7 @@ import math import pynotify import pygame.mixer -sys.path.append(os.getenv("PAPARAZZI_HOME") + "/sw/lib/python") +sys.path.append(os.getenv("PAPARAZZI_HOME") + "/sw/ext/pprzlink/lib/v1.0/python") from ivy_msg_interface import IvyMessagesInterface diff --git a/sw/ground_segment/python/guided_mode_example.py b/sw/ground_segment/python/guided_mode_example.py index ac6279e10d..3a2d147f4f 100755 --- a/sw/ground_segment/python/guided_mode_example.py +++ b/sw/ground_segment/python/guided_mode_example.py @@ -9,9 +9,10 @@ from os import path, getenv # file is a reasonable substitute PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../'))) sys.path.append(PPRZ_SRC + "/sw/lib/python") +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") from ivy_msg_interface import IvyMessagesInterface -from pprz_msg.message import PprzMessage +from pprzlink.message import PprzMessage from settings_xml_parse import PaparazziACSettings from math import radians diff --git a/sw/ground_segment/python/messages_app/messagesframe.py b/sw/ground_segment/python/messages_app/messagesframe.py index f3190a13c6..2c475371b8 100644 --- a/sw/ground_segment/python/messages_app/messagesframe.py +++ b/sw/ground_segment/python/messages_app/messagesframe.py @@ -10,11 +10,12 @@ from os import path, getenv # file is a reasonable substitute PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../../'))) sys.path.append(PPRZ_SRC + "/sw/lib/python") +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") PPRZ_HOME = getenv("PAPARAZZI_HOME", PPRZ_SRC) from ivy_msg_interface import IvyMessagesInterface -from pprz_msg.message import PprzMessage +from pprzlink.message import PprzMessage WIDTH = 450 LABEL_WIDTH = 166 diff --git a/sw/ground_segment/python/move_waypoint_example.py b/sw/ground_segment/python/move_waypoint_example.py index 128b143060..093d01eee2 100755 --- a/sw/ground_segment/python/move_waypoint_example.py +++ b/sw/ground_segment/python/move_waypoint_example.py @@ -9,10 +9,10 @@ from time import sleep # if PAPARAZZI_SRC not set, then assume the tree containing this # file is a reasonable substitute PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../'))) -sys.path.append(PPRZ_SRC + "/sw/lib/python") +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") from ivy_msg_interface import IvyMessagesInterface -from pprz_msg.message import PprzMessage +from pprzlink.message import PprzMessage class WaypointMover(object): diff --git a/sw/ground_segment/python/real_time_plot/messagepicker.py b/sw/ground_segment/python/real_time_plot/messagepicker.py index ff33b7365c..81c3412eb7 100755 --- a/sw/ground_segment/python/real_time_plot/messagepicker.py +++ b/sw/ground_segment/python/real_time_plot/messagepicker.py @@ -10,10 +10,10 @@ from os import path, getenv # if PAPARAZZI_SRC not set, then assume the tree containing this # file is a reasonable substitute PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../../'))) -sys.path.append(PPRZ_SRC + "/sw/lib/python") +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") from ivy_msg_interface import IvyMessagesInterface -from pprz_msg.message import PprzMessage +from pprzlink.message import PprzMessage class Message(PprzMessage): diff --git a/sw/ground_segment/python/real_time_plot/plotpanel.py b/sw/ground_segment/python/real_time_plot/plotpanel.py index c4172af87a..9674f8c89e 100644 --- a/sw/ground_segment/python/real_time_plot/plotpanel.py +++ b/sw/ground_segment/python/real_time_plot/plotpanel.py @@ -15,9 +15,10 @@ import messagepicker # file is a reasonable substitute PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../../'))) sys.path.append(PPRZ_SRC + "/sw/lib/python") +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") import pprz_env -from pprz_msg import messages_xml_map +from pprzlink import messages_xml_map class PlotData: diff --git a/sw/ground_segment/python/redundant_link/link_combiner.py b/sw/ground_segment/python/redundant_link/link_combiner.py index 07aaacaf42..6f850b61b1 100755 --- a/sw/ground_segment/python/redundant_link/link_combiner.py +++ b/sw/ground_segment/python/redundant_link/link_combiner.py @@ -31,9 +31,10 @@ from ivy.std_api import * PPRZ_HOME = os.getenv("PAPARAZZI_HOME") sys.path.append(PPRZ_HOME + "/sw/lib/python") +sys.path.append(PPRZ_HOME + "/sw/ext/pprzlink/lib/v1.0/python") import pprz_env -from pprz_msg import messages_xml_map +from pprzlink import messages_xml_map class Circular_Buffer: def __init__(self, size): diff --git a/sw/ground_segment/python/udp_link/udp_link.py b/sw/ground_segment/python/udp_link/udp_link.py index 4fc09d3367..56c6d9b872 100755 --- a/sw/ground_segment/python/udp_link/udp_link.py +++ b/sw/ground_segment/python/udp_link/udp_link.py @@ -14,9 +14,10 @@ import time PPRZ_SRC = os.getenv("PAPARAZZI_SRC", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../../../'))) sys.path.append(PPRZ_SRC + "/sw/lib/python") +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") import pprz_env -from pprz_msg import messages_xml_map +from pprzlink import messages_xml_map PING_PERIOD = 5.0 STATUS_PERIOD = 1.0 diff --git a/sw/lib/python/ivy_msg_interface.py b/sw/lib/python/ivy_msg_interface.py deleted file mode 100644 index 48a691201b..0000000000 --- a/sw/lib/python/ivy_msg_interface.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import absolute_import, division, print_function - -from ivy.std_api import * -from ivy.ivy import IvyIllegalStateError -import logging -import os -import sys -import re -import pprz_env - -sys.path.append(pprz_env.PAPARAZZI_SRC + "/sw/lib/python") - -from pprz_msg.message import PprzMessage -from pprz_msg import messages_xml_map - - -class IvyMessagesInterface(object): - def __init__(self, callback=None, init=True, verbose=False, bind_regex='(.*)', ivy_bus=pprz_env.IVY_BUS): - self.callback = callback - self.ivy_id = 0 - self.verbose = verbose - self.ivy_bus = ivy_bus - # make sure all messages are parsed before we start creating them in callbacks - messages_xml_map.parse_messages() - self.init_ivy(init, bind_regex) - - def stop(self): - IvyUnBindMsg(self.ivy_id) - - def shutdown(self): - try: - IvyStop() - self.stop() - except IvyIllegalStateError as e: - print(e) - - def __del__(self): - try: - self.shutdown() - except: - pass - - def init_ivy(self, init=True, bind_regex='(.*)'): - if init: - IvyInit("Messages %i" % os.getpid(), "READY", 0, lambda x,y: y, lambda x,y: y) - logging.getLogger('Ivy').setLevel(logging.WARN) - IvyStart(self.ivy_bus) - self.ivy_id = IvyBindMsg(self.on_ivy_msg, bind_regex) - - def on_ivy_msg(self, agent, *larg): - """ Split ivy message up into the separate parts - Basically parts/args in string are separated by space, but char array can also contain a space: - |f,o,o, ,b,a,r| in old format or "foo bar" in new format - """ - # return if no callback is set - if self.callback is None: - return - - # first split on array delimiters - l = re.split('([|\"][^|\"]*[|\"])', larg[0]) - # strip spaces and filter out emtpy strings - l = [str.strip(s) for s in l if str.strip(s) is not ''] - data = [] - for s in l: - # split non-array strings further up - if '|' not in s and '"' not in s: - data += s.split(' ') - else: - data.append(s) - # ignore ivy message with less than 3 elements - if len(data) < 3: - return - - # check which message class it is - msg_name = data[1] - msg_class, msg_name = messages_xml_map.find_msg_by_name(msg_name) - if msg_class is None: - print("Ignoring unknown message " + larg[0]) - return - # pass non-telemetry messages with ac_id 0 - if msg_class == "telemetry": - try: - ac_id = int(data[0]) - except ValueError: - print("ignoring message " + larg[0]) - sys.stdout.flush() - else: - ac_id = 0 - values = list(filter(None, data[2:])) - msg = PprzMessage(msg_class, msg_name) - msg.set_values(values) - self.callback(ac_id, msg) - - def send_raw_datalink(self, msg): - if not isinstance(msg, PprzMessage): - print("Can only send PprzMessage") - return - if "datalink" not in msg.msg_class: - print("Message to embed in RAW_DATALINK needs to be of 'datalink' class") - return - raw = PprzMessage("ground", "RAW_DATALINK") - raw['ac_id'] = msg['ac_id'] - raw['message'] = msg.to_csv() - self.send(raw) - - def send(self, msg, ac_id=None): - if isinstance(msg, PprzMessage): - if "telemetry" in msg.msg_class: - if ac_id is None: - print("ac_id needed to send telemetry message.") - else: - IvySendMsg("%d %s %s" % (ac_id, msg.name, msg.payload_to_ivy_string())) - else: - IvySendMsg("%s %s %s" % (msg.msg_class, msg.name, msg.payload_to_ivy_string())) - else: - IvySendMsg(msg) diff --git a/sw/lib/python/pprz_msg/__init__.py b/sw/lib/python/pprz_msg/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sw/lib/python/pprz_msg/message.py b/sw/lib/python/pprz_msg/message.py deleted file mode 100644 index 2ca5679a74..0000000000 --- a/sw/lib/python/pprz_msg/message.py +++ /dev/null @@ -1,229 +0,0 @@ -""" -Paparazzi message representation - -""" - -from __future__ import division, print_function -import sys -import json -import struct -import messages_xml_map - - -class PprzMessageError(Exception): - def __init__(self, message, inner_exception=None): - self.message = message - self.inner_exception = inner_exception - self.exception_info = sys.exc_info() - - def __str__(self): - return self.message - - -class PprzMessage(object): - """base Paparazzi message class""" - - def __init__(self, class_name, msg): - self._class_name = class_name - if isinstance(msg, int): - self._id = msg - self._name = messages_xml_map.get_msg_name(class_name, msg) - else: - self._name = msg - self._id = messages_xml_map.get_msg_id(class_name, msg) - self._fieldnames = messages_xml_map.get_msg_fields(class_name, self._name) - self._fieldtypes = messages_xml_map.get_msg_fieldtypes(class_name, self._id) - self._fieldcoefs = messages_xml_map.get_msg_fieldcoefs(class_name, self._id) - self._fieldvalues = [] - # set empty values according to type - for t in self._fieldtypes: - if t == "char[]": - self._fieldvalues.append('') - elif '[' in t: - self._fieldvalues.append([0]) - else: - self._fieldvalues.append(0) - - @property - def name(self): - """Get the message name.""" - return self._name - - @property - def msg_id(self): - """Get the message id.""" - return self._id - - @property - def msg_class(self): - """Get the message class.""" - return self._class_name - - @property - def fieldnames(self): - """Get list of field names.""" - return self._fieldnames - - @property - def fieldvalues(self): - """Get list of field values.""" - return self._fieldvalues - - @property - def fieldtypes(self): - """Get list of field types.""" - return self._fieldtypes - - @property - def fieldcoefs(self): - """Get list of field coefs.""" - return self._fieldcoefs - - def fieldbintypes(self, t): - """Get type and length for binary format""" - data_types = { - 'float': ['f', 4], - 'double': ['d', 8], - 'uint8': ['B', 1], - 'uint16': ['H', 2], - 'uint32': ['L', 4], - 'int8': ['b', 1], - 'int16': ['h', 2], - 'int32': ['l', 4], - 'char': ['c', 1] - } - base_type = t.split('[')[0] - return data_types[base_type] - - def get_field(self, idx): - """Get field value by index.""" - return self._fieldvalues[idx] - - def __getattr__(self, attr): - # Try to dynamically return the field value for the given name - for idx, f in enumerate(self.fieldnames): - if f == attr: - return self.fieldvalues[idx] - raise AttributeError("No such attribute %s" % attr) - - def __getitem__(self, key): - # Try to dynamically return the field value for the given name - for idx, f in enumerate(self.fieldnames): - if f == key: - return self.fieldvalues[idx] - raise AttributeError("Msg %s has no field of name %s" % (self.name, key)) - - def __setitem__(self, key, value): - self.set_value_by_name(key, value) - - def set_values(self, values): - #print("msg %s: %s" % (self.name, ", ".join(self.fieldnames))) - if len(values) == len(self.fieldnames): - self._fieldvalues = values - else: - raise PprzMessageError("Error: Msg %s has %d fields, tried to set %i values" % - (self.name, len(self.fieldnames), len(values))) - - def set_value_by_name(self, name, value): - # Try to set a value from its name - for idx, f in enumerate(self.fieldnames): - if f == name: - self._fieldvalues[idx] = value - return - raise AttributeError("Msg %s has no field of name %s" % (self.name, name)) - - def __str__(self): - ret = '%s.%s {' % (self.msg_class, self.name) - for idx, f in enumerate(self.fieldnames): - ret += '%s : %s, ' % (f, self.fieldvalues[idx]) - ret = ret[0:-2] + '}' - return ret - - def to_dict(self, payload_only=False): - d = {} - if not payload_only: - d['msgname'] = self.name - d['msgclass'] = self.msg_class - for idx, f in enumerate(self.fieldnames): - d[f] = self.fieldvalues[idx] - return d - - def to_json(self, payload_only=False): - return json.dumps(self.to_dict(payload_only)) - - def to_csv(self, payload_only=False): - """ return message as CSV string for use with RAW_DATALINK - msg_name;field1;field2; - """ - return str(self.name) + ';' + self.payload_to_ivy_string(sep=';') - - def payload_to_ivy_string(self, sep=' '): - ivy_str = '' - for idx, t in enumerate(self.fieldtypes): - if "char[" in t: - ivy_str += '"' + self.fieldvalues[idx] + '"' - elif '[' in t: - ivy_str += ','.join([str(x) for x in self.fieldvalues[idx]]) - else: - ivy_str += str(self.fieldvalues[idx]) - ivy_str += sep - return ivy_str - - def payload_to_binary(self): - struct_string = "<" - data = [] - length = 0 - for idx, t in enumerate(self.fieldtypes): - bin_type = self.fieldbintypes(t) - struct_string += bin_type[0] - array_length = 1 - if "char[" in t: - array_length = len(self.fieldvalues[idx]) - for c in self.fieldvalues[idx]: - data.append(int(c)) - elif '[' in t: - array_length = len(self.fieldvalues[idx]) - for x in self.fieldvalues[idx]: - data.append(x) - else: - data.append(self.fieldvalues[idx]) - length += bin_type[1] * array_length - msg = struct.pack(struct_string, *data) - return msg - - def binary_to_payload(self, data): - msg_offset = 0 - values = [] - for idx, t in enumerate(self.fieldtypes): - bin_type = self.fieldbintypes(t) - if '[' in t: - array_length = data[msg_offset] - msg_offset += 1 - array_value = [] - for count in range(0, array_length): - array_value.append(struct.unpack('<' + bin_type[0], data[msg_offset:msg_offset + bin_type[1]])[0]) - msg_offset = msg_offset + bin_type[1] - values.append(array_value) - else: - value = struct.unpack('<' + bin_type[0], data[msg_offset:msg_offset + bin_type[1]])[0] - msg_offset = msg_offset + bin_type[1] - values.append(value) - self.set_values(values) - - -def test(): - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("-f", "--file", help="path to messages.xml file") - parser.add_argument("-c", "--class", help="message class", dest="msg_class", default="telemetry") - args = parser.parse_args() - messages_xml_map.parse_messages(args.file) - messages = [PprzMessage(args.msg_class, n) for n in messages_xml_map.get_msgs(args.msg_class)] - print("Listing %i messages in '%s' msg_class" % (len(messages), args.msg_class)) - for msg in messages: - print(msg) - - -if __name__ == '__main__': - test() diff --git a/sw/lib/python/pprz_msg/messages_xml_map.py b/sw/lib/python/pprz_msg/messages_xml_map.py deleted file mode 100755 index c0c78b0b0b..0000000000 --- a/sw/lib/python/pprz_msg/messages_xml_map.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env python - -from __future__ import absolute_import, print_function - -import os - -# if PAPARAZZI_HOME not set, then assume the tree containing this -# file is a reasonable substitute -PPRZ_HOME = os.getenv("PAPARAZZI_HOME", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../../..'))) - -default_messages_file = '%s/conf/messages.xml' % PPRZ_HOME - -message_dictionary = {} -message_dictionary_types = {} -message_dictionary_coefs = {} -message_dictionary_id_name = {} -message_dictionary_name_id = {} - - -class MessagesNotFound(Exception): - def __init__(self, filename): - self.filename = filename - - def __str__(self): - return "messages file " + repr(self.filename) + " not found" - - -def parse_messages(messages_file=''): - if not messages_file: - messages_file = default_messages_file - if not os.path.isfile(messages_file): - raise MessagesNotFound(messages_file) - #print("Parsing %s" % messages_file) - from lxml import etree - tree = etree.parse(messages_file) - for the_class in tree.xpath("//msg_class[@name]"): - class_name = the_class.attrib['name'] - if class_name not in message_dictionary: - message_dictionary_id_name[class_name] = {} - message_dictionary_name_id[class_name] = {} - message_dictionary[class_name] = {} - message_dictionary_types[class_name] = {} - message_dictionary_coefs[class_name] = {} - for the_message in the_class.xpath("message[@name]"): - message_name = the_message.attrib['name'] - if 'id' in the_message.attrib: - message_id = the_message.attrib['id'] - else: - message_id = the_message.attrib['ID'] - if message_id[0:2] == "0x": - message_id = int(message_id, 16) - else: - message_id = int(message_id) - - message_dictionary_id_name[class_name][message_id] = message_name - message_dictionary_name_id[class_name][message_name] = message_id - - # insert this message into our dictionary as a list with room for the fields - message_dictionary[class_name][message_name] = [] - message_dictionary_types[class_name][message_id] = [] - message_dictionary_coefs[class_name][message_id] = [] - - for the_field in the_message.xpath('field[@name]'): - # for now, just save the field names -- in the future maybe expand this to save a struct? - message_dictionary[class_name][message_name].append(the_field.attrib['name']) - message_dictionary_types[class_name][message_id].append(the_field.attrib['type']) - try: - message_dictionary_coefs[class_name][message_id].append(float(the_field.attrib['alt_unit_coef'])) - except KeyError: - # print("no such key") - message_dictionary_coefs[class_name][message_id].append(1.) - - -def find_msg_by_name(name): - if not message_dictionary: - parse_messages() - for msg_class in message_dictionary: - if name in message_dictionary[msg_class]: - #print("found msg name %s in class %s" % (name, msg_class)) - return msg_class, name - print("Error: msg_name %s not found." % name) - return None, None - - -def get_msgs(msg_class): - if not message_dictionary: - parse_messages() - if msg_class in message_dictionary: - return message_dictionary[msg_class] - else: - print("Error: msg_class %s not found." % msg_class) - return [] - - -def get_msg_name(msg_class, msg_id): - if not message_dictionary: - parse_messages() - if msg_class in message_dictionary: - if msg_id in message_dictionary_id_name[msg_class]: - return message_dictionary_id_name[msg_class][msg_id] - else: - print("Error: msg_id %d not found in msg_class %s." % (msg_id, msg_class)) - else: - print("Error: msg_class %s not found." % msg_class) - return "" - - -def get_msg_fields(msg_class, msg_name): - if not message_dictionary: - parse_messages() - if msg_class in message_dictionary: - if msg_name in message_dictionary[msg_class]: - return message_dictionary[msg_class][msg_name] - else: - print("Error: msg_name %s not found in msg_class %s." % (msg_name, msg_class)) - else: - print("Error: msg_class %s not found." % msg_class) - return [] - - -def get_msg_id(msg_class, msg_name): - if not message_dictionary: - parse_messages() - try: - return message_dictionary_name_id[msg_class][msg_name] - except KeyError: - print("Error: msg_name %s not found in msg_class %s." % (msg_name, msg_class)) - return 0 - - -def get_msg_fieldtypes(msg_class, msg_id): - if not message_dictionary: - parse_messages() - if msg_class in message_dictionary_types: - if msg_id in message_dictionary_types[msg_class]: - return message_dictionary_types[msg_class][msg_id] - else: - print("Error: message with ID %d not found in msg_class %s." % (msg_id, msg_class)) - else: - print("Error: msg_class %s not found." % msg_class) - return [] - -def get_msg_fieldcoefs(msg_class, msg_id): - if not message_dictionary: - parse_messages() - if msg_class in message_dictionary_coefs: - if msg_id in message_dictionary_coefs[msg_class]: - return message_dictionary_coefs[msg_class][msg_id] - else: - print("Error: message with ID %d not found in msg_class %s." % (msg_id, msg_class)) - else: - print("Error: msg_class %s not found." % msg_class) - return [] - - -def test(): - import argparse - parser = argparse.ArgumentParser() - parser.add_argument("-f", "--file", help="path to messages.xml file") - parser.add_argument("-l", "--list", help="list parsed messages", action="store_true", dest="list_messages") - parser.add_argument("-c", "--class", help="message class", dest="msg_class", default="telemetry") - args = parser.parse_args() - parse_messages(args.file) - if args.list_messages: - print("Listing %i messages in '%s' msg_class" % (len(message_dictionary[args.msg_class]), args.msg_class)) - for msg_name, msg_fields in message_dictionary[args.msg_class].iteritems(): - print(msg_name + ": " + ", ".join(msg_fields)) - -if __name__ == '__main__': - test() diff --git a/sw/lib/python/pprz_msg/pprz_transport.py b/sw/lib/python/pprz_msg/pprz_transport.py deleted file mode 100644 index 4de72990fa..0000000000 --- a/sw/lib/python/pprz_msg/pprz_transport.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Paparazzi transport encoding utilities - -""" - -from __future__ import absolute_import, division, print_function -import struct -from .message import PprzMessage - -# use Enum from python 3.4 if available (https://www.python.org/dev/peps/pep-0435/) -# (backports as enum34 on pypi) -try: - from enum import Enum -except ImportError: - Enum = object - -STX = 0x99 -STX_TS = 0x98 - -class PprzParserState(Enum): - WaitSTX = 1 - GotSTX = 2 - GotLength = 3 - GotPayload = 4 - GotCRC1 = 5 - -class PprzTransport(object): - """parser for binary Paparazzi messages""" - def __init__(self, msg_class='telemetry'): - self.msg_class = msg_class - self.reset_parser() - - def reset_parser(self): - self.state = PprzParserState.WaitSTX - self.length = 0 - self.buf = [] - self.ck_a = 0 - self.ck_b = 0 - self.idx = 0 - - def parse_byte(self, c): - """parse new byte, return True when a new full message is available""" - b = struct.unpack("