[pprzlink] allow to compile paparazzi with PPRZLINK v2 (#2083)

To activate, build project with correct option:
 PPRZLINK_LIB_VERSION=2.0 make
(note that 'make clean' is usualy required before that)
This commit is contained in:
Gautier Hattenberger
2017-06-29 10:38:30 +02:00
committed by Michal Podhradsky
parent 2aabe921b3
commit 6a72c5da84
6 changed files with 220 additions and 278 deletions
+9 -4
View File
@@ -78,6 +78,11 @@ LOGALIZER=sw/logalizer
SUBDIRS = $(PPRZCENTER) $(MISC) $(LOGALIZER) sw/tools
#
# Communication protocol version
#
PPRZLINK_LIB_VERSION ?= 1.0
#
# xml files used as input for header generation
#
@@ -91,7 +96,7 @@ XSENS_XML = $(CONF)/xsens_MTi-G.xml
# generated header files
#
PPRZLINK_DIR=sw/ext/pprzlink
PPRZLINK_INSTALL=$(PAPARAZZI_HOME)/var/lib/ocaml
PPRZLINK_INSTALL=$(PAPARAZZI_HOME)/var/lib
MESSAGES_INSTALL=$(PAPARAZZI_HOME)/var
UBX_PROTOCOL_H=$(STATICINCLUDE)/ubx_protocol.h
MTK_PROTOCOL_H=$(STATICINCLUDE)/mtk_protocol.h
@@ -137,7 +142,7 @@ static: cockpit tmtc generators sim_static joystick static_h
libpprzlink:
$(MAKE) -C $(EXT) pprzlink.update
$(Q)Q=$(Q) DESTDIR=$(PPRZLINK_INSTALL) $(MAKE) -C $(PPRZLINK_DIR) libpprzlink-install
$(Q)Q=$(Q) DESTDIR=$(PPRZLINK_INSTALL) PPRZLINK_LIB_VERSION=${PPRZLINK_LIB_VERSION} $(MAKE) -C $(PPRZLINK_DIR) libpprzlink-install
libpprz: libpprzlink _save_build_version
$(MAKE) -C $(LIB)/ocaml
@@ -191,10 +196,10 @@ pprzlink_protocol :
$(Q)test -d $(STATICLIB) || mkdir -p $(STATICLIB)
ifeq ("$(wildcard $(CUSTOM_MESSAGES_XML))","")
@echo GENERATE $@ with default messages
$(Q)Q=$(Q) MESSAGES_INSTALL=$(MESSAGES_INSTALL) VALIDATE_XML=FALSE $(MAKE) -C $(PPRZLINK_DIR) pymessages
$(Q)Q=$(Q) MESSAGES_INSTALL=$(MESSAGES_INSTALL) VALIDATE_XML=FALSE PPRZLINK_LIB_VERSION=${PPRZLINK_LIB_VERSION} $(MAKE) -C $(PPRZLINK_DIR) pymessages
else
@echo GENERATE $@ with custome messages from $(CUSTOM_MESSAGES_XML)
$(Q)Q=$(Q) MESSAGES_XML=$(CUSTOM_MESSAGES_XML) MESSAGES_INSTALL=$(MESSAGES_INSTALL) $(MAKE) -C $(PPRZLINK_DIR) pymessages
$(Q)Q=$(Q) MESSAGES_XML=$(CUSTOM_MESSAGES_XML) MESSAGES_INSTALL=$(MESSAGES_INSTALL) PPRZLINK_LIB_VERSION=${PPRZLINK_LIB_VERSION} $(MAKE) -C $(PPRZLINK_DIR) pymessages
endif
+99 -59
View File
@@ -66,81 +66,121 @@ void dl_parse_msg(struct link_device *dev, struct transport_tx *trans, uint8_t *
}
}
} else {
/* parse telemetry messages coming from ground station */
switch (msg_id) {
case DL_PING: {
pprz_msg_send_PONG(trans, dev, AC_ID);
}
break;
#if PPRZLINK_DEFAULT_VER == 2
// Check that the message is really a datalink message
if (pprzlink_get_msg_class_id(buf) == DL_datalink_CLASS_ID) {
#endif
/* parse datalink messages coming from ground station */
switch (msg_id) {
case DL_PING: {
#if PPRZLINK_DEFAULT_VER == 2
// Reply to the sender of the message
struct pprzlink_msg msg;
msg.trans = trans;
msg.dev = dev;
msg.sender_id = AC_ID;
msg.receiver_id = sender_id;
msg.component_id = 0;
pprzlink_msg_send_PONG(&msg);
#else
pprz_msg_send_PONG(trans, dev, AC_ID);
#endif
}
break;
case DL_SETTING : {
if (DL_SETTING_ac_id(buf) != AC_ID) { break; }
uint8_t i = DL_SETTING_index(buf);
float var = DL_SETTING_value(buf);
DlSetting(i, var);
pprz_msg_send_DL_VALUE(trans, dev, AC_ID, &i, &var);
}
break;
case DL_SETTING : {
if (DL_SETTING_ac_id(buf) != AC_ID) { break; }
uint8_t i = DL_SETTING_index(buf);
float var = DL_SETTING_value(buf);
DlSetting(i, var);
#if PPRZLINK_DEFAULT_VER == 2
// Reply to the sender of the message
struct pprzlink_msg msg;
msg.trans = trans;
msg.dev = dev;
msg.sender_id = AC_ID;
msg.receiver_id = sender_id;
msg.component_id = 0;
pprzlink_msg_send_DL_VALUE(&msg, &i, &var);
#else
pprz_msg_send_DL_VALUE(trans, dev, AC_ID, &i, &var);
#endif
}
break;
case DL_GET_SETTING : {
if (DL_GET_SETTING_ac_id(buf) != AC_ID) { break; }
uint8_t i = DL_GET_SETTING_index(buf);
float val = settings_get_value(i);
pprz_msg_send_DL_VALUE(trans, dev, AC_ID, &i, &val);
}
break;
case DL_GET_SETTING : {
if (DL_GET_SETTING_ac_id(buf) != AC_ID) { break; }
uint8_t i = DL_GET_SETTING_index(buf);
float val = settings_get_value(i);
#if PPRZLINK_DEFAULT_VER == 2
// Reply to the sender of the message
struct pprzlink_msg msg;
msg.trans = trans;
msg.dev = dev;
msg.sender_id = AC_ID;
msg.receiver_id = sender_id;
msg.component_id = 0;
pprzlink_msg_send_DL_VALUE(&msg, &i, &val);
#else
pprz_msg_send_DL_VALUE(trans, dev, AC_ID, &i, &val);
#endif
}
break;
#ifdef RADIO_CONTROL_TYPE_DATALINK
case DL_RC_3CH :
#ifdef RADIO_CONTROL_DATALINK_LED
LED_TOGGLE(RADIO_CONTROL_DATALINK_LED);
#endif
parse_rc_3ch_datalink(
DL_RC_3CH_throttle_mode(buf),
DL_RC_3CH_roll(buf),
DL_RC_3CH_pitch(buf));
break;
case DL_RC_4CH :
if (DL_RC_4CH_ac_id(buf) == AC_ID) {
case DL_RC_3CH :
#ifdef RADIO_CONTROL_DATALINK_LED
LED_TOGGLE(RADIO_CONTROL_DATALINK_LED);
#endif
parse_rc_4ch_datalink(DL_RC_4CH_mode(buf),
DL_RC_4CH_throttle(buf),
DL_RC_4CH_roll(buf),
DL_RC_4CH_pitch(buf),
DL_RC_4CH_yaw(buf));
}
break;
parse_rc_3ch_datalink(
DL_RC_3CH_throttle_mode(buf),
DL_RC_3CH_roll(buf),
DL_RC_3CH_pitch(buf));
break;
case DL_RC_4CH :
if (DL_RC_4CH_ac_id(buf) == AC_ID) {
#ifdef RADIO_CONTROL_DATALINK_LED
LED_TOGGLE(RADIO_CONTROL_DATALINK_LED);
#endif
parse_rc_4ch_datalink(DL_RC_4CH_mode(buf),
DL_RC_4CH_throttle(buf),
DL_RC_4CH_roll(buf),
DL_RC_4CH_pitch(buf),
DL_RC_4CH_yaw(buf));
}
break;
#endif // RADIO_CONTROL_TYPE_DATALINK
#if USE_GPS
case DL_GPS_INJECT : {
// Check if the GPS is for this AC
if (DL_GPS_INJECT_ac_id(buf) != AC_ID) { break; }
case DL_GPS_INJECT : {
// Check if the GPS is for this AC
if (DL_GPS_INJECT_ac_id(buf) != AC_ID) { break; }
// GPS parse data
gps_inject_data(
DL_GPS_INJECT_packet_id(buf),
DL_GPS_INJECT_data_length(buf),
DL_GPS_INJECT_data(buf)
);
}
break;
// GPS parse data
gps_inject_data(
DL_GPS_INJECT_packet_id(buf),
DL_GPS_INJECT_data_length(buf),
DL_GPS_INJECT_data(buf)
);
}
break;
#if USE_GPS_UBX_RTCM
case DL_RTCM_INJECT : {
// GPS parse data
gps_inject_data(DL_RTCM_INJECT_packet_id(buf),
DL_RTCM_INJECT_data_length(buf),
DL_RTCM_INJECT_data(buf));
}
break;
case DL_RTCM_INJECT : {
// GPS parse data
gps_inject_data(DL_RTCM_INJECT_packet_id(buf),
DL_RTCM_INJECT_data_length(buf),
DL_RTCM_INJECT_data(buf));
}
break;
#endif // USE_GPS_UBX_RTCM
#endif // USE_GPS
default:
break;
default:
break;
}
#if PPRZLINK_DEFAULT_VER == 2
}
#endif
}
/* Parse firmware specific datalink */
firmware_parse_msg(dev, trans, buf);
@@ -36,10 +36,6 @@
#include "std.h"
#include "pprzlink/dl_protocol.h"
/* Message id helpers */
#define SenderIdOfPprzMsg(x) (x[0])
#define IdOfPprzMsg(x) (x[1])
/** Datalink kinds */
#define PPRZ 1
#define XBEE 2
+111 -207
View File
@@ -1,155 +1,147 @@
#!/usr/bin/env python
from ivy.std_api import *
import socket
import struct
import os
import logging
import sys
import threading
import time
# if PAPARAZZI_SRC not set, then assume the tree containing this
# file is a reasonable substitute
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")
PAPARAZZI_HOME = os.getenv("PAPARAZZI_HOME", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),'../../../../')))
sys.path.append(PAPARAZZI_HOME + "/var/lib/python")
import pprz_env
from pprzlink import messages_xml_map
from ivy.std_api import *
import threading
import time
import pprzlink.udp
import pprzlink.ivy
import pprzlink.messages_xml_map as messages_xml_map
import pprzlink.message as message
DEFAULT_BROADCAST= "127.255.255.255"
REPEAT="Repeat"
IP_BCAST="IPBcast"
PING_PERIOD = 5.0
STATUS_PERIOD = 1.0
STX = 0x99
STX_TS = 0x98
DATALINK_PORT = 4243
DOWNLINK_PORT = 4242
class DownLinkStatus():
def __init__(self, ac_id, address):
self.ac_id = ac_id
self.address = address
self.rx_bytes = 0
self.rx_msgs = 0
self.tx_msgs = 0
self.run_time = 0
self.last_rx_bytes = 0
self.last_rx_msgs = 0
self.last_msg_time = 0
self.last_ping_time = 0
self.last_pong_time = 0
class IvyUdpLink():
def __init__(self):
self.InitIvy()
self.ivy_id = 0
self.status_timer = threading.Timer(STATUS_PERIOD, self.sendStatus)
self.ping_timer = threading.Timer(STATUS_PERIOD, self.sendPing)
class UDPLink:
def __init__(self,opts):
messages_xml_map.parse_messages()
self.run_event = threading.Event()
self.uplink_port = opts.uplink_port
self.downlink_port = opts.downlink_port
self.udp = pprzlink.udp.UdpMessagesInterface(self.proccess_downlink_msg, False, self.uplink_port, self.downlink_port)
self.ivy = pprzlink.ivy.IvyMessagesInterface("UDPLink", True, False, opts.bus)
self.ac_downlink_status = {}
self.rx_err = 0
self.status_timer = threading.Timer(STATUS_PERIOD, self.sendStatus)
self.ping_timer = threading.Timer(PING_PERIOD, self.sendPing)
self.bcast_method = opts.broadcast_method
self.bcast_addr = opts.broadcast_address
messages_xml_map.parse_messages()
self.data_types = {'float': ['f', 4],
'uint8': ['B', 1],
'uint16': ['H', 2],
'uint32': ['L', 4],
'int8': ['b', 1],
'int16': ['h', 2],
'int32': ['l', 4]
}
def updateStatus(self, ac_id, length, address, isPong):
if not self.ac_downlink_status.has_key(ac_id):
self.ac_downlink_status[ac_id] = DownLinkStatus(ac_id, address)
self.ac_downlink_status[ac_id].rx_msgs += 1
self.ac_downlink_status[ac_id].rx_bytes += length
self.ac_downlink_status[ac_id].last_msg_time = time.time()
if isPong:
self.ac_downlink_status[ac_id].last_pong_time = time.time() - self.ac_downlink_status[ac_id].last_ping_time
def __del__(self):
self.stop()
def proccess_downlink_msg(self,sender,address,msg,length,receiver_id=None, component_id=None):
if self.run_event.is_set():
# print("new message from %i (%s) [%d Bytes]: %s" % (sender, address, length, msg))
self.ivy.send(msg,sender,receiver_id,component_id)
self.updateStatus(sender, length, address,msg.name == "PONG")
def stop(self):
self.status_timer.cancel()
self.ping_timer.cancel()
IvyUnBindMsg(self.ivy_id)
IvyStop()
def Unpack(self, data_fields, type, start, length):
return struct.unpack(type, "".join(data_fields[start:start + length]))[0]
def InitIvy(self):
# initialising the bus
IvyInit("Link", # application name for Ivy
"READY", # ready message
0, # main loop is local (ie. using IvyMainloop)
lambda x, y: y, # handler called on connection/deconnection
lambda x, y: y # handler called when a diemessage is received
)
# starting the bus
logging.getLogger('Ivy').setLevel(logging.WARN)
IvyStart(pprz_env.IVY_BUS)
self.ivy_id = IvyBindMsg(self.OnSettingMsg, "(^.* SETTING .*)")
def calculate_checksum(self, msg):
ck_a = 0
ck_b = 0
# start char not included in checksum for pprz protocol
for c in msg[1:]:
ck_a = (ck_a + ord(c)) % 256
ck_b = (ck_b + ck_a) % 256
return (ck_a, ck_b)
def buildPprzMsg(self, msg_id, *args):
stx = STX
length = 6
sender = 0
msg_fields = messages_xml_map.message_dictionary_types["datalink"][msg_id]
struct_string = "=BBBB"
typed_args = []
idx = 0
for msg_type in msg_fields:
struct_string += self.data_types[msg_type][0]
length += self.data_types[msg_type][1]
if (msg_type == "float"):
typed_args.append(float(args[idx]))
def proccess_uplink_msg(self,ac_id,msg):
# print ('New IVY message to %s : %s' % (ac_id,msg))
if msg.broadcasted:
if self.bcast_method==IP_BCAST:
self.udp.send(msg,0,(self.bcast_addr,self.uplink_port))
else:
typed_args.append(int(args[idx]))
idx += 1
msg = struct.pack(struct_string, stx, length, sender, msg_id, *typed_args)
(ck_a, ck_b) = self.calculate_checksum(msg)
msg = msg + struct.pack('=BB', ck_a, ck_b)
return msg
for dest in self.ac_downlink_status.keys():
self.udp.send(msg, 0, (self.ac_downlink_status[dest].address[0], self.uplink_port))
self.ac_downlink_status[dest].tx_msgs += 1
else:
if isinstance(ac_id,str):
ac_id = int(ac_id)
# Only send message if the ac is known
if self.ac_downlink_status.has_key(ac_id):
self.udp.send(msg,0,(self.ac_downlink_status[ac_id].address[0],self.uplink_port),ac_id)
self.ac_downlink_status[ac_id].tx_msgs+=1
else:
print ('Message for unknown ac %d' % ac_id)
def OnSettingMsg(self, agent, *larg):
list = larg[0].split(' ')
sender = list[0]
msg_name = list[1]
ac_id = list[3]
args = list[2:]
msg_id = messages_xml_map.message_dictionary_name_id["datalink"][msg_name]
if self.ac_downlink_status.has_key(int(ac_id)):
msgbuf = self.buildPprzMsg(msg_id, *args)
address = (self.ac_downlink_status[int(ac_id)].address[0], DATALINK_PORT)
self.server.sendto(msgbuf, address)
def initial_ivy_binds(self):
# Subscribe to all datalink messages
messages_datalink = messages_xml_map.get_msgs("datalink")
for msg in messages_datalink:
self.ivy.subscribe(self.proccess_uplink_msg, message.PprzMessage("datalink", msg))
def run(self):
print ('Starting UDPLink for protocol version %s' % (messages_xml_map.PROTOCOL_VERSION))
self.udp.start()
self.ivy.start()
self.run_event.set()
self.status_timer.start()
self.ping_timer.start()
self.initial_ivy_binds()
try:
while True:
time.sleep(.5)
except KeyboardInterrupt:
print ("Stopping UDPLink.")
self.status_timer.cancel()
self.ping_timer.cancel()
self.run_event.clear()
# t.join()
self.udp.stop()
self.ivy.stop()
self.udp.join()
def sendPing(self):
for (ac_id, value) in self.ac_downlink_status.items():
msg_id = messages_xml_map.message_dictionary_name_id["datalink"]["PING"]
msgbuf = self.buildPprzMsg(msg_id)
address = (self.ac_downlink_status[int(ac_id)].address[0], DATALINK_PORT)
self.server.sendto(msgbuf, address)
value.last_ping_time = time.clock()
self.ping_timer = threading.Timer(STATUS_PERIOD, self.sendPing)
if messages_xml_map.PROTOCOL_VERSION=="2.0":
# For pprzlink V2.0 set the receiver id
self.udp.send(message.PprzMessage('datalink','PING'), 0, self.ac_downlink_status[int(ac_id)].address, ac_id)
else:
self.udp.send(message.PprzMessage('datalink','PING'),0,self.ac_downlink_status[int(ac_id)].address)
value.last_ping_time = time.time()
self.ping_timer = threading.Timer(PING_PERIOD, self.sendPing)
self.ping_timer.start()
def sendStatus(self):
for (key, value) in self.ac_downlink_status.items():
IvySendMsg("%i DOWNLINK_STATUS %i %i %i %i %i %i %i" % (
self.ivy.send("link LINK_REPORT %i %i %i %i %i %i %i %i %i %i %i" % (
value.ac_id,
-1,
value.run_time,
time.time() - value.last_msg_time,
value.rx_bytes,
value.rx_msgs,
self.rx_err,
value.rx_bytes - value.last_rx_bytes,
value.rx_msgs - value.last_rx_msgs,
value.tx_msgs,
1000 * value.last_pong_time))
value.last_rx_bytes = value.rx_bytes
value.last_rx_msgs = value.rx_msgs
@@ -158,104 +150,16 @@ class IvyUdpLink():
self.status_timer = threading.Timer(STATUS_PERIOD, self.sendStatus)
self.status_timer.start()
def updateStatus(self, ac_id, length, address, isPong):
if not self.ac_downlink_status.has_key(ac_id):
self.ac_downlink_status[ac_id] = DownLinkStatus(ac_id, address)
self.ac_downlink_status[ac_id].rx_msgs += 1
self.ac_downlink_status[ac_id].rx_bytes += length
if isPong:
self.ac_downlink_status[ac_id].last_pong_time = time.clock() - self.ac_downlink_status[ac_id].last_ping_time
def ProcessPacket(self, msg, address):
if len(msg) < 4:
self.rx_err = self.rx_err + 1
return
msg_offset = 0
while msg_offset < len(msg):
start_byte = ord(msg[msg_offset])
msg_start_idx = msg_offset
msg_offset = msg_offset + 1
if start_byte != STX and start_byte != STX_TS:
self.rx_err = self.rx_err + 1
return
msg_length = ord(msg[msg_offset])
msg_offset = msg_offset + 1
if (start_byte == STX_TS):
timestamp = int(self.Unpack(msg, 'L', msg_offset, 4))
msg_offset = msg_offset + 4
ac_id = ord(msg[msg_offset])
msg_offset = msg_offset + 1
msg_id = ord(msg[msg_offset])
msg_offset = msg_offset + 1
msg_name = messages_xml_map.message_dictionary_id_name["telemetry"][msg_id]
msg_fields = messages_xml_map.message_dictionary_types["telemetry"][msg_id]
ivy_msg = "%i %s " % (ac_id, msg_name)
for field in msg_fields:
if field[-2:] == "[]":
baseType = field[:-2]
array_length = int(self.Unpack(msg, 'B', msg_offset, 1))
msg_offset = msg_offset + 1
for count in range(0, array_length):
array_value = str(
self.Unpack(msg, self.data_types[baseType][0], msg_offset, self.data_types[baseType][1]))
msg_offset = msg_offset + self.data_types[baseType][1]
if (count == array_length - 1):
ivy_msg += array_value + " "
else:
ivy_msg += array_value + ","
else:
ivy_msg += str(
self.Unpack(msg, self.data_types[field][0], msg_offset, self.data_types[field][1])) + " "
msg_offset = msg_offset + self.data_types[field][1]
if (msg_offset > len(msg)):
print "finished without parsing %s" % field
break
(ck_a, ck_b) = self.calculate_checksum(msg[msg_start_idx:msg_offset])
msg_ck_a = int(self.Unpack(msg, 'B', msg_offset, 1))
msg_offset += 1
msg_ck_b = int(self.Unpack(msg, 'B', msg_offset, 1))
msg_offset += 1
# check for valid checksum
if (ck_a, ck_b) == (msg_ck_a, msg_ck_b):
self.updateStatus(ac_id, msg_length, address,
msg_id == messages_xml_map.message_dictionary_name_id["telemetry"]["PONG"])
# strip off trailing whitespace
ivy_msg = ivy_msg[:-1]
IvySendMsg(ivy_msg)
def Run(self):
self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.server.bind(('0.0.0.0', DOWNLINK_PORT))
self.status_timer.start()
self.ping_timer.start()
try:
while True:
(msg, address) = self.server.recvfrom(2048)
self.ProcessPacket(msg, address)
except KeyboardInterrupt:
print("Stopping server on request")
def main():
udp_interface = IvyUdpLink()
udp_interface.Run()
udp_interface.stop()
if __name__ == '__main__':
main()
from argparse import ArgumentParser
parser = ArgumentParser(description="UDP link for paparazzi (python version)")
parser.add_argument("--broadcast_method", choices=[IP_BCAST,REPEAT] , default=IP_BCAST, help="Broadcast method - repeating to all known aircraft or sending to IP broadcast address. [default: %(default)s]")
parser.add_argument("--broadcast_address", default=DEFAULT_BROADCAST, help="IP address used for broadcast when broadcast method is IP_BCAST. [default: %(default)s]")
parser.add_argument("--uplink_port", default=pprzlink.udp.UPLINK_PORT, help="Uplink UDP port. [default: %(default)s]")
parser.add_argument("--downlink_port", default=pprzlink.udp.DOWNLINK_PORT, help="Downlink UDP port. [default: %(default)s]")
parser.add_argument("--bus", default=pprzlink.ivy.IVY_BUS, help="Ivy bus. [default to system IVY bus]")
args = parser.parse_args()
link = UDPLink(args)
link.run()
-3
View File
@@ -37,9 +37,6 @@
#include "pprzlink/pprz_transport.h"
#include "generated/airframe.h"
/* Message id helpers */
#define SenderIdOfPprzMsg(x) (x[0])
#define IdOfPprzMsg(x) (x[1])
#include "nps_main.h"
#include "nps_sensors.h"