diff --git a/.gitignore b/.gitignore index b2c3b9ff546..87ca5a87c7b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ make/local.mk launch.json .vscode/tasks.json .vscode/c_cpp_properties.json +/cmake-build-debug/ diff --git a/docs/Settings.md b/docs/Settings.md index c919327194b..1d6d74fae8d 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -182,6 +182,36 @@ Calculated value after '6 position avanced calibration'. See Wiki page. --- +### adsb_distance_warning + +Distance in meters of ADSB aircraft that is displayed + +| Default | Min | Max | +| --- |-----| --- | +| 20000 | 1 | 64000 | + +--- + +### adsb_distance_alert + +Distance inside which ADSB data flashes for proximity warning + +| Default | Min | Max | +| --- |---| --- | +| 3000 | 1 | 64000 | + +--- + +### adsb_ignore_plane_above_me_limit + +Ignore adsb planes above, limit, 0 disabled (meters) + +| Default | Min | Max | +|---------|-----| --- | +| 0 | 0 | 64000 | + +--- + ### ahrs_acc_ignore_rate Total gyro rotation rate threshold [deg/s] before scaling to consider accelerometer trustworthy diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index b6e433d97d8..2ab5f7c15f0 100755 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -342,6 +342,7 @@ main_sources(COMMON_SRC flight/dynamic_lpf.c flight/dynamic_lpf.h + io/adsb.c io/beeper.c io/beeper.h io/servo_sbus.c diff --git a/src/main/drivers/osd_symbols.h b/src/main/drivers/osd_symbols.h index d867cc50c05..7e1d44c8a71 100644 --- a/src/main/drivers/osd_symbols.h +++ b/src/main/drivers/osd_symbols.h @@ -179,6 +179,8 @@ #define SYM_TERRAIN_FOLLOWING 0xFB // 251 Terrain following (also Alt adjust) #define SYM_CROSS_TRACK_ERROR 0xFC // 252 Cross track error +#define SYM_ADSB 0xFD // 253 ADSB + #define SYM_LOGO_START 0x101 // 257 to 297, INAV logo #define SYM_LOGO_WIDTH 10 #define SYM_LOGO_HEIGHT 4 diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index f2ccec9ec07..dce31465f0a 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -85,6 +85,7 @@ bool cliMode = false; #include "flight/pid.h" #include "flight/servos.h" +#include "io/adsb.h" #include "io/asyncfatfs/asyncfatfs.h" #include "io/beeper.h" #include "io/flashfs.h" @@ -2690,6 +2691,44 @@ static void cliFeature(char *cmdline) } } +#ifdef USE_ADSB +static void cliAdsbVehicles(char *cmdLine) +{ + UNUSED(cmdLine); + + adsbVehicle_t* adsbVehicle; + adsbVehicle_t* adsbTheClosestVehicle = findVehicleClosest(); + cliPrintf("%-10s%-10s%-10s%-10s%-10s%-16s%-16s%-10s%-14s%-10s%-20s\n", "#", "callsign", "icao", "lat", "lon", " alt (m)", " dist (km)", "dir", "vert dist (m)", "tslc", "ttl", "note"); + + for(uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++){ + adsbVehicle = findVehicle(i); + if(adsbVehicle != NULL && adsbVehicle->ttl > 0){ + + cliPrintf("%-10d%-10s%-10d%-10u%-10d%-16f%-16f%-10d%-14f%-10d%-10d%-20s \n", + i, + adsbVehicle->vehicleValues.callsign, + adsbVehicle->vehicleValues.icao, + adsbVehicle->vehicleValues.lat, + adsbVehicle->vehicleValues.lon, + (double)CENTIMETERS_TO_METERS(adsbVehicle->vehicleValues.alt), + (double)CENTIMETERS_TO_METERS(adsbVehicle->calculatedVehicleValues.dist) / 1000, + adsbVehicle->calculatedVehicleValues.dir, + (double)CENTIMETERS_TO_METERS(adsbVehicle->calculatedVehicleValues.verticalDistance), + adsbVehicle->vehicleValues.tslc, + adsbVehicle->ttl, + adsbTheClosestVehicle != NULL && adsbTheClosestVehicle->vehicleValues.icao == adsbVehicle->vehicleValues.icao ? "the closest": "" + ); + } + } + + cliPrintf("Messages count from device: %d\n", getAdsbStatus()->vehiclesMessagesTotal); + + if(!enviromentOkForCalculatingDistaceBearing()){ + cliPrintErrorLine("No GPS FIX, can't calculate distance and direction"); + } +} +#endif + #ifdef USE_BLACKBOX static void printBlackbox(uint8_t dumpMask, const blackboxConfig_t *config, const blackboxConfig_t *configDefault) { @@ -3997,6 +4036,9 @@ const clicmd_t cmdTable[] = { #if defined(USE_BOOTLOG) CLI_COMMAND_DEF("bootlog", "show boot events", NULL, cliBootlog), #endif +#if defined(USE_ADSB) + CLI_COMMAND_DEF("adsb_vehicles", "the closest vehicle", NULL, cliAdsbVehicles), +#endif #ifdef USE_LED_STRIP CLI_COMMAND_DEF("color", "configure colors", NULL, cliColor), CLI_COMMAND_DEF("mode_color", "configure mode and special colors", NULL, cliModeColor), diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 0f3d80829ab..c973d9d0d84 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -82,6 +82,7 @@ #include "config/config_eeprom.h" #include "config/feature.h" +#include "io/adsb.h" #include "io/asyncfatfs/asyncfatfs.h" #include "io/flashfs.h" #include "io/gps.h" @@ -924,6 +925,33 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF sbufWriteU16(dst, gpsSol.epv); break; #endif + case MSP2_ADSB_VEHICLE_LIST: +#ifdef USE_ADSB + sbufWriteU8(dst, MAX_ADSB_VEHICLES); + sbufWriteU8(dst, ADSB_CALL_SIGN_MAX_LENGTH); + + for(uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++){ + + adsbVehicle_t *adsbVehicle = findVehicle(i); + + for(uint8_t ii = 0; ii < ADSB_CALL_SIGN_MAX_LENGTH; ii++){ + sbufWriteU8(dst, adsbVehicle->vehicleValues.callsign[ii]); + } + + sbufWriteU32(dst, adsbVehicle->vehicleValues.icao); + sbufWriteU32(dst, adsbVehicle->vehicleValues.lat); + sbufWriteU32(dst, adsbVehicle->vehicleValues.lon); + sbufWriteU32(dst, adsbVehicle->vehicleValues.alt); + sbufWriteU16(dst, (uint16_t)CENTIDEGREES_TO_DEGREES(adsbVehicle->vehicleValues.heading)); + sbufWriteU8(dst, adsbVehicle->vehicleValues.tslc); + sbufWriteU8(dst, adsbVehicle->vehicleValues.emitterType); + sbufWriteU8(dst, adsbVehicle->ttl); + } +#else + sbufWriteU8(dst, 0); + sbufWriteU8(dst, 0); +#endif + break; case MSP_DEBUG: // output some useful QA statistics // debug[x] = ((hse_value / 1000000) * 1000) + (SystemCoreClock / 1000000); // XX0YY [crystal clock : core clock] @@ -1494,6 +1522,13 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF #else sbufWriteU16(dst, 0); sbufWriteU16(dst, 0); +#endif +#ifdef USE_ADSB + sbufWriteU16(dst, osdConfig()->adsb_distance_warning); + sbufWriteU16(dst, osdConfig()->adsb_distance_alert); +#else + sbufWriteU16(dst, 0); + sbufWriteU16(dst, 0); #endif break; diff --git a/src/main/fc/fc_tasks.c b/src/main/fc/fc_tasks.c index af5f27b5272..1f561798b1f 100755 --- a/src/main/fc/fc_tasks.c +++ b/src/main/fc/fc_tasks.c @@ -68,6 +68,7 @@ #include "io/osd_dji_hd.h" #include "io/displayport_msp_osd.h" #include "io/servo_sbus.h" +#include "io/adsb.h" #include "msp/msp_serial.h" @@ -180,6 +181,14 @@ void taskUpdateCompass(timeUs_t currentTimeUs) } #endif +#ifdef USE_ADSB +void taskAdsb(timeUs_t currentTimeUs) +{ + UNUSED(currentTimeUs); + adsbTtlClean(currentTimeUs); +} +#endif + #ifdef USE_BARO void taskUpdateBaro(timeUs_t currentTimeUs) { @@ -359,6 +368,9 @@ void fcTasksInit(void) #ifdef USE_PITOT setTaskEnabled(TASK_PITOT, sensors(SENSOR_PITOT)); #endif +#ifdef USE_ADSB + setTaskEnabled(TASK_ADSB, true); +#endif #ifdef USE_RANGEFINDER setTaskEnabled(TASK_RANGEFINDER, sensors(SENSOR_RANGEFINDER)); #endif @@ -490,6 +502,15 @@ cfTask_t cfTasks[TASK_COUNT] = { }, #endif +#ifdef USE_ADSB + [TASK_ADSB] = { + .taskName = "ADSB", + .taskFunc = taskAdsb, + .desiredPeriod = TASK_PERIOD_HZ(1), // ADSB is updated at 1 Hz + .staticPriority = TASK_PRIORITY_IDLE, + }, +#endif + #ifdef USE_BARO [TASK_BARO] = { .taskName = "BARO", diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 261bf14a56b..060dfd7335a 100755 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -3359,6 +3359,31 @@ groups: max: 11 default_value: 9 + - name: osd_adsb_distance_warning + description: "Distance in meters of ADSB aircraft that is displayed" + default_value: 20000 + condition: USE_ADSB + field: adsb_distance_warning + min: 1 + max: 64000 + type: uint16_t + - name: osd_adsb_distance_alert + description: "Distance inside which ADSB data flashes for proximity warning" + default_value: 3000 + condition: USE_ADSB + field: adsb_distance_alert + min: 1 + max: 64000 + type: uint16_t + - name: osd_adsb_ignore_plane_above_me_limit + description: "Ignore adsb planes above, limit, 0 disabled (meters)" + default_value: 0 + condition: USE_ADSB + field: adsb_ignore_plane_above_me_limit + min: 0 + max: 64000 + type: uint16_t + - name: osd_estimations_wind_compensation description: "Use wind estimation for remaining flight time/distance estimation" default_value: ON diff --git a/src/main/io/adsb.c b/src/main/io/adsb.c new file mode 100644 index 00000000000..564c46d5253 --- /dev/null +++ b/src/main/io/adsb.c @@ -0,0 +1,213 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight 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. + * + * Cleanflight 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 Cleanflight. If not, see . + */ + + +#include + + +#include "adsb.h" + +#include "navigation/navigation.h" +#include "navigation/navigation_private.h" + +#include "common/maths.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#include "common/mavlink.h" +#pragma GCC diagnostic pop + + +#include "math.h" + + +#ifdef USE_ADSB + +adsbVehicle_t adsbVehiclesList[MAX_ADSB_VEHICLES]; +adsbVehicleStatus_t adsbVehiclesStatus; + +adsbVehicleValues_t vehicleValues; + + +adsbVehicleValues_t* getVehicleForFill(void){ + return &vehicleValues; +} + +// use bsearch function +adsbVehicle_t *findVehicleByIcao(uint32_t avicao) { + for (uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++) { + if (avicao == adsbVehiclesList[i].vehicleValues.icao) { + return &adsbVehiclesList[i]; + } + } + return NULL; +} + +adsbVehicle_t *findVehicleFarthest(void) { + adsbVehicle_t *adsbLocal = NULL; + for (uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++) { + if (adsbVehiclesList[i].ttl > 0 && adsbVehiclesList[i].calculatedVehicleValues.valid && (adsbLocal == NULL || adsbLocal->calculatedVehicleValues.dist < adsbVehiclesList[i].calculatedVehicleValues.dist)) { + adsbLocal = &adsbVehiclesList[i]; + } + } + return adsbLocal; +} + +adsbVehicle_t *findVehicleClosest(void) { + adsbVehicle_t *adsbLocal = NULL; + for (uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++) { + if (adsbVehiclesList[i].ttl > 0 && adsbVehiclesList[i].calculatedVehicleValues.valid && (adsbLocal == NULL || adsbLocal->calculatedVehicleValues.dist > adsbVehiclesList[i].calculatedVehicleValues.dist)) { + adsbLocal = &adsbVehiclesList[i]; + } + } + return adsbLocal; +} + +adsbVehicle_t *findFreeSpaceInList(void) { + //find expired first + for (uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++) { + if (adsbVehiclesList[i].ttl == 0) { + return &adsbVehiclesList[i]; + } + } + + return NULL; +} + +adsbVehicle_t *findVehicleNotCalculated(void) { + //find expired first + for (uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++) { + if (adsbVehiclesList[i].calculatedVehicleValues.valid == false) { + return &adsbVehiclesList[i]; + } + } + + return NULL; +} + +adsbVehicle_t* findVehicle(uint8_t index) +{ + if (index < MAX_ADSB_VEHICLES){ + return &adsbVehiclesList[index]; + } + + return NULL; +} + +adsbVehicleStatus_t* getAdsbStatus(void){ + return &adsbVehiclesStatus; +} + +void gpsDistanceCmBearing(int32_t currentLat1, int32_t currentLon1, int32_t destinationLat2, int32_t destinationLon2, uint32_t *dist, int32_t *bearing) { + float GPS_scaleLonDown = cos_approx((fabsf((float) gpsSol.llh.lat) / 10000000.0f) * 0.0174532925f); + const float dLat = destinationLat2 - currentLat1; // difference of latitude in 1/10 000 000 degrees + const float dLon = (float) (destinationLon2 - currentLon1) * GPS_scaleLonDown; + + *dist = sqrtf(sq(dLat) + sq(dLon)) * DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR; + *bearing = 9000.0f + RADIANS_TO_CENTIDEGREES(atan2_approx(-dLat, dLon)); // Convert the output radians to 100xdeg + *bearing = wrap_36000(*bearing); +}; + +void adsbNewVehicle(adsbVehicleValues_t* vehicleValuesLocal) { + + // no valid lat lon or altitude + if((vehicleValuesLocal->flags & (ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_COORDS)) != (ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_COORDS)){ + return; + } + + adsbVehiclesStatus.vehiclesMessagesTotal++; + adsbVehicle_t *vehicle = NULL; + + vehicle = findVehicleByIcao(vehicleValuesLocal->icao); + if(vehicle != NULL && vehicleValuesLocal->tslc > ADSB_MAX_SECONDS_KEEP_INACTIVE_PLANE_IN_LIST){ + vehicle->ttl = 0; + return; + } + + // non GPS mode, GPS is not fix, just find free space in list or by icao and save vehicle without calculated values + if (!enviromentOkForCalculatingDistaceBearing()) { + + if(vehicle == NULL){ + vehicle = findFreeSpaceInList(); + } + + if (vehicle != NULL) { + memcpy(&(vehicle->vehicleValues), vehicleValuesLocal, sizeof(vehicle->vehicleValues)); + vehicle->ttl = ADSB_MAX_SECONDS_KEEP_INACTIVE_PLANE_IN_LIST; + vehicle->calculatedVehicleValues.valid = false; + return; + } + } else { + // GPS mode, GPS is fixed and has enough sats + + + if(vehicle == NULL){ + vehicle = findFreeSpaceInList(); + } + + if(vehicle == NULL){ + vehicle = findVehicleNotCalculated(); + } + + if(vehicle == NULL){ + vehicle = findVehicleFarthest(); + } + + if (vehicle != NULL) { + memcpy(&(vehicle->vehicleValues), vehicleValuesLocal, sizeof(vehicle->vehicleValues)); + recalculateVehicle(vehicle); + vehicle->ttl = ADSB_MAX_SECONDS_KEEP_INACTIVE_PLANE_IN_LIST; + return; + } + } +}; + +void recalculateVehicle(adsbVehicle_t* vehicle){ + gpsDistanceCmBearing(gpsSol.llh.lat, gpsSol.llh.lon, vehicle->vehicleValues.lat, vehicle->vehicleValues.lon, &(vehicle->calculatedVehicleValues.dist), &(vehicle->calculatedVehicleValues.dir)); + + if (vehicle != NULL && vehicle->calculatedVehicleValues.dist > ADSB_LIMIT_CM) { + vehicle->ttl = 0; + return; + } + + vehicle->calculatedVehicleValues.verticalDistance = vehicle->vehicleValues.alt - (int32_t)getEstimatedActualPosition(Z) - GPS_home.alt; + vehicle->calculatedVehicleValues.valid = true; +} + +void adsbTtlClean(timeUs_t currentTimeUs) { + + static timeUs_t adsbTtlLastCleanServiced = 0; + timeDelta_t adsbTtlSinceLastCleanServiced = cmpTimeUs(currentTimeUs, adsbTtlLastCleanServiced); + + + if (adsbTtlSinceLastCleanServiced > 1000000) // 1s + { + for (uint8_t i = 0; i < MAX_ADSB_VEHICLES; i++) { + if (adsbVehiclesList[i].ttl > 0) { + adsbVehiclesList[i].ttl--; + } + } + + adsbTtlLastCleanServiced = currentTimeUs; + } +}; + +bool enviromentOkForCalculatingDistaceBearing(void){ + return (STATE(GPS_FIX) && gpsSol.numSat > 4); +} + +#endif + diff --git a/src/main/io/adsb.h b/src/main/io/adsb.h new file mode 100644 index 00000000000..45eb4070f1d --- /dev/null +++ b/src/main/io/adsb.h @@ -0,0 +1,66 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight 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. + * + * Cleanflight 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 Cleanflight. If not, see . + */ + +#pragma once + +#include +#include "common/time.h" +#include "fc/runtime_config.h" + +#define ADSB_CALL_SIGN_MAX_LENGTH 9 +#define ADSB_MAX_SECONDS_KEEP_INACTIVE_PLANE_IN_LIST 10 + +typedef struct { + bool valid; + int32_t dir; // centidegrees direction to plane, pivot is inav FC + uint32_t dist; // CM distance to plane, pivot is inav FC + int32_t verticalDistance; // CM, vertical distance to plane, pivot is inav FC +} adsbVehicleCalculatedValues_t; + +typedef struct { + uint32_t icao; // ICAO address + int32_t lat; // Latitude, expressed as degrees * 1E7 + int32_t lon; // Longitude, expressed as degrees * 1E7 + int32_t alt; // Barometric/Geometric Altitude (ASL), in cm + uint16_t heading; // Course over ground in centidegrees + uint16_t flags; // Flags to indicate various statuses including valid data fields + uint8_t altitudeType; // Type from ADSB_ALTITUDE_TYPE enum + char callsign[ADSB_CALL_SIGN_MAX_LENGTH]; // The callsign, 8 chars + NULL + uint8_t emitterType; // Type from ADSB_EMITTER_TYPE enum + uint8_t tslc; // Time since last communication in seconds +} adsbVehicleValues_t; + +typedef struct { + adsbVehicleValues_t vehicleValues; + adsbVehicleCalculatedValues_t calculatedVehicleValues; + uint8_t ttl; +} adsbVehicle_t; + + + +typedef struct { + uint32_t vehiclesMessagesTotal; +} adsbVehicleStatus_t; + +void adsbNewVehicle(adsbVehicleValues_t* vehicleValuesLocal); +adsbVehicle_t * findVehicleClosest(void); +adsbVehicle_t * findVehicle(uint8_t index); +void adsbTtlClean(timeUs_t currentTimeUs); +adsbVehicleStatus_t* getAdsbStatus(void); +adsbVehicleValues_t* getVehicleForFill(void); +bool enviromentOkForCalculatingDistaceBearing(void); +void recalculateVehicle(adsbVehicle_t* vehicle); \ No newline at end of file diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 1aee556e1db..ca898c24fa6 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -62,6 +62,7 @@ #include "drivers/time.h" #include "drivers/vtx_common.h" +#include "io/adsb.h" #include "io/flashfs.h" #include "io/gps.h" #include "io/osd.h" @@ -2030,7 +2031,61 @@ static bool osdDrawSingleElement(uint8_t item) osdFormatCentiNumber(&buff[2], centiHDOP, 0, 1, 0, digits, false); break; } +#ifdef USE_ADSB + case OSD_ADSB: + { + static uint8_t adsblen = 1; + uint8_t arrowPositionX = 0; + + for (int i = 0; i <= adsblen; i++) { + buff[i] = SYM_BLANK; + } + + buff[adsblen]='\0'; + displayWrite(osdDisplayPort, elemPosX, elemPosY, buff); // clear any previous chars because variable element size + adsblen=1; + adsbVehicle_t *vehicle = findVehicleClosest(); + + if(vehicle != NULL){ + recalculateVehicle(vehicle); + } + + if ( + vehicle != NULL && + (vehicle->calculatedVehicleValues.dist > 0) && + vehicle->calculatedVehicleValues.dist < METERS_TO_CENTIMETERS(osdConfig()->adsb_distance_warning) && + (osdConfig()->adsb_ignore_plane_above_me_limit == 0 || METERS_TO_CENTIMETERS(osdConfig()->adsb_ignore_plane_above_me_limit) > vehicle->calculatedVehicleValues.verticalDistance) + ){ + buff[0] = SYM_ADSB; + osdFormatDistanceStr(&buff[1], vehicle->calculatedVehicleValues.dist); + adsblen = strlen(buff); + + buff[adsblen-1] = SYM_BLANK; + + arrowPositionX = adsblen-1; + osdFormatDistanceStr(&buff[adsblen], vehicle->calculatedVehicleValues.verticalDistance); + adsblen = strlen(buff)-1; + + if (vehicle->calculatedVehicleValues.dist < METERS_TO_CENTIMETERS(osdConfig()->adsb_distance_alert)) { + TEXT_ATTRIBUTES_ADD_BLINK(elemAttr); + } + } + + buff[adsblen]='\0'; + displayWriteWithAttr(osdDisplayPort, elemPosX, elemPosY, buff, elemAttr); + if (arrowPositionX > 0){ + 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()); + osdDrawDirArrow(osdDisplayPort, osdGetDisplayPortCanvas(), OSD_DRAW_POINT_GRID(elemPosX + arrowPositionX, elemPosY), CENTIDEGREES_TO_DEGREES(vehicle->calculatedVehicleValues.dir) - flightDirection + panHomeDirOffset); + } + + return true; + } +#endif case OSD_MAP_NORTH: { static uint16_t drawn = 0; @@ -3684,6 +3739,11 @@ PG_RESET_TEMPLATE(osdConfig_t, osdConfig, .baro_temp_alarm_min = SETTING_OSD_BARO_TEMP_ALARM_MIN_DEFAULT, .baro_temp_alarm_max = SETTING_OSD_BARO_TEMP_ALARM_MAX_DEFAULT, #endif +#ifdef USE_ADSB + .adsb_distance_warning = SETTING_OSD_ADSB_DISTANCE_WARNING_DEFAULT, + .adsb_distance_alert = SETTING_OSD_ADSB_DISTANCE_ALERT_DEFAULT, + .adsb_ignore_plane_above_me_limit = SETTING_OSD_ADSB_IGNORE_PLANE_ABOVE_ME_LIMIT_DEFAULT, +#endif #ifdef USE_SERIALRX_CRSF .snr_alarm = SETTING_OSD_SNR_ALARM_DEFAULT, .crsf_lq_format = SETTING_OSD_CRSF_LQ_FORMAT_DEFAULT, diff --git a/src/main/io/osd.h b/src/main/io/osd.h index 877437ff88e..31514a1c803 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -275,6 +275,7 @@ typedef enum { OSD_PAN_SERVO_CENTRED, OSD_MULTI_FUNCTION, OSD_ODOMETER, + OSD_ADSB, OSD_ITEM_COUNT // MUST BE LAST } osd_items_e; @@ -449,6 +450,11 @@ typedef struct osdConfig_s { char osd_switch_indicator3_name[OSD_SWITCH_INDICATOR_NAME_LENGTH + 1]; // Name to use for switch indicator 3. uint8_t osd_switch_indicator3_channel; // RC Channel to use for switch indicator 3. bool osd_switch_indicators_align_left; // Align switch indicator name to left of the switch. +#ifdef USE_ADSB + uint16_t adsb_distance_warning; // in metres + uint16_t adsb_distance_alert; // in metres + uint16_t adsb_ignore_plane_above_me_limit; // in metres +#endif } osdConfig_t; PG_DECLARE(osdConfig_t, osdConfig); diff --git a/src/main/msp/msp_protocol_v2_inav.h b/src/main/msp/msp_protocol_v2_inav.h index 9cf1881c732..7e6c0199848 100755 --- a/src/main/msp/msp_protocol_v2_inav.h +++ b/src/main/msp/msp_protocol_v2_inav.h @@ -94,3 +94,5 @@ #define MSP2_INAV_RATE_DYNAMICS 0x2060 #define MSP2_INAV_SET_RATE_DYNAMICS 0x2061 + +#define MSP2_ADSB_VEHICLE_LIST 0x2070 diff --git a/src/main/scheduler/scheduler.h b/src/main/scheduler/scheduler.h index 57707c725b1..0d91876cb72 100755 --- a/src/main/scheduler/scheduler.h +++ b/src/main/scheduler/scheduler.h @@ -70,6 +70,9 @@ typedef enum { #ifdef USE_BARO TASK_BARO, #endif +#ifdef USE_ADSB + TASK_ADSB, +#endif #ifdef USE_PITOT TASK_PITOT, #endif diff --git a/src/main/target/common.h b/src/main/target/common.h index 713458002b3..ed565955a96 100755 --- a/src/main/target/common.h +++ b/src/main/target/common.h @@ -182,6 +182,14 @@ #define USE_CMS_FONT_PREVIEW +//ADSB RECEIVER +#ifdef USE_GPS +#define USE_ADSB +#define MAX_ADSB_VEHICLES 5 +#define ADSB_LIMIT_CM 6400000 +#endif + + //Designed to free space of F722 and F411 MCUs #if (MCU_FLASH_SIZE > 512) #define USE_VTX_FFPV diff --git a/src/main/telemetry/mavlink.c b/src/main/telemetry/mavlink.c index 30237c28552..3b1ef326c04 100644 --- a/src/main/telemetry/mavlink.c +++ b/src/main/telemetry/mavlink.c @@ -58,6 +58,7 @@ #include "flight/pid.h" #include "flight/servos.h" +#include "io/adsb.h" #include "io/gps.h" #include "io/ledstrip.h" #include "io/serial.h" @@ -1052,6 +1053,50 @@ static bool handleIncoming_RC_CHANNELS_OVERRIDE(void) { return true; } +#ifdef USE_ADSB +static bool handleIncoming_ADSB_VEHICLE(void) { + mavlink_adsb_vehicle_t msg; + mavlink_msg_adsb_vehicle_decode(&mavRecvMsg, &msg); + + adsbVehicleValues_t* vehicle = getVehicleForFill(); + if(vehicle != NULL){ + vehicle->icao = msg.ICAO_address; + vehicle->lat = msg.lat; + vehicle->lon = msg.lon; + vehicle->alt = (int32_t)(msg.altitude / 10); + vehicle->heading = msg.heading; + vehicle->flags = msg.flags; + vehicle->altitudeType = msg.altitude_type; + memcpy(&(vehicle->callsign), msg.callsign, sizeof(vehicle->callsign)); + vehicle->emitterType = msg.emitter_type; + vehicle->tslc = msg.tslc; + + adsbNewVehicle(vehicle); + } + + //debug vehicle + /* if(vehicle != NULL){ + + char name[9] = "DUMMY "; + + vehicle->icao = 666; + vehicle->lat = 492383514; + vehicle->lon = 165148681; + vehicle->alt = 100000; + vehicle->heading = 180; + vehicle->flags = ADSB_FLAGS_VALID_ALTITUDE | ADSB_FLAGS_VALID_COORDS; + vehicle->altitudeType = 0; + memcpy(&(vehicle->callsign), name, sizeof(vehicle->callsign)); + vehicle->emitterType = 6; + vehicle->tslc = 0; + + adsbNewVehicle(vehicle); + }*/ + + return true; +} +#endif + static bool processMAVLinkIncomingTelemetry(void) { while (serialRxBytesWaiting(mavlinkPort) > 0) { @@ -1074,6 +1119,10 @@ static bool processMAVLinkIncomingTelemetry(void) return handleIncoming_MISSION_REQUEST(); case MAVLINK_MSG_ID_RC_CHANNELS_OVERRIDE: return handleIncoming_RC_CHANNELS_OVERRIDE(); +#ifdef USE_ADSB + case MAVLINK_MSG_ID_ADSB_VEHICLE: + return handleIncoming_ADSB_VEHICLE(); +#endif default: return false; }