diff --git a/flight/libraries/inc/CoordinateConversions.h b/flight/libraries/inc/CoordinateConversions.h index 8c5f55858..cc32cf442 100644 --- a/flight/libraries/inc/CoordinateConversions.h +++ b/flight/libraries/inc/CoordinateConversions.h @@ -75,5 +75,27 @@ void quat_inverse(float q[4]); void quat_copy(const float q[4], float qnew[4]); void quat_mult(const float q1[4], const float q2[4], float qout[4]); void rot_mult(float R[3][3], const float vec[3], float vec_out[3]); +/** + * matrix_mult_3x3f - perform a multiplication between two 3x3 float matrices + * result = a*b + * @param a + * @param b + * @param result + */ +inline void matrix_mult_3x3f(float a[3][3], float b[3][3], float result[3][3]) +{ + result[0][0] = a[0][0] * b[0][0] + a[1][0] * b[0][1] + a[2][0] * b[0][2]; + result[0][1] = a[0][1] * b[0][0] + a[1][1] * b[0][1] + a[2][1] * b[0][2]; + result[0][2] = a[0][2] * b[0][0] + a[1][2] * b[0][1] + a[2][2] * b[0][2]; + + result[1][0] = a[0][0] * b[1][0] + a[1][0] * b[1][1] + a[2][0] * b[1][2]; + result[1][1] = a[0][1] * b[1][0] + a[1][1] * b[1][1] + a[2][1] * b[1][2]; + result[1][2] = a[0][2] * b[1][0] + a[1][2] * b[1][1] + a[2][2] * b[1][2]; + + result[2][0] = a[0][0] * b[2][0] + a[1][0] * b[2][1] + a[2][0] * b[2][2]; + result[2][1] = a[0][1] * b[2][0] + a[1][1] * b[2][1] + a[2][1] * b[2][2]; + result[2][2] = a[0][2] * b[2][0] + a[1][2] * b[2][1] + a[2][2] * b[2][2]; +} + #endif // COORDINATECONVERSIONS_H_ diff --git a/flight/libraries/inc/plans.h b/flight/libraries/inc/plans.h new file mode 100644 index 000000000..207567b3a --- /dev/null +++ b/flight/libraries/inc/plans.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @addtogroup OpenPilotLibraries OpenPilot Libraries + * @{ + * @addtogroup Navigation + * @brief setups RTH/PH and other pathfollower/pathplanner status + * @{ + * + * @file plan.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * + * @see The GNU Public License (GPL) Version 3 + * + ******************************************************************************/ +/* + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PLANS_H_ +#define PLANS_H_ +#include + +/** \page standard Plans + How to use this library + \li Call plan_initialize() to init all needed struct and uavos at startup.
+ It may be safely called more than once.
+ + \li Functions called plan_setup_* needs to be called once, every time the requested function is engaged.
+ + \li Functions called plan_run_* are to be periodically called while the requested mode is engaged.
+ */ + +/** + * @brief initialize UAVOs and structs used by this library + */ +void plan_initialize(); + +/** + * @brief setup pathplanner/pathfollower for positionhold + */ +void plan_setup_positionHold(); + +/** + * @brief setup pathplanner/pathfollower for return to base + */ +void plan_setup_returnToBase(); + +/** + * @brief setup pathplanner/pathfollower for land + */ +void plan_setup_land(); + +/** + * @brief execute land + */ +void plan_run_land(); + +/** + * @brief setup pathfollower for positionvario + */ +void plan_setup_PositionVarioFPV(); +void plan_setup_PositionVarioLOS(); +void plan_setup_PositionVarioNSEW(); + +/** + * @brief run for positionvario + */ +void plan_run_PositionVarioFPV(); +void plan_run_PositionVarioLOS(); +void plan_run_PositionVarioNSEW(); + +/** + * @brief setup pathplanner/pathfollower for AutoCruise + */ +void plan_setup_AutoCruise(); + +/** + * @brief execute AutoCruise + */ +void plan_run_AutoCruise(); + +#endif /* PLANS_H_ */ diff --git a/flight/libraries/plans.c b/flight/libraries/plans.c new file mode 100644 index 000000000..9055419fa --- /dev/null +++ b/flight/libraries/plans.c @@ -0,0 +1,465 @@ +/** + ****************************************************************************** + * @addtogroup OpenPilotLibraries OpenPilot Libraries + * @{ + * @addtogroup Navigation + * @brief setups RTH/PH and other pathfollower/pathplanner status + * @{ + * + * @file plan.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * + * @see The GNU Public License (GPL) Version 3 + * + ******************************************************************************/ +/* + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UPDATE_EXPECTED 0.02f +#define UPDATE_MIN 1.0e-6f +#define UPDATE_MAX 1.0f +#define UPDATE_ALPHA 1.0e-2f + +/** + * @brief initialize UAVOs and structs used by this library + */ +void plan_initialize() +{ + TakeOffLocationInitialize(); + PositionStateInitialize(); + PathDesiredInitialize(); + FlightModeSettingsInitialize(); + AttitudeStateInitialize(); + ManualControlCommandInitialize(); +} + +/** + * @brief setup pathplanner/pathfollower for positionhold + */ +void plan_setup_positionHold() +{ + PositionStateData positionState; + + PositionStateGet(&positionState); + PathDesiredData pathDesired; + PathDesiredGet(&pathDesired); + + FlightModeSettingsPositionHoldOffsetData offset; + FlightModeSettingsPositionHoldOffsetGet(&offset); + float startingVelocity; + FlightModeSettingsPositionHoldStartingVelocityGet(&startingVelocity); + + pathDesired.End.North = positionState.North; + pathDesired.End.East = positionState.East; + pathDesired.End.Down = positionState.Down; + pathDesired.Start.North = positionState.North + offset.Horizontal; // in FlyEndPoint the direction of this vector does not matter + pathDesired.Start.East = positionState.East; + pathDesired.Start.Down = positionState.Down; + pathDesired.StartingVelocity = startingVelocity; + pathDesired.EndingVelocity = 0.0f; + pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; + + PathDesiredSet(&pathDesired); +} + +/** + * @brief setup pathplanner/pathfollower for return to base + */ +void plan_setup_returnToBase() +{ + // Simple Return To Base mode - keep altitude the same applying configured delta, fly to takeoff position + float positionStateDown; + + PositionStateDownGet(&positionStateDown); + + PathDesiredData pathDesired; + PathDesiredGet(&pathDesired); + + TakeOffLocationData takeoffLocation; + TakeOffLocationGet(&takeoffLocation); + + // TODO: right now VTOLPF does fly straight to destination altitude. + // For a safer RTB destination altitude will be the higher between takeofflocation and current position (corrected with safety margin) + + float destDown; + FlightModeSettingsReturnToBaseAltitudeOffsetGet(&destDown); + destDown = MIN(positionStateDown, takeoffLocation.Down) - destDown; + FlightModeSettingsPositionHoldOffsetData offset; + FlightModeSettingsPositionHoldOffsetGet(&offset); + float startingVelocity; + FlightModeSettingsPositionHoldStartingVelocityGet(&startingVelocity); + + pathDesired.End.North = takeoffLocation.North; + pathDesired.End.East = takeoffLocation.East; + pathDesired.End.Down = destDown; + + pathDesired.Start.North = takeoffLocation.North + offset.Horizontal; // in FlyEndPoint the direction of this vector does not matter + pathDesired.Start.East = takeoffLocation.East; + pathDesired.Start.Down = destDown; + + pathDesired.StartingVelocity = startingVelocity; + pathDesired.EndingVelocity = 0.0f; + pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; + + PathDesiredSet(&pathDesired); +} + +void plan_setup_land() +{ + plan_setup_positionHold(); +} + +/** + * @brief execute land + */ +void plan_run_land() +{ + PathDesiredEndData pathDesiredEnd; + + PathDesiredEndGet(&pathDesiredEnd); + + PositionStateDownGet(&pathDesiredEnd.Down); + pathDesiredEnd.Down += 5; + + PathDesiredEndSet(&pathDesiredEnd); +} + + +/** + * @brief positionvario functionality + */ +static bool vario_hold = true; +static float hold_position[3]; + +static void plan_setup_PositionVario() +{ + vario_hold = true; + plan_setup_positionHold(); +} + +void plan_setup_PositionVarioFPV() +{ + plan_setup_PositionVario(); +} + +void plan_setup_PositionVarioLOS() +{ + plan_setup_PositionVario(); +} + +void plan_setup_PositionVarioNSEW() +{ + plan_setup_PositionVario(); +} + + +#define DEADBAND 0.1f +static bool normalizeDeadband(float controlVector[4]) +{ + bool moving = false; + + // roll, pitch, yaw between -1 and +1 + // thrust between 0 and 1 mapped to -1 to +1 + controlVector[3] = (2.0f * controlVector[3]) - 1.0f; + int t; + + for (t = 0; t < 4; t++) { + if (controlVector[t] < -DEADBAND) { + moving = true; + controlVector[t] += DEADBAND; + } else if (controlVector[t] > DEADBAND) { + moving = true; + controlVector[t] -= DEADBAND; + } else { + controlVector[t] = 0.0f; + } + // deadband has been cut out, scale value back to [-1,+1] + controlVector[t] *= (1.0f / (1.0f - DEADBAND)); + controlVector[t] = boundf(controlVector[t], -1.0f, 1.0f); + } + + return moving; +} + +typedef enum { FPV, LOS, NSEW } vario_type; + +static void getVector(float controlVector[4], vario_type type) +{ + FlightModeSettingsPositionHoldOffsetData offset; + + FlightModeSettingsPositionHoldOffsetGet(&offset); + + // scale controlVector[3] (thrust) by vertical/horizontal to have vertical plane less sensitive + controlVector[3] *= offset.Vertical / offset.Horizontal; + + float length = sqrtf(controlVector[0] * controlVector[0] + controlVector[1] * controlVector[1] + controlVector[3] * controlVector[3]); + + if (length <= 1e-9f) { + length = 1.0f; // should never happen as getVector is not called if control within deadband + } + { + float direction[3] = { + controlVector[1] / length, // pitch is north + controlVector[0] / length, // roll is east + controlVector[3] / length // thrust is down + }; + controlVector[0] = direction[0]; + controlVector[1] = direction[1]; + controlVector[2] = direction[2]; + } + controlVector[3] = length * offset.Horizontal; + + // rotate north and east - rotation angle based on type + float angle; + switch (type) { + case NSEW: + angle = 0.0f; + // NSEW no rotation takes place + break; + case FPV: + // local rotation, using current yaw + AttitudeStateYawGet(&angle); + break; + case LOS: + // determine location based on vector from takeoff to current location + { + PositionStateData positionState; + PositionStateGet(&positionState); + TakeOffLocationData takeoffLocation; + TakeOffLocationGet(&takeoffLocation); + angle = RAD2DEG(atan2f(positionState.East - takeoffLocation.East, positionState.North - takeoffLocation.North)); + } + break; + } + // rotate horizontally by angle + { + float rotated[2] = { + controlVector[0] * cos_lookup_deg(angle) - controlVector[1] * sin_lookup_deg(angle), + controlVector[0] * sin_lookup_deg(angle) + controlVector[1] * cos_lookup_deg(angle) + }; + controlVector[0] = rotated[0]; + controlVector[1] = rotated[1]; + } +} + + +static void plan_run_PositionVario(vario_type type) +{ + float controlVector[4]; + PathDesiredData pathDesired; + + PathDesiredGet(&pathDesired); + FlightModeSettingsPositionHoldOffsetData offset; + FlightModeSettingsPositionHoldOffsetGet(&offset); + + + ManualControlCommandRollGet(&controlVector[0]); + ManualControlCommandPitchGet(&controlVector[1]); + ManualControlCommandYawGet(&controlVector[2]); + ManualControlCommandThrustGet(&controlVector[3]); + + // check if movement is desired + if (normalizeDeadband(controlVector) == false) { + // no movement desired, re-enter positionHold at current start-position + if (!vario_hold) { + vario_hold = true; + + // new hold position is the position that was previously the start position + pathDesired.End.North = hold_position[0]; + pathDesired.End.East = hold_position[1]; + pathDesired.End.Down = hold_position[2]; + // while the new start position has the same offset as in position hold + pathDesired.Start.North = pathDesired.End.North + offset.Horizontal; // in FlyEndPoint the direction of this vector does not matter + pathDesired.Start.East = pathDesired.End.East; + pathDesired.Start.Down = pathDesired.End.Down; + PathDesiredSet(&pathDesired); + } + } else { + PositionStateData positionState; + PositionStateGet(&positionState); + + // flip pitch to have pitch down (away) point north + controlVector[1] = -controlVector[1]; + getVector(controlVector, type); + + // layout of control Vector : unitVector in movement direction {0,1,2} vector length {3} velocity {4} + if (vario_hold) { + // start position is the position that was previously the hold position + vario_hold = false; + hold_position[0] = pathDesired.End.North; + hold_position[1] = pathDesired.End.East; + hold_position[2] = pathDesired.End.Down; + } else { + // start position is advanced according to movement - in the direction of ControlVector only + // projection using scalar product + float kp = (positionState.North - hold_position[0]) * controlVector[0] + + (positionState.East - hold_position[1]) * controlVector[1] + + (positionState.Down - hold_position[2]) * -controlVector[2]; + if (kp > 0.0f) { + hold_position[0] += kp * controlVector[0]; + hold_position[1] += kp * controlVector[1]; + hold_position[2] += kp * -controlVector[2]; + } + } + // new destination position is advanced based on controlVector + pathDesired.End.North = hold_position[0] + controlVector[0] * controlVector[3]; + pathDesired.End.East = hold_position[1] + controlVector[1] * controlVector[3]; + pathDesired.End.Down = hold_position[2] - controlVector[2] * controlVector[3]; + // the new start position has the same offset as in position hold + pathDesired.Start.North = pathDesired.End.North + offset.Horizontal; // in FlyEndPoint the direction of this vector does not matter + pathDesired.Start.East = pathDesired.End.East; + pathDesired.Start.Down = pathDesired.End.Down; + PathDesiredSet(&pathDesired); + } +} +void plan_run_PositionVarioFPV() +{ + plan_run_PositionVario(FPV); +} + +void plan_run_PositionVarioLOS() +{ + plan_run_PositionVario(LOS); +} + +void plan_run_PositionVarioNSEW() +{ + plan_run_PositionVario(NSEW); +} + + +/** + * @brief setup pathplanner/pathfollower for AutoCruise + */ +static PiOSDeltatimeConfig actimeval; +void plan_setup_AutoCruise() +{ + PositionStateData positionState; + + PositionStateGet(&positionState); + PathDesiredData pathDesired; + PathDesiredGet(&pathDesired); + + FlightModeSettingsPositionHoldOffsetData offset; + FlightModeSettingsPositionHoldOffsetGet(&offset); + float startingVelocity; + FlightModeSettingsPositionHoldStartingVelocityGet(&startingVelocity); + + // initialization is flight in direction of the nose. + // the velocity is not relevant, as it will be reset by the run function even during first call + float angle; + AttitudeStateYawGet(&angle); + float vector[2] = { + cos_lookup_deg(angle), + sin_lookup_deg(angle) + }; + hold_position[0] = positionState.North; + hold_position[1] = positionState.East; + hold_position[2] = positionState.Down; + pathDesired.End.North = hold_position[0] + vector[0]; + pathDesired.End.East = hold_position[1] + vector[1]; + pathDesired.End.Down = hold_position[2]; + // start position has the same offset as in position hold + pathDesired.Start.North = pathDesired.End.North + offset.Horizontal; // in FlyEndPoint the direction of this vector does not matter + pathDesired.Start.East = pathDesired.End.East; + pathDesired.Start.Down = pathDesired.End.Down; + pathDesired.StartingVelocity = startingVelocity; + pathDesired.EndingVelocity = 0.0f; + pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; + + PathDesiredSet(&pathDesired); + + // re-iniztializing deltatime is valid and also good practice here since + // getAverageSeconds() has not been called/updated in a long time if we were in a different flightmode. + PIOS_DELTATIME_Init(&actimeval, UPDATE_EXPECTED, UPDATE_MIN, UPDATE_MAX, UPDATE_ALPHA); +} + +/** + * @brief execute autocruise + */ +void plan_run_AutoCruise() +{ + PositionStateData positionState; + + PositionStateGet(&positionState); + PathDesiredData pathDesired; + PathDesiredGet(&pathDesired); + FlightModeSettingsPositionHoldOffsetData offset; + FlightModeSettingsPositionHoldOffsetGet(&offset); + + float controlVector[4]; + ManualControlCommandRollGet(&controlVector[0]); + ManualControlCommandPitchGet(&controlVector[1]); + ManualControlCommandYawGet(&controlVector[2]); + controlVector[3] = 0.5f; // dummy, thrust is normalized separately + normalizeDeadband(controlVector); // return value ignored + ManualControlCommandThrustGet(&controlVector[3]); // no deadband as we are using thrust for velocity + controlVector[3] = boundf(controlVector[3], 1e-6f, 1.0f); // bound to above zero, to prevent loss of vector direction + + // normalize old desired movement vector + float vector[3] = { pathDesired.End.North - hold_position[0], + pathDesired.End.East - hold_position[1], + pathDesired.End.Down - hold_position[2] }; + float length = sqrtf(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]); + if (length < 1e-9f) { + length = 1.0f; // should not happen since initialized properly in setup() + } + vector[0] /= length; + vector[1] /= length; + vector[2] /= length; + + // start position is advanced according to actual movement - in the direction of desired vector only + // projection using scalar product + float kp = (positionState.North - hold_position[0]) * vector[0] + + (positionState.East - hold_position[1]) * vector[1] + + (positionState.Down - hold_position[2]) * vector[2]; + if (kp > 0.0f) { + hold_position[0] += kp * vector[0]; + hold_position[1] += kp * vector[1]; + hold_position[2] += kp * vector[2]; + } + + // new angle is equal to old angle plus offset depending on yaw input and time + // (controlVector is normalized with a deadband, change is zero within deadband) + float angle = RAD2DEG(atan2f(vector[1], vector[0])); + float dT = PIOS_DELTATIME_GetAverageSeconds(&actimeval); + angle += 10.0f * controlVector[2] * dT; // TODO magic value could eventually end up in a to be created settings + + // resulting movement vector is scaled by velocity demand in controlvector[3] [0.0-1.0] + vector[0] = cosf(DEG2RAD(angle)) * offset.Horizontal * controlVector[3]; + vector[1] = sinf(DEG2RAD(angle)) * offset.Horizontal * controlVector[3]; + vector[2] = -controlVector[1] * offset.Vertical * controlVector[3]; + + pathDesired.End.North = hold_position[0] + vector[0]; + pathDesired.End.East = hold_position[1] + vector[1]; + pathDesired.End.Down = hold_position[2] + vector[2]; + // start position has the same offset as in position hold + pathDesired.Start.North = pathDesired.End.North + offset.Horizontal; // in FlyEndPoint the direction of this vector does not matter + pathDesired.Start.East = pathDesired.End.East; + pathDesired.Start.Down = pathDesired.End.Down; + PathDesiredSet(&pathDesired); +} diff --git a/flight/libraries/sanitycheck.c b/flight/libraries/sanitycheck.c index 03ab299df..270543f24 100644 --- a/flight/libraries/sanitycheck.c +++ b/flight/libraries/sanitycheck.c @@ -37,8 +37,13 @@ #include #include #include +#include #include +// a number of useful macros +#define ADDSEVERITY(check) severity = (severity != SYSTEMALARMS_ALARM_OK ? severity : ((check) ? SYSTEMALARMS_ALARM_OK : SYSTEMALARMS_ALARM_ERROR)) + + /**************************** * Current checks: * 1. If a flight mode switch allows autotune and autotune module not running @@ -46,7 +51,7 @@ ****************************/ // ! Check a stabilization mode switch position for safety -static int32_t check_stabilization_settings(int index, bool multirotor, bool coptercontrol); +static bool check_stabilization_settings(int index, bool multirotor, bool coptercontrol); /** * Run a preflight check over the hardware configuration @@ -61,8 +66,27 @@ int32_t configuration_check() const struct pios_board_info *bdinfo = &pios_board_info_blob; bool coptercontrol = bdinfo->board_type == 0x04; + // Classify navigation capability +#ifdef REVOLUTION + RevoSettingsInitialize(); + uint8_t revoFusion; + RevoSettingsFusionAlgorithmGet(&revoFusion); + bool navCapableFusion; + switch (revoFusion) { + case REVOSETTINGS_FUSIONALGORITHM_COMPLEMENTARYMAGGPSOUTDOOR: + case REVOSETTINGS_FUSIONALGORITHM_INS13GPSOUTDOOR: + navCapableFusion = true; + break; + default: + navCapableFusion = false; + } +#else + const bool navCapableFusion = false; +#endif + + // Classify airframe type - bool multirotor = true; + bool multirotor; uint8_t airframe_type; SystemSettingsAirframeTypeGet(&airframe_type); @@ -93,82 +117,54 @@ int32_t configuration_check() for (uint32_t i = 0; i < num_modes; i++) { switch (modes[i]) { case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_MANUAL: - if (multirotor) { - severity = SYSTEMALARMS_ALARM_ERROR; - } + ADDSEVERITY(!multirotor); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED1: - severity = (severity == SYSTEMALARMS_ALARM_OK) ? check_stabilization_settings(1, multirotor, coptercontrol) : severity; + ADDSEVERITY(check_stabilization_settings(1, multirotor, coptercontrol)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED2: - severity = (severity == SYSTEMALARMS_ALARM_OK) ? check_stabilization_settings(2, multirotor, coptercontrol) : severity; + ADDSEVERITY(check_stabilization_settings(2, multirotor, coptercontrol)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED3: - severity = (severity == SYSTEMALARMS_ALARM_OK) ? check_stabilization_settings(3, multirotor, coptercontrol) : severity; + ADDSEVERITY(check_stabilization_settings(3, multirotor, coptercontrol)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED4: - severity = (severity == SYSTEMALARMS_ALARM_OK) ? check_stabilization_settings(4, multirotor, coptercontrol) : severity; + ADDSEVERITY(check_stabilization_settings(4, multirotor, coptercontrol)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED5: - severity = (severity == SYSTEMALARMS_ALARM_OK) ? check_stabilization_settings(5, multirotor, coptercontrol) : severity; + ADDSEVERITY(check_stabilization_settings(5, multirotor, coptercontrol)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_STABILIZED6: - severity = (severity == SYSTEMALARMS_ALARM_OK) ? check_stabilization_settings(6, multirotor, coptercontrol) : severity; + ADDSEVERITY(check_stabilization_settings(6, multirotor, coptercontrol)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_AUTOTUNE: - if (!PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_AUTOTUNE)) { - severity = SYSTEMALARMS_ALARM_ERROR; - } - break; - case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONHOLD: - if (coptercontrol) { - severity = SYSTEMALARMS_ALARM_ERROR; - } else if (!PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_PATHFOLLOWER)) { - // Revo supports Position Hold - severity = SYSTEMALARMS_ALARM_ERROR; - } - break; - case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_LAND: - if (coptercontrol) { - severity = SYSTEMALARMS_ALARM_ERROR; - } else if (!PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_PATHFOLLOWER)) { - // Revo supports AutoLand Mode - severity = SYSTEMALARMS_ALARM_ERROR; - } - break; - case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POI: - if (coptercontrol) { - severity = SYSTEMALARMS_ALARM_ERROR; - } else if (!PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_PATHFOLLOWER)) { - // Revo supports POI Mode - severity = SYSTEMALARMS_ALARM_ERROR; - } + ADDSEVERITY(PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_AUTOTUNE)); break; case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_PATHPLANNER: - if (coptercontrol) { - severity = SYSTEMALARMS_ALARM_ERROR; - } else { - // Revo supports PathPlanner and that must be OK or we are not sane - // PathPlan alarm is uninitialized if not running - // PathPlan alarm is warning or error if the flightplan is invalid - SystemAlarmsAlarmData alarms; - SystemAlarmsAlarmGet(&alarms); - if (alarms.PathPlan != SYSTEMALARMS_ALARM_OK) { - severity = SYSTEMALARMS_ALARM_ERROR; - } - } - break; + { + // Revo supports PathPlanner and that must be OK or we are not sane + // PathPlan alarm is uninitialized if not running + // PathPlan alarm is warning or error if the flightplan is invalid + SystemAlarmsAlarmData alarms; + SystemAlarmsAlarmGet(&alarms); + ADDSEVERITY(alarms.PathPlan == SYSTEMALARMS_ALARM_OK); + } + // intentionally no break as this also needs pathfollower + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONHOLD: + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONVARIOFPV: + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONVARIOLOS: + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POSITIONVARIONSEW: + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_LAND: + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_POI: case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_RETURNTOBASE: - if (coptercontrol) { - severity = SYSTEMALARMS_ALARM_ERROR; - } else if (!PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_PATHFOLLOWER)) { - // Revo supports ReturnToBase - severity = SYSTEMALARMS_ALARM_ERROR; - } + case FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_AUTOCRUISE: + ADDSEVERITY(!coptercontrol); + ADDSEVERITY(PIOS_TASK_MONITOR_IsRunning(TASKINFO_RUNNING_PATHFOLLOWER)); + ADDSEVERITY(navCapableFusion); break; default: // Uncovered modes are automatically an error - severity = SYSTEMALARMS_ALARM_ERROR; + ADDSEVERITY(false); } // mark the first encountered erroneous setting in status and substatus if ((severity != SYSTEMALARMS_ALARM_OK) && (alarmstatus == SYSTEMALARMS_EXTENDEDALARMSTATUS_NONE)) { @@ -191,9 +187,9 @@ int32_t configuration_check() * Checks the stabiliation settings for a paritcular mode and makes * sure it is appropriate for the airframe * @param[in] index Which stabilization mode to check - * @returns SYSTEMALARMS_ALARM_OK or SYSTEMALARMS_ALARM_ERROR + * @returns true or false */ -static int32_t check_stabilization_settings(int index, bool multirotor, bool coptercontrol) +static bool check_stabilization_settings(int index, bool multirotor, bool coptercontrol) { uint8_t modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_NUMELEM]; @@ -218,7 +214,7 @@ static int32_t check_stabilization_settings(int index, bool multirotor, bool cop FlightModeSettingsStabilization6SettingsArrayGet(modes); break; default: - return SYSTEMALARMS_ALARM_ERROR; + return false; } // For multirotors verify that roll/pitch/yaw are not set to "none" @@ -226,7 +222,7 @@ static int32_t check_stabilization_settings(int index, bool multirotor, bool cop if (multirotor) { for (uint32_t i = 0; i < FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST; i++) { if (modes[i] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_MANUAL) { - return SYSTEMALARMS_ALARM_ERROR; + return false; } } } @@ -234,26 +230,26 @@ static int32_t check_stabilization_settings(int index, bool multirotor, bool cop // coptercontrol cannot do altitude holding if (coptercontrol) { if (modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEHOLD - || modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_VERTICALVELOCITY + || modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO ) { - return SYSTEMALARMS_ALARM_ERROR; + return false; } } // check that thrust modes are only set to thrust axis for (uint32_t i = 0; i < FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST; i++) { if (modes[i] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEHOLD - || modes[i] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_VERTICALVELOCITY + || modes[i] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO ) { - return SYSTEMALARMS_ALARM_ERROR; + return false; } } if (!(modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_MANUAL || modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEHOLD - || modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_VERTICALVELOCITY + || modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO || modes[FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_THRUST] == FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_CRUISECONTROL )) { - return SYSTEMALARMS_ALARM_ERROR; + return false; } // Warning: This assumes that certain conditions in the XML file are met. That @@ -261,5 +257,5 @@ static int32_t check_stabilization_settings(int index, bool multirotor, bool cop // and is the same for STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL // (this is checked at compile time by static constraint manualcontrol.h) - return SYSTEMALARMS_ALARM_OK; + return true; } diff --git a/flight/modules/FixedWingPathFollower/fixedwingpathfollower.c b/flight/modules/FixedWingPathFollower/fixedwingpathfollower.c index d381f27ff..417665b6c 100644 --- a/flight/modules/FixedWingPathFollower/fixedwingpathfollower.c +++ b/flight/modules/FixedWingPathFollower/fixedwingpathfollower.c @@ -63,6 +63,7 @@ #include "taskinfo.h" #include +#include "sin_lookup.h" #include "paths.h" #include "CoordinateConversions.h" @@ -85,6 +86,7 @@ static void updatePathVelocity(); static uint8_t updateFixedDesiredAttitude(); static void updateFixedAttitude(); static void airspeedStateUpdatedCb(UAVObjEvent *ev); +static bool correctCourse(float *C, float *V, float *F, float s); /** * Initialise the module, called on startup @@ -125,17 +127,17 @@ int32_t FixedWingPathFollowerInitialize() } MODULE_INITCALL(FixedWingPathFollowerInitialize, FixedWingPathFollowerStart); -static float northVelIntegral = 0; -static float eastVelIntegral = 0; -static float downVelIntegral = 0; +static float northVelIntegral = 0.0f; +static float eastVelIntegral = 0.0f; +static float downVelIntegral = 0.0f; -static float courseIntegral = 0; -static float speedIntegral = 0; -static float powerIntegral = 0; -static float airspeedErrorInt = 0; +static float courseIntegral = 0.0f; +static float speedIntegral = 0.0f; +static float powerIntegral = 0.0f; +static float airspeedErrorInt = 0.0f; // correct speed by measured airspeed -static float indicatedAirspeedStateBias = 0; +static float indicatedAirspeedStateBias = 0.0f; /** * Module thread, should not return. @@ -225,12 +227,12 @@ static void pathfollowerTask(__attribute__((unused)) void *parameters) } } else { // Be cleaner and get rid of global variables - northVelIntegral = 0; - eastVelIntegral = 0; - downVelIntegral = 0; - courseIntegral = 0; - speedIntegral = 0; - powerIntegral = 0; + northVelIntegral = 0.0f; + eastVelIntegral = 0.0f; + downVelIntegral = 0.0f; + courseIntegral = 0.0f; + speedIntegral = 0.0f; + powerIntegral = 0.0f; } PathStatusSet(&pathStatus); } @@ -276,14 +278,14 @@ static void updatePathVelocity() case PATHDESIRED_MODE_DRIVEVECTOR: default: groundspeed = pathDesired.StartingVelocity + (pathDesired.EndingVelocity - pathDesired.StartingVelocity) * - boundf(progress.fractional_progress, 0, 1); + boundf(progress.fractional_progress, 0.0f, 1.0f); altitudeSetpoint = pathDesired.Start.Down + (pathDesired.End.Down - pathDesired.Start.Down) * - boundf(progress.fractional_progress, 0, 1); + boundf(progress.fractional_progress, 0.0f, 1.0f); break; } // make sure groundspeed is not zero - if (groundspeed < 1e-2f) { - groundspeed = 1e-2f; + if (groundspeed < 1e-6f) { + groundspeed = 1e-6f; } // calculate velocity - can be zero if waypoints are too close @@ -302,24 +304,12 @@ static void updatePathVelocity() // in this case the plane effectively needs to be turned around // indicators: // difference between correction_direction and velocitystate >90 degrees and - // difference between path_direction and velocitystate >90 degrees ( 4th sector, facing away from eerything ) + // difference between path_direction and velocitystate >90 degrees ( 4th sector, facing away from everything ) // fix: ignore correction, steer in path direction until the situation has become better (condition doesn't apply anymore) - float angle1 = RAD2DEG(atan2f(progress.path_direction[1], progress.path_direction[0]) - atan2f(velocityState.East, velocityState.North)); - float angle2 = RAD2DEG(atan2f(progress.correction_direction[1], progress.correction_direction[0]) - atan2f(velocityState.East, velocityState.North)); - if (angle1 < -180.0f) { - angle1 += 360.0f; - } - if (angle1 > 180.0f) { - angle1 -= 360.0f; - } - if (angle2 < -180.0f) { - angle2 += 360.0f; - } - if (angle2 > 180.0f) { - angle2 -= 360.0f; - } - if (fabsf(angle1) >= 90.0f && fabsf(angle2) >= 90.0f) { - error_speed = 0; + if ( // calculating angles < 90 degrees through dot products + ((progress.path_direction[0] * velocityState.North + progress.path_direction[1] * velocityState.East) < 0.0f) && + ((progress.correction_direction[0] * velocityState.North + progress.correction_direction[1] * velocityState.East) < 0.0f)) { + error_speed = 0.0f; } // calculate correction - can also be zero if correction vector is 0 or no error present @@ -328,14 +318,16 @@ static void updatePathVelocity() // scale to correct length float l = sqrtf(velocityDesired.North * velocityDesired.North + velocityDesired.East * velocityDesired.East); - velocityDesired.North *= groundspeed / l; - velocityDesired.East *= groundspeed / l; + if (l > 1e-9f) { + velocityDesired.North *= groundspeed / l; + velocityDesired.East *= groundspeed / l; + } float downError = altitudeSetpoint - positionState.Down; - velocityDesired.Down = downError * fixedwingpathfollowerSettings.VerticalPosP; + velocityDesired.Down = downError * fixedwingpathfollowerSettings.VerticalPosP; // update pathstatus - pathStatus.error = progress.error; + pathStatus.error = progress.error; pathStatus.fractional_progress = progress.fractional_progress; VelocityDesiredSet(&velocityDesired); @@ -384,8 +376,7 @@ static uint8_t updateFixedDesiredAttitude() AirspeedStateData airspeedState; SystemSettingsData systemSettings; - float groundspeedState; - float groundspeedDesired; + float groundspeedProjection; float indicatedAirspeedState; float indicatedAirspeedDesired; float airspeedError; @@ -396,10 +387,9 @@ static uint8_t updateFixedDesiredAttitude() float descentspeedError; float powerCommand; - float bearing; - float heading; - float headingError; - float course; + float airspeedVector[2]; + float fluidMovement[2]; + float courseComponent[2]; float courseError; float courseCommand; @@ -413,24 +403,54 @@ static uint8_t updateFixedDesiredAttitude() AirspeedStateGet(&airspeedState); SystemSettingsGet(&systemSettings); - /** - * Compute speed error (required for thrust and pitch) + * Compute speed error and course */ + // missing sensors for airspeed-direction we have to assume within + // reasonable error that measured airspeed is actually the airspeed + // component in forward pointing direction + // airspeedVector is normalized + airspeedVector[0] = cos_lookup_deg(attitudeState.Yaw); + airspeedVector[1] = sin_lookup_deg(attitudeState.Yaw); - // Current ground speed - groundspeedState = sqrtf(velocityState.East * velocityState.East + velocityState.North * velocityState.North); - // note that airspeedStateBias is ( calibratedAirspeed - groundSpeed ) at the time of measurement, - // but thanks to accelerometers, groundspeed reacts faster to changes in direction + // current ground speed projected in forward direction + groundspeedProjection = velocityState.North * airspeedVector[0] + velocityState.East * airspeedVector[1]; + + // note that airspeedStateBias is ( calibratedAirspeed - groundspeedProjection ) at the time of measurement, + // but thanks to accelerometers, groundspeedProjection reacts faster to changes in direction // than airspeed and gps sensors alone - indicatedAirspeedState = groundspeedState + indicatedAirspeedStateBias; + indicatedAirspeedState = groundspeedProjection + indicatedAirspeedStateBias; - // Desired ground speed - groundspeedDesired = sqrtf(velocityDesired.North * velocityDesired.North + velocityDesired.East * velocityDesired.East); - indicatedAirspeedDesired = boundf(groundspeedDesired + indicatedAirspeedStateBias, + // fluidMovement is a vector describing the aproximate movement vector of + // the surrounding fluid in 2d space (aka wind vector) + fluidMovement[0] = velocityState.North - (indicatedAirspeedState * airspeedVector[0]); + fluidMovement[1] = velocityState.East - (indicatedAirspeedState * airspeedVector[1]); + + // calculate the movement vector we need to fly to reach velocityDesired - + // taking fluidMovement into account + courseComponent[0] = velocityDesired.North - fluidMovement[0]; + courseComponent[1] = velocityDesired.East - fluidMovement[1]; + + indicatedAirspeedDesired = boundf(sqrtf(courseComponent[0] * courseComponent[0] + courseComponent[1] * courseComponent[1]), fixedwingpathfollowerSettings.HorizontalVelMin, fixedwingpathfollowerSettings.HorizontalVelMax); + // if we could fly at arbitrary speeds, we'd just have to move towards the + // courseComponent vector as previously calculated and we'd be fine + // unfortunately however we are bound by min and max air speed limits, so + // we need to recalculate the correct course to meet at least the + // velocityDesired vector direction at our current speed + // this overwrites courseComponent + bool valid = correctCourse(courseComponent, (float *)&velocityDesired.North, fluidMovement, indicatedAirspeedDesired); + + // Error condition: wind speed too high, we can't go where we want anymore + fixedwingpathfollowerStatus.Errors.Wind = 0; + if ((!valid) && + fixedwingpathfollowerSettings.Safetymargins.Wind > 0.5f) { // alarm switched on + fixedwingpathfollowerStatus.Errors.Wind = 1; + result = 0; + } + // Airspeed error airspeedError = indicatedAirspeedDesired - indicatedAirspeedState; @@ -461,23 +481,18 @@ static uint8_t updateFixedDesiredAttitude() result = 0; } - if (indicatedAirspeedState < 1e-6f) { - // prevent division by zero, abort without controlling anything. This guidance mode is not suited for takeoff or touchdown, or handling stationary planes - // also we cannot handle planes flying backwards, lets just wait until the nose drops - fixedwingpathfollowerStatus.Errors.Lowspeed = 1; - return 0; - } - /** * Compute desired thrust command */ // compute saturated integral error thrust response. Make integral leaky for better performance. Approximately 30s time constant. - if (fixedwingpathfollowerSettings.PowerPI.Ki > 0) { + if (fixedwingpathfollowerSettings.PowerPI.Ki > 0.0f) { powerIntegral = boundf(powerIntegral + -descentspeedError * dT, -fixedwingpathfollowerSettings.PowerPI.ILimit / fixedwingpathfollowerSettings.PowerPI.Ki, fixedwingpathfollowerSettings.PowerPI.ILimit / fixedwingpathfollowerSettings.PowerPI.Ki ) * (1.0f - 1.0f / (1.0f + 30.0f / dT)); - } else { powerIntegral = 0; } + } else { + powerIntegral = 0.0f; + } // Compute the cross feed from vertical speed to pitch, with saturation float speedErrorToPowerCommandComponent = boundf( @@ -504,9 +519,9 @@ static uint8_t updateFixedDesiredAttitude() // Error condition: plane cannot hold altitude at current speed. fixedwingpathfollowerStatus.Errors.Lowpower = 0; if (fixedwingpathfollowerSettings.ThrustLimit.Neutral + powerCommand >= fixedwingpathfollowerSettings.ThrustLimit.Max && // thrust at maximum - velocityState.Down > 0 && // we ARE going down - descentspeedDesired < 0 && // we WANT to go up - airspeedError > 0 && // we are too slow already + velocityState.Down > 0.0f && // we ARE going down + descentspeedDesired < 0.0f && // we WANT to go up + airspeedError > 0.0f && // we are too slow already fixedwingpathfollowerSettings.Safetymargins.Lowpower > 0.5f) { // alarm switched on fixedwingpathfollowerStatus.Errors.Lowpower = 1; result = 0; @@ -514,9 +529,9 @@ static uint8_t updateFixedDesiredAttitude() // Error condition: plane keeps climbing despite minimum thrust (opposite of above) fixedwingpathfollowerStatus.Errors.Highpower = 0; if (fixedwingpathfollowerSettings.ThrustLimit.Neutral + powerCommand <= fixedwingpathfollowerSettings.ThrustLimit.Min && // thrust at minimum - velocityState.Down < 0 && // we ARE going up - descentspeedDesired > 0 && // we WANT to go down - airspeedError < 0 && // we are too fast already + velocityState.Down < 0.0f && // we ARE going up + descentspeedDesired > 0.0f && // we WANT to go down + airspeedError < 0.0f && // we are too fast already fixedwingpathfollowerSettings.Safetymargins.Highpower > 0.5f) { // alarm switched on fixedwingpathfollowerStatus.Errors.Highpower = 1; result = 0; @@ -526,7 +541,6 @@ static uint8_t updateFixedDesiredAttitude() /** * Compute desired pitch command */ - if (fixedwingpathfollowerSettings.SpeedPI.Ki > 0) { // Integrate with saturation airspeedErrorInt = boundf(airspeedErrorInt + airspeedError * dT, @@ -556,48 +570,18 @@ static uint8_t updateFixedDesiredAttitude() // Error condition: high speed dive fixedwingpathfollowerStatus.Errors.Pitchcontrol = 0; if (fixedwingpathfollowerSettings.PitchLimit.Neutral + pitchCommand >= fixedwingpathfollowerSettings.PitchLimit.Max && // pitch demand is full up - velocityState.Down > 0 && // we ARE going down - descentspeedDesired < 0 && // we WANT to go up - airspeedError < 0 && // we are too fast already + velocityState.Down > 0.0f && // we ARE going down + descentspeedDesired < 0.0f && // we WANT to go up + airspeedError < 0.0f && // we are too fast already fixedwingpathfollowerSettings.Safetymargins.Pitchcontrol > 0.5f) { // alarm switched on fixedwingpathfollowerStatus.Errors.Pitchcontrol = 1; result = 0; } - /** - * Calculate where we are heading and why (wind issues) - */ - bearing = attitudeState.Yaw; - heading = RAD2DEG(atan2f(velocityState.East, velocityState.North)); - headingError = heading - bearing; - if (headingError < -180.0f) { - headingError += 360.0f; - } - if (headingError > 180.0f) { - headingError -= 360.0f; - } - // Error condition: wind speed is higher than airspeed. We are forced backwards! - fixedwingpathfollowerStatus.Errors.Wind = 0; - if ((headingError > fixedwingpathfollowerSettings.Safetymargins.Wind || - headingError < -fixedwingpathfollowerSettings.Safetymargins.Wind) && - fixedwingpathfollowerSettings.Safetymargins.Highpower > 0.5f) { // alarm switched on - // we are flying backwards - fixedwingpathfollowerStatus.Errors.Wind = 1; - result = 0; - } - /** * Compute desired roll command */ - if (groundspeedDesired > 1e-6f) { - course = RAD2DEG(atan2f(velocityDesired.East, velocityDesired.North)); - - courseError = course - heading; - } else { - // if we are not supposed to move, run in a circle - courseError = -90.0f; - result = 0; - } + courseError = RAD2DEG(atan2f(courseComponent[1], courseComponent[0])) - attitudeState.Yaw; if (courseError < -180.0f) { courseError += 360.0f; @@ -606,6 +590,19 @@ static uint8_t updateFixedDesiredAttitude() courseError -= 360.0f; } + // overlap calculation. Theres a dead zone behind the craft where the + // counter-yawing of some craft while rolling could render a desired right + // turn into a desired left turn. Making the turn direction based on + // current roll angle keeps the plane committed to a direction once chosen + if (courseError < -180.0f + (fixedwingpathfollowerSettings.ReverseCourseOverlap * 0.5f) + && attitudeState.Roll > 0.0f) { + courseError += 360.0f; + } + if (courseError > 180.0f - (fixedwingpathfollowerSettings.ReverseCourseOverlap * 0.5f) + && attitudeState.Roll < 0.0f) { + courseError -= 360.0f; + } + courseIntegral = boundf(courseIntegral + courseError * dT * fixedwingpathfollowerSettings.CoursePI.Ki, -fixedwingpathfollowerSettings.CoursePI.ILimit, fixedwingpathfollowerSettings.CoursePI.ILimit); @@ -628,7 +625,7 @@ static uint8_t updateFixedDesiredAttitude() * Compute desired yaw command */ // TODO implement raw control mode for yaw and base on Accels.Y - stabDesired.Yaw = 0; + stabDesired.Yaw = 0.0f; stabDesired.StabilizationMode.Roll = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE; @@ -656,10 +653,116 @@ static void airspeedStateUpdatedCb(__attribute__((unused)) UAVObjEvent *ev) AirspeedStateGet(&airspeedState); VelocityStateGet(&velocityState); - float groundspeed = sqrtf(velocityState.East * velocityState.East + velocityState.North * velocityState.North); + float airspeedVector[2]; + float yaw; + AttitudeStateYawGet(&yaw); + airspeedVector[0] = cos_lookup_deg(yaw); + airspeedVector[1] = sin_lookup_deg(yaw); + // vector projection of groundspeed on airspeed vector to handle both forward and backwards movement + float groundspeedProjection = velocityState.North * airspeedVector[0] + velocityState.East * airspeedVector[1]; - - indicatedAirspeedStateBias = airspeedState.CalibratedAirspeed - groundspeed; - // note - we do fly by Indicated Airspeed (== calibrated airspeed) - // however since airspeed is updated less often than groundspeed, we use sudden changes to groundspeed to offset the airspeed by the same measurement. + indicatedAirspeedStateBias = airspeedState.CalibratedAirspeed - groundspeedProjection; + // note - we do fly by Indicated Airspeed (== calibrated airspeed) however + // since airspeed is updated less often than groundspeed, we use sudden + // changes to groundspeed to offset the airspeed by the same measurement. + // This has a side effect that in the absence of any airspeed updates, the + // pathfollower will fly using groundspeed. +} + + +/** + * Function to calculate course vector C based on airspeed s, fluid movement F + * and desired movement vector V + * parameters in: V,F,s + * parameters out: C + * returns true if a valid solution could be found for V,F,s, false if not + * C will be set to a best effort attempt either way + */ +static bool correctCourse(float *C, float *V, float *F, float s) +{ + // Approach: + // Let Sc be a circle around origin marking possible movement vectors + // of the craft with airspeed s (all possible options for C) + // Let Vl be a line through the origin along movement vector V where fr any + // point v on line Vl v = k * (V / |V|) = k' * V + // Let Wl be a line parallel to Vl where for any point v on line Vl exists + // a point w on WL with w = v - F + // Then any intersection between circle Sc and line Wl represents course + // vector which would result in a movement vector + // V' = k * ( V / |V|) = k' * V + // If there is no intersection point, S is insufficient to compensate + // for F and we can only try to fly in direction of V (thus having wind drift + // but at least making progress orthogonal to wind) + + s = fabsf(s); + float f = sqrtf(F[0] * F[0] + F[1] * F[1]); + + // normalize Cn=V/|V|, |V| must be >0 + float v = sqrtf(V[0] * V[0] + V[1] * V[1]); + if (v < 1e-6f) { + // if |V|=0, we aren't supposed to move, turn into the wind + // (this allows hovering) + C[0] = -F[0]; + C[1] = -F[1]; + // if desired airspeed matches fluidmovement a hover is actually + // intended so return true + return fabsf(f - s) < 1e-3f; + } + float Vn[2] = { V[0] / v, V[1] / v }; + + // project F on V + float fp = F[0] * Vn[0] + F[1] * Vn[1]; + + // find component Fo of F that is orthogonal to V + // (which is exactly the distance between Vl and Wl) + float Fo[2] = { F[0] - (fp * Vn[0]), F[1] - (fp * Vn[1]) }; + float fo2 = Fo[0] * Fo[0] + Fo[1] * Fo[1]; + + // find k where k * Vn = C - Fo + // |C|=s is the hypothenuse in any rectangular triangle formed by k * Vn and Fo + // so k^2 + fo^2 = s^2 (since |Vn|=1) + float k2 = s * s - fo2; + if (k2 <= -1e-3f) { + // there is no solution, we will be drifted off either way + // fallback: fly stupidly in direction of V and hope for the best + C[0] = V[0]; + C[1] = V[1]; + return false; + } else if (k2 <= 1e-3f) { + // there is exactly one solution: -Fo + C[0] = -Fo[0]; + C[1] = -Fo[1]; + return true; + } + // we have two possible solutions k positive and k negative as there are + // two intersection points between Wl and Sc + // which one is better? two criteria: + // 1. we MUST move in the right direction, if any k leads to -v its invalid + // 2. we should minimize the speed error + float k = sqrt(k2); + float C1[2] = { -k * Vn[0] - Fo[0], -k * Vn[1] - Fo[1] }; + float C2[2] = { k *Vn[0] - Fo[0], k * Vn[1] - Fo[1] }; + // project C+F on Vn to find signed resulting movement vector length + float vp1 = (C1[0] + F[0]) * Vn[0] + (C1[1] + F[1]) * Vn[1]; + float vp2 = (C2[0] + F[0]) * Vn[0] + (C2[1] + F[1]) * Vn[1]; + if (vp1 >= 0.0f && fabsf(v - vp1) < fabsf(v - vp2)) { + // in this case the angle between course and resulting movement vector + // is greater than 90 degrees - so we actually fly backwards + C[0] = C1[0]; + C[1] = C1[1]; + return true; + } + C[0] = C2[0]; + C[1] = C2[1]; + if (vp2 >= 0.0f) { + // in this case the angle between course and movement vector is less than + // 90 degrees, but we do move in the right direction + return true; + } else { + // in this case we actually get driven in the opposite direction of V + // with both solutions for C + // this might be reached in headwind stronger than maximum allowed + // airspeed. + return false; + } } diff --git a/flight/modules/ManualControl/inc/manualcontrol.h b/flight/modules/ManualControl/inc/manualcontrol.h index 0d5c00d72..e3a1d6405 100644 --- a/flight/modules/ManualControl/inc/manualcontrol.h +++ b/flight/modules/ManualControl/inc/manualcontrol.h @@ -75,6 +75,18 @@ void pathFollowerHandler(bool newinit); */ void pathPlannerHandler(bool newinit); +/** + * @brief Handler to setup takeofflocation on arming. it is set up during Arming + * @input: NONE: + * @output: NONE + */ +void takeOffLocationHandler(); + +/** + * @brief Initialize TakeoffLocationHanlder + */ +void takeOffLocationHandlerInit(); + /* * These are assumptions we make in the flight code about the order of settings and their consistency between * objects. Please keep this synchronized to the UAVObjects @@ -91,7 +103,7 @@ void pathPlannerHandler(bool newinit); ((int)FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_RELAYRATE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYRATE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_RELAYATTITUDE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYATTITUDE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEHOLD == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD) && \ - ((int)FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_VERTICALVELOCITY == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY) && \ + ((int)FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_ALTITUDEVARIO == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION1SETTINGS_CRUISECONTROL == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL) && \ 1 \ ) @@ -108,7 +120,7 @@ void pathPlannerHandler(bool newinit); ((int)FLIGHTMODESETTINGS_STABILIZATION2SETTINGS_RELAYRATE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYRATE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION2SETTINGS_RELAYATTITUDE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYATTITUDE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION2SETTINGS_ALTITUDEHOLD == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD) && \ - ((int)FLIGHTMODESETTINGS_STABILIZATION2SETTINGS_VERTICALVELOCITY == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY) && \ + ((int)FLIGHTMODESETTINGS_STABILIZATION2SETTINGS_ALTITUDEVARIO == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION2SETTINGS_CRUISECONTROL == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL) && \ 1 \ ) @@ -125,7 +137,7 @@ void pathPlannerHandler(bool newinit); ((int)FLIGHTMODESETTINGS_STABILIZATION3SETTINGS_RELAYRATE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYRATE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION3SETTINGS_RELAYATTITUDE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYATTITUDE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION3SETTINGS_ALTITUDEHOLD == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD) && \ - ((int)FLIGHTMODESETTINGS_STABILIZATION3SETTINGS_VERTICALVELOCITY == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY) && \ + ((int)FLIGHTMODESETTINGS_STABILIZATION3SETTINGS_ALTITUDEVARIO == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION3SETTINGS_CRUISECONTROL == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL) && \ 1 \ ) @@ -142,7 +154,7 @@ void pathPlannerHandler(bool newinit); ((int)FLIGHTMODESETTINGS_STABILIZATION4SETTINGS_RELAYRATE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYRATE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION4SETTINGS_RELAYATTITUDE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYATTITUDE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION4SETTINGS_ALTITUDEHOLD == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD) && \ - ((int)FLIGHTMODESETTINGS_STABILIZATION4SETTINGS_VERTICALVELOCITY == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY) && \ + ((int)FLIGHTMODESETTINGS_STABILIZATION4SETTINGS_ALTITUDEVARIO == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION4SETTINGS_CRUISECONTROL == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL) && \ 1 \ ) @@ -159,7 +171,7 @@ void pathPlannerHandler(bool newinit); ((int)FLIGHTMODESETTINGS_STABILIZATION5SETTINGS_RELAYRATE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYRATE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION5SETTINGS_RELAYATTITUDE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYATTITUDE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION5SETTINGS_ALTITUDEHOLD == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD) && \ - ((int)FLIGHTMODESETTINGS_STABILIZATION5SETTINGS_VERTICALVELOCITY == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY) && \ + ((int)FLIGHTMODESETTINGS_STABILIZATION5SETTINGS_ALTITUDEVARIO == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION5SETTINGS_CRUISECONTROL == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL) && \ 1 \ ) @@ -176,7 +188,7 @@ void pathPlannerHandler(bool newinit); ((int)FLIGHTMODESETTINGS_STABILIZATION6SETTINGS_RELAYRATE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYRATE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION6SETTINGS_RELAYATTITUDE == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_RELAYATTITUDE) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION6SETTINGS_ALTITUDEHOLD == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD) && \ - ((int)FLIGHTMODESETTINGS_STABILIZATION6SETTINGS_VERTICALVELOCITY == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY) && \ + ((int)FLIGHTMODESETTINGS_STABILIZATION6SETTINGS_ALTITUDEVARIO == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO) && \ ((int)FLIGHTMODESETTINGS_STABILIZATION6SETTINGS_CRUISECONTROL == (int)STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL) && \ 1 \ ) diff --git a/flight/modules/ManualControl/manualcontrol.c b/flight/modules/ManualControl/manualcontrol.c index 1bd29329f..64167f978 100644 --- a/flight/modules/ManualControl/manualcontrol.c +++ b/flight/modules/ManualControl/manualcontrol.c @@ -134,6 +134,9 @@ int32_t ManualControlStart() // Make sure unarmed on power up armHandler(true); +#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES + takeOffLocationHandlerInit(); +#endif // Start main task PIOS_CALLBACKSCHEDULER_Dispatch(callbackHandle); @@ -167,7 +170,9 @@ static void manualControlTask(void) { // Process Arming armHandler(false); - +#ifndef PIOS_EXCLUDE_ADVANCED_FEATURES + takeOffLocationHandler(); +#endif // Process flight mode FlightStatusData flightStatus; @@ -200,9 +205,13 @@ static void manualControlTask(void) break; #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD: + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV: + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS: + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW: case FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE: case FLIGHTSTATUS_FLIGHTMODE_LAND: case FLIGHTSTATUS_FLIGHTMODE_POI: + case FLIGHTSTATUS_FLIGHTMODE_AUTOCRUISE: handler = &handler_PATHFOLLOWER; break; case FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER: diff --git a/flight/modules/ManualControl/pathfollowerhandler.c b/flight/modules/ManualControl/pathfollowerhandler.c index 0f9bfe902..a0ba7d1e6 100644 --- a/flight/modules/ManualControl/pathfollowerhandler.c +++ b/flight/modules/ManualControl/pathfollowerhandler.c @@ -35,8 +35,9 @@ #include #include - #if defined(REVOLUTION) +#include + // Private constants // Private types @@ -51,69 +52,63 @@ void pathFollowerHandler(bool newinit) { if (newinit) { - PathDesiredInitialize(); - PositionStateInitialize(); + plan_initialize(); } - FlightStatusData flightStatus; - FlightStatusGet(&flightStatus); + uint8_t flightMode; + FlightStatusFlightModeGet(&flightMode); if (newinit) { // After not being in this mode for a while init at current height - PositionStateData positionState; - PositionStateGet(&positionState); - FlightModeSettingsData settings; - FlightModeSettingsGet(&settings); - PathDesiredData pathDesired; - PathDesiredGet(&pathDesired); - switch (flightStatus.FlightMode) { + switch (flightMode) { case FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE: - // Simple Return To Base mode - keep altitude the same, fly to home position - - - pathDesired.Start.North = 0; - pathDesired.Start.East = 0; - pathDesired.Start.Down = positionState.Down - settings.ReturnToHomeAltitudeOffset; - pathDesired.End.North = 0; - pathDesired.End.East = 0; - pathDesired.End.Down = positionState.Down - settings.ReturnToHomeAltitudeOffset; - pathDesired.StartingVelocity = 1; - pathDesired.EndingVelocity = 0; - pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; + plan_setup_returnToBase(); break; - default: - pathDesired.Start.North = positionState.North; - pathDesired.Start.East = positionState.East; - pathDesired.Start.Down = positionState.Down; - pathDesired.End.North = positionState.North; - pathDesired.End.East = positionState.East; - pathDesired.End.Down = positionState.Down; - pathDesired.StartingVelocity = 1; - pathDesired.EndingVelocity = 0; - pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; - /* Disable this section, until such time as proper discussion can be had about how to implement it for all types of crafts. - } else { - PathDesiredData pathDesired; - PathDesiredGet(&pathDesired); - pathDesired.End[PATHDESIRED_END_NORTH] += dT * -cmd->Pitch; - pathDesired.End[PATHDESIRED_END_EAST] += dT * cmd->Roll; - pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; - PathDesiredSet(&pathDesired); - */ + case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD: + plan_setup_positionHold(); + break; + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV: + plan_setup_PositionVarioFPV(); + break; + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS: + plan_setup_PositionVarioLOS(); + break; + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW: + plan_setup_PositionVarioNSEW(); + break; + + case FLIGHTSTATUS_FLIGHTMODE_LAND: + plan_setup_land(); + break; + case FLIGHTSTATUS_FLIGHTMODE_AUTOCRUISE: + plan_setup_AutoCruise(); + break; + + default: + plan_setup_positionHold(); break; } - PathDesiredSet(&pathDesired); } - // special handling of autoland - behaves like positon hold but with slow altitude decrease - if (flightStatus.FlightMode == FLIGHTSTATUS_FLIGHTMODE_LAND) { - PositionStateData positionState; - PositionStateGet(&positionState); - PathDesiredData pathDesired; - PathDesiredGet(&pathDesired); - pathDesired.End.Down = positionState.Down + 5; - PathDesiredSet(&pathDesired); + switch (flightMode) { + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOFPV: + plan_run_PositionVarioFPV(); + break; + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIOLOS: + plan_run_PositionVarioLOS(); + break; + case FLIGHTSTATUS_FLIGHTMODE_POSITIONVARIONSEW: + plan_run_PositionVarioNSEW(); + break; + case FLIGHTSTATUS_FLIGHTMODE_LAND: + plan_run_land(); + break; + case FLIGHTSTATUS_FLIGHTMODE_AUTOCRUISE: + plan_run_AutoCruise(); + break; + default: + break; } } diff --git a/flight/modules/ManualControl/takeofflocationhandler.c b/flight/modules/ManualControl/takeofflocationhandler.c new file mode 100644 index 000000000..42a10777d --- /dev/null +++ b/flight/modules/ManualControl/takeofflocationhandler.c @@ -0,0 +1,125 @@ +/** + ****************************************************************************** + * + * @file takeofflocationhandler.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief handles TakeOffLocation + * -- + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "inc/manualcontrol.h" +#include +#include +#include +#include + +// Private constants + +// Private types + +// Private variables +static bool locationSet; +// Private functions +static void SetTakeOffLocation(); + +void takeOffLocationHandlerInit() +{ + TakeOffLocationInitialize(); + // check whether there is a preset/valid takeoff location + uint8_t mode; + uint8_t status; + TakeOffLocationModeGet(&mode); + TakeOffLocationStatusGet(&status); + // preset with invalid location will actually behave like FirstTakeoff + if (mode == TAKEOFFLOCATION_MODE_PRESET && status == TAKEOFFLOCATION_STATUS_VALID) { + locationSet = true; + } else { + locationSet = false; + status = TAKEOFFLOCATION_STATUS_INVALID; + TakeOffLocationStatusSet(&status); + } +} +/** + * Handles TakeOffPosition location setup + * @param newinit + */ +void takeOffLocationHandler() +{ + uint8_t armed; + uint8_t status; + + FlightStatusArmedGet(&armed); + + // Location already acquired/preset + if (armed == FLIGHTSTATUS_ARMED_ARMED && locationSet) { + return; + } + + TakeOffLocationStatusGet(&status); + + switch (armed) { + case FLIGHTSTATUS_ARMED_ARMING: + case FLIGHTSTATUS_ARMED_ARMED: + if (!locationSet || status != TAKEOFFLOCATION_STATUS_VALID) { + uint8_t mode; + TakeOffLocationModeGet(&mode); + + if ((mode != TAKEOFFLOCATION_MODE_PRESET) || (status == TAKEOFFLOCATION_STATUS_INVALID)) { + SetTakeOffLocation(); + } else { + locationSet = true; + } + } + break; + + case FLIGHTSTATUS_ARMED_DISARMED: + // unset if location is to be acquired at each arming + if (locationSet) { + uint8_t mode; + TakeOffLocationModeGet(&mode); + if (mode == TAKEOFFLOCATION_MODE_ARMINGLOCATION) { + locationSet = false; + status = TAKEOFFLOCATION_STATUS_INVALID; + TakeOffLocationStatusSet(&status); + } + } + break; + } +} + +/** + * Retrieve TakeOffLocation from current PositionStatus + */ +void SetTakeOffLocation() +{ + TakeOffLocationData takeOffLocation; + + TakeOffLocationGet(&takeOffLocation); + + PositionStateData positionState; + PositionStateGet(&positionState); + + takeOffLocation.North = positionState.North; + takeOffLocation.East = positionState.East; + takeOffLocation.Down = positionState.Down; + takeOffLocation.Status = TAKEOFFLOCATION_STATUS_VALID; + + TakeOffLocationSet(&takeOffLocation); + locationSet = true; +} diff --git a/flight/modules/PathPlanner/pathplanner.c b/flight/modules/PathPlanner/pathplanner.c index 8cc50c4aa..c25d3a0b7 100644 --- a/flight/modules/PathPlanner/pathplanner.c +++ b/flight/modules/PathPlanner/pathplanner.c @@ -45,6 +45,7 @@ #include "flightmodesettings.h" #include #include "paths.h" +#include "plans.h" // Private constants #define STACK_SIZE_BYTES 1024 @@ -89,6 +90,7 @@ static bool pathplanner_active = false; */ int32_t PathPlannerStart() { + plan_initialize(); // when the active waypoint changes, update pathDesired WaypointConnectCallback(commandUpdated); WaypointActiveConnectCallback(commandUpdated); @@ -169,21 +171,7 @@ static void pathPlannerTask() if (!failsafeRTHset) { failsafeRTHset = 1; // copy pasta: same calculation as in manualcontrol, set return to home coordinates - PositionStateData positionState; - PositionStateGet(&positionState); - FlightModeSettingsData settings; - FlightModeSettingsGet(&settings); - - pathDesired.Start.North = 0; - pathDesired.Start.East = 0; - pathDesired.Start.Down = positionState.Down - settings.ReturnToHomeAltitudeOffset; - pathDesired.End.North = 0; - pathDesired.End.East = 0; - pathDesired.End.Down = positionState.Down - settings.ReturnToHomeAltitudeOffset; - pathDesired.StartingVelocity = 1; - pathDesired.EndingVelocity = 0; - pathDesired.Mode = PATHDESIRED_MODE_FLYENDPOINT; - PathDesiredSet(&pathDesired); + plan_setup_positionHold(); } AlarmsSet(SYSTEMALARMS_ALARM_PATHPLAN, SYSTEMALARMS_ALARM_ERROR); diff --git a/flight/modules/Sensors/sensors.c b/flight/modules/Sensors/sensors.c index c1c76ee8f..bd35945fb 100644 --- a/flight/modules/Sensors/sensors.c +++ b/flight/modules/Sensors/sensors.c @@ -62,6 +62,7 @@ #include #include +#include // Private constants #define STACK_SIZE_BYTES 1000 @@ -474,35 +475,7 @@ static void settingsUpdatedCb(__attribute__((unused)) UAVObjEvent *objEv) RPY2Quaternion(rpy, rotationQuat); Quaternion2R(rotationQuat, R); - mag_transform[0][0] = R[0][0] * cal.mag_transform.r0c0 + - R[1][0] * cal.mag_transform.r0c1 + - R[2][0] * cal.mag_transform.r0c2; - mag_transform[0][1] = R[0][1] * cal.mag_transform.r0c0 + - R[1][1] * cal.mag_transform.r0c1 + - R[2][1] * cal.mag_transform.r0c2; - mag_transform[0][2] = R[0][2] * cal.mag_transform.r0c0 + - R[1][2] * cal.mag_transform.r0c1 + - R[2][2] * cal.mag_transform.r0c2; - - mag_transform[1][0] = R[0][0] * cal.mag_transform.r1c0 + - R[1][0] * cal.mag_transform.r1c1 + - R[2][0] * cal.mag_transform.r1c2; - mag_transform[1][1] = R[0][1] * cal.mag_transform.r1c0 + - R[1][1] * cal.mag_transform.r1c1 + - R[2][1] * cal.mag_transform.r1c2; - mag_transform[1][2] = R[0][2] * cal.mag_transform.r1c0 + - R[1][2] * cal.mag_transform.r1c1 + - R[2][2] * cal.mag_transform.r1c2; - - mag_transform[1][0] = R[0][0] * cal.mag_transform.r2c0 + - R[1][0] * cal.mag_transform.r2c1 + - R[2][0] * cal.mag_transform.r2c2; - mag_transform[2][1] = R[0][1] * cal.mag_transform.r2c0 + - R[1][1] * cal.mag_transform.r2c1 + - R[2][1] * cal.mag_transform.r2c2; - mag_transform[2][2] = R[0][2] * cal.mag_transform.r2c0 + - R[1][2] * cal.mag_transform.r2c1 + - R[2][2] * cal.mag_transform.r2c2; + matrix_mult_3x3f((float(*)[3])cast_struct_to_array(cal.mag_transform, cal.mag_transform.r0c0), R, mag_transform); } /** * @} diff --git a/flight/modules/Stabilization/outerloop.c b/flight/modules/Stabilization/outerloop.c index d80c00185..b5ffb4d35 100644 --- a/flight/modules/Stabilization/outerloop.c +++ b/flight/modules/Stabilization/outerloop.c @@ -247,7 +247,7 @@ static void stabilizationOuterloopTask() case STABILIZATIONSTATUS_OUTERLOOP_ALTITUDE: rateDesiredAxis[t] = stabilizationAltitudeHold(stabilizationDesiredAxis[t], ALTITUDEHOLD, reinit); break; - case STABILIZATIONSTATUS_OUTERLOOP_VERTICALVELOCITY: + case STABILIZATIONSTATUS_OUTERLOOP_ALTITUDEVARIO: rateDesiredAxis[t] = stabilizationAltitudeHold(stabilizationDesiredAxis[t], ALTITUDEVARIO, reinit); break; #endif /* REVOLUTION */ diff --git a/flight/modules/Stabilization/stabilization.c b/flight/modules/Stabilization/stabilization.c index 15b8c3caf..b9ff0541a 100644 --- a/flight/modules/Stabilization/stabilization.c +++ b/flight/modules/Stabilization/stabilization.c @@ -174,8 +174,8 @@ static void StabilizationDesiredUpdatedCb(__attribute__((unused)) UAVObjEvent *e cast_struct_to_array(status.OuterLoop, status.OuterLoop.Roll)[t] = STABILIZATIONSTATUS_OUTERLOOP_ALTITUDE; cast_struct_to_array(status.InnerLoop, status.InnerLoop.Roll)[t] = STABILIZATIONSTATUS_INNERLOOP_CRUISECONTROL; break; - case STABILIZATIONDESIRED_STABILIZATIONMODE_VERTICALVELOCITY: - cast_struct_to_array(status.OuterLoop, status.OuterLoop.Roll)[t] = STABILIZATIONSTATUS_OUTERLOOP_VERTICALVELOCITY; + case STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO: + cast_struct_to_array(status.OuterLoop, status.OuterLoop.Roll)[t] = STABILIZATIONSTATUS_OUTERLOOP_ALTITUDEVARIO; cast_struct_to_array(status.InnerLoop, status.InnerLoop.Roll)[t] = STABILIZATIONSTATUS_INNERLOOP_CRUISECONTROL; break; case STABILIZATIONDESIRED_STABILIZATIONMODE_CRUISECONTROL: diff --git a/flight/targets/boards/discoveryf4bare/firmware/Makefile b/flight/targets/boards/discoveryf4bare/firmware/Makefile index 8b14a385e..7472d176a 100644 --- a/flight/targets/boards/discoveryf4bare/firmware/Makefile +++ b/flight/targets/boards/discoveryf4bare/firmware/Makefile @@ -82,6 +82,7 @@ ifndef TESTAPP ## Misc library functions SRC += $(FLIGHTLIB)/paths.c + SRC += $(FLIGHTLIB)/plans.c SRC += $(FLIGHTLIB)/WorldMagModel.c SRC += $(FLIGHTLIB)/insgps13state.c diff --git a/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc b/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc index 44c43d080..3905af42f 100644 --- a/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc +++ b/flight/targets/boards/discoveryf4bare/firmware/UAVObjects.inc @@ -113,6 +113,7 @@ UAVOBJSRCFILENAMES += poilocation UAVOBJSRCFILENAMES += poilearnsettings UAVOBJSRCFILENAMES += mpu6000settings UAVOBJSRCFILENAMES += txpidsettings +UAVOBJSRCFILENAMES += takeofflocation UAVOBJSRC = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),$(OPUAVSYNTHDIR)/$(UAVOBJSRCFILE).c ) UAVOBJDEFINE = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),-DUAVOBJ_INIT_$(UAVOBJSRCFILE) ) diff --git a/flight/targets/boards/revolution/firmware/Makefile b/flight/targets/boards/revolution/firmware/Makefile index 88b8dd6b6..8c3337a1a 100644 --- a/flight/targets/boards/revolution/firmware/Makefile +++ b/flight/targets/boards/revolution/firmware/Makefile @@ -84,6 +84,7 @@ ifndef TESTAPP ## Misc library functions SRC += $(FLIGHTLIB)/paths.c + SRC += $(FLIGHTLIB)/plans.c SRC += $(FLIGHTLIB)/WorldMagModel.c SRC += $(FLIGHTLIB)/insgps13state.c diff --git a/flight/targets/boards/revolution/firmware/UAVObjects.inc b/flight/targets/boards/revolution/firmware/UAVObjects.inc index 44c43d080..3905af42f 100644 --- a/flight/targets/boards/revolution/firmware/UAVObjects.inc +++ b/flight/targets/boards/revolution/firmware/UAVObjects.inc @@ -113,6 +113,7 @@ UAVOBJSRCFILENAMES += poilocation UAVOBJSRCFILENAMES += poilearnsettings UAVOBJSRCFILENAMES += mpu6000settings UAVOBJSRCFILENAMES += txpidsettings +UAVOBJSRCFILENAMES += takeofflocation UAVOBJSRC = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),$(OPUAVSYNTHDIR)/$(UAVOBJSRCFILE).c ) UAVOBJDEFINE = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),-DUAVOBJ_INIT_$(UAVOBJSRCFILE) ) diff --git a/flight/targets/boards/revoproto/firmware/Makefile b/flight/targets/boards/revoproto/firmware/Makefile index affe5a570..39d2d7f3b 100644 --- a/flight/targets/boards/revoproto/firmware/Makefile +++ b/flight/targets/boards/revoproto/firmware/Makefile @@ -83,6 +83,7 @@ ifndef TESTAPP ## Misc library functions SRC += $(FLIGHTLIB)/paths.c + SRC += $(FLIGHTLIB)/plans.c SRC += $(FLIGHTLIB)/WorldMagModel.c SRC += $(FLIGHTLIB)/insgps13state.c diff --git a/flight/targets/boards/revoproto/firmware/UAVObjects.inc b/flight/targets/boards/revoproto/firmware/UAVObjects.inc index 44c43d080..3905af42f 100644 --- a/flight/targets/boards/revoproto/firmware/UAVObjects.inc +++ b/flight/targets/boards/revoproto/firmware/UAVObjects.inc @@ -113,6 +113,7 @@ UAVOBJSRCFILENAMES += poilocation UAVOBJSRCFILENAMES += poilearnsettings UAVOBJSRCFILENAMES += mpu6000settings UAVOBJSRCFILENAMES += txpidsettings +UAVOBJSRCFILENAMES += takeofflocation UAVOBJSRC = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),$(OPUAVSYNTHDIR)/$(UAVOBJSRCFILE).c ) UAVOBJDEFINE = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),-DUAVOBJ_INIT_$(UAVOBJSRCFILE) ) diff --git a/flight/targets/boards/simposix/firmware/Makefile b/flight/targets/boards/simposix/firmware/Makefile index 8d77fd4f1..9eaa00725 100644 --- a/flight/targets/boards/simposix/firmware/Makefile +++ b/flight/targets/boards/simposix/firmware/Makefile @@ -94,6 +94,7 @@ SRC += $(FLIGHTLIB)/fifo_buffer.c SRC += $(FLIGHTLIB)/WorldMagModel.c SRC += $(FLIGHTLIB)/insgps13state.c SRC += $(FLIGHTLIB)/paths.c +SRC += $(FLIGHTLIB)/plans.c SRC += $(FLIGHTLIB)/sanitycheck.c SRC += $(MATHLIB)/sin_lookup.c diff --git a/flight/targets/boards/simposix/firmware/UAVObjects.inc b/flight/targets/boards/simposix/firmware/UAVObjects.inc index 724adc0a6..907ff6833 100644 --- a/flight/targets/boards/simposix/firmware/UAVObjects.inc +++ b/flight/targets/boards/simposix/firmware/UAVObjects.inc @@ -111,6 +111,7 @@ UAVOBJSRCFILENAMES += revosettings UAVOBJSRCFILENAMES += altitudeholdstatus UAVOBJSRCFILENAMES += ekfconfiguration UAVOBJSRCFILENAMES += ekfstatevariance +UAVOBJSRCFILENAMES += takeofflocation UAVOBJSRC = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),$(UAVOBJSYNTHDIR)/$(UAVOBJSRCFILE).c ) UAVOBJDEFINE = $(foreach UAVOBJSRCFILE,$(UAVOBJSRCFILENAMES),-DUAVOBJ_INIT_$(UAVOBJSRCFILE) ) diff --git a/ground/openpilotgcs/share/openpilotgcs/default_configurations/OpenPilotGCS.xml b/ground/openpilotgcs/share/openpilotgcs/default_configurations/OpenPilotGCS.xml index bf4e7ccbc..88032dc41 100644 --- a/ground/openpilotgcs/share/openpilotgcs/default_configurations/OpenPilotGCS.xml +++ b/ground/openpilotgcs/share/openpilotgcs/default_configurations/OpenPilotGCS.xml @@ -2582,7 +2582,7 @@ splitter 2 - @Variant(AAAACQAAAAIAAAACAAAB7wAAAAIAAAEf) + @Variant(AAAACQAAAAIAAAACAAABqQAAAAIAAADm) splitter @@ -2759,7 +2759,7 @@ uavGadget 1 - @Variant(AAAACQAAAAIAAAACAAABQgAAAAIAAAGM) + @Variant(AAAACQAAAAIAAAACAAAAxwAAAAIAAAFi) splitter diff --git a/ground/openpilotgcs/share/openpilotgcs/diagrams/default/system-health.svg b/ground/openpilotgcs/share/openpilotgcs/diagrams/default/system-health.svg index 884c78717..2a65f1202 100644 --- a/ground/openpilotgcs/share/openpilotgcs/diagrams/default/system-health.svg +++ b/ground/openpilotgcs/share/openpilotgcs/diagrams/default/system-health.svg @@ -10,7 +10,7 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="94.2584" + width="113.03149" height="79.57505" id="svg3604" version="1.1" @@ -27,10 +27,10 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="29.851822" - inkscape:cx="82.604436" - inkscape:cy="54.252252" - inkscape:current-layer="background" + inkscape:zoom="5.2771064" + inkscape:cx="50.100274" + inkscape:cy="37.563212" + inkscape:current-layer="layer36" id="namedview3608" showgrid="true" inkscape:window-width="1366" @@ -40,7 +40,8 @@ inkscape:window-maximized="1" showguides="true" inkscape:guide-bbox="true" - inkscape:snap-global="false"> + inkscape:snap-global="false" + units="mm"> + id="grid4540" + empspacing="5" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /> + style="fill:#918a6f;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.09500301;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + + + + + + - - - - - - - - - - SYSTEM HEALTH + x="529.24725" + y="379.43469">SYSTEM HEALTH Sensors + id="tspan5393" + x="-426.10474" + y="440.35391">SENSR Auto + id="tspan5375" + x="-408.18991" + y="440.35474">AUTO Misc. + id="tspan5383" + x="-463.11954" + y="481.81909">MISC Link - Power + id="tspan5387" + x="-462.91629" + y="440.35474">PWR Syst. + id="tspan5389" + x="-478.508" + y="440.35391">SYS - - - - - I/O + x="-444.87003" + y="440.35474">I/O + x="531.36542" + y="383.37735" /> - - + rx="1.3377603" /> + style="fill:#28170b;fill-opacity:1;stroke:none;display:inline" /> + + + + + + + + LINK + + style="display:none"> @@ -1113,15 +1090,15 @@ inkscape:groupmode="layer" id="layer11" inkscape:label="Airspeed-OK" - style="display:inline"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> @@ -1203,88 +1180,88 @@ inkscape:groupmode="layer" id="layer6" inkscape:label="Attitude-OK" - style="display:inline"> + style="display:none"> + style="display:none"> + style="display:none"> + width="19.072226" + height="9.5041265" + x="7.0544181" + y="66.481804" + ry="0.92200589" /> + style="display:none"> + style="display:none"> + style="display:none"> @@ -1292,123 +1269,122 @@ inkscape:groupmode="layer" id="layer21" inkscape:label="I2C-OK" - style="display:inline"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + rx="1.0773503" /> + style="display:none"> + rx="1.0672916" /> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + width="19.113802" + height="9.550107" + x="7.0522652" + y="66.473434" + ry="0.92646646" /> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + rx="1.0032097" /> + rx="1.2054718" /> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> @@ -1792,73 +1767,73 @@ inkscape:groupmode="layer" id="g5432" inkscape:label="Stabilization-Critical" - style="display:inline"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + width="19.139223" + height="9.5711241" + x="7.0209193" + y="66.448311" + ry="0.92850536" /> + style="display:none"> @@ -1866,29 +1841,29 @@ inkscape:groupmode="layer" id="g5448" inkscape:label="Telemetry-Critical" - style="display:inline"> + style="display:none"> + style="display:none"> @@ -1896,92 +1871,92 @@ inkscape:groupmode="layer" id="g5454" inkscape:label="I2C-Critical" - style="display:inline"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + style="display:none"> + rx="1.4796101" /> + rx="0.97841418" /> + style="display:none"> + transform="matrix(1.303815,0,0,1.0006407,-2.9181954,0.76728593)"> + style="display:none"> + inkscape:label="#g3394" + transform="matrix(1.3060972,0,0,1.0001794,-9.286657,0.26650193)"> + style="display:none"> + inkscape:label="#g3390" + transform="matrix(1.3027754,0,0,1.0133326,-6.8947082,0.12363438)"> + style="display:none"> + inkscape:label="#g3386" + transform="matrix(1.2992362,0,0,1.0066736,-4.7769775,0.17889491)"> + style="display:none"> + transform="matrix(1.2972911,0,0,1.0167477,-6.6181285,0.34411746)"> + transform="matrix(1.299095,0,0,1.0167223,19.677703,0.38071462)"> + style="display:none"> + inkscape:label="#g3402" + transform="matrix(1.2955541,0,0,1.0099811,-4.6286669,0.49590947)"> + style="display:none"> + transform="matrix(1.011145,0,0,1,-1.2370971,0)" + inkscape:label="#g4252"> + d="m 77.702794,38.300164 32.588486,8.785433" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 77.775534,47.107864 110.25087,38.334251" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + transform="matrix(1.2670269,0,0,1.0033347,-2.5810186,20.235292)"> + style="display:none"> + transform="matrix(1,0,0,0.99018386,0,0.46977719)" + inkscape:label="#g4238"> + d="m 32.903038,38.329601 23.045017,8.819383" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 32.871366,47.123473 56.000932,38.328432" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + + + + + style="display:none"> + transform="matrix(0.99487041,0,0,0.96829624,0.0347729,1.7751095)" + inkscape:label="#g4456"> + d="m 7.5498082,52.475403 18.0821878,9.14342" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 7.476225,61.518003 25.673064,52.498768" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + transform="matrix(0.99485498,0,0,0.97451479,0.20647751,1.4858328)" + inkscape:label="#g4450"> + d="M 27.859968,52.440362 45.94248,61.519868" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="m 27.809675,61.534279 18.104198,-9.06903" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + transform="matrix(1.0165533,0,0,0.96183155,-1.1730886,2.2079995)" + inkscape:label="#g4438"> + d="m 55.345762,52.390536 22.824505,9.161262" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.36421418;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:none;display:inline" /> + d="M 55.34692,61.535992 78.294994,52.441927" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.36357629;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:none;display:inline" /> + style="display:none"> + transform="matrix(0.99024567,0,0,0.95640592,0.16475989,3.1867166)" + inkscape:label="#g4462"> + d="M 7.3536543,66.894221 26.479682,75.463174" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 7.2898997,75.446981 26.492007,66.951786" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + inkscape:label="#g4230"> + d="M 7.431639,4.1245053 31.174293,12.957929" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.36696184;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 7.4395349,12.867421 31.253381,4.1209894" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.36225235;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + transform="matrix(0.99672458,0,0,0.95325152,0.02398426,3.264316)" + inkscape:label="#g4468"> + d="M 28.467187,75.543139 47.482776,66.983913" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="m 28.485471,66.956843 19.057384,8.607021" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> - - - - - + style="display:none" /> + style="display:none"> + transform="matrix(0.99998704,0,0,0.94992153,-0.13295657,3.5178711)" + inkscape:label="#g4480"> + d="M 70.524713,75.503806 89.549784,66.963359" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="m 70.587491,66.948946 18.977538,8.603257" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + transform="matrix(1.0098947,0,0,0.9498994,-0.68444657,3.4889072)" + inkscape:label="#g4474"> + d="M 49.577304,75.608168 68.400595,67.02704" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 49.568595,67.011059 68.46447,75.537737" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + style="display:none"> + transform="matrix(1,0,0,0.94690781,0,3.7499736)" + inkscape:label="#g4486"> + d="M 91.332053,75.577785 110.51399,66.934663" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + d="M 91.383458,66.928045 110.4856,75.596579" + style="fill:#ff0000;stroke:#ff0000;stroke-width:1.41732287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + width="112.74295" + height="78.96769" + x="0.30367571" + y="0.30368456" + ry="0.99887496" /> PATH + x="78.238228" + y="9.280242">PATH PLAN + x="108.69147" + y="9.2802429">PLAN ATTITUDE + id="tspan5813" + x="15.664986" + y="9.2802429">ATTI STAB + x="46.510185" + y="9.2787771">STAB GPS + x="16.553135" + y="21.460487">GPS SENSORS + id="tspan5858" + x="41.87249" + y="21.460487">SENSOR AIRSPEED + id="tspan5860" + x="75.206001" + y="21.460487">AIRSPD MAG + x="110.314" + y="21.461952">MAG CPU + x="118.93848" + y="59.436008">CPU EVENT + id="tspan5907" + x="88.885544" + y="59.436008">EVENT MEM. + id="tspan3865" + x="39.798244" + y="59.436008">MEM STACK + id="tspan5905" + x="62.485939" + y="59.434544">STACK I2C + x="74.377625" + y="36.536701">I2C CONF. + id="tspan5978" + x="68.291183" + y="49.551472">CONFIG BOOT + id="tspan5864" + x="11.171983" + y="61.392075">BOOT TELEM. + id="tspan5862" + x="94.109077" + y="37.710892">TELEMETRY BATT. + id="tspan4077" + x="11.343655" + y="49.551472">BATT TIME - + x="36.921585" + y="49.551472">TIME + style="fill:none;stroke:#ffffff;stroke-width:0.70866144;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> - - - - - - - - - - - - - - - - - - - - + style="fill:none;stroke:#ffffff;stroke-width:0.70866144;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> INPUT - - + id="tspan5909" + x="13.357648" + y="37.674271">INPUT OUTPUT + id="tspan5911" + x="40.654816" + y="37.710892">OUTPUT + + + + + + + + + + + + + + + + + + + +