1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-13 11:29:56 +03:00

Inital commit

This commit is contained in:
Scavanger 2024-11-02 10:51:48 -03:00
parent f3da487264
commit ec46d9ee91
25 changed files with 3243 additions and 23 deletions

View file

@ -4,7 +4,8 @@
"name": "Linux",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/src/main/**"
"${workspaceRoot}/src/main/**",
"${workspaceRoot}/debug_SITL/src/main/target/SITL/SITL/*"
],
"browse": {
"limitSymbolsToIncludedHeaders": false,
@ -12,10 +13,14 @@
"${workspaceRoot}/**"
]
},
"intelliSenseMode": "msvc-x64",
"intelliSenseMode": "gcc-arm",
"cStandard": "c11",
"cppStandard": "c++17",
"defines": [
"uint16_t=unsigned short",
"int16_t=short",
"uint32_t=unsigned int",
"MCU_FLASH_SIZE=1024",
"USE_OSD",
"USE_GYRO_NOTCH_1",
"USE_GYRO_NOTCH_2",
@ -35,9 +40,10 @@
"USE_PCF8574",
"USE_ESC_SENSOR",
"USE_ADAPTIVE_FILTER",
"MCU_FLASH_SIZE 1024",
],
"configurationProvider": "ms-vscode.cmake-tools"
"SITL_BUILD",
"USE_GEOZONE"
]
//"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4

View file

@ -4,10 +4,14 @@
"cmath": "c",
"ranges": "c",
"navigation.h": "c",
"rth_trackback.h": "c"
"rth_trackback.h": "c",
"platform.h": "c",
"timer.h": "c",
"bus.h": "c"
"bus.h": "c",
"controlrate_profile.h": "c",
"settings.h": "c",
"settings_generated.h": "c",
"parameter_group.h": "c"
},
"editor.tabSize": 4,
"editor.insertSpaces": true,

61
.vscode/tasks.json vendored
View file

@ -4,9 +4,9 @@
"version": "2.0.0",
"tasks": [
{
"label": "Build AOCODARCH7DUAL",
"label": "Build Matek F722-SE",
"type": "shell",
"command": "make AOCODARCH7DUAL",
"command": "make MATEKF722SE",
"group": "build",
"problemMatcher": [],
"options": {
@ -14,12 +14,11 @@
}
},
{
"label": "Build AOCODARCH7DUAL",
"label": "Build Matek F722",
"type": "shell",
"command": "make AOCODARCH7DUAL",
"command": "make MATEKF722",
"group": {
"kind": "build",
"isDefault": true
"kind": "build"
},
"problemMatcher": [],
"options": {
@ -36,6 +35,56 @@
"options": {
"cwd": "${workspaceFolder}/build"
}
},
{
"label": "CMAKE Build SITL",
"type": "shell",
"command": "mkdir -p build_SITL && cd build_SITL && cmake -DSITL=ON ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "CMAKE Debug SITL",
"type": "shell",
"command": "mkdir -p debug_SITL && cd debug_SITL && cmake -DCMAKE_BUILD_TYPE=Debug -DSITL=ON ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "CMAKE Release SITL",
"type": "shell",
"command": "mkdir -p release_SITL && cd release_SITL && cmake -DCMAKE_BUILD_TYPE=Release -DSITL=ON ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Debug SITL",
"type": "shell",
"command": "make",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/debug_SITL"
}
},
{
"label": "Release SITL",
"type": "shell",
"command": "make",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/release_SITL"
}
}
]
}

View file

@ -576,6 +576,7 @@ main_sources(COMMON_SRC
navigation/navigation_pos_estimator_flow.c
navigation/navigation_private.h
navigation/navigation_rover_boat.c
navigation/navigation_geozone.c
navigation/sqrt_controller.c
navigation/sqrt_controller.h
navigation/rth_trackback.c

View file

@ -29,6 +29,13 @@ typedef union {
};
} fpVector3_t;
typedef union {
float v[2];
struct {
float x,y;
};
} fpVector2_t;
typedef struct {
float m[3][3];
} fpMat3_t;
@ -116,3 +123,79 @@ static inline fpVector3_t * vectorScale(fpVector3_t * result, const fpVector3_t
*result = ab;
return result;
}
static inline fpVector3_t* vectorSub(fpVector3_t* result, const fpVector3_t* a, const fpVector3_t* b)
{
fpVector3_t ab;
ab.x = a->x - b->x;
ab.y = a->y - b->y;
ab.z = a->z - b->z;
*result = ab;
return result;
}
static inline float vectorDotProduct(const fpVector3_t* a, const fpVector3_t* b)
{
return a->x * b->x + a->y * b->y + a->z * b->z;
}
static inline float vector2NormSquared(const fpVector2_t * v)
{
return sq(v->x) + sq(v->y);
}
static inline fpVector2_t* vector2Normalize(fpVector2_t* result, const fpVector2_t* v)
{
float sqa = vector2NormSquared(v);
float length = sqrtf(sqa);
if (length != 0) {
result->x = v->x / length;
result->y = v->y / length;
} else {
result->x = 0;
result->y = 0;
}
return result;
}
static inline fpVector2_t* vector2Sub(fpVector2_t* result, const fpVector2_t* a, const fpVector2_t* b)
{
fpVector2_t ab;
ab.x = a->x - b->x;
ab.y = a->y - b->y;
*result = ab;
return result;
}
static inline fpVector2_t * vector2Add(fpVector2_t * result, const fpVector2_t * a, const fpVector2_t * b)
{
fpVector2_t ab;
ab.x = a->x + b->x;
ab.y = a->y + b->y;
*result = ab;
return result;
}
static inline float vector2DotProduct(const fpVector2_t* a, const fpVector2_t* b)
{
return a->x * b->x + a->y * b->y;
}
static inline fpVector2_t * vector2Scale(fpVector2_t * result, const fpVector2_t * a, const float b)
{
fpVector2_t ab;
ab.x = a->x * b;
ab.y = a->y * b;
*result = ab;
return result;
}

View file

@ -129,7 +129,10 @@
#define PG_GIMBAL_CONFIG 1039
#define PG_GIMBAL_SERIAL_CONFIG 1040
#define PG_HEADTRACKER_CONFIG 1041
#define PG_INAV_END PG_HEADTRACKER_CONFIG
#define PG_GEOZONE_CONFIG 1042
#define PG_GEOZONES 1043
#define PG_GEOZONE_VERTICES 1044
#define PG_INAV_END PG_GEOZONE_VERTICES
// OSD configuration (subject to change)
//#define PG_OSD_FONT_CONFIG 2047

View file

@ -152,7 +152,7 @@ static uint8_t commandBatchErrorCount = 0;
// sync this with features_e
static const char * const featureNames[] = {
"THR_VBAT_COMP", "VBAT", "TX_PROF_SEL", "BAT_PROF_AUTOSWITCH", "MOTOR_STOP",
"", "SOFTSERIAL", "GPS", "RPM_FILTERS",
"GEOZONE", "SOFTSERIAL", "GPS", "RPM_FILTERS",
"", "TELEMETRY", "CURRENT_METER", "REVERSIBLE_MOTORS", "",
"", "RSSI_ADC", "LED_STRIP", "DASHBOARD", "",
"BLACKBOX", "", "TRANSPONDER", "AIRMODE",
@ -1561,6 +1561,251 @@ static void cliSafeHomes(char *cmdline)
}
#endif
#if defined(USE_GEOZONE)
static void printGeozones(uint8_t dumpMask, const geoZoneConfig_t *geoZone, const geoZoneConfig_t *defaultGeoZone)
{
const char *format = "geozone %u %u %u %d %d %u";
for (uint8_t i = 0; i < MAX_GEOZONES_IN_CONFIG; i++) {
bool equalsDefault = false;
if (defaultGeoZone) {
equalsDefault = geoZone[i].fenceAction == defaultGeoZone->fenceAction
&& geoZone[i].shape == defaultGeoZone->shape
&& geoZone[i].type == defaultGeoZone->type
&& geoZone[i].maxAltitude == defaultGeoZone->maxAltitude
&& geoZone[i].minAltitude == defaultGeoZone->minAltitude;
cliDefaultPrintLinef(dumpMask, equalsDefault, format, defaultGeoZone[i].shape, defaultGeoZone[i].type, defaultGeoZone[i].minAltitude, defaultGeoZone[i].maxAltitude, defaultGeoZone[i].fenceAction);
}
cliDumpPrintLinef(dumpMask, equalsDefault, format, i, geoZone[i].shape, geoZone[i].type, geoZone[i].minAltitude, geoZone[i].maxAltitude, geoZone[i].fenceAction);
}
}
static void printGeozoneVertices(uint8_t dumpMask, const vertexConfig_t *vertices, const vertexConfig_t *defaultVertices)
{
const char *format = "geozone vertex %d %u %d %d";
for (uint8_t i = 0; i < MAX_VERTICES_IN_CONFIG; i++) {
bool equalsDefault = false;
if (defaultVertices) {
equalsDefault = vertices[i].idx == defaultVertices->idx
&& vertices[i].lat == defaultVertices->lat
&& vertices[i].lon == defaultVertices->lon
&& vertices[i].zoneId == defaultVertices->zoneId;
cliDefaultPrintLinef(dumpMask, equalsDefault, format, defaultVertices[i].zoneId, defaultVertices[i].idx, defaultVertices[i].lat, defaultVertices[i].lon);
}
cliDumpPrintLinef(dumpMask, equalsDefault, format, vertices[i].zoneId, vertices[i].idx, vertices[i].lat, vertices[i].lon);
}
if (!defaultVertices) {
uint8_t totalVertices = geozoneGetUsedVerticesCount();
cliPrintLinef("# %u vertices free (Used %u of %u)", MAX_VERTICES_IN_CONFIG - totalVertices, totalVertices, MAX_VERTICES_IN_CONFIG);
}
}
static void cliGeozone(char* cmdLine)
{
if (isEmpty(cmdLine)) {
printGeozones(DUMP_MASTER, geoZonesConfig(0), NULL);
} else if (sl_strcasecmp(cmdLine, "vertex") == 0) {
printGeozoneVertices(DUMP_MASTER, geoZoneVertices(0), NULL);
} else if (sl_strncasecmp(cmdLine, "vertex reset", 12) == 0) {
const char* ptr = &cmdLine[12];
uint8_t zoneId = 0, idx = 0;
uint8_t argumentCount = 1;
if ((ptr = nextArg(ptr))) {
zoneId = fastA2I(ptr);
} else {
geozoneResetVertices(-1, -1);
return;
}
if ((ptr = nextArg(ptr))) {
argumentCount++;
idx = fastA2I(ptr);
} else {
geozoneResetVertices(zoneId, -1);
return;
}
if (argumentCount != 2) {
cliShowParseError();
return;
}
geozoneResetVertices(zoneId, idx);
} else if (sl_strncasecmp(cmdLine, "vertex", 6) == 0) {
int32_t lat = 0, lon = 0;
int8_t zoneId = 0;
int16_t vertexIdx = -1;
uint8_t vertexZoneIdx = 0;
const char* ptr = cmdLine;
uint8_t argumentCount = 1;
if ((ptr = nextArg(ptr))) {
zoneId = fastA2I(ptr);
if (zoneId < 0) {
return;
}
if (zoneId >= MAX_GEOZONES_IN_CONFIG) {
cliShowArgumentRangeError("geozone index", 0, MAX_GEOZONES_IN_CONFIG - 1);
return;
}
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))) {
argumentCount++;
vertexZoneIdx = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))) {
argumentCount++;
lat = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))) {
argumentCount++;
lon = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))) {
argumentCount++;
}
if (argumentCount != 4) {
cliShowParseError();
return;
}
for (uint8_t i = 0; i < MAX_VERTICES_IN_CONFIG; i++) {
if (geoZoneVertices(i)->zoneId == zoneId && geoZoneVertices(i)->idx == vertexZoneIdx) {
geoZoneVerticesMutable(i)->lat = lat;
geoZoneVerticesMutable(i)->lon = lon;
return;
}
}
for (uint8_t i = 0; i < MAX_VERTICES_IN_CONFIG; i++) {
if (geoZoneVertices(i)->zoneId == -1) {
vertexIdx = i;
break;
}
}
if (vertexIdx < 0 || vertexIdx >= MAX_VERTICES_IN_CONFIG || vertexZoneIdx > MAX_VERTICES_IN_CONFIG) {
cliPrintError("Maximum number of vertices reached.");
return;
}
geoZoneVerticesMutable(vertexIdx)->lat = lat;
geoZoneVerticesMutable(vertexIdx)->lon = lon;
geoZoneVerticesMutable(vertexIdx)->zoneId = zoneId;
geoZoneVerticesMutable(vertexIdx)->idx = vertexZoneIdx;
geoZonesConfigMutable(zoneId)->vertexCount++;
uint8_t totalVertices = geozoneGetUsedVerticesCount();
cliPrintLinef("# %u vertices free (Used %u of %u)", MAX_VERTICES_IN_CONFIG - totalVertices, totalVertices, MAX_VERTICES_IN_CONFIG);
} else {
int8_t idx = 0, isPolygon = 0, isInclusive = 0, fenceAction = 0;
int32_t minAltitude = 0, maxAltitude = 0;
const char* ptr = cmdLine;
uint8_t argumentCount = 1;
idx = fastA2I(ptr);
if (idx < 0 || idx > MAX_GEOZONES_IN_CONFIG) {
cliShowArgumentRangeError("geozone index", 0, MAX_GEOZONES_IN_CONFIG - 1);
return;
}
if ((ptr = nextArg(ptr))) {
argumentCount++;
isPolygon = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))){
argumentCount++;
isInclusive = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))){
argumentCount++;
minAltitude = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))){
argumentCount++;
maxAltitude = fastA2I(ptr);
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))){
argumentCount++;
fenceAction = fastA2I(ptr);
if (fenceAction < 0 || fenceAction > GEOFENCE_ACTION_RTH) {
cliShowArgumentRangeError("fence action", 0, GEOFENCE_ACTION_RTH);
return;
}
} else {
cliShowParseError();
return;
}
if ((ptr = nextArg(ptr))){
argumentCount++;
}
if (argumentCount != 6) {
cliShowParseError();
return;
}
if (isPolygon) {
geoZonesConfigMutable(idx)->shape = GEOZONE_SHAPE_POLYGON;
} else {
geoZonesConfigMutable(idx)->shape = GEOZONE_SHAPE_CIRCULAR;
}
if (isInclusive) {
geoZonesConfigMutable(idx)->type = GEOZONE_TYPE_INCLUSIVE;
} else {
geoZonesConfigMutable(idx)->type = GEOZONE_TYPE_EXCLUSIVE;
}
geoZonesConfigMutable(idx)->maxAltitude = maxAltitude;
geoZonesConfigMutable(idx)->minAltitude = minAltitude;
geoZonesConfigMutable(idx)->fenceAction = fenceAction;
}
}
#endif
#if defined(NAV_NON_VOLATILE_WAYPOINT_STORAGE) && defined(NAV_NON_VOLATILE_WAYPOINT_CLI)
static void printWaypoints(uint8_t dumpMask, const navWaypoint_t *navWaypoint, const navWaypoint_t *defaultNavWaypoint)
{
@ -4167,6 +4412,14 @@ static void printConfig(const char *cmdline, bool doDiff)
printFwAutolandApproach(dumpMask, fwAutolandApproachConfig_CopyArray, fwAutolandApproachConfig(0));
#endif
#if defined(USE_GEOZONE)
cliPrintHashLine("geozone");
printGeozones(dumpMask, geoZonesConfig_CopyArray, geoZonesConfig(0));
cliPrintHashLine("geozone vertices");
printGeozoneVertices(dumpMask, geoZoneVertices_CopyArray, geoZoneVertices(0));
#endif
cliPrintHashLine("features");
printFeature(dumpMask, &featureConfig_Copy, featureConfig());
@ -4550,6 +4803,9 @@ const clicmd_t cmdTable[] = {
CLI_COMMAND_DEF("fwapproach", "Fixed Wing Approach Settings", NULL, cliFwAutolandApproach),
#endif
CLI_COMMAND_DEF("get", "get variable value", "[name]", cliGet),
#ifdef USE_GEOZONE
CLI_COMMAND_DEF("geozone", "get or set geo zones", NULL, cliGeozone),
#endif
#ifdef USE_GPS
CLI_COMMAND_DEF("gpspassthrough", "passthrough gps to serial", NULL, cliGpsPassthrough),
CLI_COMMAND_DEF("gpssats", "show GPS satellites", NULL, cliUbloxPrintSatelites),

View file

@ -36,7 +36,7 @@ typedef enum {
FEATURE_VBAT = 1 << 1,
FEATURE_TX_PROF_SEL = 1 << 2, // Profile selection by TX stick command
FEATURE_BAT_PROFILE_AUTOSWITCH = 1 << 3,
FEATURE_UNUSED_12 = 1 << 4, //was FEATURE_MOTOR_STOP
FEATURE_GEOZONE = 1 << 4, //was FEATURE_MOTOR_STOP
FEATURE_UNUSED_1 = 1 << 5, // was FEATURE_SERVO_TILT was FEATURE_DYNAMIC_FILTERS
FEATURE_SOFTSERIAL = 1 << 6,
FEATURE_GPS = 1 << 7,

View file

@ -278,6 +278,14 @@ static void updateArmingStatus(void)
}
#endif
#if defined(USE_GEOZONE) && defined (USE_GPS)
if (geozoneIsInsideNFZ()) {
ENABLE_ARMING_FLAG(ARMING_DISABLED_GEOZONE);
} else {
DISABLE_ARMING_FLAG(ARMING_DISABLED_GEOZONE);
}
#endif
/* CHECK: */
if (
sensors(SENSOR_ACC) &&

View file

@ -1768,6 +1768,53 @@ static mspResult_e mspFwApproachOutCommand(sbuf_t *dst, sbuf_t *src)
}
#endif
#if defined(USE_GEOZONE) && defined (USE_GPS)
static mspResult_e mspFcGeozoneOutCommand(sbuf_t *dst, sbuf_t *src)
{
const uint8_t idx = sbufReadU8(src);
if (idx < MAX_GEOZONES_IN_CONFIG) {
sbufWriteU8(dst, geoZonesConfig(idx)->type);
sbufWriteU8(dst, geoZonesConfig(idx)->shape);
sbufWriteU32(dst, geoZonesConfig(idx)->minAltitude);
sbufWriteU32(dst, geoZonesConfig(idx)->maxAltitude);
sbufWriteU8(dst, geoZonesConfig(idx)->fenceAction);
sbufWriteU8(dst, geoZonesConfig(idx)->vertexCount);
sbufWriteU8(dst, idx);
return MSP_RESULT_ACK;
} else {
return MSP_RESULT_ERROR;
}
}
static mspResult_e mspFcGeozoneVerteciesOutCommand(sbuf_t *dst, sbuf_t *src)
{
const uint8_t zoneId = sbufReadU8(src);
const uint8_t vertexId = sbufReadU8(src);
if (zoneId < MAX_GEOZONES_IN_CONFIG) {
int8_t vertexIdx = geozoneGetVertexIdx(zoneId, vertexId);
if (vertexIdx >= 0) {
sbufWriteU8(dst, geoZoneVertices(vertexIdx)->zoneId);
sbufWriteU8(dst, geoZoneVertices(vertexIdx)->idx);
sbufWriteU32(dst, geoZoneVertices(vertexIdx)->lat);
sbufWriteU32(dst, geoZoneVertices(vertexIdx)->lon);
if (geoZonesConfig(zoneId)->shape == GEOZONE_SHAPE_CIRCULAR) {
int8_t vertexRadiusIdx = geozoneGetVertexIdx(zoneId, vertexId + 1);
if (vertexRadiusIdx >= 0) {
sbufWriteU32(dst, geoZoneVertices(vertexRadiusIdx)->lat);
} else {
return MSP_RESULT_ERROR;
}
}
return MSP_RESULT_ACK;
} else {
return MSP_RESULT_ERROR;
}
} else {
return MSP_RESULT_ERROR;
}
}
#endif
static mspResult_e mspFcLogicConditionCommand(sbuf_t *dst, sbuf_t *src) {
const uint8_t idx = sbufReadU8(src);
if (idx < MAX_LOGIC_CONDITIONS) {
@ -3301,6 +3348,49 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src)
gpsUbloxSendCommand(src->ptr, dataSize, 0);
break;
#if defined(USE_GEOZONE) && defined (USE_GPS)
case MSP2_INAV_SET_GEOZONE:
if (dataSize == 13) {
uint8_t geozoneId;
if (!sbufReadU8Safe(&geozoneId, src) || geozoneId >= MAX_GEOZONES_IN_CONFIG) {
return MSP_RESULT_ERROR;
}
geozoneResetVertices(geozoneId, -1);
geoZonesConfigMutable(geozoneId)->type = sbufReadU8(src);
geoZonesConfigMutable(geozoneId)->shape = sbufReadU8(src);
geoZonesConfigMutable(geozoneId)->minAltitude = sbufReadU32(src);
geoZonesConfigMutable(geozoneId)->maxAltitude = sbufReadU32(src);
geoZonesConfigMutable(geozoneId)->fenceAction = sbufReadU8(src);
geoZonesConfigMutable(geozoneId)->vertexCount = sbufReadU8(src);
} else {
return MSP_RESULT_ERROR;
}
break;
case MSP2_INAV_SET_GEOZONE_VERTEX:
if (dataSize == 10 || dataSize == 14) {
uint8_t geozoneId = 0;
if (!sbufReadU8Safe(&geozoneId, src) || geozoneId >= MAX_GEOZONES_IN_CONFIG) {
return MSP_RESULT_ERROR;
}
uint8_t vertexId = sbufReadU8(src);
int32_t lat = sbufReadU32(src);
int32_t lon = sbufReadU32(src);
if (!geozoneSetVertex(geozoneId, vertexId, lat, lon)) {
return MSP_RESULT_ERROR;
}
if (geoZonesConfig(geozoneId)->shape == GEOZONE_SHAPE_CIRCULAR) {
if (!geozoneSetVertex(geozoneId, vertexId + 1, sbufReadU32(src), 0)) {
return MSP_RESULT_ERROR;
}
}
} else {
return MSP_RESULT_ERROR;
}
break;
#endif
#ifdef USE_EZ_TUNE
case MSP2_INAV_EZ_TUNE_SET:
@ -3862,6 +3952,14 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu
*ret = mspFwApproachOutCommand(dst, src);
break;
#endif
#if defined(USE_GEOZONE) && defined (USE_GPS)
case MSP2_INAV_GEOZONE:
*ret = mspFcGeozoneOutCommand(dst, src);
break;
case MSP2_INAV_GEOZONE_VERTEX:
*ret = mspFcGeozoneVerteciesOutCommand(dst, src);
break;
#endif
#ifdef USE_SIMULATOR
case MSP_SIMULATOR:
tmp_u8 = sbufReadU8(src); // Get the Simulator MSP version

View file

@ -339,6 +339,15 @@ void taskUpdateAux(timeUs_t currentTimeUs)
#endif
}
#if defined(USE_GEOZONE) && defined (USE_GPS)
void geozoneUpdateTask(timeUs_t currentTimeUs)
{
if (feature(FEATURE_GEOZONE)) {
geozoneUpdate(currentTimeUs);
}
}
#endif
void fcTasksInit(void)
{
schedulerInit();
@ -453,6 +462,11 @@ void fcTasksInit(void)
#if defined(SITL_BUILD)
serialProxyStart();
#endif
#if defined(USE_GEOZONE) && defined (USE_GPS)
setTaskEnabled(TASK_GEOZONE, feature(FEATURE_GEOZONE));
#endif
}
cfTask_t cfTasks[TASK_COUNT] = {
@ -740,4 +754,13 @@ cfTask_t cfTasks[TASK_COUNT] = {
},
#endif
#if defined(USE_GEOZONE) && defined (USE_GPS)
[TASK_GEOZONE] = {
.taskName = "GEOZONE",
.taskFunc = geozoneUpdateTask,
.desiredPeriod = TASK_PERIOD_HZ(5),
.staticPriority = TASK_PRIORITY_MEDIUM,
},
#endif
};

