mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-08-19 22:12:35 -04:00
679 lines
27 KiB
C++
679 lines
27 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
// //
|
|
// 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 "mapitem.h"
|
|
|
|
MapItem::MapItem(const QObject *sourcePipe, const QString &group, MapSettings::MapItemSettings *itemSettings, SWGSDRangel::SWGMapItem *mapItem) :
|
|
m_altitude(0.0)
|
|
{
|
|
m_sourcePipe = sourcePipe;
|
|
m_group = group;
|
|
m_itemSettings = itemSettings;
|
|
m_name = *mapItem->getName();
|
|
m_hashKey = m_sourcePipe->objectName() + m_name;
|
|
}
|
|
|
|
void MapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|
{
|
|
if (mapItem->getLabel()) {
|
|
m_label = *mapItem->getLabel();
|
|
} else {
|
|
m_label = "";
|
|
}
|
|
if (mapItem->getLabelDateTime()) {
|
|
m_labelDateTime = QDateTime::fromString(*mapItem->getLabelDateTime(), Qt::ISODateWithMs);
|
|
} else {
|
|
m_labelDateTime = QDateTime();
|
|
}
|
|
m_latitude = mapItem->getLatitude();
|
|
m_longitude = mapItem->getLongitude();
|
|
m_altitude = mapItem->getAltitude();
|
|
}
|
|
|
|
QGeoCoordinate MapItem::getCoordinates()
|
|
{
|
|
QGeoCoordinate coords;
|
|
coords.setLatitude(m_latitude);
|
|
coords.setLongitude(m_longitude);
|
|
return coords;
|
|
}
|
|
|
|
WhittakerEilers ObjectMapItem::m_filter;
|
|
|
|
void ObjectMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|
{
|
|
MapItem::update(mapItem);
|
|
if (mapItem->getPositionDateTime()) {
|
|
m_positionDateTime = QDateTime::fromString(*mapItem->getPositionDateTime(), Qt::ISODateWithMs);
|
|
} else {
|
|
m_positionDateTime = QDateTime();
|
|
}
|
|
if (mapItem->getAltitudeDateTime()) {
|
|
m_altitudeDateTime = QDateTime::fromString(*mapItem->getAltitudeDateTime(), Qt::ISODateWithMs);
|
|
} else {
|
|
m_altitudeDateTime = QDateTime();
|
|
}
|
|
m_useHeadingPitchRoll = mapItem->getOrientation() == 1;
|
|
m_heading = mapItem->getHeading();
|
|
m_pitch = mapItem->getPitch();
|
|
m_roll = mapItem->getRoll();
|
|
if (mapItem->getOrientationDateTime()) {
|
|
m_orientationDateTime = QDateTime::fromString(*mapItem->getOrientationDateTime(), Qt::ISODateWithMs);
|
|
} else {
|
|
m_orientationDateTime = QDateTime();
|
|
}
|
|
m_image = *mapItem->getImage();
|
|
m_imageRotation = mapItem->getImageRotation();
|
|
QString *text = mapItem->getText();
|
|
if (text != nullptr) {
|
|
m_text = text->replace("\n", "<br>"); // Convert to HTML
|
|
} else {
|
|
m_text = "";
|
|
}
|
|
if (mapItem->getModel()) {
|
|
m_model = *mapItem->getModel();
|
|
} else {
|
|
m_model = "";
|
|
}
|
|
m_labelAltitudeOffset = mapItem->getLabelAltitudeOffset();
|
|
m_modelAltitudeOffset = mapItem->getModelAltitudeOffset();
|
|
// FIXME: See nodeTransformations comment in czml.cpp
|
|
// We can't use nodeTransformations, so adjust altitude instead
|
|
if (m_modelAltitudeOffset != 0)
|
|
{
|
|
m_labelAltitudeOffset -= m_modelAltitudeOffset;
|
|
m_altitude += m_modelAltitudeOffset;
|
|
m_modelAltitudeOffset = 0;
|
|
}
|
|
m_altitudeReference = mapItem->getAltitudeReference();
|
|
m_fixedPosition = mapItem->getFixedPosition();
|
|
QList<SWGSDRangel::SWGMapAnimation *> *animations = mapItem->getAnimations();
|
|
if (animations)
|
|
{
|
|
for (auto animation : *animations) {
|
|
m_animations.append(new CesiumInterface::Animation(animation));
|
|
}
|
|
}
|
|
findFrequencies();
|
|
if (!m_fixedPosition)
|
|
{
|
|
updateTrack(mapItem->getTrack(), m_itemSettings);
|
|
updatePredictedTrack(mapItem->getPredictedTrack());
|
|
}
|
|
if (mapItem->getAvailableFrom()) {
|
|
m_availableFrom = QDateTime::fromString(*mapItem->getAvailableFrom(), Qt::ISODateWithMs);
|
|
} else {
|
|
m_availableFrom = QDateTime();
|
|
}
|
|
if (mapItem->getAvailableUntil()) {
|
|
m_availableUntil = QDateTime::fromString(*mapItem->getAvailableUntil(), Qt::ISODateWithMs);
|
|
} else {
|
|
m_availableUntil = QDateTime();
|
|
}
|
|
if (mapItem->getAircraftState()) {
|
|
if (!m_aircraftState) {
|
|
m_aircraftState = new MapAircraftState();
|
|
}
|
|
SWGSDRangel::SWGMapAircraftState *as = mapItem->getAircraftState();
|
|
if (as->getCallsign()) {
|
|
m_aircraftState->m_callsign = *as->getCallsign();
|
|
}
|
|
if (as->getAircraftType()) {
|
|
m_aircraftState->m_aircraftType = *as->getAircraftType();
|
|
}
|
|
m_aircraftState->m_onSurface = as->getOnSurface();
|
|
m_aircraftState->m_indicatedAirspeed = as->getAirspeed();
|
|
if (as->getAirspeedDateTime()) {
|
|
m_aircraftState->m_indicatedAirspeedDateTime = *as->getAirspeedDateTime();
|
|
} else {
|
|
m_aircraftState->m_indicatedAirspeedDateTime = QString();
|
|
}
|
|
m_aircraftState->m_trueAirspeed = as->getTrueAirspeed();
|
|
m_aircraftState->m_groundspeed = as->getGroundspeed();
|
|
m_aircraftState->m_mach = as->getMach();
|
|
m_aircraftState->m_altitude = as->getAltitude();
|
|
if (as->getAltitudeDateTime()) {
|
|
m_aircraftState->m_altitudeDateTime = *as->getAltitudeDateTime();
|
|
} else {
|
|
m_aircraftState->m_altitudeDateTime = QString();
|
|
}
|
|
m_aircraftState->m_qnh = as->getQnh();
|
|
m_aircraftState->m_verticalSpeed = as->getVerticalSpeed();
|
|
m_aircraftState->m_heading = as->getHeading();
|
|
m_aircraftState->m_track = as->getTrack();
|
|
m_aircraftState->m_selectedAltitude = as->getSelectedAltitude();
|
|
m_aircraftState->m_selectedHeading = as->getSelectedHeading();
|
|
m_aircraftState->m_autopilot = as->getAutopilot();
|
|
m_aircraftState->m_verticalMode = (MapAircraftState::VerticalMode) as->getVerticalMode();
|
|
m_aircraftState->m_lateralMode = (MapAircraftState::LateralMode) as->getLateralMode();
|
|
m_aircraftState->m_tcasMode = (MapAircraftState::TCASMode) as->getTcasMode();
|
|
m_aircraftState->m_windSpeed = as->getWindSpeed();
|
|
m_aircraftState->m_windDirection = as->getWindDirection();
|
|
m_aircraftState->m_staticAirTemperature = as->getStaticAirTemperature();
|
|
}
|
|
}
|
|
|
|
void ImageMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|
{
|
|
MapItem::update(mapItem);
|
|
|
|
m_image = "data:image/png;base64," + *mapItem->getImage();
|
|
m_imageZoomLevel = mapItem->getImageZoomLevel();
|
|
|
|
float east = mapItem->getImageTileEast();
|
|
float west = mapItem->getImageTileWest();
|
|
float north = mapItem->getImageTileNorth();
|
|
float south = mapItem->getImageTileSouth();
|
|
m_latitude = north - (north - south) / 2.0;
|
|
m_longitude = east - (east - west) / 2.0;
|
|
m_bounds = QGeoRectangle(QGeoCoordinate(north, west), QGeoCoordinate(south, east));
|
|
}
|
|
|
|
void PolygonMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|
{
|
|
MapItem::update(mapItem);
|
|
m_extrudedHeight = mapItem->getExtrudedHeight();
|
|
m_colorValid = mapItem->getColorValid();
|
|
m_color = mapItem->getColor();
|
|
m_altitudeReference = mapItem->getAltitudeReference();
|
|
m_deleted = *mapItem->getImage() == "";
|
|
|
|
qDeleteAll(m_points);
|
|
m_points.clear();
|
|
QList<SWGSDRangel::SWGMapCoordinate *> *coords = mapItem->getCoordinates();
|
|
if (coords)
|
|
{
|
|
for (int i = 0; i < coords->size(); i++)
|
|
{
|
|
SWGSDRangel::SWGMapCoordinate* p = coords->at(i);
|
|
QGeoCoordinate *c = new QGeoCoordinate(p->getLatitude(), p->getLongitude(), p->getAltitude());
|
|
m_points.append(c);
|
|
}
|
|
}
|
|
|
|
// Calculate bounds
|
|
m_polygon.clear();
|
|
qreal latMin = 90.0, latMax = -90.0, lonMin = 180.0, lonMax = -180.0;
|
|
for (const auto p : m_points)
|
|
{
|
|
QGeoCoordinate coord = *p;
|
|
latMin = std::min(latMin, coord.latitude());
|
|
latMax = std::max(latMax, coord.latitude());
|
|
lonMin = std::min(lonMin, coord.longitude());
|
|
lonMax = std::max(lonMax, coord.longitude());
|
|
m_polygon.push_back(QVariant::fromValue(coord));
|
|
}
|
|
m_bounds = QGeoRectangle(QGeoCoordinate(latMax, lonMin), QGeoCoordinate(latMin, lonMax));
|
|
}
|
|
|
|
void PolylineMapItem::update(SWGSDRangel::SWGMapItem *mapItem)
|
|
{
|
|
MapItem::update(mapItem);
|
|
m_colorValid = mapItem->getColorValid();
|
|
m_color = mapItem->getColor();
|
|
m_altitudeReference = mapItem->getAltitudeReference();
|
|
m_deleted = *mapItem->getImage() == "";
|
|
|
|
qDeleteAll(m_points);
|
|
m_points.clear();
|
|
QList<SWGSDRangel::SWGMapCoordinate *> *coords = mapItem->getCoordinates();
|
|
if (coords)
|
|
{
|
|
for (int i = 0; i < coords->size(); i++)
|
|
{
|
|
SWGSDRangel::SWGMapCoordinate* p = coords->at(i);
|
|
QGeoCoordinate *c = new QGeoCoordinate(p->getLatitude(), p->getLongitude(), p->getAltitude());
|
|
m_points.append(c);
|
|
}
|
|
}
|
|
|
|
// Calculate bounds
|
|
m_polyline.clear();
|
|
qreal latMin = 90.0, latMax = -90.0, lonMin = 180.0, lonMax = -180.0;
|
|
for (const auto p : m_points)
|
|
{
|
|
QGeoCoordinate coord = *p;
|
|
latMin = std::min(latMin, coord.latitude());
|
|
latMax = std::max(latMax, coord.latitude());
|
|
lonMin = std::min(lonMin, coord.longitude());
|
|
lonMax = std::max(lonMax, coord.longitude());
|
|
m_polyline.push_back(QVariant::fromValue(coord));
|
|
}
|
|
m_bounds = QGeoRectangle(QGeoCoordinate(latMax, lonMin), QGeoCoordinate(latMin, lonMax));
|
|
}
|
|
|
|
// Look for a frequency in the text for this object
|
|
void ObjectMapItem::findFrequencies()
|
|
{
|
|
m_frequencies.clear();
|
|
m_frequencyStrings.clear();
|
|
|
|
const QRegularExpression re("(([0-9]+(\\.[0-9]+)?) *([kMG])?Hz)");
|
|
QRegularExpressionMatchIterator itr = re.globalMatch(m_text);
|
|
while (itr.hasNext())
|
|
{
|
|
QRegularExpressionMatch match = itr.next();
|
|
QStringList capture = match.capturedTexts();
|
|
double frequency = capture[2].toDouble();
|
|
|
|
if (capture.length() == 5)
|
|
{
|
|
QChar unit = capture[4][0];
|
|
if (unit == 'k') {
|
|
frequency *= 1000;
|
|
} else if (unit == 'M') {
|
|
frequency *= 1000000;
|
|
} else if (unit == 'G') {
|
|
frequency *= 1000000000;
|
|
}
|
|
}
|
|
m_frequencies.append((qint64)frequency);
|
|
m_frequencyStrings.append(capture[0]);
|
|
}
|
|
}
|
|
|
|
void ObjectMapItem::extrapolatePosition(QGeoCoordinate *c, const QDateTime& dateTime)
|
|
{
|
|
int p1;
|
|
int p2;
|
|
|
|
// Find last two non extrapolated position
|
|
for (p2 = m_takenTrackPositionExtrapolated.size() - 1 ; p2 >= 0; p2--)
|
|
{
|
|
if (!m_takenTrackPositionExtrapolated[p2]) {
|
|
break;
|
|
}
|
|
}
|
|
for (p1 = p2 - 1 ; p1 >= 0; p1--)
|
|
{
|
|
if (!m_takenTrackPositionExtrapolated[p1]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p1 < 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
|
qint64 t2 = m_takenTrackDateTimes[p2]->msecsTo(dateTime);
|
|
|
|
double latV = (m_takenTrackCoords[p2]->latitude() - m_takenTrackCoords[p1]->latitude()) / t1;
|
|
double lonV = (m_takenTrackCoords[p2]->longitude() - m_takenTrackCoords[p1]->longitude()) / t1;
|
|
|
|
double newLat = m_takenTrackCoords[p2]->latitude() + latV * t2;
|
|
double newLon = m_takenTrackCoords[p2]->longitude() + lonV * t2;
|
|
|
|
c->setLatitude(newLat);
|
|
c->setLongitude(newLon);
|
|
}
|
|
|
|
void ObjectMapItem::extrapolateAltitude(QGeoCoordinate *c, const QDateTime& dateTime)
|
|
{
|
|
int p1;
|
|
int p2;
|
|
|
|
// Find last two non extrapolated position
|
|
for (p2 = m_takenTrackPositionExtrapolated.size() - 1 ; p2 >= 0; p2--)
|
|
{
|
|
if (!m_takenTrackPositionExtrapolated[p2]) {
|
|
break;
|
|
}
|
|
}
|
|
for (p1 = p2 - 1 ; p1 >= 0; p1--)
|
|
{
|
|
if (!m_takenTrackPositionExtrapolated[p1]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (p1 < 0) {
|
|
return;
|
|
}
|
|
|
|
|
|
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
|
qint64 t2 = m_takenTrackDateTimes[p2]->msecsTo(dateTime);
|
|
|
|
double vertV = (m_takenTrackCoords[p2]->altitude() - m_takenTrackCoords[p1]->altitude()) / t1;
|
|
|
|
double newAlt = m_takenTrackCoords[p2]->latitude() + vertV * t2;
|
|
|
|
c->setAltitude(newAlt);
|
|
}
|
|
|
|
void ObjectMapItem::interpolatePosition(int p2, const float p3Latitude, const float p3Longitude, const QDateTime &p3DateTime)
|
|
{
|
|
// p1 last non extrapolated position
|
|
// p2 interpolated position
|
|
// p3 current non extrapolated position
|
|
|
|
// Find last non extrapolated position
|
|
int p1;
|
|
|
|
for (p1 = p2 - 1; p1 >= 0; p1--)
|
|
{
|
|
if (!m_takenTrackPositionExtrapolated[p1]) {
|
|
break;
|
|
}
|
|
}
|
|
if (p1 < 0) {
|
|
return;
|
|
}
|
|
|
|
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(p3DateTime);
|
|
qint64 t2 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
|
|
|
double latV = (p3Latitude - m_takenTrackCoords[p1]->latitude()) / t1;
|
|
double lonV = (p3Longitude - m_takenTrackCoords[p1]->longitude()) / t1;
|
|
|
|
double newLat = m_takenTrackCoords[p1]->latitude() + latV * t2;
|
|
double newLon = m_takenTrackCoords[p1]->longitude() + lonV * t2;
|
|
|
|
m_takenTrackCoords[p2]->setLatitude(newLat);
|
|
m_takenTrackCoords[p2]->setLongitude(newLon);
|
|
|
|
m_interpolatedCoords.append(m_takenTrackCoords[p2]);
|
|
m_interpolatedDateTimes.append(m_takenTrackDateTimes[p2]);
|
|
}
|
|
|
|
void ObjectMapItem::interpolateAltitude(int p2, const float p3Altitude, const QDateTime &p3DateTime)
|
|
{
|
|
// p1 last non extrapolated position
|
|
// p2 interpolated position
|
|
// p3 current non extrapolated position
|
|
|
|
// Find last non extrapolated position
|
|
int p1;
|
|
|
|
for (p1 = p2 - 1; p1 >= 0; p1--)
|
|
{
|
|
if (!m_takenTrackPositionExtrapolated[p1]) {
|
|
break;
|
|
}
|
|
}
|
|
if (p1 < 0) {
|
|
return;
|
|
}
|
|
|
|
qint64 t1 = m_takenTrackDateTimes[p1]->msecsTo(p3DateTime);
|
|
qint64 t2 = m_takenTrackDateTimes[p1]->msecsTo(*m_takenTrackDateTimes[p2]);
|
|
|
|
double vertV = (p3Altitude - m_takenTrackCoords[p1]->altitude()) / t1;
|
|
|
|
double newAlt = m_takenTrackCoords[p1]->altitude() + vertV * t2;
|
|
|
|
m_takenTrackCoords[p2]->setAltitude(newAlt);
|
|
|
|
m_interpolatedCoords.append(m_takenTrackCoords[p2]);
|
|
m_interpolatedDateTimes.append(m_takenTrackDateTimes[p2]);
|
|
}
|
|
|
|
void ObjectMapItem::updateTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track, MapSettings::MapItemSettings *itemSettings)
|
|
{
|
|
if (track != nullptr)
|
|
{
|
|
qDeleteAll(m_takenTrackCoords);
|
|
m_takenTrackCoords.clear();
|
|
qDeleteAll(m_takenTrackDateTimes);
|
|
m_takenTrackDateTimes.clear();
|
|
m_takenTrackPositionExtrapolated.clear();
|
|
m_takenTrackAltitudeExtrapolated.clear();
|
|
m_takenTrack.clear();
|
|
m_takenTrack1.clear();
|
|
m_takenTrack2.clear();
|
|
for (int i = 0; i < track->size(); i++)
|
|
{
|
|
SWGSDRangel::SWGMapCoordinate* p = track->at(i);
|
|
QGeoCoordinate *c = new QGeoCoordinate(p->getLatitude(), p->getLongitude(), p->getAltitude());
|
|
QDateTime *d = new QDateTime(QDateTime::fromString(*p->getDateTime(), Qt::ISODate));
|
|
m_takenTrackCoords.push_back(c);
|
|
m_takenTrackDateTimes.push_back(d);
|
|
m_takenTrackPositionExtrapolated.push_back(false);
|
|
m_takenTrackAltitudeExtrapolated.push_back(false);
|
|
m_takenTrack.push_back(QVariant::fromValue(*c));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Automatically create a track
|
|
if (m_takenTrackCoords.size() == 0)
|
|
{
|
|
QGeoCoordinate *c = new QGeoCoordinate(m_latitude, m_longitude, m_altitude);
|
|
m_takenTrackCoords.push_back(c);
|
|
if (m_altitudeDateTime.isValid()) {
|
|
m_takenTrackDateTimes.push_back(new QDateTime(m_altitudeDateTime));
|
|
} else if (m_positionDateTime.isValid()) {
|
|
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
|
} else {
|
|
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
|
}
|
|
m_takenTrackPositionExtrapolated.push_back(false);
|
|
m_takenTrackAltitudeExtrapolated.push_back(false);
|
|
m_takenTrack.push_back(QVariant::fromValue(*c));
|
|
}
|
|
else
|
|
{
|
|
// For Whittaker-Eilers filtering, we need to make sure we don't have 2 data with the same time
|
|
// so we just update the last item if the prev time is the same
|
|
// To reduce size of list for stationary items, we only store two items with same position
|
|
// We store two, rather than one, so that we have the times this position was arrived at and left
|
|
|
|
const bool interpolate = false;
|
|
const bool onlyActual2D = true; // Extrapolation / smoothing doesn't look good on 2D map, so only use actual data from aircraft, not extrapolated/interpolated points
|
|
|
|
QGeoCoordinate *prev1 = m_takenTrackCoords.last();
|
|
bool samePos1 = (prev1->latitude() == m_latitude) && (prev1->longitude() == m_longitude) && (prev1->altitude() == m_altitude);
|
|
QGeoCoordinate *prev2 = m_takenTrackCoords.size() > 1 ? m_takenTrackCoords[m_takenTrackCoords.size() - 2] : nullptr;
|
|
bool samePos2 = prev2 && samePos1 ? (prev2->latitude() == m_latitude) && (prev2->longitude() == m_longitude) && (prev2->altitude() == m_altitude) : false;
|
|
|
|
QDateTime *prevDateTime = m_takenTrackDateTimes.last();
|
|
|
|
QGeoCoordinate c(m_latitude, m_longitude, m_altitude);
|
|
QGeoCoordinate cActual = c;
|
|
int prevSize = m_takenTrackPositionExtrapolated.size();
|
|
|
|
if (m_altitudeDateTime.isValid() && m_positionDateTime.isValid() && (m_altitudeDateTime > m_positionDateTime))
|
|
{
|
|
if (interpolate)
|
|
{
|
|
for (int i = m_takenTrackAltitudeExtrapolated.size() - 1; (i >= 0) && m_takenTrackAltitudeExtrapolated[i]; i--)
|
|
{
|
|
interpolateAltitude(i, m_altitude, m_altitudeDateTime);
|
|
m_takenTrackAltitudeExtrapolated[i] = false;
|
|
}
|
|
}
|
|
|
|
if (samePos2)
|
|
{
|
|
*m_takenTrackDateTimes.last() = m_altitudeDateTime;
|
|
}
|
|
else
|
|
{
|
|
extrapolatePosition(&c, m_altitudeDateTime);
|
|
if (m_altitudeDateTime == *prevDateTime)
|
|
{
|
|
m_takenTrackPositionExtrapolated[m_takenTrackPositionExtrapolated.size() - 1] = true;
|
|
*m_takenTrackCoords.last() = c;
|
|
m_takenTrack.last() = QVariant::fromValue(onlyActual2D ? cActual : c);
|
|
}
|
|
else
|
|
{
|
|
m_takenTrackDateTimes.push_back(new QDateTime(m_altitudeDateTime));
|
|
m_takenTrackPositionExtrapolated.push_back(true);
|
|
m_takenTrackAltitudeExtrapolated.push_back(false);
|
|
m_takenTrackCoords.push_back(new QGeoCoordinate(c));
|
|
m_takenTrack.push_back(QVariant::fromValue(onlyActual2D ? cActual : c));
|
|
}
|
|
}
|
|
}
|
|
else if (m_positionDateTime.isValid())
|
|
{
|
|
if (interpolate)
|
|
{
|
|
for (int i = m_takenTrackPositionExtrapolated.size() - 1; (i >= 0) && m_takenTrackPositionExtrapolated[i]; i--)
|
|
{
|
|
interpolatePosition(i, m_latitude, m_longitude, m_positionDateTime);
|
|
m_takenTrackPositionExtrapolated[i] = false;
|
|
}
|
|
}
|
|
|
|
if (m_positionDateTime > *m_takenTrackDateTimes.last())
|
|
{
|
|
if (samePos2)
|
|
{
|
|
*m_takenTrackDateTimes.last() = m_positionDateTime;
|
|
}
|
|
else
|
|
{
|
|
bool extrapolateAlt = m_altitudeDateTime.isValid() && (m_positionDateTime > m_altitudeDateTime);
|
|
if (extrapolateAlt) {
|
|
extrapolateAltitude(&c, m_positionDateTime);
|
|
}
|
|
if (m_positionDateTime == *prevDateTime)
|
|
{
|
|
m_takenTrackAltitudeExtrapolated[m_takenTrackPositionExtrapolated.size() - 1] = extrapolateAlt;
|
|
*m_takenTrackCoords.last() = c;
|
|
m_takenTrack.last() = QVariant::fromValue(onlyActual2D ? cActual : c);
|
|
}
|
|
else
|
|
{
|
|
m_takenTrackDateTimes.push_back(new QDateTime(m_positionDateTime));
|
|
m_takenTrackPositionExtrapolated.push_back(false);
|
|
m_takenTrackAltitudeExtrapolated.push_back(extrapolateAlt);
|
|
m_takenTrackCoords.push_back(new QGeoCoordinate(c));
|
|
m_takenTrack.push_back(QVariant::fromValue(onlyActual2D ? cActual : c));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//qDebug() << "m_positionDateTime matches last datetime" << samePos1 << samePos2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_takenTrackDateTimes.push_back(new QDateTime(QDateTime::currentDateTime()));
|
|
m_takenTrackPositionExtrapolated.push_back(false);
|
|
m_takenTrackAltitudeExtrapolated.push_back(false);
|
|
m_takenTrackCoords.push_back(new QGeoCoordinate(c));
|
|
m_takenTrack.push_back(QVariant::fromValue(onlyActual2D ? cActual : c));
|
|
}
|
|
|
|
/*if (m_takenTrackDateTimes.size() >= 2) {
|
|
if (*m_takenTrackDateTimes[m_takenTrackDateTimes.size() - 1] < *m_takenTrackDateTimes[m_takenTrackDateTimes.size() - 2]) {
|
|
qDebug() << "ObjectMapItem::updateTrack: Out of order";
|
|
}
|
|
}*/
|
|
|
|
if ((m_takenTrackPositionExtrapolated.size() > 0) && (prevSize != m_takenTrackPositionExtrapolated.size()))
|
|
{
|
|
const int filterLen = itemSettings->m_smoothingWindow;
|
|
if ((filterLen > 0) && (m_takenTrackCoords.size() >= filterLen) && (m_takenTrackCoords.size() % (filterLen/2)) == 0)
|
|
{
|
|
// Filter last filterLen coords
|
|
QVector<double> x(filterLen);
|
|
QVector<double> y1(filterLen);
|
|
QVector<double> y2(filterLen);
|
|
QVector<double> y3(filterLen);
|
|
QVector<double> w1(filterLen);
|
|
QVector<double> w3(filterLen);
|
|
|
|
//qDebug() << "Filter from" << (m_takenTrackCoords.size() - (filterLen - 0)) << "to" << (m_takenTrackCoords.size() - 1);
|
|
for (int i = 0; i < filterLen; i++)
|
|
{
|
|
int idx = m_takenTrackCoords.size() - (filterLen - i);
|
|
x[i] = (m_takenTrackDateTimes[idx]->toMSecsSinceEpoch() - m_takenTrackDateTimes[0]->toMSecsSinceEpoch()) / 1000.0;
|
|
y1[i] = m_takenTrackCoords[idx]->latitude();
|
|
y2[i] = m_takenTrackCoords[idx]->longitude();
|
|
y3[i] = m_takenTrackCoords[idx]->altitude();
|
|
if (i < (filterLen / 4))
|
|
{
|
|
w1[i] = 10.0; // Try to avoid discontinuities between windows
|
|
w3[i] = 10.0;
|
|
}
|
|
else if (i == filterLen - 1)
|
|
{
|
|
w1[i] = 1.0;
|
|
w3[i] = 1.0;
|
|
}
|
|
else
|
|
{
|
|
w1[i] = m_takenTrackPositionExtrapolated[idx] ? 0.0 : 1.0;
|
|
w3[i] = m_takenTrackAltitudeExtrapolated[idx] ? 0.0 : 1.0;
|
|
}
|
|
}
|
|
|
|
const double lambda = itemSettings->m_smoothingLambda;
|
|
m_filter.filter(x.data(), y1.data(), w1.data(), filterLen, lambda);
|
|
m_filter.filter(x.data(), y2.data(), w1.data(), filterLen, lambda);
|
|
m_filter.filter(x.data(), y3.data(), w3.data(), filterLen, lambda);
|
|
|
|
for (int i = 0; i < filterLen; i++)
|
|
{
|
|
int idx = m_takenTrackCoords.size() - (filterLen - i);
|
|
m_takenTrackCoords[idx]->setLatitude(y1[i]);
|
|
m_takenTrackCoords[idx]->setLongitude(y2[i]);
|
|
m_takenTrackCoords[idx]->setAltitude(y3[i]);
|
|
m_takenTrackPositionExtrapolated[idx] = false;
|
|
m_takenTrackAltitudeExtrapolated[idx] = false;
|
|
if (!onlyActual2D) {
|
|
m_takenTrack[idx] = QVariant::fromValue(QGeoCoordinate(y1[i], y2[i], y3[i]));
|
|
}
|
|
|
|
m_interpolatedCoords.append(m_takenTrackCoords[idx]);
|
|
m_interpolatedDateTimes.append(m_takenTrackDateTimes[idx]);
|
|
}
|
|
|
|
// Update current position - Don't do this, as it can make aircraft move backwards on 2D map
|
|
if (!onlyActual2D)
|
|
{
|
|
m_latitude = m_takenTrackCoords.back()->latitude();
|
|
m_longitude = m_takenTrackCoords.back()->longitude();
|
|
m_altitude = m_takenTrackCoords.back()->altitude();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ObjectMapItem::updatePredictedTrack(QList<SWGSDRangel::SWGMapCoordinate *> *track)
|
|
{
|
|
if (track != nullptr)
|
|
{
|
|
qDeleteAll(m_predictedTrackCoords);
|
|
m_predictedTrackCoords.clear();
|
|
qDeleteAll(m_predictedTrackDateTimes);
|
|
m_predictedTrackDateTimes.clear();
|
|
m_predictedTrack.clear();
|
|
m_predictedTrack1.clear();
|
|
m_predictedTrack2.clear();
|
|
for (int i = 0; i < track->size(); i++)
|
|
{
|
|
SWGSDRangel::SWGMapCoordinate* p = track->at(i);
|
|
QGeoCoordinate *c = new QGeoCoordinate(p->getLatitude(), p->getLongitude(), p->getAltitude());
|
|
QDateTime *d = new QDateTime(QDateTime::fromString(*p->getDateTime(), Qt::ISODate));
|
|
m_predictedTrackCoords.push_back(c);
|
|
m_predictedTrackDateTimes.push_back(d);
|
|
m_predictedTrack.push_back(QVariant::fromValue(*c));
|
|
}
|
|
}
|
|
}
|
|
|