Rework heading/track to include HRD/TAH.

Clean up TIS-B flag output.
This commit is contained in:
Oliver Jowett 2017-06-15 21:07:53 +01:00
parent 05e9601903
commit 1ec32903ba
6 changed files with 169 additions and 61 deletions

View file

@ -552,8 +552,9 @@ static int decodeBDS50(struct modesMessage *mm, bool store)
}
if (track_valid) {
mm->track_valid = 1;
mm->track = track;
mm->heading_valid = 1;
mm->heading = track;
mm->heading_type = HEADING_GROUND_TRACK;
}
if (gs_valid) {
@ -704,8 +705,9 @@ static int decodeBDS60(struct modesMessage *mm, bool store)
mm->commb_format = COMMB_HEADING_SPEED;
if (heading_valid) {
mm->mag_heading_valid = 1;
mm->mag_heading = heading;
mm->heading_valid = 1;
mm->heading = heading;
mm->heading_type = HEADING_MAGNETIC;
}
if (ias_valid) {

View file

@ -177,11 +177,6 @@ typedef enum {
AG_UNCERTAIN
} airground_t;
typedef enum {
HEADING_TRUE,
HEADING_MAGNETIC
} heading_source_t;
typedef enum {
SIL_PER_SAMPLE, SIL_PER_HOUR
} sil_type_t;
@ -190,6 +185,14 @@ typedef enum {
CPR_SURFACE, CPR_AIRBORNE, CPR_COARSE
} cpr_type_t;
typedef enum {
HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north
HEADING_TRUE, // Heading, degrees clockwise from true north
HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north
HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus
HEADING_TRACK_OR_HEADING // HEADING_GROUND_TRACK or HEADING_REF_DIR depending on the TAH bit in opstatus
} heading_type_t;
typedef enum {
COMMB_UNKNOWN,
COMMB_EMPTY_RESPONSE,
@ -414,7 +417,7 @@ struct modesMessage {
unsigned altitude_valid : 1;
unsigned track_valid : 1;
unsigned track_rate_valid : 1;
unsigned mag_heading_valid : 1;
unsigned heading_valid : 1;
unsigned roll_valid : 1;
unsigned gs_valid : 1;
unsigned ias_valid : 1;
@ -449,9 +452,9 @@ struct modesMessage {
// following fields are valid if the corresponding _valid field is set:
int geom_delta; // Difference between geometric and baro alt
float track; // True ground track, degrees (0-359). Reported directly or computed from from EW and NS velocity
float heading; // ground track or heading, degrees (0-359). Reported directly or computed from from EW and NS velocity
heading_type_t heading_type;// how to interpret 'track_or_heading'
float track_rate; // Rate of change of track, degrees/second
float mag_heading; // Magnetic heading, degrees (0-359)
float roll; // Roll, degrees, negative is left roll
unsigned gs; // Groundspeed, kts, reported directly or computed from from EW and NS velocity
unsigned ias; // Indicated airspeed, kts
@ -506,8 +509,8 @@ struct modesMessage {
unsigned nic_baro : 1;
sil_type_t sil_type;
enum { ANGLE_HEADING, ANGLE_TRACK } track_angle;
heading_source_t hrd;
heading_type_t tah;
heading_type_t hrd;
unsigned cc_lw;
unsigned cc_antenna_offset;

View file

@ -765,12 +765,13 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
mm->gs_valid = 1;
if (mm->gs) {
float heading = atan2(ew_vel, ns_vel) * 180.0 / M_PI;
float ground_track = atan2(ew_vel, ns_vel) * 180.0 / M_PI;
// We don't want negative values but a 0-360 scale
if (heading < 0)
heading += 360;
mm->track = heading;
mm->track_valid = 1;
if (ground_track < 0)
ground_track += 360;
mm->heading = ground_track;
mm->heading_type = HEADING_GROUND_TRACK;
mm->heading_valid = 1;
}
}
break;
@ -791,8 +792,9 @@ static void decodeESAirborneVelocity(struct modesMessage *mm, int check_imf)
}
if (getbit(me, 14)) {
mm->mag_heading_valid = 1;
mm->mag_heading = getbits(me, 15, 24) * 360.0 / 1024.0;
mm->heading_valid = 1;
mm->heading = getbits(me, 15, 24) * 360.0 / 1024.0;
mm->heading_type = HEADING_MAGNETIC_OR_TRUE;
}
break;
}
@ -828,8 +830,9 @@ static void decodeESSurfacePosition(struct modesMessage *mm, int check_imf)
}
if (getbit(me, 13)) {
mm->track_valid = 1;
mm->track = getbits(me, 14, 20) * 360.0 / 128.0;
mm->heading_valid = 1;
mm->heading = getbits(me, 14, 20) * 360.0 / 128.0;
mm->heading_type = HEADING_TRACK_OR_HEADING;
}
}
@ -1022,12 +1025,12 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf)
mm->opstatus.nic_supp_a = getbit(me, 44);
mm->opstatus.nac_p = getbits(me, 45, 48);
mm->opstatus.sil = getbits(me, 51, 52);
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
if (mm->mesub == 0) {
mm->opstatus.nic_baro = getbit(me, 53);
} else {
mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING;
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
}
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
break;
case 2:
@ -1064,13 +1067,13 @@ static void decodeESOperationalStatus(struct modesMessage *mm, int check_imf)
mm->opstatus.nic_supp_a = getbit(me, 44);
mm->opstatus.nac_p = getbits(me, 45, 48);
mm->opstatus.sil = getbits(me, 51, 52);
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
if (mm->mesub == 0) {
mm->opstatus.gva = getbits(me, 49, 50);
mm->opstatus.nic_baro = getbit(me, 53);
} else {
mm->opstatus.track_angle = getbit(me, 53) ? ANGLE_TRACK : ANGLE_HEADING;
mm->opstatus.tah = getbit(me, 53) ? HEADING_GROUND_TRACK : mm->opstatus.hrd;
}
mm->opstatus.hrd = getbit(me, 54) ? HEADING_MAGNETIC : HEADING_TRUE;
mm->opstatus.sil_type = getbit(me, 55) ? SIL_PER_SAMPLE : SIL_PER_HOUR;
break;
}
@ -1299,6 +1302,23 @@ static const char *cpr_type_to_string(cpr_type_t type) {
}
}
static const char *heading_type_to_string(heading_type_t type) {
switch (type) {
case HEADING_GROUND_TRACK:
return "Ground track";
case HEADING_MAGNETIC:
return "Mag heading";
case HEADING_TRUE:
return "True heading";
case HEADING_MAGNETIC_OR_TRUE:
return "Heading";
case HEADING_TRACK_OR_HEADING:
return "Track/Heading";
default:
return "unknown heading type";
}
}
static const char *commb_format_to_string(commb_format_t format) {
switch (format) {
case COMMB_EMPTY_RESPONSE:
@ -1585,12 +1605,8 @@ void displayModesMessage(struct modesMessage *mm) {
mm->geom_delta);
}
if (mm->track_valid) {
printf(" Track: %.1f\n", mm->track);
}
if (mm->mag_heading_valid) {
printf(" Mag heading: %.1f\n", mm->mag_heading);
if (mm->heading_valid) {
printf(" %-13s %.1f\n", heading_type_to_string(mm->heading_type), mm->heading);
}
if (mm->track_rate_valid) {
@ -1701,8 +1717,8 @@ void displayModesMessage(struct modesMessage *mm) {
if (mm->opstatus.nic_baro) printf(" NICbaro: %d\n", mm->opstatus.nic_baro);
if (mm->mesub == 1)
printf(" Heading type: %s\n", (mm->opstatus.track_angle == ANGLE_HEADING ? "heading" : "track angle"));
printf(" Heading reference: %s\n", (mm->opstatus.hrd == HEADING_TRUE ? "true north" : "magnetic north"));
printf(" Track/heading: %s\n", heading_type_to_string(mm->opstatus.tah));
printf(" Heading ref dir: %s\n", heading_type_to_string(mm->opstatus.hrd));
}
if (mm->intent.valid) {

View file

@ -604,14 +604,14 @@ static void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a) {
// Field 13 is the ground Speed (if we have it)
if (mm->gs_valid) {
p += sprintf(p, ",%d", mm->gs);
p += sprintf(p, ",%u", mm->gs);
} else {
p += sprintf(p, ",");
}
// Field 14 is the ground Heading (if we have it)
if (mm->track_valid) {
p += sprintf(p, ",%.0f", mm->track);
if (mm->heading_valid && mm->heading_type == HEADING_GROUND_TRACK) {
p += sprintf(p, ",%.0f", mm->heading);
} else {
p += sprintf(p, ",");
}
@ -1201,6 +1201,10 @@ char *generateAircraftJson(const char *url_path, int *len) {
p += snprintf(p, end-p, ",\"track\":%.1f", a->track);
if (trackDataValid(&a->track_rate_valid))
p += snprintf(p, end-p, ",\"track_rate\":%.2f", a->track_rate);
if (trackDataValid(&a->mag_heading_valid))
p += snprintf(p, end-p, ",\"mag_heading\":%.1f", a->mag_heading);
if (trackDataValid(&a->true_heading_valid))
p += snprintf(p, end-p, ",\"true_heading\":%.1f", a->true_heading);
if (trackDataValid(&a->gs_valid))
p += snprintf(p, end-p, ",\"gs\":%u", a->gs);
if (trackDataValid(&a->ias_valid))
@ -1821,18 +1825,47 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a)
typedef enum {
TISB_IDENT = 1,
TISB_SQUAWK = 2,
TISB_ALTITUDE = 4,
TISB_ALTITUDE_GEOM = 8,
TISB_ALT = 4,
TISB_ALT_GEOM = 8,
TISB_GS = 16,
TISB_IAS = 32,
TISB_TAS = 64,
TISB_POSITION = 128,
TISB_TRACK = 256,
TISB_MAG_HEADING = 512,
TISB_AIRGROUND = 1024,
TISB_CATEGORY = 2048
TISB_LAT = 128,
TISB_LON = 256,
TISB_TRACK = 512,
TISB_MAG_HEADING = 1024,
TISB_TRUE_HEADING = 2048,
TISB_AIRGROUND = 4096,
TISB_CATEGORY = 8192,
TISB_INTENT_ALT = 16384,
TISB_INTENT_HEADING = 32768,
TISB_ALT_SETTING = 65536
} tisb_flags;
struct {
tisb_flags flag;
const char *name;
} tisb_flag_names[] = {
{ TISB_IDENT, "ident" },
{ TISB_SQUAWK, "squawk" },
{ TISB_ALT, "alt" },
{ TISB_ALT_GEOM, "alt_geom" },
{ TISB_GS, "gs" },
{ TISB_IAS, "ias" },
{ TISB_TAS, "tas" },
{ TISB_LAT, "lat" },
{ TISB_LON, "lat" },
{ TISB_TRACK, "track" },
{ TISB_MAG_HEADING, "mag_heading" },
{ TISB_TRUE_HEADING, "true_heading" },
{ TISB_AIRGROUND, "airGround" },
{ TISB_CATEGORY, "category" },
{ TISB_INTENT_ALT, "intent_alt" },
{ TISB_INTENT_HEADING, "intent_heading" },
{ TISB_ALT_SETTING, "alt_setting" },
{ 0, NULL }
};
static inline unsigned unsigned_difference(unsigned v1, unsigned v2)
{
return (v1 > v2) ? (v1 - v2) : (v2 - v1);
@ -1887,6 +1920,7 @@ static void writeFATSV()
int trackValid = trackDataValidEx(&a->track_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES
int trackRateValid = trackDataValidEx(&a->track_rate_valid, now, 15000, SOURCE_MODE_S); // Comm-B
int rollValid = trackDataValidEx(&a->roll_valid, now, 15000, SOURCE_MODE_S); // Comm-B
int trueHeadingValid = trackDataValidEx(&a->true_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES
int magHeadingValid = trackDataValidEx(&a->mag_heading_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES
int gsValid = trackDataValidEx(&a->gs_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES
int iasValid = trackDataValidEx(&a->ias_valid, now, 15000, SOURCE_MODE_S); // Comm-B or ES
@ -1921,7 +1955,7 @@ static void writeFATSV()
if (geomRateValid && abs(a->geom_rate - a->fatsv_emitted_geom_rate) > 500) {
changed = 1;
}
if (trackValid && heading_difference(a->track, a->fatsv_emitted_heading) >= 2) {
if (trackValid && heading_difference(a->track, a->fatsv_emitted_track) >= 2) {
changed = 1;
}
if (trackRateValid && fabs(a->track_rate - a->fatsv_emitted_track_rate) >= 0.5) {
@ -1930,7 +1964,10 @@ static void writeFATSV()
if (rollValid && fabs(a->roll - a->fatsv_emitted_roll) >= 5.0) {
changed = 1;
}
if (magHeadingValid && heading_difference(a->mag_heading, a->fatsv_emitted_heading_magnetic) >= 2) {
if (magHeadingValid && heading_difference(a->mag_heading, a->fatsv_emitted_mag_heading) >= 2) {
changed = 1;
}
if (trueHeadingValid && heading_difference(a->true_heading, a->fatsv_emitted_true_heading) >= 2) {
changed = 1;
}
if (gsValid && unsigned_difference(a->gs, a->fatsv_emitted_speed) >= 25) {
@ -2039,14 +2076,14 @@ static void writeFATSV()
p += snprintf(p, bufsize(p,end), "\talt\t%d", a->altitude);
a->fatsv_emitted_altitude = a->altitude;
useful = 1;
tisb |= (a->altitude_valid.source == SOURCE_TISB) ? TISB_ALTITUDE : 0;
tisb |= (a->altitude_valid.source == SOURCE_TISB) ? TISB_ALT : 0;
}
if (altGeomValid && a->altitude_geom_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\talt_geom\t%d", a->altitude_geom);
a->fatsv_emitted_altitude_gnss = a->altitude_geom;
useful = 1;
tisb |= (a->altitude_geom_valid.source == SOURCE_TISB) ? TISB_ALTITUDE_GEOM : 0;
tisb |= (a->altitude_geom_valid.source == SOURCE_TISB) ? TISB_ALT_GEOM : 0;
}
if (baroRateValid && a->baro_rate_valid.updated > a->fatsv_last_emitted) {
@ -2091,12 +2128,12 @@ static void writeFATSV()
if (positionValid && a->position_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon);
useful = 1;
tisb |= (a->position_valid.source == SOURCE_TISB) ? TISB_POSITION : 0;
tisb |= (a->position_valid.source == SOURCE_TISB) ? (TISB_LAT | TISB_LON) : 0;
}
if (trackValid && a->track_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\ttrack\t%.0f", a->track);
a->fatsv_emitted_heading = a->track;
a->fatsv_emitted_track = a->track;
useful = 1;
tisb |= (a->track_valid.source == SOURCE_TISB) ? TISB_TRACK : 0;
}
@ -2115,11 +2152,18 @@ static void writeFATSV()
if (magHeadingValid && a->mag_heading_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tmag_heading\t%.0f", a->mag_heading);
a->fatsv_emitted_heading_magnetic = a->mag_heading;
a->fatsv_emitted_mag_heading = a->mag_heading;
useful = 1;
tisb |= (a->mag_heading_valid.source == SOURCE_TISB) ? TISB_MAG_HEADING : 0;
}
if (trueHeadingValid && a->true_heading_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\true_heading\t%.0f", a->true_heading);
a->fatsv_emitted_true_heading = a->true_heading;
useful = 1;
tisb |= (a->true_heading_valid.source == SOURCE_TISB) ? TISB_TRUE_HEADING : 0;
}
if (airgroundValid && (a->airground == AG_GROUND || a->airground == AG_AIRBORNE) && a->airground_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tairGround\t%s", a->airground == AG_GROUND ? "G+" : "A+");
a->fatsv_emitted_airground = a->airground;
@ -2138,18 +2182,21 @@ static void writeFATSV()
p += snprintf(p, bufsize(p,end), "\tintent_alt\t%u", a->intent_altitude);
a->fatsv_emitted_intent_altitude = a->intent_altitude;
useful = 1;
tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_ALT : 0;
}
if (intentHeadingValid && a->intent_heading_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\tintent_heading\t%.0f", a->intent_heading);
a->fatsv_emitted_intent_heading = a->intent_heading;
useful = 1;
tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_INTENT_HEADING : 0;
}
if (altSettingValid && a->alt_setting_valid.updated > a->fatsv_last_emitted) {
p += snprintf(p, bufsize(p,end), "\talt_setting\t%.1f", a->alt_setting);
a->fatsv_emitted_alt_setting = a->alt_setting;
useful = 1;
tisb |= (a->category_valid.source == SOURCE_TISB) ? TISB_ALT_SETTING : 0;
}
@ -2160,7 +2207,12 @@ static void writeFATSV()
}
if (tisb != 0) {
p += snprintf(p, bufsize(p,end), "\ttisb\t%d", (int)tisb);
p += snprintf(p, bufsize(p,end), "\ttisb\t");
for (int i = 0; tisb_flag_names[i].name; ++i) {
if (tisb & tisb_flag_names[i].flag) {
p += snprintf(p, bufsize(p,end), "%s ", tisb_flag_names[i].name);
}
}
}
p += snprintf(p, bufsize(p,end), "\n");

39
track.c
View file

@ -81,6 +81,11 @@ struct aircraft *trackCreateAircraft(struct modesMessage *mm) {
a->fatsv_emitted_bds_30[0] = 0x30;
a->fatsv_emitted_es_acas_ra[0] = 0xE2;
// defaults until we see an op status message
a->adsb_version = -1;
a->adsb_hrd = HEADING_MAGNETIC;
a->adsb_tah = HEADING_GROUND_TRACK;
// Copy the first message so we can emit it later when a second message arrives.
a->first_message = *mm;
@ -543,6 +548,10 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
if (mm->addrtype < a->addrtype)
a->addrtype = mm->addrtype;
// if we saw some direct ADS-B for the first time, assume version 0
if (mm->source == SOURCE_ADSB && a->adsb_version < 0)
a->adsb_version = 0;
if (mm->altitude_valid && mm->altitude_source == ALTITUDE_BARO && accept_data(&a->altitude_valid, mm->source, now)) {
if (a->modeC_hit) {
int new_modeC = (a->altitude + 49) / 100;
@ -570,8 +579,21 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
a->geom_delta = mm->geom_delta;
}
if (mm->track_valid && accept_data(&a->track_valid, mm->source, now)) {
a->track = mm->track;
if (mm->heading_valid) {
heading_type_t htype = mm->heading_type;
if (htype == HEADING_MAGNETIC_OR_TRUE) {
htype = a->adsb_hrd;
} else if (htype == HEADING_TRACK_OR_HEADING) {
htype = a->adsb_tah;
}
if (htype == HEADING_GROUND_TRACK && accept_data(&a->track_valid, mm->source, now)) {
a->track = mm->heading;
} else if (htype == HEADING_MAGNETIC && accept_data(&a->mag_heading_valid, mm->source, now)) {
a->mag_heading = mm->heading;
} else if (htype == HEADING_TRUE && accept_data(&a->true_heading_valid, mm->source, now)) {
a->true_heading = mm->heading;
}
}
if (mm->track_rate_valid && accept_data(&a->track_rate_valid, mm->source, now)) {
@ -582,10 +604,6 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
a->roll = mm->roll;
}
if (mm->mag_heading_valid && accept_data(&a->mag_heading_valid, mm->source, now)) {
a->mag_heading = mm->mag_heading;
}
if (mm->gs_valid && accept_data(&a->gs_valid, mm->source, now)) {
a->gs = mm->gs;
}
@ -654,6 +672,15 @@ struct aircraft *trackUpdateFromMessage(struct modesMessage *mm)
a->cpr_odd_nuc = mm->cpr_nucp;
}
// operational status message
if (mm->opstatus.valid) {
a->adsb_version = mm->opstatus.version;
if (mm->opstatus.version > 0) {
a->adsb_hrd = mm->opstatus.hrd;
a->adsb_tah = mm->opstatus.tah;
}
}
// Now handle derived data
// derive geometric altitude if we have baro + delta

12
track.h
View file

@ -118,6 +118,9 @@ struct aircraft {
data_validity mag_heading_valid;
float mag_heading; // Magnetic heading
data_validity true_heading_valid;
float true_heading; // True heading
data_validity baro_rate_valid;
int baro_rate; // Vertical rate (barometric)
@ -158,6 +161,10 @@ struct aircraft {
double lat, lon; // Coordinated obtained from CPR encoded data
unsigned pos_nuc; // NUCp of last computed position
int adsb_version; // ADS-B version (from ADS-B operational status); -1 means no ADS-B messages seen
heading_type_t adsb_hrd; // Heading Reference Direction setting (from ADS-B operational status)
heading_type_t adsb_tah; // Track Angle / Heading setting (from ADS-B operational status)
int modeA_hit; // did our squawk match a possible mode A reply in the last check period?
int modeC_hit; // did our altitude match a possible mode C reply in the last check period?
@ -165,9 +172,10 @@ struct aircraft {
int fatsv_emitted_altitude_gnss; // -"- GNSS altitude
int fatsv_emitted_baro_rate; // -"- barometric rate
int fatsv_emitted_geom_rate; // -"- geometric rate
float fatsv_emitted_heading; // -"- true track
float fatsv_emitted_heading_magnetic; // -"- magnetic heading
float fatsv_emitted_track; // -"- true track
float fatsv_emitted_track_rate; // -"- track rate of change
float fatsv_emitted_mag_heading; // -"- magnetic heading
float fatsv_emitted_true_heading; // -"- true heading
float fatsv_emitted_roll; // -"- roll angle
unsigned fatsv_emitted_speed; // -"- groundspeed
unsigned fatsv_emitted_speed_ias; // -"- IAS