View file

@ -24,6 +24,7 @@ typedef enum {
SIMULATOR_MODE_HITL = (1 << 4),
SIMULATOR_MODE_SITL = (1 << 5),
ARMING_DISABLED_GEOZONE = (1 << 6),
ARMING_DISABLED_FAILSAFE_SYSTEM = (1 << 7),
ARMING_DISABLED_NOT_LEVEL = (1 << 8),
ARMING_DISABLED_SENSORS_CALIBRATING = (1 << 9),
@ -49,8 +50,8 @@ typedef enum {
ARMING_DISABLED_DSHOT_BEEPER = (1 << 29),
ARMING_DISABLED_LANDING_DETECTED = (1 << 30),
ARMING_DISABLED_ALL_FLAGS = (ARMING_DISABLED_FAILSAFE_SYSTEM | ARMING_DISABLED_NOT_LEVEL | ARMING_DISABLED_SENSORS_CALIBRATING |
ARMING_DISABLED_SYSTEM_OVERLOADED | ARMING_DISABLED_NAVIGATION_UNSAFE |
ARMING_DISABLED_ALL_FLAGS = (ARMING_DISABLED_GEOZONE | ARMING_DISABLED_FAILSAFE_SYSTEM | ARMING_DISABLED_NOT_LEVEL |
ARMING_DISABLED_SENSORS_CALIBRATING | ARMING_DISABLED_SYSTEM_OVERLOADED | ARMING_DISABLED_NAVIGATION_UNSAFE |
ARMING_DISABLED_COMPASS_NOT_CALIBRATED | ARMING_DISABLED_ACCELEROMETER_NOT_CALIBRATED |
ARMING_DISABLED_ARM_SWITCH | ARMING_DISABLED_HARDWARE_FAILURE | ARMING_DISABLED_BOXFAILSAFE |
ARMING_DISABLED_RC_LINK | ARMING_DISABLED_THROTTLE | ARMING_DISABLED_CLI |
@ -65,7 +66,8 @@ typedef enum {
// situations where we might just need the motors to spin so the
// aircraft can move (even unpredictably) and get unstuck (e.g.
// crashed into a high tree).
#define ARMING_DISABLED_EMERGENCY_OVERRIDE (ARMING_DISABLED_NOT_LEVEL \
#define ARMING_DISABLED_EMERGENCY_OVERRIDE (ARMING_DISABLED_GEOZONE \
| ARMING_DISABLED_NOT_LEVEL \
| ARMING_DISABLED_NAVIGATION_UNSAFE \
| ARMING_DISABLED_COMPASS_NOT_CALIBRATED \
| ARMING_DISABLED_ACCELEROMETER_NOT_CALIBRATED \
@ -106,6 +108,7 @@ typedef enum {
SOARING_MODE = (1 << 16),
ANGLEHOLD_MODE = (1 << 17),
NAV_FW_AUTOLAND = (1 << 18),
NAV_SEND_TO = (1 << 19),
} flightModeFlags_e;
extern uint32_t flightModeFlags;

View file

@ -198,6 +198,11 @@ tables:
- name: headtracker_dev_type
values: ["NONE", "SERIAL", "MSP"]
enum: headTrackerDevType_e
- name: fence_action
values: ["NONE", "AVOID", "POS_HOLD", "RTH"]
enum: fenceAction_e
- name: geozone_rth_no_way_home
values: [RTH, EMRG_LAND]
constants:
RPYL_PID_MIN: 0
@ -3776,6 +3781,7 @@ groups:
condition: USE_OSD || USE_DJI_HD_OSD
members:
- name: osd_speed_source
condition: USE_GEOZONE
description: "Sets the speed type displayed by the DJI OSD and OSD canvas (FrSky Pixel): GROUND, 3D, AIR"
default_value: "GROUND"
field: speedSource
@ -4314,3 +4320,49 @@ groups:
field: roll_ratio
min: 0
max: 5
- name: PG_GEOZONE_CONFIG
type: geozone_config_t
headers: ["navigation/navigation.h"]
condition: USE_GEOZONE && USE_GPS
members:
- name: geozone_detection_distance
description: "Distance from which a geozone is detected"
default_value: 50000
field: fenceDetectionDistance
min: 0
max: 1000000
- name: geozone_avoid_altitude_range
description: "Altitude range in which an attempt is made to avoid a geozone upwards"
default_value: 5000
field: avoidAltitudeRange
min: 0
max: 1000000
- name: geozone_safe_altitude_distance
description: "Vertical distance that must be maintained to the upper and lower limits of the zone."
default_value: 1000
field: safeAltitudeDistance
min: 0
max: 10000
- name: geozone_safehome_as_inclusive
description: "Treat nearest safehome as inclusive geozone"
type: bool
field: nearestSafeHomeAsInclusivZone
default_value: OFF
- name: geozone_safehome_zone_action
description: "Fence action for safehome zone"
default_value: "NONE"
field: safeHomeFenceAction
table: fence_action
type: uint8_t
- name: geozone_mr_stop_distance
description: "Distance in which multirotors stops before the border"
default_value: 15000
min: 0
max: 100000
field: copterFenceStopDistance
- name: geozone_no_way_home_action
description: "Action if RTH with active geozones is unable to calculate a course to home"
default_value: RTH
field: noWayHomeAction
table: geozone_rth_no_way_home
type: uint8_t

View file

@ -866,6 +866,8 @@ static const char * osdArmingDisabledReasonMessage(void)
return OSD_MESSAGE_STR(OSD_MSG_NO_PREARM);
case ARMING_DISABLED_DSHOT_BEEPER:
return OSD_MESSAGE_STR(OSD_MSG_DSHOT_BEEPER);
case ARMING_DISABLED_GEOZONE:
return OSD_MESSAGE_STR(OSD_MSG_NFZ);
// Cases without message
case ARMING_DISABLED_LANDING_DETECTED:
FALLTHROUGH;
@ -2336,6 +2338,11 @@ static bool osdDrawSingleElement(uint8_t item)
if (FLIGHT_MODE(NAV_FW_AUTOLAND))
p = "LAND";
else
#endif
#ifdef USE_GEOZONE
if (FLIGHT_MODE(NAV_SEND_TO))
p = "AUTO";
else
#endif
if (FLIGHT_MODE(FAILSAFE_MODE))
p = "!FS!";
@ -3797,6 +3804,52 @@ static bool osdDrawSingleElement(uint8_t item)
clearMultiFunction = true;
break;
}
#if defined(USE_GEOZONE)
case OSD_COURSE_TO_FENCE:
{
if (navigationPositionEstimateIsHealthy() && isGeozoneActive()) {
int16_t panHomeDirOffset = 0;
if (!(osdConfig()->pan_servo_pwm2centideg == 0)){
panHomeDirOffset = osdGetPanServoOffset();
}
int16_t flightDirection = STATE(AIRPLANE) ? CENTIDEGREES_TO_DEGREES(posControl.actualState.cog) : DECIDEGREES_TO_DEGREES(osdGetHeading());
int direction = CENTIDEGREES_TO_DEGREES(geozone.directionToNearestZone) - flightDirection + panHomeDirOffset;
osdDrawDirArrow(osdDisplayPort, osdGetDisplayPortCanvas(), OSD_DRAW_POINT_GRID(elemPosX, elemPosY), direction);
} else {
if (isGeozoneActive()) {
TEXT_ATTRIBUTES_ADD_BLINK(elemAttr);
}
displayWriteCharWithAttr(osdDisplayPort, elemPosX, elemPosY, '-', elemAttr);
}
break;
}
case OSD_H_DIST_TO_FENCE:
{
if (navigationPositionEstimateIsHealthy() && isGeozoneActive()) {
char buff2[12];
osdFormatDistanceSymbol(buff2, geozone.distanceHorToNearestZone, 0);
tfp_sprintf(buff, "FD %s", buff2 );
} else {
strcpy(buff, "FD ---");
}
}
break;
case OSD_V_DIST_TO_FENCE:
{
if (navigationPositionEstimateIsHealthy() && isGeozoneActive()) {
char buff2[12];
osdFormatAltitudeSymbol(buff2, abs(geozone.distanceVertToNearestZone));
tfp_sprintf(buff, "FD%s", buff2);
displayWriteCharWithAttr(osdDisplayPort, elemPosX + 8, elemPosY, geozone.distanceVertToNearestZone < 0 ? SYM_DECORATION + 4 : SYM_DECORATION, elemAttr);
} else {
strcpy(buff, "FD ---");
}
break;
}
#endif
default:
return false;
@ -4998,7 +5051,7 @@ uint8_t drawStat_ESCTemperature(uint8_t col, uint8_t row, uint8_t statValX)
uint8_t drawStat_GForce(uint8_t col, uint8_t row, uint8_t statValX)
{
char buff[12];
char outBuff[12];
char outBuff[20];
const float max_gforce = accGetMeasuredMaxG();
const acc_extremes_t *acc_extremes = accGetMeasuredExtremes();
@ -5784,6 +5837,12 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter
messages[messageCount++] = safehomeMessage;
}
#endif
#ifdef USE_GEOZONE
if (geozone.avoidInRTHInProgress) {
messages[messageCount++] = OSD_MSG_AVOID_ZONES_RTH;
}
#endif
if (FLIGHT_MODE(FAILSAFE_MODE)) { // In FS mode while armed
if (NAV_Status.state == MW_NAV_STATE_LAND_SETTLE && posControl.landingDelay > 0) {
uint16_t remainingHoldSec = MS2S(posControl.landingDelay - millis());
@ -5846,6 +5905,64 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter
messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_PITCH);
}
}
#ifdef USE_GEOZONE
char buf[12], buf1[12];
switch (geozone.messageState) {
case GEOZONE_MESSAGE_STATE_NFZ:
messages[messageCount++] = OSD_MSG_NFZ;
break;
case GEOZONE_MESSAGE_STATE_LEAVING_FZ:
osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0);
tfp_sprintf(messageBuf, OSD_MSG_LEAVING_FZ, buf);
messages[messageCount++] = messageBuf;
break;
case GEOZONE_MESSAGE_STATE_OUTSIDE_FZ:
messages[messageCount++] = OSD_MSG_OUTSIDE_FZ;
break;
case GEOZONE_MESSAGE_STATE_ENTERING_NFZ:
osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0);
if (geozone.zoneInfo == INT32_MAX) {
tfp_sprintf(buf1, "%s%c", "INF", SYM_ALT_M);
} else {
osdFormatAltitudeSymbol(buf1, geozone.zoneInfo);
}
tfp_sprintf(messageBuf, OSD_MSG_ENTERING_NFZ, buf, buf1);
messages[messageCount++] = messageBuf;
break;
case GEOZONE_MESSAGE_STATE_AVOIDING_FB:
messages[messageCount++] = OSD_MSG_AVOIDING_FB;
if (!posControl.sendTo.lockSticks) {
messages[messageCount++] = OSD_MSG_MOVE_STICKS;
}
break;
case GEOZONE_MESSAGE_STATE_RETURN_TO_ZONE:
messages[messageCount++] = OSD_MSG_RETURN_TO_ZONE;
if (!posControl.sendTo.lockSticks) {
messages[messageCount++] = OSD_MSG_MOVE_STICKS;
}
break;
case GEOZONE_MESSAGE_STATE_AVOIDING_ALTITUDE_BREACH:
messages[messageCount++] = OSD_MSG_AVOIDING_ALT_BREACH;
if (!posControl.sendTo.lockSticks) {
messages[messageCount++] = OSD_MSG_MOVE_STICKS;
}
break;
case GEOZONE_MESSAGE_STATE_FLYOUT_NFZ:
messages[messageCount++] = OSD_MSG_FLYOUT_NFZ;
if (!posControl.sendTo.lockSticks) {
messages[messageCount++] = OSD_MSG_MOVE_STICKS;
}
break;
case GEOZONE_MESSAGE_STATE_LOITER:
messages[messageCount++] = OSD_MSG_AVOIDING_FB;
if (!geozone.sticksLocked) {
messages[messageCount++] = OSD_MSG_MOVE_STICKS;
}
break;
case GEOZONE_MESSAGE_STATE_NONE:
break;
}
#endif
} else if (STATE(MULTIROTOR)) { /* ADDS MAXIMUM OF 2 MESSAGES TO TOTAL */
if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE)) {
if (posControl.cruise.multicopterSpeed >= 50.0f) {

View file

@ -132,6 +132,19 @@
#define OSD_MSG_LOITERING_SAFEHOME "LOITERING AROUND SAFEHOME"
#endif
#if defined(USE_GEOZONE)
#define OSD_MSG_NFZ "NO FLY ZONE"
#define OSD_MSG_LEAVING_FZ "LEAVING FZ IN %s"
#define OSD_MSG_OUTSIDE_FZ "OUTSIDE FZ"
#define OSD_MSG_ENTERING_NFZ "ENTERING NFZ IN %s %s"
#define OSD_MSG_AVOIDING_FB "AVOIDING FENCE BREACH"
#define OSD_MSG_RETURN_TO_ZONE "RETURN TO FZ"
#define OSD_MSG_FLYOUT_NFZ "FLY OUT NFZ"
#define OSD_MSG_AVOIDING_ALT_BREACH "REACHED ZONE ALTITUDE LIMIT"
#define OSD_MSG_AVOID_ZONES_RTH "AVOIDING NO FLY ZONES"
#define OSD_MSG_GEOZONE_ACTION "PERFORM ACTION IN %s %s"
#endif
typedef enum {
OSD_RSSI_VALUE,
OSD_MAIN_BATT_VOLTAGE,
@ -287,6 +300,9 @@ typedef enum {
OSD_ADSB_INFO,
OSD_BLACKBOX,
OSD_FORMATION_FLIGHT, //153
OSD_COURSE_TO_FENCE,
OSD_H_DIST_TO_FENCE,
OSD_V_DIST_TO_FENCE,
OSD_ITEM_COUNT // MUST BE LAST
} osd_items_e;
@ -467,6 +483,10 @@ typedef struct osdConfig_s {
uint16_t adsb_ignore_plane_above_me_limit; // in metres
#endif
uint8_t radar_peers_display_time; // in seconds
#ifdef USE_GEOZONE
uint8_t geozoneDistanceWarning; // Distance to fence or action
bool geozoneDistanceType; // Shows a countdown timer or distance to fence/action
#endif
} osdConfig_t;
PG_DECLARE(osdConfig_t, osdConfig);

View file

@ -525,6 +525,8 @@ static char * osdArmingDisabledReasonMessage(void)
case ARMING_DISABLED_DSHOT_BEEPER:
return OSD_MESSAGE_STR("MOTOR BEEPER ACTIVE");
// Cases without message
case ARMING_DISABLED_GEOZONE:
return OSD_MESSAGE_STR("NO FLY ZONE");
case ARMING_DISABLED_LANDING_DETECTED:
FALLTHROUGH;
case ARMING_DISABLED_CMS_MENU:

View file

@ -112,4 +112,9 @@
#define MSP2_INAV_SET_CUSTOM_OSD_ELEMENTS 0x2101
#define MSP2_INAV_SERVO_CONFIG 0x2200
#define MSP2_INAV_SET_SERVO_CONFIG 0x2201
#define MSP2_INAV_SET_SERVO_CONFIG 0x2201
#define MSP2_INAV_GEOZONE 0x2210
#define MSP2_INAV_SET_GEOZONE 0x2211
#define MSP2_INAV_GEOZONE_VERTEX 0x2212
#define MSP2_INAV_SET_GEOZONE_VERTEX 0x2213

View file

@ -356,6 +356,11 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_FW_LANDING_FLARE(naviga
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_FW_LANDING_FINISHED(navigationFSMState_t previousState);
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_FW_LANDING_ABORT(navigationFSMState_t previousState);
#endif
#ifdef USE_GEOZONE
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_SEND_TO_INITALIZE(navigationFSMState_t previousState);
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_SEND_TO_IN_PROGRESS(navigationFSMState_t previousState);
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_SEND_TO_FINISHED(navigationFSMState_t previousState);
#endif
static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
/** Idle state ******************************************************/
@ -377,6 +382,7 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
[NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD] = NAV_STATE_COURSE_HOLD_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_CRUISE] = NAV_STATE_CRUISE_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_MIXERAT] = NAV_STATE_MIXERAT_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_SEND_TO] = NAV_STATE_SEND_TO_INITALIZE,
}
},
@ -449,6 +455,7 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD] = NAV_STATE_COURSE_HOLD_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_CRUISE] = NAV_STATE_CRUISE_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_SEND_TO] = NAV_STATE_SEND_TO_INITALIZE,
}
},
/** CRUISE_HOLD mode ************************************************/
@ -485,6 +492,7 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
[NAV_FSM_EVENT_SWITCH_TO_RTH] = NAV_STATE_RTH_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_WAYPOINT] = NAV_STATE_WAYPOINT_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_SEND_TO] = NAV_STATE_SEND_TO_INITALIZE,
}
},
@ -507,6 +515,7 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
[NAV_FSM_EVENT_SWITCH_TO_RTH] = NAV_STATE_RTH_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_WAYPOINT] = NAV_STATE_WAYPOINT_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_SEND_TO] = NAV_STATE_SEND_TO_INITALIZE,
}
},
@ -544,6 +553,7 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
[NAV_FSM_EVENT_SWITCH_TO_RTH] = NAV_STATE_RTH_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_WAYPOINT] = NAV_STATE_WAYPOINT_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_SEND_TO] = NAV_STATE_SEND_TO_INITALIZE,
}
},
@ -566,6 +576,7 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
[NAV_FSM_EVENT_SWITCH_TO_RTH] = NAV_STATE_RTH_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_WAYPOINT] = NAV_STATE_WAYPOINT_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_SEND_TO] = NAV_STATE_SEND_TO_INITALIZE,
}
},
@ -1178,6 +1189,63 @@ static const navigationFSMStateDescriptor_t navFSM[NAV_STATE_COUNT] = {
}
},
#endif
#ifdef USE_GEOZONE
[NAV_STATE_SEND_TO_INITALIZE] = {
.persistentId = NAV_PERSISTENT_ID_SEND_TO_INITALIZE,
.onEntry = navOnEnteringState_NAV_STATE_SEND_TO_INITALIZE,
.timeoutMs = 0,
.stateFlags = NAV_CTL_ALT | NAV_CTL_POS | NAV_CTL_YAW | NAV_REQUIRE_ANGLE | NAV_REQUIRE_MAGHOLD | NAV_REQUIRE_THRTILT | NAV_AUTO_WP,
.mapToFlightModes = NAV_SEND_TO,
.mwState = MW_NAV_STATE_NONE,
.mwError = MW_NAV_ERROR_NONE,
.onEvent = {
[NAV_FSM_EVENT_SUCCESS] = NAV_STATE_SEND_TO_IN_PROGESS,
[NAV_FSM_EVENT_ERROR] = NAV_STATE_IDLE,
[NAV_FSM_EVENT_SWITCH_TO_IDLE] = NAV_STATE_IDLE,
}
},
[NAV_STATE_SEND_TO_IN_PROGESS] = {
.persistentId = NAV_PERSISTENT_ID_SEND_TO_IN_PROGRES,
.onEntry = navOnEnteringState_NAV_STATE_SEND_TO_IN_PROGRESS,
.timeoutMs = 10,
.stateFlags = NAV_CTL_ALT | NAV_CTL_POS | NAV_CTL_YAW | NAV_REQUIRE_ANGLE | NAV_REQUIRE_MAGHOLD | NAV_REQUIRE_THRTILT | NAV_AUTO_WP,
.mapToFlightModes = NAV_SEND_TO,
.mwState = MW_NAV_STATE_NONE,
.mwError = MW_NAV_ERROR_NONE,
.onEvent = {
[NAV_FSM_EVENT_TIMEOUT] = NAV_STATE_SEND_TO_IN_PROGESS, // re-process the state
[NAV_FSM_EVENT_SUCCESS] = NAV_STATE_SEND_TO_FINISHED,
[NAV_FSM_EVENT_SWITCH_TO_IDLE] = NAV_STATE_IDLE,
[NAV_FSM_EVENT_SWITCH_TO_ALTHOLD] = NAV_STATE_ALTHOLD_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D] = NAV_STATE_POSHOLD_3D_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_RTH] = NAV_STATE_RTH_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD] = NAV_STATE_COURSE_HOLD_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_CRUISE] = NAV_STATE_CRUISE_INITIALIZE,
}
},
[NAV_STATE_SEND_TO_FINISHED] = {
.persistentId = NAV_PERSISTENT_ID_SEND_TO_FINISHED,
.onEntry = navOnEnteringState_NAV_STATE_SEND_TO_FINISHED,
.timeoutMs = 0,
.stateFlags = NAV_CTL_ALT | NAV_CTL_POS | NAV_CTL_YAW | NAV_REQUIRE_ANGLE | NAV_REQUIRE_MAGHOLD | NAV_REQUIRE_THRTILT | NAV_AUTO_WP,
.mapToFlightModes = NAV_SEND_TO,
.mwState = MW_NAV_STATE_NONE,
.mwError = MW_NAV_ERROR_NONE,
.onEvent = {
[NAV_FSM_EVENT_TIMEOUT] = NAV_STATE_SEND_TO_FINISHED, // re-process the state
[NAV_FSM_EVENT_SWITCH_TO_IDLE] = NAV_STATE_IDLE,
[NAV_FSM_EVENT_SWITCH_TO_ALTHOLD] = NAV_STATE_ALTHOLD_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D] = NAV_STATE_POSHOLD_3D_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_RTH] = NAV_STATE_RTH_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING] = NAV_STATE_EMERGENCY_LANDING_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_COURSE_HOLD] = NAV_STATE_COURSE_HOLD_INITIALIZE,
[NAV_FSM_EVENT_SWITCH_TO_CRUISE] = NAV_STATE_CRUISE_INITIALIZE,
}
},
#endif
};
static navigationFSMStateFlags_t navGetStateFlags(navigationFSMState_t state)
@ -1444,6 +1512,10 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_INITIALIZE(navigati
// If we have valid position sensor or configured to ignore it's loss at initial stage - continue
if ((posControl.flags.estPosStatus >= EST_USABLE) || navConfig()->general.flags.rth_climb_ignore_emerg) {
// Prepare controllers
#ifdef USE_GEOZONE
geozoneResetRTH();
geozoneSetupRTH();
#endif
resetPositionController();
resetAltitudeController(false); // Make sure surface tracking is not enabled - RTH uses global altitude, not AGL
setupAltitudeController();
@ -1610,6 +1682,36 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigatio
// If we have position sensor - continue home
if ((posControl.flags.estPosStatus >= EST_USABLE)) {
#ifdef USE_GEOZONE
// Check for NFZ in our way
int8_t wpCount = geozoneCheckForNFZAtCourse(true);
if (wpCount > 0) {
calculateAndSetActiveWaypointToLocalPosition(geozoneGetCurrentRthAvoidWaypoint());
return NAV_FSM_EVENT_NONE;
} else if (geozone.avoidInRTHInProgress) {
if (isWaypointReached(geozoneGetCurrentRthAvoidWaypoint(), &posControl.activeWaypoint.bearing)) {
if (geoZoneIsLastRthWaypoint()) {
// Already at Home?
fpVector3_t *tmpHomePos = rthGetHomeTargetPosition(RTH_HOME_ENROUTE_PROPORTIONAL);
if (isWaypointReached(tmpHomePos, &posControl.activeWaypoint.bearing)) {
setDesiredPosition(tmpHomePos, posControl.rthState.homePosition.heading, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_HEADING);
return NAV_FSM_EVENT_SUCCESS;
}
posControl.rthState.rthInitialAltitude = posControl.actualState.abs.pos.z;
return NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_RTH_INITIALIZE;
} else {
geozoneAdvanceRthAvoidWaypoint();
calculateAndSetActiveWaypointToLocalPosition(geozoneGetCurrentRthAvoidWaypoint());
return NAV_FSM_EVENT_NONE;
}
}
setDesiredPosition(geozoneGetCurrentRthAvoidWaypoint(), 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_BEARING);
return NAV_FSM_EVENT_NONE;
} else if (wpCount < 0 && geoZoneConfig()->noWayHomeAction == NO_WAY_HOME_ACTION_EMRG_LAND) {
return NAV_FSM_EVENT_SWITCH_TO_EMERGENCY_LANDING;
} else {
#endif
fpVector3_t * tmpHomePos = rthGetHomeTargetPosition(RTH_HOME_ENROUTE_PROPORTIONAL);
if (isWaypointReached(tmpHomePos, &posControl.activeWaypoint.bearing)) {
@ -1626,6 +1728,9 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_HEAD_HOME(navigatio
setDesiredPosition(tmpHomePos, 0, NAV_POS_UPDATE_Z | NAV_POS_UPDATE_XY);
return NAV_FSM_EVENT_NONE;
}
#ifdef USE_GEOZONE
}
#endif
}
/* Position sensor failure timeout - land */
else if (checkForPositionSensorTimeout()) {
@ -1797,6 +1902,10 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_RTH_FINISHED(navigation
updateClimbRateToAltitudeController(-1.1f * navConfig()->general.land_minalt_vspd, 0, ROC_TO_ALT_CONSTANT); // FIXME
}
#ifdef USE_GEOZONE
geozoneResetRTH();
#endif
// Prevent I-terms growing when already landed
pidResetErrorAccumulators();
return NAV_FSM_EVENT_NONE;
@ -2452,6 +2561,64 @@ static navigationFSMEvent_t navOnEnteringState_NAV_STATE_FW_LANDING_ABORT(naviga
}
#endif
#ifdef USE_GEOZONE
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_SEND_TO_INITALIZE(navigationFSMState_t previousState)
{
UNUSED(previousState);
if (checkForPositionSensorTimeout()) {
return NAV_FSM_EVENT_SWITCH_TO_IDLE;
}
resetPositionController();
resetAltitudeController(false);
if (navConfig()->general.flags.rth_tail_first && !STATE(FIXED_WING_LEGACY)) {
setDesiredPosition(&posControl.sendTo.targetPos, 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_BEARING_TAIL_FIRST);
} else {
setDesiredPosition(&posControl.sendTo.targetPos, 0, NAV_POS_UPDATE_XY | NAV_POS_UPDATE_Z | NAV_POS_UPDATE_BEARING);
}
posControl.sendTo.startTime = millis();
return NAV_FSM_EVENT_SUCCESS;
}
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_SEND_TO_IN_PROGRESS(navigationFSMState_t previousState)
{
UNUSED(previousState);
// "Send to" is designed to kick in even user is making inputs, lock sticks for a short time to avoid the mode ends immediately
if (posControl.sendTo.lockSticks && millis() - posControl.sendTo.startTime > posControl.sendTo.lockStickTime) {
posControl.sendTo.lockSticks = false;
}
if (!posControl.sendTo.lockSticks && areSticksDeflected()) {
abortSendTo();
return NAV_FSM_EVENT_SWITCH_TO_IDLE;
}
if (calculateDistanceToDestination(&posControl.sendTo.targetPos) <= posControl.sendTo.targetRange ) {
if (posControl.sendTo.altitudeTargetRange > 0) {
if ((getEstimatedActualPosition(Z) > posControl.sendTo.targetPos.z - posControl.sendTo.altitudeTargetRange) && (getEstimatedActualPosition(Z) < posControl.sendTo.targetPos.z + posControl.sendTo.altitudeTargetRange)) {
return NAV_FSM_EVENT_SUCCESS;
} else {
return NAV_FSM_EVENT_NONE;
}
}
return NAV_FSM_EVENT_SUCCESS;
}
return NAV_FSM_EVENT_NONE;
}
static navigationFSMEvent_t navOnEnteringState_NAV_STATE_SEND_TO_FINISHED(navigationFSMState_t previousState)
{
UNUSED(previousState);
posControl.sendTo.lockSticks = false;
posControl.flags.sendToActive = false;
return NAV_FSM_EVENT_NONE;
}
#endif
static navigationFSMState_t navSetNewFSMState(navigationFSMState_t newState)
{
navigationFSMState_t previousState;
@ -2539,6 +2706,11 @@ static fpVector3_t * rthGetHomeTargetPosition(rthTargetMode_e mode)
switch (mode) {
case RTH_HOME_ENROUTE_INITIAL:
posControl.rthState.homeTmpWaypoint.z = posControl.rthState.rthInitialAltitude;
#ifdef USE_GEOZONE
if (geozone.currentzoneMaxAltitude > 0) {
posControl.rthState.homeTmpWaypoint.z = MIN(geozone.currentzoneMaxAltitude, posControl.rthState.homeTmpWaypoint.z);
}
#endif
break;
case RTH_HOME_ENROUTE_PROPORTIONAL:
@ -2552,6 +2724,11 @@ static fpVector3_t * rthGetHomeTargetPosition(rthTargetMode_e mode)
posControl.rthState.homeTmpWaypoint.z = posControl.rthState.rthFinalAltitude;
}
}
#ifdef USE_GEOZONE
if (geozone.distanceVertToNearestZone < 0 && ABS(geozone.distanceVertToNearestZone) < geoZoneConfig()->safeAltitudeDistance) {
posControl.rthState.homeTmpWaypoint.z += geoZoneConfig()->safeAltitudeDistance;
}
#endif
break;
case RTH_HOME_ENROUTE_FINAL:
@ -2757,6 +2934,17 @@ const navEstimatedPosVel_t * navGetCurrentActualPositionAndVelocity(void)
return posControl.flags.isTerrainFollowEnabled ? &posControl.actualState.agl : &posControl.actualState.abs;
}
/*-----------------------------------------------------------
* Calculates 2D distance between two points
*-----------------------------------------------------------*/
float calculateDistance2(const fpVector2_t* startPos, const fpVector2_t* destinationPos)
{
const float deltaX = destinationPos->x - startPos->x;
const float deltaY = destinationPos->y - startPos->y;
return calc_length_pythagorean_2D(deltaX, deltaY);
}
/*-----------------------------------------------------------
* Calculates distance and bearing to destination point
*-----------------------------------------------------------*/
@ -2944,6 +3132,11 @@ static void updateDesiredRTHAltitude(void)
posControl.rthState.rthInitialAltitude = posControl.actualState.abs.pos.z;
posControl.rthState.rthFinalAltitude = posControl.actualState.abs.pos.z;
}
#if defined(USE_GEOZONE)
if (geozone.homeHasMaxAltitue) {
posControl.rthState.rthFinalAltitude = MIN(posControl.rthState.rthFinalAltitude, geozone.maxHomeAltitude);
}
#endif
}
/*-----------------------------------------------------------
@ -3154,6 +3347,9 @@ void updateHomePosition(void)
setHome = true;
break;
}
#if defined(USE_GEOZONE) && defined (USE_GPS)
geozoneUpdateMaxHomeAltitude();
#endif
}
}
else {
@ -3409,7 +3605,14 @@ bool isProbablyStillFlying(void)
* Z-position controller
*-----------------------------------------------------------*/
float getDesiredClimbRate(float targetAltitude, timeDelta_t deltaMicros)
{
{
#ifdef USE_GEOZONE
if ((geozone.currentzoneMaxAltitude > 0 && navGetCurrentActualPositionAndVelocity()->pos.z >= geozone.currentzoneMaxAltitude && posControl.desiredState.climbRateDemand > 0) ||
(geozone.currentzoneMinAltitude > 0 && navGetCurrentActualPositionAndVelocity()->pos.z <= geozone.currentzoneMinAltitude && posControl.desiredState.climbRateDemand < 0 )) {
return 0.0f;
}
#endif
const bool emergLandingIsActive = navigationIsExecutingAnEmergencyLanding();
float maxClimbRate = STATE(MULTIROTOR) ? navConfig()->mc.max_auto_climb_rate : navConfig()->fw.max_auto_climb_rate;
@ -4095,6 +4298,10 @@ void applyWaypointNavigationAndAltitudeHold(void)
posControl.activeWaypointIndex = posControl.startWpIndex;
// Reset RTH trackback
resetRthTrackBack();
#ifdef USE_GEOZONE
posControl.flags.sendToActive = false;
#endif
return;
}
@ -4145,7 +4352,7 @@ void applyWaypointNavigationAndAltitudeHold(void)
void switchNavigationFlightModes(void)
{
const flightModeFlags_e enabledNavFlightModes = navGetMappedFlightModes(posControl.navState);
const flightModeFlags_e disabledFlightModes = (NAV_ALTHOLD_MODE | NAV_RTH_MODE | NAV_FW_AUTOLAND | NAV_POSHOLD_MODE | NAV_WP_MODE | NAV_LAUNCH_MODE | NAV_COURSE_HOLD_MODE) & (~enabledNavFlightModes);
const flightModeFlags_e disabledFlightModes = (NAV_ALTHOLD_MODE | NAV_RTH_MODE | NAV_FW_AUTOLAND | NAV_POSHOLD_MODE | NAV_WP_MODE | NAV_LAUNCH_MODE | NAV_COURSE_HOLD_MODE | NAV_SEND_TO) & (~enabledNavFlightModes);
DISABLE_FLIGHT_MODE(disabledFlightModes);
ENABLE_FLIGHT_MODE(enabledNavFlightModes);
}
@ -4296,6 +4503,14 @@ static navigationFSMEvent_t selectNavEventFromBoxModeInput(void)
return NAV_FSM_EVENT_SWITCH_TO_RTH;
}
if (posControl.flags.sendToActive) {
return NAV_FSM_EVENT_SWITCH_TO_SEND_TO;
}
if (posControl.flags.forcedPosholdActive) {
return NAV_FSM_EVENT_SWITCH_TO_POSHOLD_3D;
}
/* WP mission activation control:
* canActivateWaypoint & waypointWasActivated are used to prevent WP mission
* auto restarting after interruption by Manual or RTH modes.
@ -4769,6 +4984,14 @@ void navigationInit(void)
#ifdef USE_MULTI_MISSION
posControl.multiMissionCount = 0;
#endif
#ifdef USE_GEOZONE
posControl.flags.sendToActive = false;
posControl.sendTo.lockSticks = false;
posControl.sendTo.lockStickTime = 0;
posControl.sendTo.startTime = 0;
posControl.sendTo.targetRange = 0;
#endif
/* Set initial surface invalid */
posControl.actualState.surfaceMin = -1.0f;
@ -4851,6 +5074,40 @@ rthState_e getStateOfForcedRTH(void)
}
}
#ifdef USE_GEOZONE
// "Send to" is not to intended to be activated by user, only by external event
void activateSendTo(void)
{
if (!geozone.avoidInRTHInProgress) {
abortFixedWingLaunch();
posControl.flags.sendToActive = true;
navProcessFSMEvents(selectNavEventFromBoxModeInput());
}
}
void abortSendTo(void)
{
posControl.flags.sendToActive = false;
navProcessFSMEvents(selectNavEventFromBoxModeInput());
}
void activateForcedPosHold(void)
{
if (!geozone.avoidInRTHInProgress) {
abortFixedWingLaunch();
posControl.flags.forcedPosholdActive = true;
navProcessFSMEvents(selectNavEventFromBoxModeInput());
}
}
void abortForcedPosHold(void)
{
posControl.flags.forcedPosholdActive = false;
navProcessFSMEvents(selectNavEventFromBoxModeInput());
}
#endif
/*-----------------------------------------------------------
* Ability to execute Emergency Landing on external event
*-----------------------------------------------------------*/

