32 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
35 if (hasFix && parts.size() > 6 && !parts[6].isEmpty())
36 *hasFix = parts[6].toInt() > 0;
38 if (parts.size() > 1 && !parts[1].isEmpty()) {
40 if (QLocationUtils::getNmeaTime(parts[1], &time))
41 info->setTimestamp(QDateTime(QDate(), time, QTimeZone::UTC));
44 if (parts.size() > 5 && parts[3].size() == 1 && parts[5].size() == 1) {
47 if (QLocationUtils::getNmeaLatLong(parts[2], parts[3][0], parts[4], parts[5][0], &lat, &lng)) {
48 coord.setLatitude(lat);
49 coord.setLongitude(lng);
53 if (parts.size() > 8 && !parts[8].isEmpty()) {
55 double hdop = parts[8].toDouble(&hasHdop);
57 info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere);
60 if (parts.size() > 9 && !parts[9].isEmpty()) {
62 double alt = parts[9].toDouble(&hasAlt);
64 coord.setAltitude(alt);
67 if (coord.type() != QGeoCoordinate::InvalidCoordinate)
68 info->setCoordinate(coord);
74 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
80 if (hasFix && parts.size() > 2 && !parts[2].isEmpty())
81 *hasFix = parts[2].toInt() > 1;
83 if (parts.size() > 16 && !parts[16].isEmpty()) {
85 double hdop = parts[16].toDouble(&hasHdop);
87 info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere);
90 if (parts.size() > 17 && !parts[17].isEmpty()) {
92 double vdop = parts[17].toDouble(&hasVdop);
94 info->setAttribute(QGeoPositionInfo::VerticalAccuracy, 2 * vdop * uere);
117 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
118 QGeoCoordinate coord;
120 if (hasFix && parts.size() > 6 && !parts[6].isEmpty())
121 *hasFix = (parts[6][0] ==
'A');
123 if (parts.size() > 5 && !parts[5].isEmpty()) {
125 if (QLocationUtils::getNmeaTime(parts[5], &time))
126 info->setTimestamp(QDateTime(QDate(), time, QTimeZone::UTC));
129 if (parts.size() > 4 && parts[2].size() == 1 && parts[4].size() == 1) {
132 if (QLocationUtils::getNmeaLatLong(parts[1], parts[2][0], parts[3], parts[4][0], &lat, &lng)) {
133 coord.setLatitude(lat);
134 coord.setLongitude(lng);
138 if (coord.type() != QGeoCoordinate::InvalidCoordinate)
139 info->setCoordinate(coord);
144 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
145 QGeoCoordinate coord;
149 if (hasFix && parts.size() > 2 && !parts[2].isEmpty())
150 *hasFix = (parts[2][0] ==
'A');
152 if (parts.size() > 9 && parts[9].size() == 6) {
153 date = QDate::fromString(QString::fromLatin1(parts[9]), QStringLiteral(
"ddMMyy"));
155 date = date.addYears(100);
158 if (parts.size() > 1 && !parts[1].isEmpty())
159 QLocationUtils::getNmeaTime(parts[1], &time);
161 if (parts.size() > 6 && parts[4].size() == 1 && parts[6].size() == 1) {
164 if (QLocationUtils::getNmeaLatLong(parts[3], parts[4][0], parts[5], parts[6][0], &lat, &lng)) {
165 coord.setLatitude(lat);
166 coord.setLongitude(lng);
172 if (parts.size() > 7 && !parts[7].isEmpty()) {
173 value = parts[7].toDouble(&parsed);
175 info->setAttribute(QGeoPositionInfo::GroundSpeed, qreal(value * 1.852 / 3.6));
177 if (parts.size() > 8 && !parts[8].isEmpty()) {
178 value = parts[8].toDouble(&parsed);
180 info->setAttribute(QGeoPositionInfo::Direction, qreal(value));
182 if (parts.size() > 11 && parts[11].size() == 1
183 && (parts[11][0] ==
'E' || parts[11][0] ==
'W')) {
184 value = parts[10].toDouble(&parsed);
186 if (parts[11][0] ==
'W')
188 info->setAttribute(QGeoPositionInfo::MagneticVariation, qreal(value));
192 if (coord.type() != QGeoCoordinate::InvalidCoordinate)
193 info->setCoordinate(coord);
195 info->setTimestamp(QDateTime(date, time, QTimeZone::UTC));
203 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
207 if (parts.size() > 1 && !parts[1].isEmpty()) {
208 value = parts[1].toDouble(&parsed);
210 info->setAttribute(QGeoPositionInfo::Direction, qreal(value));
212 if (parts.size() > 7 && !parts[7].isEmpty()) {
213 value = parts[7].toDouble(&parsed);
215 info->setAttribute(QGeoPositionInfo::GroundSpeed, qreal(value / 3.6));
224 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
228 if (parts.size() > 1 && !parts[1].isEmpty())
229 QLocationUtils::getNmeaTime(parts[1], &time);
231 if (parts.size() > 4 && !parts[2].isEmpty() && !parts[3].isEmpty()
232 && parts[4].size() == 4) {
233 int day = parts[2].toInt();
234 int month = parts[3].toInt();
235 int year = parts[4].toInt();
236 if (day > 0 && month > 0 && year > 0)
237 date.setDate(year, month, day);
240 info->setTimestamp(QDateTime(date, time, QTimeZone::UTC));
274QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatelliteSystem(QByteArrayView bv)
276 if (bv.size() < 6 || bv[0] !=
'$' || !hasValidNmeaChecksum(bv))
277 return QGeoSatelliteInfo::Undefined;
279 QByteArrayView key = bv.sliced(1);
282 if (key.startsWith(
"GP"))
283 return QGeoSatelliteInfo::GPS;
286 if (key.startsWith(
"GL"))
287 return QGeoSatelliteInfo::GLONASS;
290 if (key.startsWith(
"GA"))
291 return QGeoSatelliteInfo::GALILEO;
294 if (key.startsWith(
"BD") || key.startsWith(
"GB"))
295 return QGeoSatelliteInfo::BEIDOU;
298 if (key.startsWith(
"GQ") || key.startsWith(
"PQ") || key.startsWith(
"QZ"))
299 return QGeoSatelliteInfo::QZSS;
302 if (key.startsWith(
"GI"))
303 return QGeoSatelliteInfo::IRNSS;
308 if (key.startsWith(
"GN"))
309 return QGeoSatelliteInfo::Multiple;
311 return QGeoSatelliteInfo::Undefined;
314QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatelliteSystemBySatelliteId(
int satId)
316 if (satId >= 1 && satId <= 32)
317 return QGeoSatelliteInfo::GPS;
319 if (satId >= 33 && satId <= 64)
320 return QGeoSatelliteInfo::SBAS;
322 if (satId >= 65 && satId <= 96)
323 return QGeoSatelliteInfo::GLONASS;
325 if (satId >= 193 && satId <= 200)
326 return QGeoSatelliteInfo::QZSS;
328 if ((satId >= 201 && satId <= 235) || (satId >= 401 && satId <= 437))
329 return QGeoSatelliteInfo::BEIDOU;
331 if (satId >= 301 && satId <= 336)
332 return QGeoSatelliteInfo::GALILEO;
334 return QGeoSatelliteInfo::Undefined;
337bool QLocationUtils::getPosInfoFromNmea(QByteArrayView bv, QGeoPositionInfo *info,
338 double uere,
bool *hasFix)
346 NmeaSentence nmeaType = getNmeaSentenceType(bv);
347 if (nmeaType == NmeaSentenceInvalid)
351 qsizetype idx = bv.indexOf(
'*');
352 QByteArrayView key = idx < 0 ? bv : bv.first(idx);
355 case NmeaSentenceGGA:
356 qlocationutils_readGga(key, info, uere, hasFix);
358 case NmeaSentenceGSA:
359 qlocationutils_readGsa(key, info, uere, hasFix);
361 case NmeaSentenceGLL:
362 qlocationutils_readGll(key, info, hasFix);
364 case NmeaSentenceRMC:
365 qlocationutils_readRmc(key, info, hasFix);
367 case NmeaSentenceVTG:
368 qlocationutils_readVtg(key, info, hasFix);
370 case NmeaSentenceZDA:
371 qlocationutils_readZda(key, info, hasFix);
379QLocationUtils::getSatInfoFromNmea(QByteArrayView bv, QList<QGeoSatelliteInfo> &infos, QGeoSatelliteInfo::SatelliteSystem &system)
382 return QNmeaSatelliteInfoSource::NotParsed;
384 NmeaSentence nmeaType = getNmeaSentenceType(bv);
385 if (nmeaType != NmeaSentenceGSV)
386 return QNmeaSatelliteInfoSource::NotParsed;
390 system = getSatelliteSystem(bv);
394 qsizetype idx = bv.indexOf(
'*');
396 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(),
397 idx < 0 ? bv.size() : idx).split(
',');
399 if (parts.size() <= 3) {
401 return QNmeaSatelliteInfoSource::FullyParsed;
408 static constexpr quint32 MaxTotalSentences = 250;
410 const quint32 totalSentences = parts.at(1).toUInt(&ok);
411 if (!ok || totalSentences > MaxTotalSentences) {
413 return QNmeaSatelliteInfoSource::FullyParsed;
416 const quint32 sentence = parts.at(2).toUInt(&ok);
417 if (!ok || sentence > totalSentences) {
419 return QNmeaSatelliteInfoSource::FullyParsed;
422 const quint32 totalSats = parts.at(3).toUInt(&ok);
425 return QNmeaSatelliteInfoSource::FullyParsed;
430 else if (infos.isEmpty())
431 return QNmeaSatelliteInfoSource::FullyParsed;
433 static constexpr qsizetype MaxTotalSatellites = 1000;
434 const auto currentSatsSize = infos.size();
435 if (currentSatsSize > qint64(totalSats) || currentSatsSize > MaxTotalSatellites) {
438 return QNmeaSatelliteInfoSource::FullyParsed;
441 const qint64 numSatInSentence = qMin(sentence * 4, totalSats) - (sentence - 1) * 4;
442 if (parts.size() < (4 + numSatInSentence * 4)) {
444 return QNmeaSatelliteInfoSource::FullyParsed;
448 for (qint64 i = 0; i < numSatInSentence; ++i) {
449 QGeoSatelliteInfo info;
450 info.setSatelliteSystem(system);
451 int prn = parts.at(field++).toInt(&ok);
461 if (ok && (system == QGeoSatelliteInfo::GLONASS)) {
469 if (ok && system == QGeoSatelliteInfo::Undefined)
470 info.setSatelliteSystem(getSatelliteSystemBySatelliteId(prn));
472 info.setSatelliteIdentifier((ok) ? prn : 0);
473 const int elevation = parts.at(field++).toInt(&ok);
474 info.setAttribute(QGeoSatelliteInfo::Elevation, (ok) ? elevation : 0);
475 const int azimuth = parts.at(field++).toInt(&ok);
476 info.setAttribute(QGeoSatelliteInfo::Azimuth, (ok) ? azimuth : 0);
477 const int snr = parts.at(field++).toInt(&ok);
478 info.setSignalStrength((ok) ? snr : -1);
482 if (sentence == totalSentences)
483 return QNmeaSatelliteInfoSource::FullyParsed;
485 return QNmeaSatelliteInfoSource::PartiallyParsed;
488QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatInUseFromNmea(QByteArrayView bv,
489 QList<
int> &pnrsInUse)
492 return QGeoSatelliteInfo::Undefined;
494 NmeaSentence nmeaType = getNmeaSentenceType(bv);
495 if (nmeaType != NmeaSentenceGSA)
496 return QGeoSatelliteInfo::Undefined;
498 auto systemType = getSatelliteSystem(bv);
499 if (systemType == QGeoSatelliteInfo::Undefined)
507 qsizetype idx = bv.indexOf(
'*');
508 QByteArrayView key = idx < 0 ? bv : bv.first(idx);
510 qlocationutils_readGsa(key, pnrsInUse);
521 if (systemType == QGeoSatelliteInfo::GLONASS) {
522 std::for_each(pnrsInUse.begin(), pnrsInUse.end(), [](
int &id) {
528 if ((systemType == QGeoSatelliteInfo::Multiple) && !pnrsInUse.isEmpty()) {
532 auto tempSystemType = getSatelliteSystemBySatelliteId(pnrsInUse.front());
533 if (tempSystemType != QGeoSatelliteInfo::Undefined)
534 systemType = tempSystemType;
540bool QLocationUtils::hasValidNmeaChecksum(QByteArrayView bv)
542 qsizetype asteriskIndex = bv.indexOf(
'*');
544 constexpr qsizetype CSUM_LEN = 2;
545 if (asteriskIndex < 0 || asteriskIndex >= bv.size() - CSUM_LEN)
550 for (qsizetype i = 1; i < asteriskIndex; ++i)
553
554
555
556
558 QByteArrayView checkSumBytes = bv.sliced(asteriskIndex + 1, 2);
560 int checksum = checkSumBytes.toInt(&ok,16);
561 return ok && checksum == result;
578bool QLocationUtils::getNmeaLatLong(
const QByteArray &latString,
char latDirection,
const QByteArray &lngString,
char lngDirection,
double *lat,
double *lng)
580 if ((latDirection !=
'N' && latDirection !=
'S')
581 || (lngDirection !=
'E' && lngDirection !=
'W')) {
586 bool hasLong =
false;
587 double tempLat = latString.toDouble(&hasLat);
588 double tempLng = lngString.toDouble(&hasLong);
589 if (hasLat && hasLong) {
590 tempLat = qlocationutils_nmeaDegreesToDecimal(tempLat);
591 if (latDirection ==
'S')
593 tempLng = qlocationutils_nmeaDegreesToDecimal(tempLng);
594 if (lngDirection ==
'W')
597 if (isValidLat(tempLat) && isValidLong(tempLng)) {