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;
}