View file

@ -116,6 +116,119 @@ void resetFwAutolandApproach(int8_t idx);
#endif
#if defined(USE_GEOZONE)
#define MAX_GEOZONES_IN_CONFIG 63
#define MAX_VERTICES_IN_CONFIG 126
typedef enum {
GEOZONE_MESSAGE_STATE_NONE,
GEOZONE_MESSAGE_STATE_NFZ,
GEOZONE_MESSAGE_STATE_LEAVING_FZ,
GEOZONE_MESSAGE_STATE_OUTSIDE_FZ,
GEOZONE_MESSAGE_STATE_ENTERING_NFZ,
GEOZONE_MESSAGE_STATE_AVOIDING_FB,
GEOZONE_MESSAGE_STATE_RETURN_TO_ZONE,
GEOZONE_MESSAGE_STATE_FLYOUT_NFZ,
GEOZONE_MESSAGE_STATE_AVOIDING_ALTITUDE_BREACH,
GEOZONE_MESSAGE_STATE_LOITER
} geozoneMessageState_e;
enum fenceAction_e {
GEOFENCE_ACTION_NONE,
GEOFENCE_ACTION_AVOID,
GEOFENCE_ACTION_POS_HOLD,
GEOFENCE_ACTION_RTH,
};
enum noWayHomeAction {
NO_WAY_HOME_ACTION_RTH,
NO_WAY_HOME_ACTION_EMRG_LAND,
};
#define GEOZONE_SHAPE_CIRCULAR 0
#define GEOZONE_SHAPE_POLYGON 1
#define GEOZONE_TYPE_EXCLUSIVE 0
#define GEOZONE_TYPE_INCLUSIVE 1
typedef struct geoZoneConfig_s
{
uint8_t shape;
uint8_t type;
int32_t minAltitude;
int32_t maxAltitude;
uint8_t fenceAction;
uint8_t vertexCount;
} geoZoneConfig_t;
typedef struct geozone_config_s
{
uint32_t fenceDetectionDistance;
uint16_t avoidAltitudeRange;
uint16_t safeAltitudeDistance;
bool nearestSafeHomeAsInclusivZone;
uint8_t safeHomeFenceAction;
uint32_t copterFenceStopDistance;
uint8_t noWayHomeAction;
} geozone_config_t;
typedef struct vertexConfig_s
{
int8_t zoneId;
uint8_t idx;
int32_t lat;
int32_t lon;
} vertexConfig_t;
PG_DECLARE(geozone_config_t, geoZoneConfig);
PG_DECLARE_ARRAY(geoZoneConfig_t, MAX_GEOZONES_IN_CONFIG, geoZonesConfig);
PG_DECLARE_ARRAY(vertexConfig_t, MAX_VERTICES_IN_CONFIG, geoZoneVertices);
typedef struct geozone_s {
bool insideFz;
bool insideNfz;
uint32_t distanceToZoneBorder3d;
int32_t vertDistanceToZoneBorder;
geozoneMessageState_e messageState;
int32_t directionToNearestZone;
int32_t distanceHorToNearestZone;
int32_t distanceVertToNearestZone;
int32_t zoneInfo;
int32_t currentzoneMaxAltitude;
int32_t currentzoneMinAltitude;
bool sticksLocked;
int8_t loiterDir;
bool avoidInRTHInProgress;
int32_t maxHomeAltitude;
bool homeHasMaxAltitue;
} geozone_t;
extern geozone_t geozone;
bool geozoneSetVertex(uint8_t zoneId, uint8_t vertexId, int32_t lat, int32_t lon);
int8_t geozoneGetVertexIdx(uint8_t zoneId, uint8_t vertexId);
bool isGeozoneActive(void);
uint8_t geozoneGetUsedVerticesCount(void);
void geozoneResetVertices(int8_t zoneId, int16_t idx);
void geozoneUpdate(timeUs_t curentTimeUs);
bool geozoneIsInsideNFZ(void);
void geozoneAdvanceRthAvoidWaypoint(void);
int8_t geozoneCheckForNFZAtCourse(bool isRTH);
bool geoZoneIsLastRthWaypoint(void);
fpVector3_t *geozoneGetCurrentRthAvoidWaypoint(void);
void geozoneSetupRTH(void);
void geozoneResetRTH(void);
void geozoneUpdateMaxHomeAltitude(void);
uint32_t geozoneGetDetectionDistance(void);
void activateSendTo(void);
void abortSendTo(void);
void activateForcedPosHold(void);
void abortForcedPosHold(void);
#endif
#ifndef NAV_MAX_WAYPOINTS
#define NAV_MAX_WAYPOINTS 15
#endif

