/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
/*
This program 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 program 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 .
*/
/*
simulator connector for FlightAxis
*/
#include "SIM_FlightAxis.h"
#include
#include
#include
#include
#include
#include
#include
#include
extern const AP_HAL::HAL& hal;
#define FLIGHTAXIS_SERVER_IP "192.168.2.48"
#define FLIGHTAXIS_SERVER_PORT 18083
namespace SITL {
// the asprintf() calls are not worth checking for SITL
#pragma GCC diagnostic ignored "-Wunused-result"
FlightAxis::FlightAxis(const char *home_str, const char *frame_str) :
Aircraft(home_str, frame_str)
{
use_time_sync = false;
rate_hz = 250 / target_speedup;
heli_demix = strstr(frame_str, "helidemix") != NULL;
rev4_servos = strstr(frame_str, "rev4") != NULL;
}
/*
extremely primitive SOAP parser that assumes the format used by FlightAxis
*/
void FlightAxis::parse_reply(const char *reply)
{
const char *reply0 = reply;
for (uint16_t i=0; i= sizeof(reply)) {
printf("Reply too large %u\n", expected_length);
return nullptr;
}
while (ret < expected_length) {
ssize_t ret2 = sock.recv(&reply[ret], sizeof(reply)-(1+ret), 100);
if (ret2 <= 0) {
return nullptr;
}
ret += ret2;
}
return strdup(reply);
}
void FlightAxis::exchange_data(const struct sitl_input &input)
{
if (!controller_started) {
printf("Starting controller\n");
// call a restore first. This allows us to connect after the aircraft is changed in RealFlight
char *reply = soap_request("RestoreOriginalControllerDevice", R"(
12
)");
free(reply);
reply = soap_request("InjectUAVControllerInterface", R"(
12
)");
free(reply);
activation_frame_counter = frame_counter;
controller_started = true;
}
float scaled_servos[8];
for (uint8_t i=0; i<8; i++) {
scaled_servos[i] = (input.servos[i] - 1000) / 1000.0f;
}
if (rev4_servos) {
// swap first 4 and last 4 servos, for quadplane testing
float saved[4];
memcpy(saved, &scaled_servos[0], sizeof(saved));
memcpy(&scaled_servos[0], &scaled_servos[4], sizeof(saved));
memcpy(&scaled_servos[4], saved, sizeof(saved));
}
if (heli_demix) {
// FlightAxis expects "roll/pitch/collective/yaw" input
float swash1 = scaled_servos[0];
float swash2 = scaled_servos[1];
float swash3 = scaled_servos[2];
float roll_rate = swash1 - swash2;
float pitch_rate = -((swash1+swash2) / 2.0f - swash3);
scaled_servos[0] = constrain_float(roll_rate + 0.5, 0, 1);
scaled_servos[1] = constrain_float(pitch_rate + 0.5, 0, 1);
}
char *reply = soap_request("ExchangeData", R"(
255
- %.4f
- %.4f
- %.4f
- %.4f
- %.4f
- %.4f
- %.4f
- %.4f
)",
scaled_servos[0],
scaled_servos[1],
scaled_servos[2],
scaled_servos[3],
scaled_servos[4],
scaled_servos[5],
scaled_servos[6],
scaled_servos[7]);
if (reply) {
parse_reply(reply);
free(reply);
}
}
/*
update the FlightAxis simulation by one time step
*/
void FlightAxis::update(const struct sitl_input &input)
{
Vector3f last_velocity_ef = velocity_ef;
exchange_data(input);
float dt_seconds = state.m_currentPhysicsTime_SEC - last_time_s;
if (dt_seconds <= 0) {
return;
}
if (initial_time_s <= 0) {
dt_seconds = 0.001f;
initial_time_s = state.m_currentPhysicsTime_SEC - dt_seconds;
}
Quaternion quat(state.m_orientationQuaternion_W,
state.m_orientationQuaternion_Z,
-state.m_orientationQuaternion_Y,
-state.m_orientationQuaternion_Z);
quat.rotation_matrix(dcm);
dcm.from_euler(radians(state.m_roll_DEG),
radians(state.m_inclination_DEG),
-radians(state.m_azimuth_DEG));
Quaternion quat2;
quat2.from_rotation_matrix(dcm);
gyro = Vector3f(radians(constrain_float(state.m_rollRate_DEGpSEC, -2000, 2000)),
radians(constrain_float(state.m_pitchRate_DEGpSEC, -2000, 2000)),
-radians(constrain_float(state.m_yawRate_DEGpSEC, -2000, 2000))) * target_speedup;
velocity_ef = Vector3f(state.m_velocityWorldU_MPS,
state.m_velocityWorldV_MPS,
state.m_velocityWorldW_MPS);
position = Vector3f(state.m_aircraftPositionY_MTR,
state.m_aircraftPositionX_MTR,
-state.m_altitudeAGL_MTR);
// offset based on first position to account for offset in RF world
if (position_offset.is_zero()) {
position_offset = position;
}
position -= position_offset;
// the accel values given in the state are very strange. Calculate
// it from delta-velocity instead, although this does introduce
// noise
Vector3f accel_ef = (velocity_ef - last_velocity_ef) / dt_seconds;
accel_ef.z -= GRAVITY_MSS;
accel_body = dcm.transposed() * accel_ef;
accel_body.x = constrain_float(accel_body.x, -16, 16);
accel_body.y = constrain_float(accel_body.y, -16, 16);
accel_body.z = constrain_float(accel_body.z, -16, 16);
airspeed = state.m_airspeed_MPS;
battery_voltage = state.m_batteryVoltage_VOLTS;
battery_current = state.m_batteryCurrentDraw_AMPS;
rpm1 = state.m_propRPM;
rpm2 = state.m_heliMainRotorRPM;
update_position();
time_now_us = (state.m_currentPhysicsTime_SEC - initial_time_s)*1.0e6;
if (frame_counter++ % 1000 == 0) {
if (last_frame_count_s != 0) {
printf("%.2f FPS\n",
1000 / (state.m_currentPhysicsTime_SEC - last_frame_count_s));
printf("(%.3f %.3f %.3f %.3f) (%.3f %.3f %.3f %.3f)\n",
quat.q1, quat.q2, quat.q3, quat.q4,
quat2.q1, quat2.q2, quat2.q3, quat2.q4);
} else {
printf("Initial position %f %f %f\n", position.x, position.y, position.z);
}
last_frame_count_s = state.m_currentPhysicsTime_SEC;
}
last_time_s = state.m_currentPhysicsTime_SEC;
}
} // namespace SITL