You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
8.5 KiB
241 lines
8.5 KiB
/* |
|
* This file is free software: you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License as published by the |
|
* Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This file is distributed in the hope that it will be useful, but |
|
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
* See the GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License along |
|
* with this program. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
|
|
/* |
|
* Adapted from Pierre Kancir's ArduPilot plugin for Gazebo |
|
*/ |
|
|
|
#include "libAP_JSON.h" |
|
|
|
#define DEBUG_ENABLED 0 |
|
|
|
// SITL JSON interface supplies 16 servo channels |
|
#define MAX_SERVO_CHANNELS 16 |
|
|
|
// The servo packet received from ArduPilot SITL. Defined in SIM_JSON.h. |
|
struct servo_packet { |
|
uint16_t magic; // 18458 expected magic value |
|
uint16_t frame_rate; |
|
uint32_t frame_count; |
|
uint16_t pwm[16]; |
|
}; |
|
|
|
bool libAP_JSON::InitSockets(const char *fdm_address, const uint16_t fdm_port_in) { |
|
// configure port |
|
sock.set_blocking(false); |
|
sock.reuseaddress(); |
|
|
|
// bind the socket |
|
if (!sock.bind(fdm_address, fdm_port_in)) |
|
{ |
|
std::cout << "[libAP_JSON] " << "failed to bind with " |
|
<< fdm_address << ":" << fdm_port_in << std::endl; |
|
return false; |
|
} |
|
std::cout << "[libAP_JSON] " << "flight dynamics model at " |
|
<< fdm_address << ":" << fdm_port_in << std::endl; |
|
return true; |
|
} |
|
|
|
bool libAP_JSON::ReceiveServoPacket(uint16_t servo_out[]) { |
|
// Added detection for whether ArduPilot is online or not. |
|
// If ArduPilot is detected (receive of fdm packet from someone), |
|
// then socket receive wait time is increased from 1ms to 1 sec |
|
// to accommodate network jitter. |
|
// If ArduPilot is not detected, receive call blocks for 1ms |
|
// on each call. |
|
// Once ArduPilot presence is detected, it takes this many |
|
// missed receives before declaring the FCS offline. |
|
|
|
uint32_t waitMs; |
|
if (ap_online) { |
|
// Increase timeout for recv once we detect a packet from ArduPilot FCS. |
|
// If this value is too high then it will block the main Gazebo update loop |
|
// and adversely affect the RTF. |
|
waitMs = 10; |
|
} else { |
|
// Otherwise skip quickly and do not set control force. |
|
waitMs = 1; |
|
} |
|
|
|
servo_packet pkt; |
|
auto recvSize = sock.recv(&pkt, sizeof(servo_packet), waitMs); |
|
// libAP_JSON::fcu_address = fcu_address; |
|
// libAP_JSON::fcu_port_out = fcu_port_out; |
|
|
|
sock.last_recv_address(fcu_address, fcu_port_out); |
|
|
|
// drain the socket in the case we're backed up |
|
int counter = 0; |
|
while (true) { |
|
servo_packet last_pkt; |
|
auto recvSize_last = sock.recv(&last_pkt, sizeof(servo_packet), 0ul); |
|
if (recvSize_last == -1) { |
|
break; |
|
} |
|
counter++; |
|
pkt = last_pkt; |
|
recvSize = recvSize_last; |
|
} |
|
if (counter > 0) { |
|
std::cout << "[libAP_JSON] " << "Drained n packets: " << counter << std::endl; |
|
} |
|
|
|
// didn't receive a packet, increment timeout count if online, then return |
|
if (recvSize == -1) { |
|
if (ap_online) { |
|
if (++connectionTimeoutCount > connectionTimeoutMaxCount) { |
|
connectionTimeoutCount = 0; |
|
ap_online = false; |
|
std::cout << "[libAP_JSON] " << "Broken ArduPilot connection (no packets received)" << std::endl; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
#if DEBUG_ENABLED |
|
// debug: inspect SITL packet |
|
std::cout << "recv " << recvSize << " bytes from " << fcu_address << ":" << fcu_port_out << "\n"; |
|
std::cout << "magic: " << pkt.magic << "\n"; |
|
std::cout << "frame_rate: " << pkt.frame_rate << "\n"; |
|
std::cout << "frame_count: " << pkt.frame_count << "\n"; |
|
std::cout << "pwm: ["; |
|
for (auto i = 0; i < MAX_SERVO_CHANNELS - 1; ++i) { |
|
std::cout << pkt.pwm[i] << ", "; |
|
} |
|
std::cout << pkt.pwm[MAX_SERVO_CHANNELS - 1] << "]\n"; |
|
std::cout << "\n"; |
|
#endif |
|
|
|
// check magic, return if invalid |
|
const uint16_t magic = 18458; |
|
if (magic != pkt.magic) { |
|
std::cout << "[libAP_JSON] Incorrect protocol magic " << pkt.magic << " should be " << magic << "\n"; |
|
return false; |
|
} |
|
|
|
// check frame rate and frame order |
|
fcu_frame_rate = pkt.frame_rate; |
|
if (pkt.frame_count < fcu_frame_count) { |
|
// @TODO - implement re-initialisation |
|
std::cout << "[libAP_JSON] ArduPilot controller has reset\n"; |
|
} |
|
else if (pkt.frame_count == fcu_frame_count) { |
|
// received duplicate frame, skip |
|
std::cout << "[libAP_JSON] Duplicate input frame count" << fcu_frame_count << "\n"; |
|
return false; |
|
} |
|
else if (pkt.frame_count != fcu_frame_count + 1 && ap_online) { |
|
// missed frames, warn only |
|
std::cout << "[libAP_JSON] Missed " << fcu_frame_count - pkt.frame_count << " input frames\n"; |
|
} |
|
fcu_frame_count = pkt.frame_count; |
|
|
|
// always reset the connection timeout so we don't accumulate |
|
connectionTimeoutCount = 0; |
|
if (!ap_online) { |
|
ap_online = true; |
|
|
|
std::cout << "[libAP_JSON] " << "Connected to ArduPilot controller @ " |
|
<< fcu_address << ":" << fcu_port_out << "\n"; |
|
} |
|
|
|
for (int i = 0; i < MAX_SERVO_CHANNELS - 1; i++) { |
|
servo_out[i] = pkt.pwm[i]; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void libAP_JSON::SendState(double timestamp, // seconds |
|
double gyro_x, double gyro_y, double gyro_z, // rad/sec |
|
double accel_x, double accel_y, double accel_z, // m/s^2 |
|
double pos_x, double pos_y, double pos_z, // m in inertial frame |
|
double phi, double theta, double psi, // attitude radians |
|
double V_x, double V_y, double V_z) // m/s (standard fixed wing frame is negative) |
|
{ |
|
// it is assumed that the imu orientation is NED, i.e.: |
|
// x forward |
|
// y right |
|
// z down |
|
|
|
// Example ArduPilot JSON interface messages. Preceed and terminate with \n |
|
// {"timestamp":2500,"imu":{"gyro":[0,0,0],"accel_body":[0,0,0]},"position":[0,0,0],"attitude":[0,0,0],"velocity":[0,0,0]} |
|
|
|
// using namespace rapidjson; |
|
|
|
// build JSON string |
|
// all the required pieces first |
|
std::string s = "\n{\"timestamp\":" + std::to_string(timestamp) |
|
+ ",\"imu\":{" |
|
+ "\"gyro\":[" + std::to_string(gyro_x) + "," + std::to_string(gyro_y) + "," + std::to_string(gyro_z) + "]" |
|
+ ",\"accel_body\":[" + std::to_string(accel_x) + "," + std::to_string(accel_y) + "," + std::to_string(accel_z) + "]" |
|
+ "}" |
|
+ ",\"position\":[" + std::to_string(pos_x) + "," + std::to_string(pos_y) + "," + std::to_string(pos_z) + "]" |
|
+ ",\"attitude\":[" + std::to_string(phi) + "," + std::to_string(theta) + "," + std::to_string(psi) + "]" |
|
+ ",\"velocity\":[" + std::to_string(V_x) + "," + std::to_string(V_y) + "," + std::to_string(V_z) + "]"; |
|
|
|
// handle the optional JSON data |
|
if (set_airspeed_flag) { |
|
s = s + ",\"airspeed\":" + std::to_string(airspeed); |
|
} |
|
if (set_windvane_flag) { |
|
s = s + ",\"windvane\":{\"direction\":" + std::to_string(windvane_direction) + ",\"speed\":" + std::to_string(windvane_speed) + "}"; |
|
} |
|
if (rangefinder_count > 0) { |
|
for (int i = 0; i < rangefinder_count; i++) { |
|
// rangefinder data is 1 indexed |
|
s = s + ",\"rng_" + std::to_string(i + 1) + "\":" + std::to_string(rangefinder[i]); |
|
} |
|
} |
|
|
|
s = s + "}\n"; // close the JSON |
|
|
|
// send JSON string to ArduPilot |
|
auto bytes_sent = sock.sendto( |
|
s.c_str(), s.size(), |
|
fcu_address, |
|
fcu_port_out); |
|
|
|
#if DEBUG_ENABLED |
|
std::cout << s << std::endl; |
|
#endif |
|
} |
|
|
|
void libAP_JSON::setAirspeed(double airspeed_in) |
|
{ |
|
airspeed = airspeed_in; |
|
set_airspeed_flag = true; |
|
} |
|
|
|
void libAP_JSON::setWindvane(double direction, double speed) |
|
{ |
|
windvane_direction = direction; |
|
windvane_speed = speed; |
|
set_windvane_flag = true; |
|
} |
|
|
|
void libAP_JSON::setRangefinder(double *rangefinder_in, uint8_t n) |
|
{ |
|
if (n <= 6) { |
|
rangefinder_count = n; |
|
for (int i = 0; i < n; i++) { |
|
rangefinder[i] = rangefinder_in[i]; |
|
} |
|
} else { |
|
std::cout << "[libAP_JSON] Too many rangerfinder values!" << std::endl; |
|
} |
|
}
|
|
|