View file

@ -250,6 +250,12 @@ static int8_t loiterDirection(void) {
dir *= -1;
}
#ifdef USE_GEOZONE
if (geozone.loiterDir != 0) {
dir = geozone.loiterDir;
}
#endif
return dir;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,480 @@
/*
* This file is part of INAV.
*
* INAV 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.
*
* INAV 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 INAV. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <float.h>
#include "common/vector.h"
#include "navigation/navigation_private.h"
#define K_EPSILON 1e-8
static bool isPointInCircle(const fpVector2_t *point, const fpVector2_t *center, const float radius)
{
return calculateDistance2(point, center) < radius;
}
static bool isPointInPloygon(const fpVector2_t *point, fpVector2_t* vertices, const uint8_t verticesNum)
{
bool result = false;
fpVector2_t *p1, *p2;
fpVector2_t* prev = &vertices[verticesNum - 1];
fpVector2_t *current;
for (uint8_t i = 0; i < verticesNum; i++)
{
current = &vertices[i];
if (current->x > prev->x) {
p1 = prev;
p2 = current;
} else {
p1 = current;
p2 = prev;
}
if ((current->x < point->x) == (point->x <= prev->x)
&& (point->y - p1->y) * (p2->x - p1->x) < (p2->y - p1->y) * (point->x - p1->x))
{
result = !result;
}
prev = current;
}
return result;
}
static float angelFromSideLength(const float a, const float b, const float c)
{
return acos_approx((sq(b) + sq(c) - sq(a)) / (2 * b * c));
}
static bool isPointRightFromLine(const fpVector2_t *lineStart, const fpVector2_t *lineEnd, const fpVector2_t *point) {
return (lineEnd->x - lineStart->x) * (point->y - lineStart->y) - (lineEnd->y - lineStart->y) * (point->x - lineStart->x) > 0;
}
static float calcAngelFrom3Points(const fpVector2_t* a, const fpVector2_t* b, const fpVector2_t* c)
{
float ab = calculateDistance2(a, b);
float ac = calculateDistance2(a, c);
float bc = calculateDistance2(b, c);
return RADIANS_TO_DEGREES(angelFromSideLength(ab, ac, bc));
}
static void calcPointOnLine(fpVector2_t *result, const fpVector2_t *start, fpVector2_t *end, float distance)
{
fpVector2_t dir, a;
float fac;
vector2Sub(&dir, end, start);
fac = distance / fast_fsqrtf(vector2NormSquared(&dir));
vector2Scale(&a, &dir, fac);
vector2Add(result, start, &a);
}
// Intersection of two lines https://en.wikipedia.org/wiki/Line-line_intersection
bool calcLineLineIntersection(fpVector2_t* intersection, const fpVector2_t* line1Start, const fpVector2_t* line1End, const fpVector2_t* line2Start, const fpVector2_t* line2End, bool isSegment)
{
intersection->x = intersection->y = 0;
// Double precision is needed here
double s1 = line1End->x - line1Start->x;
double t1 = -(line2End->x - line2Start->x);
double r1 = line2Start->x - line1Start->x;
double s2 = line1End->y - line1Start->y;
double t2 = -(line2End->y - line2Start->y);
double r2 = line2Start->y - line1Start->y;
// Use Cramer's rule for the solution of the system of linear equations
double determ = s1 * t2 - t1 * s2;
if (determ == 0) { // No solution
return false;
}
double s0 = (r1 * t2 - t1 * r2) / determ;
double t0 = (s1 * r2 - r1 * s2) / determ;
if (s0 == 0 && t0 == 0) {
return false;
}
// No intersection
if (isSegment && (s0 <= 0 || s0 >= 1 || t0 <= 0 || t0 >= 1)) {
return false;
}
intersection->x = (line1Start->x + (float)s0 * (line1End->x - line1Start->x));
intersection->y = (line1Start->y + (float)s0 * (line1End->y - line1Start->y));
return true;
}
float calculateDistance3(const fpVector3_t* startPos, const fpVector3_t* destinationPos)
{
return fast_fsqrtf(sq(destinationPos->x - startPos->x) + sq(destinationPos->y - startPos->y) + sq(destinationPos->z - startPos->z));
}
static fpVector3_t calcPlaneNormalFromPoints(const fpVector3_t* p1, const fpVector3_t* p2, const fpVector3_t* p3)
{
fpVector3_t result, a, b;
vectorSub(&a, p2, p1);
vectorSub(&b, p3, p1);
vectorCrossProduct(&result, &a, &b);
return result;
}
static fpVector3_t calcDirVectorFromPoints(const fpVector3_t* p1, const fpVector3_t* p2)
{
fpVector3_t result;
vectorSub(&result, p1, p2);
return result;
}
static void generatePolygonFromCircle(fpVector2_t* polygon, const fpVector2_t* center, const float radius, const uint8_t sides)
{
for (uint8_t i = 0, j = sides -1; i < sides; i++, j--) {
polygon[i].x = radius * cos_approx(2 * M_PIf * j / sides) + center->x;
polygon[i].y = radius * sin_approx(2 * M_PIf * j / sides) + center->y;
}
}
// TRUE if point is in the same direction from pos as ref
static bool isInFront(const fpVector3_t *pos, const fpVector3_t *point, const fpVector3_t *ref)
{
fpVector3_t refDir = calcDirVectorFromPoints(pos, ref);
fpVector3_t dir = calcDirVectorFromPoints(point, pos);
return vectorDotProduct(&refDir, &dir) < 0.0f;
}
static fpVector2_t calcNearestPointOnLine(const fpVector2_t *lineStart, const fpVector2_t *lineEnd, const fpVector2_t *point)
{
fpVector2_t ap, ab, prod2, result;
float distance, magAb, prod;
vector2Sub(&ap, point, lineStart);
vector2Sub(&ab, lineEnd, lineStart);
magAb = vector2NormSquared(&ab);
prod = vector2DotProduct(&ap, &ab);
distance = prod / magAb;
if (distance < 0) {
return *lineStart;
} else if (distance > 1) {
return *lineEnd;
}
vector2Scale(&prod2, &ab, distance);
vector2Add(&result, lineStart, &prod2);
return result;
}
static bool isPointOnLine2(const fpVector2_t *lineStart, const fpVector2_t *lineEnd, const fpVector2_t *linepoint)
{
float a = roundf(calculateDistance2(linepoint, lineStart));
float b = roundf(calculateDistance2(linepoint, lineEnd));
float c = roundf(calculateDistance2(lineStart, lineEnd));
return ABS(a + b - c) <= 1;
}
static bool isPointOnLine3(const fpVector3_t *lineStart, const fpVector3_t *lineEnd, const fpVector3_t *linepoint)
{
float a = roundf(calculateDistance3(linepoint, lineStart));
float b = roundf(calculateDistance3(linepoint, lineEnd));
float c = roundf(calculateDistance3(lineStart, lineEnd));
return ABS(a + b - c) <= 1;
}
static bool calcIntersectionLinePlane(fpVector3_t* result, const fpVector3_t* lineVector, const fpVector3_t* linePoint, const fpVector3_t* planeNormal, const fpVector3_t* planePoint)
{
if (vectorDotProduct(linePoint, planeNormal) == 0) {
return false;
}
fpVector3_t diff, p1, p4;
float p2 = 0, p3 = 0;
vectorSub(&diff, linePoint, planePoint);
vectorAdd(&p1, &diff, planePoint);
p2 = vectorDotProduct(&diff, planeNormal);
p3 = vectorDotProduct(lineVector, planeNormal);
vectorScale(&p4, lineVector, -p2 / p3);
vectorAdd(result, &p1, &p4);
return true;
}
// MöllerTrumbore intersection algorithm
static bool calcLineTriangleIntersection(fpVector3_t *intersection, const fpVector3_t* org, const fpVector3_t* dir, const fpVector3_t* v0, const fpVector3_t* v1, const fpVector3_t* v2)
{
fpVector3_t v0v1, v0v2, pvec, tvec, quvec, prod;
float det, invDet, t, u, v;
vectorSub(&v0v1, v1, v0);
vectorSub(&v0v2, v2, v0);
vectorCrossProduct(&pvec, dir, &v0v2);
det = vectorDotProduct(&v0v1, &pvec);
if (fabsf(det) < K_EPSILON) {
return false;
}
invDet = 1.f / det;
vectorSub(&tvec, org, v0);
u = vectorDotProduct(&tvec, &pvec) * invDet;
if (u < 0 || u > 1) {
return false;
}
vectorCrossProduct(&quvec, &tvec, &v0v1);
v = vectorDotProduct(dir, &quvec) * invDet;
if (v < 0 || u + v > 1) {
return false;
}
t = vectorDotProduct(&v0v2, &quvec) * invDet;
vectorScale(&prod, dir, t);
vectorAdd(intersection, &prod, org);
return true;
}
static bool calcLinePolygonIntersection(fpVector3_t *intersect, const fpVector3_t *pos, const fpVector3_t *pos2, const float height, fpVector2_t* vertices, const uint8_t verticesNum)
{
if (verticesNum < 3) {
return false;
}
fpVector3_t p1 = { .x = vertices[0].x, .y = vertices[0].y, .z = height };
fpVector3_t p2 = { .x = vertices[1].x, .y = vertices[1].y, .z = height };
fpVector3_t p3 = { .x = vertices[2].x, .y = vertices[2].y, .z = height };
fpVector3_t normale = calcPlaneNormalFromPoints(&p1, &p2, &p3);
fpVector3_t dir = calcDirVectorFromPoints(pos, pos2);
fpVector3_t tmp;
if (calcIntersectionLinePlane(&tmp, &dir, pos2, &normale, &p1)) {
if (isPointInPloygon((fpVector2_t*)&tmp, vertices, verticesNum)) {
memcpy(intersect, &tmp, sizeof(fpVector3_t));
return true;
}
}
return false;
}
static bool calcLineCircleIntersection(fpVector2_t *intersection1, fpVector2_t *intersection2, const fpVector2_t *startPos, const fpVector2_t *endPos, const fpVector2_t *circleCenter, const float radius)
{
// Unfortunately, we need double precision here
double slope = (double)((endPos->y - startPos->y) / (endPos->x - startPos->x));
double yIntercept = (double)startPos->y - slope * (double)startPos->x;
double a = (double)1.0 + sq(slope);
double b = (double)-2.0 * (double)circleCenter->x + (double)2.0 * slope * (yIntercept - (double)circleCenter->y);
double c = sq((double)circleCenter->x) + (yIntercept - (double)circleCenter->y) * (yIntercept - (double)circleCenter->y) - sq((double)radius);
double discr = sq(b) - (double)4.0 * a * c;
if (discr > 0)
{
double x1 = (-b + (double)fast_fsqrtf(discr)) / ((double)2.0 * a);
double y1 = slope * x1 + yIntercept;
double x2 = (-b - (double)fast_fsqrtf(discr)) / ((double)2.0 * a);
double y2 = slope * x2 + yIntercept;
intersection1->x = (float)x1;
intersection1->y = (float)y1;
intersection2->x = (float)x2;
intersection2->y = (float)y2;
return true;
}
return false;
}
static void generateOffsetPolygon(fpVector2_t* verticesNew, fpVector2_t* verticesOld, const uint8_t numVertices, const float offset)
{
fpVector2_t* prev = &verticesOld[numVertices - 1];
fpVector2_t* current;
fpVector2_t* next;
for (uint8_t i = 0; i < numVertices; i++) {
current = &verticesOld[i];
if (i + 1 < numVertices) {
next = &verticesOld[i + 1];
}
else {
next = &verticesOld[0];
}
fpVector2_t v, vn, vs, pcp1, pcp2, pcn1, pcn2, intersect;
vector2Sub(&v, current, prev);
vector2Normalize(&vn, &v);
vector2Scale(&vs, &vn, offset);
pcp1.x = prev->x - vs.y;
pcp1.y = prev->y + vs.x;
pcp2.x = current->x - vs.y;
pcp2.y = current->y + vs.x;
vector2Sub(&v, next, current);
vector2Normalize(&vn, &v);
vector2Scale(&vs, &vn, offset);
pcn1.x = current->x - vs.y;
pcn1.y = current->y + vs.x;
pcn2.x = next->x - vs.y;
pcn2.y = next->y + vs.x;
if (calcLineLineIntersection(&intersect, &pcp1, &pcp2, &pcn1, &pcn2, false)) {
verticesNew[i].x = intersect.x;
verticesNew[i].y = intersect.y;
}
prev = current;
}
}
// Calculates the nearest intersection point
// Inspired by raytracing algortyhms
static bool calcLineCylinderIntersection(fpVector3_t* intersection, float* distance, const fpVector3_t* startPos, const fpVector3_t* endPos, const fpVector3_t* circleCenter, const float radius, const float height, const bool inside)
{
float distToIntersection = FLT_MAX;
fpVector3_t intersect;
fpVector2_t i1, i2;
if (!calcLineCircleIntersection(&i1, &i2, (fpVector2_t*)startPos, (fpVector2_t*)endPos, (fpVector2_t*)circleCenter, radius)) {
return false;
}
if (calculateDistance2((fpVector2_t*)startPos, &i1) < calculateDistance2((fpVector2_t*)startPos, &i2)) {
intersect.x = i1.x;
intersect.y = i1.y;
}
else {
intersect.x = i2.x;
intersect.y = i2.y;
}
float dist1 = calculateDistance2((fpVector2_t*)endPos, (fpVector2_t*)&intersect);
float dist2 = calculateDistance2((fpVector2_t*)startPos, (fpVector2_t*)endPos);
fpVector2_t p4, p5, p6, p7;
p4.x = 0;
p4.y = endPos->z;
p5.x = dist2;
p5.y = startPos->z;
p6.x = dist1;
p6.y = circleCenter->z;
p7.x = dist1;
p7.y = circleCenter->z + height;
fpVector2_t heightIntersection;
if (calcLineLineIntersection(&heightIntersection, &p4, &p5, &p6, &p7, true)) {
intersect.z = heightIntersection.y;
if (isInFront(startPos, &intersect, endPos)) {
distToIntersection = calculateDistance3(startPos, &intersect);
}
}
fpVector3_t intersectCap;
fpVector3_t dir = calcDirVectorFromPoints(startPos, endPos);
if (startPos->z < circleCenter->z || (inside && circleCenter->z != 0)) {
fpVector3_t p1 = *circleCenter;
p1.x = circleCenter->x + radius;
fpVector3_t p2 = *circleCenter;
p2.y = circleCenter->y + radius;
fpVector3_t normal = calcPlaneNormalFromPoints(circleCenter, &p1, &p2);
if (calcIntersectionLinePlane(&intersectCap, &dir, endPos, &normal, circleCenter)
&& isPointInCircle((fpVector2_t*)&intersectCap, (fpVector2_t*)circleCenter, radius)
&& isInFront(startPos, &intersectCap, endPos)) {
float distanceCap = calculateDistance3(startPos, &intersectCap);
if (distanceCap < distToIntersection) {
distToIntersection = distanceCap;
intersect = intersectCap;
}
}
}
if (startPos->z > circleCenter->z + height || inside) {
fpVector3_t p1 = { .x = circleCenter->x + radius, .y = circleCenter->y, .z = circleCenter->z + height };
fpVector3_t p2 = { .x = circleCenter->x, .y = circleCenter->y + radius, .z = circleCenter->z + height };
fpVector3_t p3 = *circleCenter;
p3.z = circleCenter->z + height;
fpVector3_t normal = calcPlaneNormalFromPoints(&p3, &p1, &p2);
if (calcIntersectionLinePlane(&intersectCap, &dir, startPos, &normal, &p3)
&& isPointInCircle((fpVector2_t*)&intersectCap, (fpVector2_t*)circleCenter, radius)
&& isInFront(startPos, &intersectCap, endPos)) {
float distanceCap = calculateDistance3(startPos, &intersectCap);
if (distanceCap < distToIntersection) {
distToIntersection = distanceCap;
intersect = intersectCap;
}
}
}
if (distToIntersection < FLT_MAX) {
*distance = distToIntersection;
memcpy(intersection, &intersect, sizeof(fpVector3_t));
return true;
}
return false;
}
static bool calcLine3dPolygonIntersection(fpVector3_t *intersection, float *distance, const fpVector3_t *startPos, const fpVector3_t *endPos, fpVector2_t *vertices, const uint8_t numVertices, const float minHeight, const float maxHeight, const bool isInclusiveZone)
{
float distToIntersection = FLT_MAX;
fpVector3_t intersect;
fpVector2_t* prev = &vertices[numVertices - 1];
fpVector2_t* current;
for (uint8_t i = 0; i < numVertices; i++) {
current = &vertices[i];
fpVector3_t p1 = { .x = prev->x, .y = prev->y, .z = minHeight };
fpVector3_t p2 = { .x = prev->x, .y = prev->y, .z = maxHeight };
fpVector3_t p3 = { .x = current->x, .y = current->y, .z = minHeight };
fpVector3_t p4 = { .x = current->x, .y = current->y, .z = maxHeight};
fpVector3_t dir = calcDirVectorFromPoints(startPos, endPos);
fpVector3_t intersectCurrent;
if ((calcLineTriangleIntersection(&intersectCurrent, startPos, &dir, &p1, &p2, &p3)
|| calcLineTriangleIntersection(&intersectCurrent, startPos, &dir, &p3, &p4, &p2)) && isInFront(startPos, &intersectCurrent, endPos) ) {
float distWall = calculateDistance3(startPos, &intersectCurrent);
if (distWall < distToIntersection) {
distToIntersection = distWall;
intersect = intersectCurrent;
}
}
prev = current;
}
fpVector3_t intersectCap;
if (startPos->z < minHeight || (isInclusiveZone && minHeight != 0)) {
if (calcLinePolygonIntersection(&intersectCap, startPos, endPos, minHeight, vertices, numVertices) && isInFront(startPos, &intersectCap, endPos))
{
float distanceCap = calculateDistance3(startPos, &intersectCap);
if (distanceCap < distToIntersection) {
distToIntersection = distanceCap;
intersect = intersectCap;
}
}
}
if (startPos->z > maxHeight || isInclusiveZone) {
if (calcLinePolygonIntersection(&intersectCap, startPos, endPos, maxHeight, vertices, numVertices) && isInFront(startPos, &intersectCap, endPos))
{
float distanceCap = calculateDistance3(startPos, &intersectCap);
if (distanceCap < distToIntersection) {
distToIntersection = distanceCap;
intersect = intersectCap;
}
}
}
if (distToIntersection < FLT_MAX) {
*distance = distToIntersection;
memcpy(intersection, &intersect, sizeof(fpVector3_t));
return true;
}
return false;
}

View file

@ -113,6 +113,11 @@ typedef struct navigationFlags_s {
bool rthTrackbackActive; // Activation status of RTH trackback
bool wpTurnSmoothingActive; // Activation status WP turn smoothing
bool manualEmergLandActive; // Activation status of manual emergency landing
#ifdef USE_GEOZONE
bool sendToActive;
bool forcedPosholdActive;
#endif
} navigationFlags_t;
typedef struct {
@ -160,6 +165,7 @@ typedef enum {
NAV_FSM_EVENT_SWITCH_TO_COURSE_ADJ,
NAV_FSM_EVENT_SWITCH_TO_MIXERAT,
NAV_FSM_EVENT_SWITCH_TO_NAV_STATE_FW_LANDING,
NAV_FSM_EVENT_SWITCH_TO_SEND_TO,
NAV_FSM_EVENT_STATE_SPECIFIC_1, // State-specific event
NAV_FSM_EVENT_STATE_SPECIFIC_2, // State-specific event
@ -245,6 +251,10 @@ typedef enum {
NAV_PERSISTENT_ID_FW_LANDING_FLARE = 46,
NAV_PERSISTENT_ID_FW_LANDING_ABORT = 47,
NAV_PERSISTENT_ID_FW_LANDING_FINISHED = 48,
NAV_PERSISTENT_ID_SEND_TO_INITALIZE = 49,
NAV_PERSISTENT_ID_SEND_TO_IN_PROGRES = 50,
NAV_PERSISTENT_ID_SEND_TO_FINISHED = 51
} navigationPersistentId_e;
typedef enum {
@ -304,6 +314,10 @@ typedef enum {
NAV_STATE_MIXERAT_IN_PROGRESS,
NAV_STATE_MIXERAT_ABORT,
NAV_STATE_SEND_TO_INITALIZE,
NAV_STATE_SEND_TO_IN_PROGESS,
NAV_STATE_SEND_TO_FINISHED,
NAV_STATE_COUNT,
} navigationFSMState_t;
@ -406,6 +420,17 @@ typedef enum {
RTH_HOME_FINAL_LAND, // Home position and altitude
} rthTargetMode_e;
#ifdef USE_GEOZONE
typedef struct navSendTo_s {
fpVector3_t targetPos;
uint16_t altitudeTargetRange; // 0 for only "2D"
uint32_t targetRange;
bool lockSticks;
uint32_t lockStickTime;
timeMs_t startTime;
} navSendTo_t;
#endif
typedef struct {
fpVector3_t nearestSafeHome; // The nearestSafeHome found during arming
uint32_t distance; // distance to the nearest safehome
@ -478,6 +503,10 @@ typedef struct {
fwLandState_t fwLandState;
#endif
#ifdef USE_GEOZONE
navSendTo_t sendTo; // Used for Geozones
#endif
/* Internals & statistics */
int16_t rcAdjustment[4];
float totalTripDistance;
@ -502,7 +531,9 @@ const navEstimatedPosVel_t * navGetCurrentActualPositionAndVelocity(void);
bool isThrustFacingDownwards(void);
uint32_t calculateDistanceToDestination(const fpVector3_t * destinationPos);
void calculateFarAwayTarget(fpVector3_t * farAwayPos, int32_t bearing, int32_t distance);
int32_t calculateBearingToDestination(const fpVector3_t * destinationPos);
float calculateDistance2(const fpVector2_t* startPos, const fpVector2_t* destinationPos);
bool isLandingDetected(void);
void resetLandingDetector(void);

View file

@ -137,6 +137,10 @@ typedef enum {
TASK_TELEMETRY_SBUS2,
#endif
#if defined (USE_GEOZONE) && defined(USE_GPS)
TASK_GEOZONE,
#endif
/* Count of real tasks */
TASK_COUNT,

View file

@ -221,3 +221,4 @@
#define USE_EZ_TUNE
#define USE_ADAPTIVE_FILTER
#define USE_GEOZONE