2011-09-08 22:31:32 -03:00
|
|
|
// -*- tab-width: 8; Mode: C++; c-basic-offset: 8; indent-tabs-mode: nil -*-
|
|
|
|
|
|
|
|
//
|
|
|
|
// Copyright (c) 2010 Michael Smith. All rights reserved.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions
|
|
|
|
// are met:
|
|
|
|
// 1. Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
|
|
// documentation and/or other materials provided with the distribution.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
// SUCH DAMAGE.
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// This is a serial port proxy shim for use with MAVLink-capable MAV
|
|
|
|
// controllers. It allows the controller to interact with FlightGear
|
|
|
|
// and control a MAV inside the simulation.
|
|
|
|
//
|
|
|
|
// In addition to bridging between MAVLink and FlightGear, MAVLink data
|
|
|
|
// is forwarded to QGroundControl if it is running on the local system.
|
|
|
|
//
|
|
|
|
// The controller must be willing to send the RC_CHANNELS_SCALED
|
|
|
|
// stream in response to a request, and it should be able to operate
|
|
|
|
// using the data supplied by the GPS_RAW and RAW_IMU messages.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/signal.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
#include <err.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#pragma pack(1)
|
2015-08-11 03:28:41 -03:00
|
|
|
#include <GCS_MAVLink/include/mavlink/v1.0/mavlink_types.h>
|
2011-09-08 22:31:32 -03:00
|
|
|
|
|
|
|
mavlink_system_t mavlink_system;
|
|
|
|
void comm_send_ch(mavlink_channel_t chan, uint8_t ch);
|
|
|
|
#define MAVLINK_USE_CONVENIENCE_FUNCTIONS
|
2015-08-11 03:28:41 -03:00
|
|
|
#include <GCS_MAVLink/include/mavlink/v1.0/ardupilotmega/mavlink.h>
|
2011-09-08 22:31:32 -03:00
|
|
|
|
|
|
|
#define TARGET_SYSTEM 7 /* XXX what should these really be? */
|
|
|
|
#define TARGET_COMPONENT 1
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Binary packet as exchanged with FG.
|
|
|
|
*
|
|
|
|
* Described in MAVLink.xml
|
|
|
|
*/
|
|
|
|
struct fgIMUData {
|
|
|
|
// GPS
|
|
|
|
double latitude;
|
|
|
|
double longitude;
|
|
|
|
double altitude;
|
|
|
|
double heading;
|
|
|
|
double velocityN;
|
|
|
|
double velocityE;
|
|
|
|
|
|
|
|
// IMU
|
|
|
|
double accelX;
|
|
|
|
double accelY;
|
|
|
|
double accelZ;
|
|
|
|
double rateRoll;
|
|
|
|
double ratePitch;
|
|
|
|
double rateYaw;
|
|
|
|
|
|
|
|
// trailer
|
|
|
|
#define MSG_MAGIC 0x4c56414d
|
|
|
|
uint32_t magic;
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
struct fgControlData {
|
|
|
|
double aileron;
|
|
|
|
double elevator;
|
|
|
|
double rudder;
|
|
|
|
double throttle;
|
|
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
/* utility functions for transforming FG blobs */
|
|
|
|
void swap32(void *p);
|
|
|
|
void swap64(void *p);
|
|
|
|
|
|
|
|
/* diagnostic tool */
|
|
|
|
void hexdump(void *p, size_t s);
|
|
|
|
|
|
|
|
/* local port addresses for communication with FlightGear - these are the FG defaults */
|
|
|
|
#define IMU_LISTEN_PORT 5501
|
|
|
|
#define CTRL_SEND_PORT 5500
|
|
|
|
|
|
|
|
/* local port addresses for communication with QGroundControl */
|
|
|
|
#define QGCS_LISTEN_PORT 14551
|
|
|
|
#define QGCS_SEND_PORT 14550
|
|
|
|
|
|
|
|
/*
|
|
|
|
* debug logs
|
|
|
|
*/
|
|
|
|
#define FAC_MAIN 0
|
|
|
|
#define FAC_CTRL 1
|
|
|
|
#define FAC_IMU 2
|
|
|
|
#define FAC_QGCS 3
|
|
|
|
char *fac_names[] = {"", "CTRL: ", "IMU: ", "QGCS: "};
|
|
|
|
|
|
|
|
#define log(_fac, _fmt, _args...) \
|
|
|
|
do { \
|
|
|
|
fprintf(stderr, "%s" _fmt "\n", fac_names[FAC_##_fac], ##_args); \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define debug(_fac, _lvl, _fmt, _args...) \
|
|
|
|
do { \
|
|
|
|
if (gDebug >= _lvl) fprintf(stderr, "%s" _fmt "\n", fac_names[FAC_##_fac], ##_args); \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
int gShouldQuit; /* set to 1 if threads should exit */
|
|
|
|
int gDebug; /* set to 1 if -d is passed */
|
|
|
|
|
|
|
|
void usage(void); /* print usage message */
|
|
|
|
void shouldQuit(int sig); /* signal that the program should quit */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Thread handling aircraft control instructions from the MAV controller to FlightGear.
|
|
|
|
* Also forwards MAVLink packets to QGroundControl.
|
|
|
|
*/
|
|
|
|
pthread_t gCTRLThread;
|
|
|
|
void *ctrlThread(void *arg);
|
|
|
|
void ctrlHandleMessage(mavlink_message_t *msg);
|
|
|
|
|
|
|
|
/* thread handling pseudo-IMU data from FlightGear to the MAV controller */
|
|
|
|
pthread_t gIMUThread;
|
|
|
|
void *imuThread(void *arg);
|
|
|
|
int imuGetMessage(int sock, struct fgIMUData *msg);
|
|
|
|
void imuSendPacket(struct fgIMUData *msg);
|
|
|
|
|
|
|
|
/* thread handling MAVLink traffic from QGroundControl to the MAV controller */
|
|
|
|
pthread_t gQGCSThread;
|
|
|
|
void *qgsThread(void *arg);
|
|
|
|
|
|
|
|
int fgSock; /* socket used for flightgear comms */
|
|
|
|
struct sockaddr_in fgAddr = {sizeof(fgAddr), AF_INET};
|
|
|
|
|
|
|
|
int qgcsSock; /* socket used for qgroundcontrol comms */
|
|
|
|
struct sockaddr_in qgcsAddr = {sizeof(qgcsAddr), AF_INET};
|
|
|
|
|
|
|
|
int port; /* serial port connected to MAV controller */
|
|
|
|
pthread_mutex_t portMutex; /* lock that must be held while writing to the serial port */
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int ch;
|
|
|
|
struct sockaddr_in sin = {sizeof(sin), AF_INET};
|
|
|
|
int opt;
|
|
|
|
struct termios t;
|
|
|
|
|
|
|
|
/* handle arguments */
|
|
|
|
while ((ch = getopt(argc, argv, "ds:")) != -1) {
|
|
|
|
switch(ch) {
|
|
|
|
case 'd':
|
|
|
|
gDebug++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if (pthread_mutex_init(&portMutex, NULL))
|
|
|
|
errx(1, "port mutex");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set up the flightgear socket
|
|
|
|
*/
|
|
|
|
fgSock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if (fgSock < 0)
|
|
|
|
err(1, "IMU socket");
|
|
|
|
sin.sin_port = htons(IMU_LISTEN_PORT);
|
|
|
|
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
fgAddr.sin_port = htons(CTRL_SEND_PORT);
|
|
|
|
fgAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
|
|
|
|
/* bind the listening side of the socket */
|
|
|
|
for (;;) {
|
|
|
|
if (bind(fgSock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
|
|
|
if (errno != EADDRINUSE)
|
|
|
|
err(1, "IMU socket bind");
|
|
|
|
log(IMU, "socket in use, waiting...");
|
|
|
|
sleep(5);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set socket options */
|
|
|
|
opt = 1;
|
|
|
|
if (setsockopt(fgSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
|
|
|
err(1, "IMU setsockopt SO_REUSEADDR");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set up the qgroundcontrol socket
|
|
|
|
*/
|
|
|
|
qgcsSock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if (qgcsSock < 0)
|
|
|
|
err(1, "QGroundControl socket");
|
|
|
|
qgcsAddr.sin_port = htons(QGCS_SEND_PORT);
|
|
|
|
qgcsAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
|
|
|
|
sin.sin_port = htons(QGCS_LISTEN_PORT);
|
|
|
|
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
|
|
|
|
/* bind the listening side of the socket */
|
|
|
|
for (;;) {
|
|
|
|
if (bind(qgcsSock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
|
|
|
if (errno != EADDRINUSE)
|
|
|
|
err(1, "QGroundControl socket bind");
|
|
|
|
log(QGCS, "socket in use, waiting...");
|
|
|
|
sleep(5);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set socket options */
|
|
|
|
opt = 1;
|
|
|
|
if (setsockopt(qgcsSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
|
|
|
|
err(1, "QGroundControl setsockopt SO_REUSEADDR");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set up the serial port at 57600
|
|
|
|
*/
|
|
|
|
if (argc < 1)
|
|
|
|
errx(1, "missing serial port name");
|
|
|
|
if (0 >= (port = open(argv[0], O_RDWR | O_NONBLOCK)))
|
|
|
|
err(1, "could not open port %s", argv[0]);
|
|
|
|
if (tcgetattr(port, &t))
|
|
|
|
err(1, "tcgetattr");
|
|
|
|
cfmakeraw(&t);
|
|
|
|
t.c_cflag |= CLOCAL;
|
|
|
|
cfsetspeed(&t, 57600);
|
|
|
|
|
|
|
|
if (tcsetattr(port, TCSANOW, &t))
|
|
|
|
err(1, "tcsetattr");
|
|
|
|
|
|
|
|
/* start worker threads */
|
|
|
|
if (pthread_create(&gIMUThread, NULL, imuThread, NULL))
|
|
|
|
err(1, "IMU thread");
|
|
|
|
if (pthread_create(&gCTRLThread, NULL, ctrlThread, NULL))
|
|
|
|
err(1, "CTRL thread");
|
|
|
|
if (pthread_create(&gQGCSThread, NULL, qgsThread, NULL))
|
|
|
|
err(1, "QGS thread");
|
|
|
|
|
|
|
|
/* don't install new handlers until the threads are ready */
|
|
|
|
signal(SIGHUP, shouldQuit);
|
|
|
|
siginterrupt(SIGHUP, 1);
|
|
|
|
signal(SIGINT, shouldQuit);
|
|
|
|
siginterrupt(SIGINT, 1);
|
|
|
|
signal(SIGTERM, shouldQuit);
|
|
|
|
siginterrupt(SIGTERM, 1);
|
|
|
|
signal(SIGQUIT, shouldQuit);
|
|
|
|
siginterrupt(SIGQUIT, 1);
|
|
|
|
|
|
|
|
pthread_join(gIMUThread, NULL);
|
|
|
|
log(IMU, "service terminated");
|
|
|
|
pthread_join(gCTRLThread, NULL);
|
|
|
|
log(CTRL, "service terminated");
|
|
|
|
pthread_join(gQGCSThread, NULL);
|
|
|
|
log(QGCS, "service terminated");
|
|
|
|
|
|
|
|
close(port);
|
|
|
|
close(fgSock);
|
|
|
|
close(qgcsSock);
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if __linux__
|
|
|
|
#define getprogname program_invocation_short_name
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
usage()
|
|
|
|
{
|
|
|
|
fprintf(stderr, "usage: %s [-d[-d]]\n", getprogname());
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
shouldQuit(int sig)
|
|
|
|
{
|
|
|
|
if (!gShouldQuit) {
|
|
|
|
fputs("\b\b \b\b", stderr); /* overwrite tty's ^C */
|
|
|
|
if (sig)
|
|
|
|
debug(MAIN, 1, "caught signal %d, cleaning up", sig);
|
|
|
|
pthread_kill(gIMUThread, SIGINT);
|
|
|
|
pthread_kill(gCTRLThread, SIGINT);
|
|
|
|
pthread_kill(gQGCSThread, SIGINT);
|
|
|
|
gShouldQuit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IMU thread listens for datagrams from FlightGear and forwards them out
|
|
|
|
* the serial port.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
imuThread(void *arg)
|
|
|
|
{
|
|
|
|
struct fgIMUData msg;
|
|
|
|
|
|
|
|
/* do not take signals on this thread */
|
|
|
|
pthread_sigmask(SIG_BLOCK, NULL, NULL);
|
|
|
|
|
|
|
|
log(IMU, "Listening for FlightGear binary data on port %d", IMU_LISTEN_PORT);
|
|
|
|
|
|
|
|
/* handle IMU messages */
|
|
|
|
while (!gShouldQuit) {
|
|
|
|
if (imuGetMessage(fgSock, &msg))
|
|
|
|
imuSendPacket(&msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
imuGetMessage(int fgSock, struct fgIMUData *msg)
|
|
|
|
{
|
|
|
|
ssize_t received;
|
|
|
|
|
|
|
|
/* get a message */
|
|
|
|
received = recvfrom(fgSock, msg, sizeof(*msg), MSG_WAITALL, NULL, NULL);
|
|
|
|
if (received != sizeof(*msg)) {
|
|
|
|
if (received < 0) {
|
|
|
|
log(IMU, "receive error: %s", strerror(errno));
|
|
|
|
shouldQuit(0);
|
|
|
|
return(0);
|
|
|
|
} else {
|
|
|
|
if (0 == received) {
|
|
|
|
log(IMU, "Received zero-length data packet, check that MAVLink.xml is correctly installed.");
|
|
|
|
shouldQuit(0);
|
|
|
|
} else {
|
|
|
|
log(IMU, "received %ld instead of %lu", received, sizeof(*msg));
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
swap32(&msg->magic);
|
|
|
|
if (msg->magic != MSG_MAGIC) {
|
|
|
|
log(IMU, "bad magic 0x%08x not 0x%08x", msg->magic, MSG_MAGIC);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* endian-swap message fields */
|
|
|
|
swap64(&msg->latitude);
|
|
|
|
swap64(&msg->longitude);
|
|
|
|
swap64(&msg->altitude);
|
|
|
|
swap64(&msg->heading);
|
|
|
|
swap64(&msg->velocityN);
|
|
|
|
swap64(&msg->velocityE);
|
|
|
|
swap64(&msg->accelX);
|
|
|
|
swap64(&msg->accelY);
|
|
|
|
swap64(&msg->accelZ);
|
|
|
|
swap64(&msg->rateRoll);
|
|
|
|
swap64(&msg->ratePitch);
|
|
|
|
swap64(&msg->rateYaw);
|
|
|
|
|
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
imuSendPacket(struct fgIMUData *msg)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
uint64_t usec;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
usec = (uint64_t)tv.tv_sec + tv.tv_usec;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&portMutex);
|
|
|
|
|
|
|
|
debug(IMU, 2, "***");
|
|
|
|
debug(IMU, 2, "lat %f lon %f alt %f", msg->latitude, msg->longitude, msg->altitude);
|
|
|
|
debug(IMU, 2, "head %f velocityN %f velocityE %f", msg->heading, msg->velocityN, msg->velocityE);
|
|
|
|
debug(IMU, 2, "accX %f accY %f accZ %f", msg->accelX, msg->accelY, msg->accelZ);
|
|
|
|
debug(IMU, 2, "roll %f pitch %f yaw %f", msg->rateRoll, msg->ratePitch, msg->rateYaw);
|
|
|
|
|
|
|
|
|
|
|
|
#define ft2m(_x) ((_x) * 0.3408) /* feet to metres */
|
|
|
|
#define dps2mrps(_x) ((_x) * 17.453293) /* degrees per second to milliradians per second */
|
|
|
|
#define fpss2mg(_x) ((_x) * 1000/ 32.2) /* feet per second per second to milligees */
|
|
|
|
#define rad2deg(_x) fmod((((_x) * 57.29578) + 360), 360) /* radians to degrees */
|
|
|
|
|
|
|
|
mavlink_msg_gps_raw_send(0,
|
|
|
|
usec,
|
|
|
|
3, /* 3D fix */
|
|
|
|
msg->latitude,
|
|
|
|
msg->longitude,
|
|
|
|
ft2m(msg->altitude),
|
|
|
|
0, /* no uncertainty */
|
|
|
|
0, /* no uncertainty */
|
|
|
|
ft2m(sqrt((msg->velocityN * msg->velocityN) +
|
|
|
|
(msg->velocityE * msg->velocityE))),
|
|
|
|
rad2deg(atan2(msg->velocityE, msg->velocityN)));
|
|
|
|
|
|
|
|
mavlink_msg_raw_imu_send(0,
|
|
|
|
usec,
|
|
|
|
fpss2mg(msg->accelX),
|
|
|
|
fpss2mg(msg->accelY),
|
|
|
|
fpss2mg(msg->accelZ),
|
|
|
|
dps2mrps(msg->rateRoll),
|
|
|
|
dps2mrps(msg->ratePitch),
|
|
|
|
dps2mrps(msg->rateYaw),
|
|
|
|
0, /* xmag */
|
|
|
|
0, /* ymag */
|
|
|
|
0); /* zmag */
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&portMutex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
ctrlThread(void *arg)
|
|
|
|
{
|
|
|
|
fd_set readset, errorset;
|
|
|
|
mavlink_message_t msg;
|
|
|
|
mavlink_status_t status;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
/* do not take signals on this thread */
|
|
|
|
pthread_sigmask(SIG_BLOCK, NULL, NULL);
|
|
|
|
|
|
|
|
log(CTRL, "Listening for MAVLink packets and sending to %d", CTRL_SEND_PORT);
|
|
|
|
|
|
|
|
/* loop handling data arriving on the serial port */
|
|
|
|
while (!gShouldQuit) {
|
|
|
|
|
|
|
|
/* wait for data or an exceptional condition */
|
|
|
|
FD_ZERO(&readset);
|
|
|
|
FD_ZERO(&errorset);
|
|
|
|
FD_SET(port, &readset);
|
|
|
|
FD_SET(port, &errorset);
|
|
|
|
if (select(port + 1, &readset, NULL, &errorset, NULL) < 0) {
|
|
|
|
if (errno != EINTR)
|
|
|
|
log(CTRL, "select error: %s", strerror(errno));
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* exception? */
|
|
|
|
if (FD_ISSET(port, &errorset)) {
|
|
|
|
log(CTRL, "serial port error");
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no data available for reading? */
|
|
|
|
if (!FD_ISSET(port, &readset))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* read data in chunks and process */
|
|
|
|
for (;;) {
|
|
|
|
uint8_t buf[128]; /* 11ms @ 115200 */
|
|
|
|
ssize_t cnt;
|
|
|
|
uint8_t *ch;
|
|
|
|
|
|
|
|
/* port is nonblocking, cnt may be as small as 1 byte */
|
|
|
|
cnt = read(port, buf, sizeof(buf));
|
|
|
|
if (cnt < 0) {
|
|
|
|
/* have we run out of data? */
|
|
|
|
if (errno == EWOULDBLOCK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* some other error */
|
|
|
|
log(CTRL, "serial port error");
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* port closed for some reason */
|
|
|
|
if (cnt == 0) {
|
|
|
|
log(CTRL, "serial port closed");
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process characters, possibly handle messages */
|
|
|
|
for (ch = buf; cnt; ch++, cnt--)
|
|
|
|
if (mavlink_parse_char(0, *ch, &msg, &status))
|
|
|
|
ctrlHandleMessage(&msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abort:
|
|
|
|
shouldQuit(0);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ctrlHandleMessage(mavlink_message_t *msg)
|
|
|
|
{
|
|
|
|
struct fgControlData cdata;
|
|
|
|
time_t now;
|
|
|
|
static time_t lastRCMessage;
|
|
|
|
uint8_t buf[1024];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
time(&now);
|
|
|
|
|
|
|
|
switch(msg->msgid) {
|
|
|
|
case MAVLINK_MSG_ID_HEARTBEAT:
|
|
|
|
/*
|
|
|
|
* If we have received a heartbeat, we are connected
|
|
|
|
* to something. If we haven't seen an RC_CHANNELS_SCALED
|
|
|
|
* message in the last couple of seconds, ask for it to
|
|
|
|
* be added to the stream.
|
|
|
|
*/
|
|
|
|
if ((now - lastRCMessage) > 2) {
|
|
|
|
debug(CTRL, 1, "got heartbeat, requesting RC channel datastream");
|
|
|
|
pthread_mutex_lock(&portMutex);
|
|
|
|
mavlink_msg_request_data_stream_send(0,
|
|
|
|
TARGET_SYSTEM,
|
|
|
|
TARGET_COMPONENT,
|
|
|
|
MAV_DATA_STREAM_RAW_CONTROLLER,
|
|
|
|
10, /* 10Hz enough? */
|
|
|
|
1); /* start */
|
|
|
|
pthread_mutex_unlock(&portMutex);
|
|
|
|
/* suppress re-sending for 2 seconds */
|
|
|
|
lastRCMessage = now;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case MAVLINK_MSG_ID_RC_CHANNELS_SCALED:
|
|
|
|
|
|
|
|
/* build the control message for FG */
|
|
|
|
/* XXX ArduPilotMega channel ordering */
|
|
|
|
cdata.aileron = (double)mavlink_msg_rc_channels_scaled_get_chan1_scaled(msg) / 10000;
|
|
|
|
cdata.elevator = (double)mavlink_msg_rc_channels_scaled_get_chan2_scaled(msg) / 10000;
|
|
|
|
cdata.throttle = (double)mavlink_msg_rc_channels_scaled_get_chan3_scaled(msg) / 10000;
|
|
|
|
cdata.rudder = (double)mavlink_msg_rc_channels_scaled_get_chan4_scaled(msg) / 10000;
|
|
|
|
|
|
|
|
debug(CTRL, 2, "%+6.4f %+6.4f %+6.4f %+6.4f", cdata.aileron, cdata.elevator, cdata.throttle, cdata.rudder);
|
|
|
|
|
|
|
|
/* swap and send it to FG */
|
|
|
|
swap64(&cdata.aileron);
|
|
|
|
swap64(&cdata.elevator);
|
|
|
|
swap64(&cdata.throttle);
|
|
|
|
swap64(&cdata.rudder);
|
|
|
|
sendto(fgSock, &cdata, sizeof(cdata), 0, (struct sockaddr *)&fgAddr, sizeof(fgAddr));
|
|
|
|
|
|
|
|
/* update our timestamp so that we don't re-request the stream */
|
|
|
|
lastRCMessage = now;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass the message on to QGCS */
|
|
|
|
len = mavlink_msg_to_send_buffer(buf, msg);
|
|
|
|
sendto(qgcsSock, buf, len, 0, (struct sockaddr *)&qgcsAddr, sizeof(qgcsAddr));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* QGS thread listens for datagrams from QGroundControl and forwards them out
|
|
|
|
* the serial port.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
qgsThread(void *arg)
|
|
|
|
{
|
|
|
|
struct fgIMUData msg;
|
|
|
|
ssize_t received;
|
|
|
|
uint8_t buf[1024];
|
|
|
|
int sent, result;
|
|
|
|
|
|
|
|
/* do not take signals on this thread */
|
|
|
|
pthread_sigmask(SIG_BLOCK, NULL, NULL);
|
|
|
|
|
|
|
|
log(QGCS, "Listening for QGroundControl data on port %d", QGCS_LISTEN_PORT);
|
|
|
|
|
|
|
|
while (!gShouldQuit) {
|
|
|
|
|
|
|
|
/* get a message */
|
|
|
|
received = recvfrom(qgcsSock, buf, sizeof(buf), MSG_WAITALL, NULL, NULL);
|
|
|
|
|
|
|
|
/* XXX might want to intercept messages here that would turn off data that we need */
|
|
|
|
|
|
|
|
/* and forward it */
|
|
|
|
pthread_mutex_lock(&portMutex);
|
|
|
|
sent = 0;
|
|
|
|
while (sent < received) {
|
|
|
|
if (gShouldQuit)
|
|
|
|
break;
|
|
|
|
result = write(port, buf + sent, received - sent);
|
|
|
|
if (result < 0) {
|
|
|
|
if (result == EAGAIN)
|
|
|
|
continue;
|
|
|
|
warn("serial passthrough write failed");
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
if (result == 0) {
|
|
|
|
warnx("serial port closed");
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
sent += result;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&portMutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
abort:
|
|
|
|
shouldQuit(0);
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void comm_send_ch(mavlink_channel_t chan, uint8_t ch)
|
|
|
|
{
|
|
|
|
if (write(port, &ch, 1) != 1)
|
|
|
|
warn("serial packet write failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
swap32(void *p)
|
|
|
|
{
|
|
|
|
*(int32_t *)p = htonl(*(int32_t *)p);
|
|
|
|
}
|
|
|
|
|
|
|
|
union temp64 {
|
|
|
|
int64_t ll;
|
|
|
|
int32_t l[2];
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
swap64(void *p)
|
|
|
|
{
|
|
|
|
union temp64 *f, t;
|
|
|
|
|
|
|
|
f = (union temp64 *)p;
|
|
|
|
|
|
|
|
t.l[0] = htonl(f->l[1]);
|
|
|
|
t.l[1] = htonl(f->l[0]);
|
|
|
|
|
|
|
|
f->ll = t.ll;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
hexdump(void *p, size_t s)
|
|
|
|
{
|
|
|
|
int i, j, lim;
|
|
|
|
uint8_t *c = (uint8_t *)p;
|
|
|
|
|
|
|
|
for (i = 0; i < s; i += 32) {
|
|
|
|
printf("%04x:", i);
|
|
|
|
lim = s - i;
|
|
|
|
if (lim > 32)
|
|
|
|
lim = 32;
|
|
|
|
for (j = 0; j < lim; j++)
|
|
|
|
printf(" %02x", *c++);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
}
|