Skip to content

[ADS-B] Add emitter category support & HTTP endpoint for retrieving ADS-B data#3428

Open
MUSTARDTIGERFPV wants to merge 4 commits into
ArduPilot:masterfrom
MUSTARDTIGERFPV:add-adsb-http
Open

[ADS-B] Add emitter category support & HTTP endpoint for retrieving ADS-B data#3428
MUSTARDTIGERFPV wants to merge 4 commits into
ArduPilot:masterfrom
MUSTARDTIGERFPV:add-adsb-http

Conversation

@MUSTARDTIGERFPV

@MUSTARDTIGERFPV MUSTARDTIGERFPV commented Oct 14, 2024

Copy link
Copy Markdown
Contributor

This PR adds the following:

Differentiated icons for light/small/large/heavy and similar aircraft types. This lets users differentiate a 747 from a C172 at a glance. Thanks to tar1090 for these sprites.

Instead of retaining different sprites per color, I'm instead coloring at runtime and caching to improve performance.

Before, all the same size/shape
image
After, with size/shape differences matching their size/shape
image

Type/Category readouts in the popover to make the size & type data more explicit.

In addition to the shape/size of the sprite changing, you can also explicitly read what type of aircraft you're looking at and its category.

image

An endpoint on the internal webserver at /adsb which serves Mission Planner's ADS-B data as JSON

This is extremely useful for external applications to understand local traffic. Whether you're using a MAVLink-connected ADS-B In receiver or the HTTP API support I recently added, you have a unified view of the same data your MAV & MP have.

Category support on ADSB_VEHICLE messages sent to the MAV

Previously the messages sent to the MAV didn't include the category (light, small, heavy) of the aircraft. Now it does.

Speech callouts for ADS-B Traffic at emergency proximities

When traffic is very close both horizontally and vertically, turning on the speech "ADS-B Alerts" checkbox in Planner settings will give a TCAS-style callout "Traffic: 4 O'Clock high; descend" every 5 seconds.

@robertlong13

Copy link
Copy Markdown
Collaborator

I like it. Looks good to me. Why do you have it marked as draft? Other than the fact that you should squash that last commit into an earlier one, this looks good to go.

@MUSTARDTIGERFPV

Copy link
Copy Markdown
Contributor Author

I like it. Looks good to me. Why do you have it marked as draft? Other than the fact that you should squash that last commit into an earlier one, this looks good to go.

I'd like to change the behavior of speech callouts by setting the thresholds a little further out and adding a 5km, 3km, and 1km warning level. Other than that, should be good to go. :)

@MUSTARDTIGERFPV
MUSTARDTIGERFPV marked this pull request as ready for review December 21, 2024 02:57
Comment thread GCSViews/FlightData.cs
@MUSTARDTIGERFPV

Copy link
Copy Markdown
Contributor Author

Thanks @LachlanConn! Are you able to merge the PR or do we need someone else?

@LachlanConn

Copy link
Copy Markdown
Contributor

I'm unable to merge you need @meee1 or the combined power of @robertlong13 and @EosBandi

@MUSTARDTIGERFPV

Copy link
Copy Markdown
Contributor Author

@meee1 - would you be willing to get this merged soon?

@MUSTARDTIGERFPV

Copy link
Copy Markdown
Contributor Author

@meee1 any chance I can ask you to take a look at this soon?

Comment thread ExtLibs/Maps/GMapMarkerADSBPlane.cs Outdated

g.DrawImageUnscaled(bitmap, bitmap.Width / -2, bitmap.Height / -2);

lastDrawn = (Bitmap)bitmap.Clone();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're leaking here, and the clone isn't necessary (bitmap is already holding a clone by this point).

Suggested change
lastDrawn = (Bitmap)bitmap.Clone();
lastDrawn?.Dispose();
lastDrawn = bitmap;

