File indexing completed on 2024-04-28 03:50:06

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2012 Ralf Habacker <ralf.habacker@freenet.de>
0004 //
0005 
0006 #include "FlightGearPositionProviderPlugin.h"
0007 
0008 #include "MarbleDebug.h"
0009 #include <cmath>
0010 
0011 #include <QUdpSocket>
0012 #include <QIcon>
0013 
0014 using namespace Marble;
0015 /* TRANSLATOR Marble::FlightGearPositionProviderPlugin */
0016 
0017 using namespace std;
0018 
0019 FlightGearPositionProviderPlugin::FlightGearPositionProviderPlugin()
0020   : m_socket(nullptr), m_speed( 0.0 ), m_track( 0.0 )
0021 {
0022 }
0023 
0024 FlightGearPositionProviderPlugin::~FlightGearPositionProviderPlugin()
0025 {
0026     delete m_socket;
0027 }
0028 
0029 QString FlightGearPositionProviderPlugin::name() const
0030 {
0031     return tr( "FlightGear position provider Plugin" );
0032 }
0033 
0034 QString FlightGearPositionProviderPlugin::nameId() const
0035 {
0036     return QStringLiteral("flightgear");
0037 }
0038 
0039 QString FlightGearPositionProviderPlugin::guiString() const
0040 {
0041     return tr( "FlightGear" );
0042 }
0043 
0044 QString FlightGearPositionProviderPlugin::version() const
0045 {
0046     return QStringLiteral("1.0");
0047 }
0048 
0049 QString FlightGearPositionProviderPlugin::description() const
0050 {
0051     return tr( "Reports the position of running flightgear application." );
0052 }
0053 
0054 QString FlightGearPositionProviderPlugin::copyrightYears() const
0055 {
0056     return QStringLiteral("2012");
0057 }
0058 
0059 QVector<PluginAuthor> FlightGearPositionProviderPlugin::pluginAuthors() const
0060 {
0061     return QVector<PluginAuthor>()
0062             << PluginAuthor(QStringLiteral("Ralf Habacker"), QStringLiteral("ralf.habacker@freenet.de"));
0063 
0064 }
0065 
0066 QIcon FlightGearPositionProviderPlugin::icon() const
0067 {
0068     return QIcon();
0069 }
0070 
0071 void FlightGearPositionProviderPlugin::initialize()
0072 {
0073     m_status = PositionProviderStatusAcquiring;
0074     emit statusChanged( m_status );
0075 
0076     m_socket = new QUdpSocket(this);
0077     m_socket->bind(QHostAddress::Any, 5500);
0078 
0079     connect(m_socket, SIGNAL(readyRead()),
0080              this, SLOT(readPendingDatagrams()));
0081 }
0082 
0083 /**
0084  fixed case where wrong date format is used '2404112' instead of '240412'
0085 */
0086 bool fixBadGPRMC(QByteArray &line)
0087 {
0088     if (!line.startsWith("$GPRMC"))
0089         return false;
0090 
0091     QStringList parts = QString(line).split(QLatin1Char(','));
0092     if (parts[9].size() == 7) {
0093         parts[9].remove(4,1);
0094         line = parts.join(QLatin1Char(',')).toLatin1();
0095         // update crc
0096         int crc = 0;
0097         for(int i=1; i < line.size()-3; i++) {
0098             crc ^= (int) line[i];
0099         }
0100         parts[11] = parts[11][0] + parts[11][1] +  QString::number(crc, 16).toUpper();
0101 
0102         line = parts.join(QLatin1Char(',')).toLatin1();
0103         return true;
0104     }
0105     return false;
0106 }
0107 
0108 void FlightGearPositionProviderPlugin::readPendingDatagrams()
0109 {
0110     while (m_socket->hasPendingDatagrams()) {
0111         QByteArray datagram;
0112         datagram.resize(m_socket->pendingDatagramSize());
0113         QHostAddress sender;
0114         quint16 senderPort;
0115 
0116         m_socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
0117         using Iterator = QList<QByteArray>::Iterator;
0118         QList<QByteArray> split = datagram.split('\n');
0119         for (Iterator i = split.begin(); i != split.end(); i++) {
0120             fixBadGPRMC(*i);
0121             i->append( "\n" );
0122             parseNmeaSentence( *i );
0123         }
0124     }
0125 }
0126 
0127 void FlightGearPositionProviderPlugin::parseNmeaSentence( const QString &sentence )
0128 {
0129     PositionProviderStatus oldStatus = m_status;
0130     GeoDataCoordinates oldPosition = m_position;
0131 
0132     if ( sentence.startsWith( QLatin1String( "$GPRMC" ) ) ) {
0133         QStringList const values = sentence.split(QLatin1Char(','));
0134         if ( values.size() > 9 ) {
0135             if (values[2] == QLatin1String("A")) {
0136                 m_speed = values[7].toDouble() * 0.514444; // knots => m/s
0137                 m_track = values[8].toDouble();
0138                 QString const date = values[9] + QLatin1Char(' ') + values[1];
0139                 m_timestamp = QDateTime::fromString( date, "ddMMyy HHmmss" );
0140                 if (m_timestamp.date().year() <= 1930 && m_timestamp.date().year() >= 1900 ) {
0141                     m_timestamp = m_timestamp.addYears( 100 ); // Qt range is 1900-1999 for two-digits
0142                 }
0143             }
0144             // Flightgear submits geoposition twice in one datagram, once
0145             // in GPRMC and once in GPGGA. Parsing one is sufficient
0146         }
0147     } else if ( sentence.startsWith( QLatin1String( "$GPGGA" ) ) ) {
0148         QStringList const values = sentence.split(QLatin1Char(','));
0149         if ( values.size() > 10 ) {
0150             if ( values[6] == nullptr ) {
0151                 m_status = PositionProviderStatusAcquiring; // no fix
0152             } else {
0153                 double const lat = parsePosition(values[2], values[3] == QLatin1String("S"));
0154                 double const lon = parsePosition(values[4], values[5] == QLatin1String("W"));
0155                 double const unitFactor = values[10] == QLatin1String("F") ? FT2M : 1.0;
0156                 double const alt = unitFactor * values[9].toDouble();
0157                 m_position.set( lon, lat, alt, GeoDataCoordinates::Degree );
0158                 m_accuracy.level = GeoDataAccuracy::Detailed;
0159                 m_status = PositionProviderStatusAvailable;
0160             }
0161         }
0162     } else {
0163         return;
0164     }
0165 
0166     if ( m_status != oldStatus ) {
0167         emit statusChanged( m_status );
0168     }
0169     if ( m_position != oldPosition && m_status == PositionProviderStatusAvailable ) {
0170         emit positionChanged( m_position, m_accuracy );
0171     }
0172 }
0173 
0174 double FlightGearPositionProviderPlugin::parsePosition( const QString &value, bool isNegative )
0175 {
0176     double pos = value.toDouble();
0177     pos = int( pos / 100.0 ) + ( pos - 100.0 * int( pos / 100.0 ) ) / 60.0;
0178     return isNegative ? -qAbs( pos ) : pos;
0179 }
0180 
0181 bool FlightGearPositionProviderPlugin::isInitialized() const
0182 {
0183     return m_socket;
0184 }
0185 
0186 PositionProviderPlugin* FlightGearPositionProviderPlugin::newInstance() const
0187 {
0188     return new FlightGearPositionProviderPlugin;
0189 }
0190 
0191 PositionProviderStatus FlightGearPositionProviderPlugin::status() const
0192 {
0193     return m_status;
0194 }
0195 
0196 GeoDataCoordinates FlightGearPositionProviderPlugin::position() const
0197 {
0198     return m_position;
0199 }
0200 
0201 GeoDataAccuracy FlightGearPositionProviderPlugin::accuracy() const
0202 {
0203     return m_accuracy;
0204 }
0205 
0206 qreal FlightGearPositionProviderPlugin::speed() const
0207 {
0208     return m_speed;
0209 }
0210 
0211 qreal FlightGearPositionProviderPlugin::direction() const
0212 {
0213     return m_track;
0214 }
0215 
0216 QDateTime FlightGearPositionProviderPlugin::timestamp() const
0217 {
0218     return m_timestamp;
0219 }
0220 
0221 QString FlightGearPositionProviderPlugin::error() const
0222 {
0223     return QString();
0224 }
0225 
0226 #include "moc_FlightGearPositionProviderPlugin.cpp"