1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 09:48:45 -05:00

Merge pull request #1393 from srcejon/adsb_mode_s

ADS-B: Add support for decoding Comm-B replies in Mode-S frames
This commit is contained in:
Edouard Griffiths 2022-08-26 17:05:34 +02:00 committed by GitHub
commit d1a4fca49b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2174 additions and 503 deletions

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@
#include "adsbdemodsettings.h"
#include "ourairportsdb.h"
#include "osndb.h"
#include "util/osndb.h"
class PluginAPI;
class DeviceUISet;
@ -85,7 +85,7 @@ public:
}
};
// Data about an aircraft extracted from an ADS-B frames
// Data about an aircraft extracted from ADS-B and Mode-S frames
struct Aircraft {
int m_icao; // 24-bit ICAO aircraft address
QString m_icaoHex;
@ -96,14 +96,7 @@ struct Aircraft {
int m_altitude; // Altitude in feet
bool m_onSurface; // Indicates if on surface or airbourne
bool m_altitudeGNSS; // Altitude is GNSS HAE (Height above WGS-84 ellipsoid) rather than barometric alitute (relative to 29.92 Hg)
int m_speed; // Speed in knots
enum SpeedType {
GS, // Ground speed
TAS, // True air speed
IAS // Indicated air speed
} m_speedType;
static const char *m_speedTypeNames[];
float m_heading; // Heading in degrees
float m_heading; // Heading or track in degrees
int m_verticalRate; // Vertical climb rate in ft/min
QString m_emitterCategory; // Aircraft type
QString m_status; // Aircraft status
@ -113,11 +106,32 @@ struct Aircraft {
Real m_elevation; // Elevation from station to aicraft
QDateTime m_time; // When last updated
int m_selAltitude; // Selected altitude in MCP/FCU or FMS in feet
int m_selHeading; // Selected heading in MCP/FCU in degrees
int m_baro; // Aircraft baro setting in mb (Mode-S)
float m_roll; // In degrees
int m_groundspeed; // In knots
float m_turnRate; // In degrees per second
int m_trueAirspeed; // In knots
int m_indicatedAirspeed; // In knots
float m_mach; // Mach number
bool m_bdsCapabilities[16][16]; // BDS capabilities are indicaited by BDS 1.7
bool m_positionValid; // Indicates if we have valid data for the above fields
bool m_altitudeValid;
bool m_speedValid;
bool m_headingValid;
bool m_verticalRateValid;
bool m_selAltitudeValid;
bool m_selHeadingValid;
bool m_baroValid;
bool m_rollValid;
bool m_groundspeedValid;
bool m_turnRateValid;
bool m_trueAirspeedValid;
bool m_indicatedAirspeedValid;
bool m_machValid;
bool m_bdsCapabilitiesValid;
// State for calculating position using two CPR frames
bool m_cprValid[2];
@ -126,6 +140,7 @@ struct Aircraft {
QDateTime m_cprTime[2];
int m_adsbFrameCount; // Number of ADS-B frames for this aircraft
int m_tisBFrameCount;
float m_minCorrelation;
float m_maxCorrelation;
float m_correlation;
@ -158,8 +173,8 @@ struct Aircraft {
QDateTime m_headingDateTime;
QDateTime m_prevHeadingDateTime;
int m_prevHeading;
float m_pitch; // Estimated pitch based on vertical rate
float m_roll; // Estimated roll based on rate of change in heading
float m_pitchEst; // Estimated pitch based on vertical rate
float m_rollEst; // Estimated roll based on rate of change in heading
bool m_notified; // Set when a notification has been made for this aircraft, so we don't repeat it
@ -171,7 +186,6 @@ struct Aircraft {
QTableWidgetItem *m_latitudeItem;
QTableWidgetItem *m_longitudeItem;
QTableWidgetItem *m_altitudeItem;
QTableWidgetItem *m_speedItem;
QTableWidgetItem *m_headingItem;
QTableWidgetItem *m_verticalRateItem;
CustomDoubleTableWidgetItem *m_rangeItem;
@ -198,6 +212,26 @@ struct Aircraft {
QTableWidgetItem *m_staItem;
QTableWidgetItem *m_etaItem;
QTableWidgetItem *m_ataItem;
QTableWidgetItem *m_selAltitudeItem;
QTableWidgetItem *m_selHeadingItem;
QTableWidgetItem *m_baroItem;
QTableWidgetItem *m_apItem;
QTableWidgetItem *m_vModeItem;
QTableWidgetItem *m_lModeItem;
QTableWidgetItem *m_rollItem;
QTableWidgetItem *m_groundspeedItem;
QTableWidgetItem *m_turnRateItem;
QTableWidgetItem *m_trueAirspeedItem;
QTableWidgetItem *m_indicatedAirspeedItem;
QTableWidgetItem *m_machItem;
QTableWidgetItem *m_headwindItem;
QTableWidgetItem *m_estAirTempItem;
QTableWidgetItem *m_windSpeedItem;
QTableWidgetItem *m_windDirItem;
QTableWidgetItem *m_staticPressureItem;
QTableWidgetItem *m_staticAirTempItem;
QTableWidgetItem *m_humidityItem;
QTableWidgetItem *m_tisBItem;
Aircraft(ADSBDemodGUI *gui) :
m_icao(0),
@ -206,18 +240,35 @@ struct Aircraft {
m_altitude(0),
m_onSurface(false),
m_altitudeGNSS(false),
m_speed(0),
m_speedType(GS),
m_heading(0),
m_verticalRate(0),
m_azimuth(0),
m_elevation(0),
m_selAltitude(0),
m_selHeading(0),
m_baro(0),
m_roll(0.0f),
m_groundspeed(0),
m_turnRate(0.0f),
m_trueAirspeed(0),
m_indicatedAirspeed(0),
m_mach(0.0f),
m_positionValid(false),
m_altitudeValid(false),
m_speedValid(false),
m_headingValid(false),
m_verticalRateValid(false),
m_selAltitudeValid(false),
m_selHeadingValid(false),
m_baroValid(false),
m_rollValid(false),
m_groundspeedValid(false),
m_turnRateValid(false),
m_trueAirspeedValid(false),
m_indicatedAirspeedValid(false),
m_machValid(false),
m_bdsCapabilitiesValid(false),
m_adsbFrameCount(0),
m_tisBFrameCount(0),
m_minCorrelation(INFINITY),
m_maxCorrelation(-INFINITY),
m_correlation(0.0f),
@ -234,21 +285,24 @@ struct Aircraft {
m_flaps(0.0),
m_rotorStarted(false),
m_engineStarted(false),
m_pitch(0.0),
m_roll(0.0),
m_pitchEst(0.0),
m_rollEst(0.0),
m_notified(false)
{
for (int i = 0; i < 2; i++)
{
for (int i = 0; i < 2; i++) {
m_cprValid[i] = false;
}
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
m_bdsCapabilities[i][j] = false;
}
}
// These are deleted by QTableWidget
m_icaoItem = new QTableWidgetItem();
m_callsignItem = new QTableWidgetItem();
m_modelItem = new QTableWidgetItem();
m_airlineItem = new QTableWidgetItem();
m_altitudeItem = new QTableWidgetItem();
m_speedItem = new QTableWidgetItem();
m_headingItem = new QTableWidgetItem();
m_verticalRateItem = new QTableWidgetItem();
m_rangeItem = new CustomDoubleTableWidgetItem();
@ -277,6 +331,26 @@ struct Aircraft {
m_staItem = new QTableWidgetItem();
m_etaItem = new QTableWidgetItem();
m_ataItem = new QTableWidgetItem();
m_selAltitudeItem = new QTableWidgetItem();
m_selHeadingItem = new QTableWidgetItem();
m_baroItem = new QTableWidgetItem();
m_apItem = new QTableWidgetItem();
m_vModeItem = new QTableWidgetItem();
m_lModeItem = new QTableWidgetItem();
m_rollItem = new QTableWidgetItem();
m_groundspeedItem = new QTableWidgetItem();
m_turnRateItem = new QTableWidgetItem();
m_trueAirspeedItem = new QTableWidgetItem();
m_indicatedAirspeedItem = new QTableWidgetItem();
m_machItem = new QTableWidgetItem();
m_headwindItem = new QTableWidgetItem();
m_estAirTempItem = new QTableWidgetItem();
m_windSpeedItem = new QTableWidgetItem();
m_windDirItem = new QTableWidgetItem();
m_staticPressureItem = new QTableWidgetItem();
m_staticAirTempItem = new QTableWidgetItem();
m_humidityItem = new QTableWidgetItem();
m_tisBItem = new QTableWidgetItem();
}
QString getImage() const;
@ -826,11 +900,6 @@ private:
AirportModel m_airportModel;
AirspaceModel m_airspaceModel;
NavAidModel m_navAidModel;
QHash<QString, QIcon *> m_airlineIcons; // Hashed on airline ICAO
QHash<QString, bool> m_airlineMissingIcons; // Hash containing which ICAOs we don't have icons for
QHash<QString, QIcon *> m_flagIcons; // Hashed on country
QHash<QString, QString> *m_prefixMap; // Registration to country (flag name)
QHash<QString, QString> *m_militaryMap; // Operator airforce to military (flag name)
QList<Airspace *> m_airspaces;
QList<NavAid *> m_navAids;
@ -867,6 +936,15 @@ private:
QTimer m_redrawMapTimer;
QNetworkAccessManager *m_networkManager;
static const char m_idMap[];
static const QString m_categorySetA[];
static const QString m_categorySetB[];
static const QString m_categorySetC[];
static const QString m_emergencyStatus[];
static const QString m_flightStatuses[];
static const QString m_hazardSeverity[];
static const QString m_fomSources[];
explicit ADSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~ADSBDemodGUI();
@ -881,12 +959,18 @@ private:
bool updateLocalPosition(Aircraft *aircraft, double latitude, double longitude, bool surfacePosition);
void sendToMap(Aircraft *aircraft, QList<SWGSDRangel::SWGMapAnimation *> *animations);
Aircraft *getAircraft(int icao, bool &newAircraft);
void callsignToFlight(Aircraft *aircraft);
int roundTo50Feet(int alt);
bool calcAirTemp(Aircraft *aircraft);
void handleADSB(
const QByteArray data,
const QDateTime dateTime,
float correlation,
float correlationOnes,
unsigned crc,
bool updateModel);
void decodeModeS(const QByteArray data, int df, Aircraft *aircraft);
void decodeCommB(const QByteArray data, const QDateTime dateTime, int df, Aircraft *aircraft, bool &updatedCallsign);
QList<SWGSDRangel::SWGMapAnimation *> *animate(QDateTime dateTime, Aircraft *aircraft);
SWGSDRangel::SWGMapAnimation *gearAnimation(QDateTime startDateTime, bool up);
SWGSDRangel::SWGMapAnimation *flapsAnimation(QDateTime startDateTime, float currentFlaps, float flaps);
@ -915,10 +999,6 @@ private:
void updateAirports();
void updateAirspaces();
void updateNavAids();
QString getAirlineIconPath(const QString &operatorICAO);
QIcon *getAirlineIcon(const QString &operatorICAO);
QString getFlagIconPath(const QString &country);
QIcon *getFlagIcon(const QString &country);
void updateDeviceSetList();
QAction *createCheckableItem(QString& text, int idx, bool checked);
Aircraft* findAircraftByFlight(const QString& flight);
@ -929,6 +1009,8 @@ private:
void updatePhotoText(Aircraft *aircraft);
void updatePhotoFlightInformation(Aircraft *aircraft);
void findOnChannelMap(Aircraft *aircraft);
int squawkDecode(int code) const;
int gillhamToFeet(int n) const;
int grayToBinary(int gray, int bits) const;
void redrawMap();
void applyImportSettings();

View File

@ -873,6 +873,54 @@
<string>Airline logo</string>
</property>
</column>
<column>
<property name="text">
<string>Country</string>
</property>
<property name="toolTip">
<string>Country of registration</string>
</property>
</column>
<column>
<property name="text">
<string>GS (kn)</string>
</property>
<property name="toolTip">
<string>Groundspeed in knots or kilometers per hour</string>
</property>
</column>
<column>
<property name="text">
<string>TAS (kn)</string>
</property>
<property name="toolTip">
<string>True airpeed in knots or kilometers per hour</string>
</property>
</column>
<column>
<property name="text">
<string>IAS (kn)</string>
</property>
<property name="toolTip">
<string>Indicated airspeed in knots or kilometers per hour</string>
</property>
</column>
<column>
<property name="text">
<string>Mach</string>
</property>
<property name="toolTip">
<string>Mach number</string>
</property>
</column>
<column>
<property name="text">
<string>Sel Alt (ft)</string>
</property>
<property name="toolTip">
<string>Selected altitude in feet or metres (As set on MCP/FCU or by FMS)</string>
</property>
</column>
<column>
<property name="text">
<string>Alt (ft)</string>
@ -883,10 +931,18 @@
</column>
<column>
<property name="text">
<string>Spd (kn)</string>
<string>VR (ft/m)</string>
</property>
<property name="toolTip">
<string>Speed in knots or kilometres per hour</string>
<string>Vertical climb rate in feet per minute or metres per second</string>
</property>
</column>
<column>
<property name="text">
<string>Sel Hd (°)</string>
</property>
<property name="toolTip">
<string>Selected heading in degrees (As set on MCP/FCU or by FMS)</string>
</property>
</column>
<column>
@ -894,15 +950,23 @@
<string>Hd (°)</string>
</property>
<property name="toolTip">
<string>Aircraft heading in degrees</string>
<string>Aircraft heading / track in degrees</string>
</property>
</column>
<column>
<property name="text">
<string>VR (ft/m)</string>
<string>TR (°/s)</string>
</property>
<property name="toolTip">
<string>Vertical climb rate in feet per minute or metres per second</string>
<string>Turn rate in degrees per second</string>
</property>
</column>
<column>
<property name="text">
<string>Roll (°)</string>
</property>
<property name="toolTip">
<string>Roll angle in degrees</string>
</property>
</column>
<column>
@ -921,22 +985,6 @@
<string>Azimuth and elevation to aircraft from My Position. Double click to set as target.</string>
</property>
</column>
<column>
<property name="text">
<string>Lat (°)</string>
</property>
<property name="toolTip">
<string extracomment="Double click to centre the map on this aircraft">Latitude in degrees postive towards the North</string>
</property>
</column>
<column>
<property name="text">
<string>Lon (°)</string>
</property>
<property name="toolTip">
<string extracomment="Double click to centre the map on this aircraft">Longitude in degrees. Positive towards the East</string>
</property>
</column>
<column>
<property name="text">
<string>Cat</string>
@ -969,14 +1017,6 @@
<string>Aircraft registration</string>
</property>
</column>
<column>
<property name="text">
<string>Country</string>
</property>
<property name="toolTip">
<string>Country of registration</string>
</property>
</column>
<column>
<property name="text">
<string>Registered</string>
@ -1009,6 +1049,110 @@
<string>Aircraft operator ICAO code</string>
</property>
</column>
<column>
<property name="text">
<string>AP</string>
</property>
<property name="toolTip">
<string>Autopilot enabled</string>
</property>
</column>
<column>
<property name="text">
<string>V Mode</string>
</property>
<property name="toolTip">
<string>MCP/FCU vertical mode</string>
</property>
</column>
<column>
<property name="text">
<string>L Mode</string>
</property>
<property name="toolTip">
<string>MCP/FCU lateral mode</string>
</property>
</column>
<column>
<property name="text">
<string>Baro (mb)</string>
</property>
<property name="toolTip">
<string>Barometer setting in millibars</string>
</property>
</column>
<column>
<property name="text">
<string>H Wnd (kn)</string>
</property>
<property name="toolTip">
<string>Headwind in knots or kilometers per hour</string>
</property>
</column>
<column>
<property name="text">
<string>OAT (C)</string>
</property>
<property name="toolTip">
<string>Outside air temperature in degrees Celsuis estimated from Mach and TAS</string>
</property>
</column>
<column>
<property name="text">
<string>Wnd (kn)</string>
</property>
<property name="toolTip">
<string>Wind speed in knots</string>
</property>
</column>
<column>
<property name="text">
<string>Wnd (°)</string>
</property>
<property name="toolTip">
<string>Wind direction in degrees</string>
</property>
</column>
<column>
<property name="text">
<string>P (hPa)</string>
</property>
<property name="toolTip">
<string>Static air pressure in hectopascals</string>
</property>
</column>
<column>
<property name="text">
<string>T (C)</string>
</property>
<property name="toolTip">
<string>Static air temperature in degrees Celsuis</string>
</property>
</column>
<column>
<property name="text">
<string>U (%)</string>
</property>
<property name="toolTip">
<string>Humidity in percent</string>
</property>
</column>
<column>
<property name="text">
<string>Lat (°)</string>
</property>
<property name="toolTip">
<string extracomment="Double click to centre the map on this aircraft">Latitude in degrees postive towards the North</string>
</property>
</column>
<column>
<property name="text">
<string>Lon (°)</string>
</property>
<property name="toolTip">
<string extracomment="Double click to centre the map on this aircraft">Longitude in degrees. Positive towards the East</string>
</property>
</column>
<column>
<property name="text">
<string>Updated</string>
@ -1025,6 +1169,14 @@
<string>Number of ADS-B frames received from this aircraft</string>
</property>
</column>
<column>
<property name="text">
<string>TIS-B</string>
</property>
<property name="toolTip">
<string>Number of TIS-B frames received with this aircrafts ICAO</string>
</property>
</column>
<column>
<property name="text">
<string>Correlation</string>

View File

@ -26,7 +26,7 @@
// Map main ADS-B table column numbers to combo box indicies
std::vector<int> ADSBDemodNotificationDialog::m_columnMap = {
ADSB_COL_ICAO, ADSB_COL_CALLSIGN, ADSB_COL_MODEL,
ADSB_COL_ALTITUDE, ADSB_COL_SPEED, ADSB_COL_RANGE,
ADSB_COL_ALTITUDE, ADSB_COL_GROUND_SPEED, ADSB_COL_RANGE,
ADSB_COL_CATEGORY, ADSB_COL_STATUS, ADSB_COL_SQUAWK,
ADSB_COL_REGISTRATION, ADSB_COL_MANUFACTURER, ADSB_COL_OWNER, ADSB_COL_OPERATOR_ICAO
};
@ -118,7 +118,7 @@ void ADSBDemodNotificationDialog::addRow(ADSBDemodSettings::NotificationSettings
match->addItem("Callsign");
match->addItem("Aircraft");
match->addItem("Alt (ft)");
match->addItem("Spd (kn)");
match->addItem("GS (kn)");
match->addItem("D (km)");
match->addItem("Cat");
match->addItem("Status");

View File

@ -39,15 +39,17 @@ public:
QDateTime getDateTime() const { return m_dateTime; }
float getPreambleCorrelation() const { return m_preambleCorrelation; }
float getCorrelationOnes() const { return m_correlationOnes; }
unsigned getCRC() const { return m_crc; }
static MsgReportADSB* create(
QByteArray data,
float preambleCorrelation,
float correlationOnes,
QDateTime dateTime
QDateTime dateTime,
unsigned crc
)
{
return new MsgReportADSB(data, preambleCorrelation, correlationOnes, dateTime);
return new MsgReportADSB(data, preambleCorrelation, correlationOnes, dateTime, crc);
}
private:
@ -55,18 +57,21 @@ public:
QDateTime m_dateTime;
float m_preambleCorrelation;
float m_correlationOnes;
unsigned m_crc;
MsgReportADSB(
QByteArray data,
float preambleCorrelation,
float correlationOnes,
QDateTime dateTime
QDateTime dateTime,
unsigned crc
) :
Message(),
m_data(data),
m_dateTime(dateTime),
m_preambleCorrelation(preambleCorrelation),
m_correlationOnes(correlationOnes)
m_correlationOnes(correlationOnes),
m_crc(crc)
{
}
};

View File

@ -76,7 +76,7 @@ void ADSBDemodSettings::resetToDefaults()
m_tableFontSize = 9;
m_displayDemodStats = false;
m_correlateFullPreamble = true;
m_demodModeS = false;
m_demodModeS = true;
m_deviceIndex = -1;
m_autoResizeTableColumns = false;
m_interpolatorPhaseSteps = 4; // Higher than these two values will struggle to run in real-time
@ -264,7 +264,7 @@ bool ADSBDemodSettings::deserialize(const QByteArray& data)
d.readS32(26, &m_tableFontSize, 9);
d.readBool(27, &m_displayDemodStats, false);
d.readBool(28, &m_correlateFullPreamble, true);
d.readBool(29, &m_demodModeS, false);
d.readBool(29, &m_demodModeS, true);
d.readBool(30, &m_autoResizeTableColumns, false);
d.readS32(31, &m_interpolatorPhaseSteps, 4);
d.readFloat(32, &m_interpolatorTapsPerPhase, 3.5f);

View File

@ -29,43 +29,62 @@
class Serializable;
// Number of columns in the table
#define ADSBDEMOD_COLUMNS 34
#define ADSBDEMOD_COLUMNS 53
// ADS-B table columns
#define ADSB_COL_ICAO 0
#define ADSB_COL_CALLSIGN 1
#define ADSB_COL_MODEL 2
#define ADSB_COL_AIRLINE 3
#define ADSB_COL_ALTITUDE 4
#define ADSB_COL_SPEED 5
#define ADSB_COL_HEADING 6
#define ADSB_COL_VERTICALRATE 7
#define ADSB_COL_RANGE 8
#define ADSB_COL_AZEL 9
#define ADSB_COL_LATITUDE 10
#define ADSB_COL_LONGITUDE 11
#define ADSB_COL_CATEGORY 12
#define ADSB_COL_STATUS 13
#define ADSB_COL_SQUAWK 14
#define ADSB_COL_REGISTRATION 15
#define ADSB_COL_COUNTRY 16
#define ADSB_COL_REGISTERED 17
#define ADSB_COL_MANUFACTURER 18
#define ADSB_COL_OWNER 19
#define ADSB_COL_OPERATOR_ICAO 20
#define ADSB_COL_TIME 21
#define ADSB_COL_FRAMECOUNT 22
#define ADSB_COL_CORRELATION 23
#define ADSB_COL_RSSI 24
#define ADSB_COL_FLIGHT_STATUS 25
#define ADSB_COL_DEP 26
#define ADSB_COL_ARR 27
#define ADSB_COL_STD 28
#define ADSB_COL_ETD 29
#define ADSB_COL_ATD 30
#define ADSB_COL_STA 31
#define ADSB_COL_ETA 32
#define ADSB_COL_ATA 33
#define ADSB_COL_ICAO 0
#define ADSB_COL_CALLSIGN 1
#define ADSB_COL_MODEL 2
#define ADSB_COL_AIRLINE 3
#define ADSB_COL_COUNTRY 4
#define ADSB_COL_GROUND_SPEED 5
#define ADSB_COL_TRUE_AIRSPEED 6
#define ADSB_COL_INDICATED_AIRSPEED 7
#define ADSB_COL_MACH 8
#define ADSB_COL_SEL_ALTITUDE 9
#define ADSB_COL_ALTITUDE 10
#define ADSB_COL_VERTICALRATE 11
#define ADSB_COL_SEL_HEADING 12
#define ADSB_COL_HEADING 13
#define ADSB_COL_TURNRATE 14
#define ADSB_COL_ROLL 15
#define ADSB_COL_RANGE 16
#define ADSB_COL_AZEL 17
#define ADSB_COL_CATEGORY 18
#define ADSB_COL_STATUS 19
#define ADSB_COL_SQUAWK 20
#define ADSB_COL_REGISTRATION 21
#define ADSB_COL_REGISTERED 22
#define ADSB_COL_MANUFACTURER 23
#define ADSB_COL_OWNER 24
#define ADSB_COL_OPERATOR_ICAO 25
#define ADSB_COL_AP 26
#define ADSB_COL_V_MODE 27
#define ADSB_COL_L_MODE 28
#define ADSB_COL_BARO 29
#define ADSB_COL_HEADWIND 30
#define ADSB_COL_EST_AIR_TEMP 31
#define ADSB_COL_WIND_SPEED 32
#define ADSB_COL_WIND_DIR 33
#define ADSB_COL_STATIC_PRESSURE 34
#define ADSB_COL_STATIC_AIR_TEMP 35
#define ADSB_COL_HUMIDITY 36
#define ADSB_COL_LATITUDE 37
#define ADSB_COL_LONGITUDE 38
#define ADSB_COL_TIME 39
#define ADSB_COL_FRAMECOUNT 40
#define ADSB_COL_TIS_B 41
#define ADSB_COL_CORRELATION 42
#define ADSB_COL_RSSI 43
#define ADSB_COL_FLIGHT_STATUS 44
#define ADSB_COL_DEP 45
#define ADSB_COL_ARR 46
#define ADSB_COL_STD 47
#define ADSB_COL_ETD 48
#define ADSB_COL_ATD 49
#define ADSB_COL_STA 50
#define ADSB_COL_ETA 51
#define ADSB_COL_ATA 52
struct ADSBDemodSettings
{

View File

@ -195,6 +195,9 @@ void ADSBDemodSinkWorker::run()
{
// Got a valid frame
m_demodStats.m_adsbFrames++;
// Get 24-bit ICAO and save in hash of ICAOs that have been seen
unsigned icao = ((data[1] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[3] & 0xff);
m_icaos.insert(icao, true);
// Don't try to re-demodulate the same frame
// We could possibly allow a partial overlap here
readIdx += (ADS_B_ES_BITS+ADS_B_PREAMBLE_BITS)*ADS_B_CHIPS_PER_BIT*samplesPerChip - 1;
@ -205,7 +208,8 @@ void ADSBDemodSinkWorker::run()
QByteArray((char*)data, sizeof(data)),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer));
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToGUI()->push(msg);
}
// Pass to worker to feed to other servers
@ -215,7 +219,8 @@ void ADSBDemodSinkWorker::run()
QByteArray((char*)data, sizeof(data)),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer));
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToWorker()->push(msg);
}
}
@ -226,23 +231,48 @@ void ADSBDemodSinkWorker::run()
{
int bytes;
m_crc.init();
if ((df == 0) || (df == 4) || (df == 5) || (df == 11))
// Determine number of bytes in frame depending on downlink format
if ((df == 0) || (df == 4) || (df == 5) || (df == 11)) {
bytes = 56/8;
else if ((df == 16) || (df == 20) || (df == 21) || (df >= 24))
} else if ((df == 16) || (df == 20) || (df == 21) || (df >= 24)) {
bytes = 112/8;
else
} else {
bytes = 0;
}
if (bytes > 0)
{
// Extract received parity
int parity = (data[bytes-3] << 16) | (data[bytes-2] << 8) | data[bytes-1];
// Calculate CRC on received frame
m_crc.init();
m_crc.calculate(data, bytes-3);
int crc = m_crc.get();
// DF4 / DF5 / DF20 / DF21 have ICAO address XORed in to parity.
// Extract ICAO from parity and see if it matches an aircraft we've already
// received an ADS-B frame from
if ((df == 4) || (df == 5) || (df == 20) || (df == 21))
{
unsigned icao = (parity ^ crc) & 0xffffff;
if (m_icaos.contains(icao)) {
crc ^= icao;
}
}
// For DF11, the last 7 bits may have an address/interogration indentifier (II)
// XORed in, so we ignore those bits
if ((parity == crc) || ((df == 11) && (parity & 0xffff80) == (crc & 0xffff80)))
if ((parity == crc) || ((df == 11) && ((parity & 0xffff80) == (crc & 0xffff80))))
{
m_demodStats.m_modesFrames++;
// Pass to GUI (only pass formats it can decode)
if (m_sink->getMessageQueueToGUI() && ((df == 4) || (df == 5) || (df == 20) || (df == 21)))
{
ADSBDemodReport::MsgReportADSB *msg = ADSBDemodReport::MsgReportADSB::create(
QByteArray((char*)data, sizeof(data)),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToGUI()->push(msg);
}
// Pass to worker to feed to other servers
if (m_sink->getMessageQueueToWorker())
{
@ -250,12 +280,15 @@ void ADSBDemodSinkWorker::run()
QByteArray((char*)data, sizeof(data)),
preambleCorrelation * m_correlationScale,
preambleCorrelationOnes / samplesPerChip,
rxDateTime(firstIdx, readBuffer));
rxDateTime(firstIdx, readBuffer),
m_crc.get());
m_sink->getMessageQueueToWorker()->push(msg);
}
}
else
{
m_demodStats.m_crcFails++;
}
}
else
m_demodStats.m_typeFails++;

View File

@ -76,6 +76,7 @@ private:
Real m_correlationThresholdLinear;
Real m_correlationScale;
crcadsb m_crc; //!< Have as member to avoid recomputing LUT
QHash<int, bool> m_icaos; //!< ICAO addresses that have been received
QDateTime rxDateTime(int firstIdx, int readBuffer) const;

View File

@ -2,11 +2,9 @@
<h2>Introduction</h2>
The top and bottom bars of the device window are described [here](../../../sdrgui/device/readme.md)
The ADS-B demodulator plugin can be used to receive and display ADS-B and Mode-S frames broadcast by aircraft. These contain information about an aircraft, such as position, altitude, heading and speed, broadcast by aircraft on 1090MHz. ADS-B and Mode-S frames have a chip rate of 2Mchip/s, so the baseband sample rate should be set to 2MSa/s or greater.
The ADS-B demodulator plugin can be used to receive and display ADS-B aircraft information. This is information about an aircraft, such as position, altitude, heading and speed, broadcast by aircraft on 1090MHz, in the 1090ES (Extended Squitter) format. 1090ES frames have a chip rate of 2Mchip/s, so the baseband sample rate should be set to be greater than 2MSa/s.
As well as displaying information received via ADS-B, the plugin can also combine information from a number of databases to display more information about the aircraft and flight, airports and weather.
As well as displaying information received via ADS-B and Mode-S, the plugin can also combine information from a number of databases to display more information about the aircraft and flight, airports and weather.
![ADS-B Demodulator plugin GUI](../../../doc/img/ADSBDemod_plugin.png)
@ -42,13 +40,11 @@ This is the bandwidth in MHz of the channel signal before demodulation.
This specifies the channel sample rate the demodulator uses. Values of 2M-12MSa/s are supported, 2MSa/s steps. Ideally, this should be set to the same value as the baseband sample rate (the sample rate received from the radio). If it is different from the baseband sample rate, interpolation or decimation will be performed as needed to match the rates. However, interpolation currently requires a significant amount of processing power and may result in dropped samples.
2 MSa/s should give decent decodes. Higher rates may be experienced with if your hardware allows it (radio device and processing power). However the higher the rate the more processing power required.
2 MSa/s should give decent decodes. Higher rates may be experimented with if your hardware allows it (radio device and processing power). However the higher the rate the more processing power required.
Higher channel sample rates may help decode more frames, but will require more processing power.
<h3>6: S - Demodulate Mode-S frames</h3>
<h3>6: S - Demodulate all Mode-S frames</h3>
Checking the S button will enable demodulation of all Mode-S frames, not just ADS-B. Mode-S frames will not effect the data displayed in the table or map, but can be feed to aggregators.
Checking the S button will enable demodulation of Mode-S ELS (Elementary Surveillance), EHS (Enhanced Surveillance) and MRAR (Meteorological Routine Air Report) frames.
<h3>7: FP - Correlate Against Full Preamable</h3>
@ -182,11 +178,18 @@ In the Speech and Command strings, variables can be used to substitute in data f
* ${icao},
* ${callsign}
* ${aircraft}
* ${latitude}
* ${longitude}
* ${verticalRate}
* ${gs}
* ${tas}
* ${is}
* ${mach}
* ${selAltitude}
* ${altitude}
* ${speed}
* $(verticalRate}
* ${selHeading}
* ${heading}
* ${turnRate}
* ${roll}
* ${range}
* ${azel}
* ${category}
@ -196,6 +199,18 @@ In the Speech and Command strings, variables can be used to substitute in data f
* ${manufacturer}
* ${owner}
* ${operator}
* ${ap}
* ${vMode}
* ${lMode}
* ${baro}
* ${headwind}
* ${windSpeed}
* ${windDirection}
* ${staticPressure}
* ${staticAirTemperature}
* ${humidity}
* ${latitude}
* ${longitude}
* ${rssi}
* ${flightstatus}
* ${departure}
@ -225,32 +240,52 @@ Specify the SDRangel device set that will be have its centre frequency set when
<h3>ADS-B Data</h3>
The table displays the decoded ADS-B data for each aircraft along side data available for the aircraft from the Opensky Network database (DB) and aviationstack (API). The data is not all able to be transmitted in a single ADS-B frame, so the table displays an amalgamation of the latest received data of each type.
The table displays the decoded ADS-B and Mode-S data for each aircraft along side data available for the aircraft from the Opensky Network database (DB) and aviationstack (API). The data is not all able to be transmitted in a single ADS-B frame, so the table displays an amalgamation of the latest received data of each type.
![ADS-B Demodulator Data](../../../doc/img/ADSBDemod_plugin_table.png)
* ICAO ID - 24-bit hexadecimal ICAO aircraft address. This is unique for each aircraft. (ADS-B)
* Callsign - Aircraft callsign (which is sometimes also the flight number). (ADS-B)
* Callsign - Aircraft callsign (which is sometimes also the flight number). (ADS-B / Mode-S)
* Aircraft - The aircraft model. (DB)
* Airline - The logo of the operator of the aircraft (or owner if no operator known). (DB)
* Altitude (Alt) - Altitude in feet or metres. (ADS-B)
* Speed (Spd) - Speed (either ground speed, indicated airspeed, or true airspeed) in knots or kph. (ADS-B)
* Heading (Hd) - The direction the aircraft is heading, in degrees. (ADS-B)
* Vertical rate (VR) - The vertical climb rate (or descent rate if negative) in feet/minute or m/s. (ADS-B)
* Distance (D) - The distance to the aircraft from the receiving antenna in kilometres. The location of the receiving antenna is set under the Preferences > My Position menu.
* Country - The flag of the country the aircraft is registered in. (DB)
* GS - Groundspeed in knots or kilometers per hour. (Mode-S)
* TAS - True airspeed in knots or kilometers per hour. (Mode-S)
* IAS - Indicated airspeed in knots or kilometers per hour. (Mode-S)
* Mach - Mach number. (Mode-S)
* Sel Alt - Selected altitude (as set on MCP/FCU or by FMS) in feet or metres. (ADS-B / Mode-S)
* Alt - Altitude in feet or metres. (ADS-B / Mode-S)
* VR - The vertical climb rate (or descent rate if negative) in feet/minute or m/s. (ADS-B / Mode-S)
* Sel Hd - Selected heading (as set on MCP/FCU or by FMS) in degrees. (ADS-B / Mode-S)
* Hd - The aircraft heading or track, in degrees. (ADS-B / Mode-S)
* TR - Turn rate in degrees per second. (Mode-S)
* Roll - Roll angle in degrees. Positive is right wing down. (Mode-S)
* D - The distance to the aircraft from the receiving antenna in kilometres. The location of the receiving antenna is set under the Preferences > My Position menu.
* Az/El - The azimuth and elevation angles to the aircraft from the receiving antenna in degrees. These values can be sent to a rotator controller Feature plugin to track the aircraft.
* Latitude (Lat) - Vertical position coordinate, in decimal degrees. (ADS-B)
* Longitude (Lon) - Horizontal position coordinate, in decimal degrees. (ADS-B)
* Category (Cat) - The vehicle category, such as Light, Large, Heavy or Rotorcraft. (ADS-B)
* Status - The status of the flight, including if there is an emergency. (ADS-B)
* Squawk - The squawk code (Mode-A transponder code). (ADS-B)
* Squawk - The squawk code (Mode-A transponder code). (ADS-B / Mode-S)
* Registration (Reg) - The registration number of the aircraft. (DB)
* Country - The flag of the country the aircraft is registered in. (DB)
* Registered - The date when the aircraft was registered. (DB)
* Manufacturer - The manufacturer of the aircraft. (DB)
* Owner - The owner of the aircraft. (DB)
* Updated - The local time at which the last ADS-B message was received.
* RX Frames - A count of the number of ADS-B frames received from this aircraft.
* Operator - The operator ICAO. (DB)
* AP - Whether autopilot is enabled. (ADS-B)
* V Mode - Vertical navigation mode. This may be VNAV (Vertical navigation), HOLD (Altitude hold) or APP (Approach). (ADS-B / Mode-S)
* L Mode - Lateral navigation mode. This may be LNAV (Lateral navigation) or APP (Approach). (ADS-B)
* Baro - Baro setting in mb. (ADS-B / Mode-S)
* H Wnd - Headwind in knots or kilometers per hour. Negative values indicate a tailwind. (Derived from Mode-S)
* OAT - Outside air temperature in degrees Celsuis, estimated from Mach and TAS. (Derived from Mode-S)
* Wnd - Wind speed in knots or kilometers per hour. (Mode-S)
* Wnd - Wind direction in degrees. (Mode-S)
* P - Average static air pressure in hectopascals. (Mode-S)
* T - Static air temperature in degrees Celsuis. (Mode-S)
* U - Humidity in percent. (Mode-S)
* Latitude (Lat) - Vertical position coordinate, in decimal degrees. North positive. (ADS-B)
* Longitude (Lon) - Horizontal position coordinate, in decimal degrees. East positive. (ADS-B)
* Updated - The local time at which the last message was received. (ADS-B / Mode-S)
* RX Frames - A count of the number of frames received from this aircraft. (ADS-B / Mode-S)
* TIS-B - A count of the number of TIS-B frames for this aircraft. (ADS-B)
* Correlation - Displays the minimum, average and maximum of the preamable correlation in dB for each received frame. These values can be used to help select a threshold setting. This correlation value is the ratio between the presence and absence of the signal corresponding to the "ones" and the "zeros" of the sync word adjusted by the bits ratio. It can be interpreted as a SNR estimation.
* RSSI - This Received Signal Strength Indicator is based on the signal power during correlation estimation. This is the power sum during the expected presence of the signal i.e. the "ones" of the sync word.
* Flight status - scheduled, active, landed, cancelled, incident or diverted. (API)

View File

@ -191,6 +191,7 @@ set(sdrbase_SOURCES
util/messagequeue.cpp
util/morse.cpp
util/openaip.cpp
util/osndb.cpp
util/planespotters.cpp
util/png.cpp
util/prettyprint.cpp
@ -409,6 +410,7 @@ set(sdrbase_HEADERS
util/morse.h
util/movingaverage.h
util/openaip.h
util/osndb.h
util/planespotters.h
util/png.h
util/prettyprint.h

25
sdrbase/util/osndb.cpp Normal file
View File

@ -0,0 +1,25 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Jon Beniston, M7RCE //
// //
// This program 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 as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "util/osndb.h"
QHash<QString, QIcon *> AircraftInformation::m_airlineIcons;
QHash<QString, bool> AircraftInformation::m_airlineMissingIcons;
QHash<QString, QIcon *> AircraftInformation::m_flagIcons;
QHash<QString, QString> *AircraftInformation::m_prefixMap;
QHash<QString, QString> *AircraftInformation::m_militaryMap;
QMutex AircraftInformation::m_mutex;

View File

@ -24,15 +24,20 @@
#include <QHash>
#include <QList>
#include <QDebug>
#include <QIcon>
#include <QMutex>
#include <QStandardPaths>
#include <QResource>
#include <stdio.h>
#include <string.h>
#include "util/csv.h"
#include "export.h"
#define OSNDB_URL "https://opensky-network.org/datasets/metadata/aircraftDatabase.zip"
struct AircraftInformation {
struct SDRBASE_API AircraftInformation {
int m_icao;
QString m_registration;
@ -43,6 +48,23 @@ struct AircraftInformation {
QString m_operatorICAO;
QString m_registered;
static QHash<QString, QIcon *> m_airlineIcons; // Hashed on airline ICAO
static QHash<QString, bool> m_airlineMissingIcons; // Hash containing which ICAOs we don't have icons for
static QHash<QString, QIcon *> m_flagIcons; // Hashed on country
static QHash<QString, QString> *m_prefixMap; // Registration to country (flag name)
static QHash<QString, QString> *m_militaryMap; // Operator airforce to military (flag name)
static QMutex m_mutex;
static void init()
{
QMutexLocker locker(&m_mutex);
// Read registration prefix to country map
m_prefixMap = CSV::hash(":/flags/regprefixmap.csv");
// Read operator air force to military map
m_militaryMap = CSV::hash(":/flags/militarymap.csv");
}
// Read OpenSky Network CSV file
// This is large and contains lots of data we don't want, so we convert to
// a smaller version to speed up loading time
@ -219,6 +241,20 @@ struct AircraftInformation {
return aircraftInfo;
}
// Create hash table using registration as key
static QHash<QString, AircraftInformation *> *registrationHash(const QHash<int, AircraftInformation *> *in)
{
QHash<QString, AircraftInformation *> *out = new QHash<QString, AircraftInformation *>();
QHashIterator<int, AircraftInformation *> i(*in);
while (i.hasNext())
{
i.next();
AircraftInformation *info = i.value();
out->insert(info->m_registration, info);
}
return out;
}
// Write a reduced size and validated version of the DB, so it loads quicker
static bool writeFastDB(const QString &filename, QHash<int, AircraftInformation *> *aircraftInfo)
{
@ -313,6 +349,169 @@ struct AircraftInformation {
return aircraftInfo;
}
// Get flag based on registration
QString getFlag() const
{
QString flag;
if (m_prefixMap)
{
int idx = m_registration.indexOf('-');
if (idx >= 0)
{
QString prefix;
// Some countries use AA-A - try these first as first letters are common
prefix = m_registration.left(idx + 2);
if (m_prefixMap->contains(prefix))
{
flag = m_prefixMap->value(prefix);
}
else
{
// Try letters before '-'
prefix = m_registration.left(idx);
if (m_prefixMap->contains(prefix)) {
flag = m_prefixMap->value(prefix);
}
}
}
else
{
// No '-' Could be one of a few countries or military.
// See: https://en.wikipedia.org/wiki/List_of_aircraft_registration_prefixes
if (m_registration.startsWith("N")) {
flag = m_prefixMap->value("N"); // US
} else if (m_registration.startsWith("JA")) {
flag = m_prefixMap->value("JA"); // Japan
} else if (m_registration.startsWith("HL")) {
flag = m_prefixMap->value("HL"); // Korea
} else if (m_registration.startsWith("YV")) {
flag = m_prefixMap->value("YV"); // Venezuela
} else if ((m_militaryMap != nullptr) && (m_militaryMap->contains(m_operator))) {
flag = m_militaryMap->value(m_operator);
}
}
}
return flag;
}
static QString getOSNDBZipFilename()
{
return getDataDir() + "/aircraftDatabase.zip";
}
static QString getOSNDBFilename()
{
return getDataDir() + "/aircraftDatabase.csv";
}
static QString getFastDBFilename()
{
return getDataDir() + "/aircraftDatabaseFast.csv";
}
static QString getDataDir()
{
// Get directory to store app data in (aircraft & airport databases and user-definable icons)
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
// First dir is writable
return locations[0];
}
static QString getAirlineIconPath(const QString &operatorICAO)
{
QString endPath = QString("/airlinelogos/%1.bmp").arg(operatorICAO);
// Try in user directory first, so they can customise
QString userIconPath = getDataDir() + endPath;
QFile file(userIconPath);
if (file.exists())
{
return userIconPath;
}
else
{
// Try in resources
QString resourceIconPath = ":" + endPath;
QResource resource(resourceIconPath);
if (resource.isValid())
{
return resourceIconPath;
}
}
return QString();
}
// Try to find an airline logo based on ICAO
static QIcon *getAirlineIcon(const QString &operatorICAO)
{
if (m_airlineIcons.contains(operatorICAO))
{
return m_airlineIcons.value(operatorICAO);
}
else
{
QIcon *icon = nullptr;
QString path = getAirlineIconPath(operatorICAO);
if (!path.isEmpty())
{
icon = new QIcon(path);
m_airlineIcons.insert(operatorICAO, icon);
}
else
{
if (!m_airlineMissingIcons.contains(operatorICAO))
{
qDebug() << "ADSBDemodGUI: No airline logo for " << operatorICAO;
m_airlineMissingIcons.insert(operatorICAO, true);
}
}
return icon;
}
}
static QString getFlagIconPath(const QString &country)
{
QString endPath = QString("/flags/%1.bmp").arg(country);
// Try in user directory first, so they can customise
QString userIconPath = getDataDir() + endPath;
QFile file(userIconPath);
if (file.exists())
{
return userIconPath;
}
else
{
// Try in resources
QString resourceIconPath = ":" + endPath;
QResource resource(resourceIconPath);
if (resource.isValid())
{
return resourceIconPath;
}
}
return QString();
}
// Try to find an flag logo based on a country
static QIcon *getFlagIcon(const QString &country)
{
if (m_flagIcons.contains(country))
{
return m_flagIcons.value(country);
}
else
{
QIcon *icon = nullptr;
QString path = getFlagIconPath(country);
if (!path.isEmpty())
{
icon = new QIcon(path);
m_flagIcons.insert(country, icon);
}
return icon;
}
}
};
#endif

View File

@ -57,7 +57,7 @@ void PlaneSpotters::getAircraftPhoto(const QString& icao)
{
// Create a new photo hash table entry
PlaneSpottersPhoto *photo = new PlaneSpottersPhoto();
photo->m_icao = icao;
photo->m_id = icao;
m_photos.insert(icao, photo);
// Fetch from network
@ -69,13 +69,35 @@ void PlaneSpotters::getAircraftPhoto(const QString& icao)
}
}
void PlaneSpotters::getAircraftPhotoByRegistration(const QString& registration)
{
if (m_photos.contains(registration))
{
emit aircraftPhoto(m_photos[registration]);
}
else
{
// Create a new photo hash table entry
PlaneSpottersPhoto *photo = new PlaneSpottersPhoto();
photo->m_id = registration;
m_photos.insert(registration, photo);
// Fetch from network
QUrl url(QString("https://api.planespotters.net/pub/photos/reg/%1").arg(registration));
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "SDRangel/1.0"); // Get 403 error without this
request.setOriginatingObject(photo);
m_networkManager->get(request);
}
}
void PlaneSpotters::handleReply(QNetworkReply* reply)
{
if (reply)
{
if (!reply->error())
{
if (reply->url().path().startsWith("/pub/photos/hex")) {
if (reply->url().path().startsWith("/pub/photos/hex") || reply->url().path().startsWith("/pub/photos/reg")) {
parseJson((PlaneSpottersPhoto *)reply->request().originatingObject(), reply->readAll());
} else {
parsePhoto((PlaneSpottersPhoto *)reply->request().originatingObject(), reply->readAll());
@ -95,7 +117,9 @@ void PlaneSpotters::handleReply(QNetworkReply* reply)
void PlaneSpotters::parsePhoto(PlaneSpottersPhoto *photo, QByteArray bytes)
{
photo->m_pixmap.loadFromData(bytes);
if (!photo->m_pixmap.loadFromData(bytes)) {
qDebug() << "PlaneSpotters::parsePhoto: Failed to loadFromData - " << bytes.size() << "bytes of data" ;
}
emit aircraftPhoto(photo);
}

View File

@ -38,13 +38,11 @@ class SDRBASE_API PlaneSpottersPhoto : public QObject {
};
public:
QString m_icao;
QString m_id;
Thumbnail m_thumbnail;
Thumbnail m_largeThumbnail;
QString m_link;
QString m_photographer
;
QString m_photographer;
QPixmap m_pixmap;
};
@ -61,6 +59,7 @@ public:
void getAircraftPhoto(const QString& icao);
void getAircraftPhotoByRegistration(const QString& registration);
signals:
void aircraftPhoto(const PlaneSpottersPhoto *photo); // Called when photo is available.

View File

@ -46,6 +46,11 @@ public:
return metres * 3.28084f;
}
static inline int metresToIntegerFeet(float metres)
{
return (int)std::round(metresToFeet(metres));
}
static inline float nauticalMilesToMetres(float nauticalMiles)
{
return nauticalMiles * 1855.0f;
@ -76,6 +81,11 @@ public:
return (int)std::round(knotsToMPH(knots));
}
static float knotsToMetresPerSecond(float knots)
{
return knots * 0.514444f;
}
static float kmpsToKPH(float kps)
{
return kps * (60.0 * 60.0);