Now, if you want to get even more clear with it (so it doesn't look like we forgot a clone here), you could rename the cloned copy in a new local, but I don't mind either way

            // Clone so we can recolor without touching the shared static sprite (we own this copy)
            Bitmap colored = (Bitmap)bitmap.Clone();

            g.ScaleTransform(DrawScale, DrawScale);
            // ... alert-level fillColor switch, IsOnGround, unchanged ...

            ColorSprite(colored, fillColor);
            g.DrawImageUnscaled(colored, colored.Width / -2, colored.Height / -2);

            lastDrawn?.Dispose();
            lastDrawn = colored;

Comment thread Utilities/httpserver.cs Outdated
byte[] temp = asciiEncoding.GetBytes(header);
stream.Write(temp, 0, temp.Length);

var adsbdata = MainV2.instance.adsbPlanes.ToJSON();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, this is very evil. adsbPlanes contains an object reference Source. You're deserializing the entire MAVLinkInterface/CurrentState/ICommsSerial here. Just to be sure, I connected to SITL and checked.

ToJSON_no_no.txt

You could chuck some [JsonIgnore]s into PointLatLngAltHdg, but someone could break that in the future by adding another field without knowing about this trap. Probably better to build up a small class with just the fields you want explicitly listed in it.

Comment thread MainV2.cs Outdated
Comment on lines +3145 to +3215
// Speech alert if plane is too close
var closestPlane = relevantPlanes.FirstOrDefault();
if (
lastSpeech.AddSeconds(5) < DateTime.Now &&
closestPlane != null &&
closestPlane.GetDistance(ourLocation) < 10000 &&
Math.Abs(closestPlane.Alt - comPort.MAV.cs.altasl) < 1000
)
{
if (speechEnable && speechEngine != null)
{
if (Settings.Instance.GetBoolean("speechadsbenabled"))
{

// Bearing to traffic from our plane
int bearingToTraffic = (int)ourLocation.GetBearing(closestPlane);

// Heading of our plane
int heading = (int)comPort.MAV.cs.yaw;

// Calculate the bearing to traffic relative to our heading
bearingToTraffic = (bearingToTraffic - heading + 360) % 360;
int clock = (int)Math.Round(bearingToTraffic / 30.0, MidpointRounding.AwayFromZero);
if (clock == 0)
{
clock = 12;
}
// Log it including callsign
log.InfoFormat("Traffic: {0} {1}; {2} {3}",
bearingToTraffic,
closestPlane.Alt > ourLocation.Alt ? "high" : "low",
closestPlane.CallSign,
closestPlane.Tag
);
string verticalDirection = closestPlane.Alt > ourLocation.Alt ? "high" : "low";
string recommendedAction = closestPlane.Alt > ourLocation.Alt ? "descend" : "climb";
string speech = "";
// Switch message urgency based on proximity
if (closestPlane.GetDistance(ourLocation) < 1000)
{
// Peak urgency, start with the recommended action
speech = string.Format("{2} NOW! {2} NOW! Traffic {0} O'Clock {1}",
clock,
verticalDirection,
recommendedAction
);
}
else if (closestPlane.GetDistance(ourLocation) < 5000)
{
// High urgency
speech = string.Format("Traffic Close! {0} O'Clock {1}; {2};",
clock,
verticalDirection,
recommendedAction
);
}
else
{
// Low urgency
speech = string.Format("Traffic; {0} O'Clock {1};",
clock,
verticalDirection
);
}

MainV2.speechEngine.SpeakAsync(speech);
lastSpeech = DateTime.Now;
}
}

}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, the distance and alt thresholds need to be adjustable. The values you have wouldn't work for me.

Second, we're duplicating a lot of threat classification logic that ArduPilot is able to handle here. Which is fine, there are real issues with relying on the round-trip for the autopilot to classify it for you if you're using internet-based adsb and not onboard. But I think we should mirror the logic that ArduPilot has in AP_Avoidance, which works by projecting positions over a time horizon, and gets the point of closest approach, and classifies by that. We should have options for:

And have them be definable for all three threat levels (I'd personally go with two, and drop your third low-urgency one, but I won't object to having three levels if you like them). Alerts should be prioritized by:

  • threat level
  • time to event

and not by the presently closest aircraft.

We should also probably use this threat classification higher up to determine which ones are the most pressing threats to send to the autopilot.

Comment thread MainV2.cs Outdated
Comment on lines +3145 to +3153
// Speech alert if plane is too close
var closestPlane = relevantPlanes.FirstOrDefault();
if (
lastSpeech.AddSeconds(5) < DateTime.Now &&
closestPlane != null &&
closestPlane.GetDistance(ourLocation) < 10000 &&
Math.Abs(closestPlane.Alt - comPort.MAV.cs.altasl) < 1000
)
{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a masking bug here. We're grabbing the closest aircraft laterally, then seeing if it's close enough to our altitude to care. The filter by altitude would really need to come before grabbing the closest, or an aircraft flying by at FL350 can mask out one you care about. Now, in practice, such an event wouldn't last long enough to matter probably, and I have some larger changes I'd like in this area anyway that will probably address this in the process.

Comment on lines +143 to +151
case MAVLink.ADSB_EMITTER_TYPE.GLIDER:
bitmap = adsb_lighter_air;
break;
case MAVLink.ADSB_EMITTER_TYPE.PARACHUTE:
bitmap = adsb_parachute;
break;
case MAVLink.ADSB_EMITTER_TYPE.ULTRA_LIGHT:
bitmap = adsb_lighter_air;
break;

@robertlong13 robertlong13 Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this in my previous pass. You're drawing gliders and ultra lights as balloons? And seem to be missing the case for assigning the balloons to their sprite (getting unknown instead).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants