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(
',');
76 if (hasFix && parts.size() > 2 && !parts[2].isEmpty())
77 *hasFix = parts[2].toInt() > 0;
79 if (parts.size() > 16 && !parts[16].isEmpty()) {
81 double hdop = parts[16].toDouble(&hasHdop);
83 info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere);
86 if (parts.size() > 17 && !parts[17].isEmpty()) {
88 double vdop = parts[17].toDouble(&hasVdop);
90 info->setAttribute(QGeoPositionInfo::VerticalAccuracy, 2 * vdop * uere);
96 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
98 if (parts.size() <= 2)
101 for (qsizetype i = 3; i < qMin(15, parts.size()); ++i) {
103 if (pnrString.isEmpty())
105 int pnr = pnrString.toInt(&ok);
107 pnrsInUse.append(pnr);
113 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
114 QGeoCoordinate coord;
116 if (hasFix && parts.size() > 6 && !parts[6].isEmpty())
117 *hasFix = (parts[6][0] ==
'A');
119 if (parts.size() > 5 && !parts[5].isEmpty()) {
121 if (QLocationUtils::getNmeaTime(parts[5], &time))
122 info->setTimestamp(QDateTime(QDate(), time, QTimeZone::UTC));
125 if (parts.size() > 4 && parts[2].size() == 1 && parts[4].size() == 1) {
128 if (QLocationUtils::getNmeaLatLong(parts[1], parts[2][0], parts[3], parts[4][0], &lat, &lng)) {
129 coord.setLatitude(lat);
130 coord.setLongitude(lng);
134 if (coord.type() != QGeoCoordinate::InvalidCoordinate)
135 info->setCoordinate(coord);
140 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
141 QGeoCoordinate coord;
145 if (hasFix && parts.size() > 2 && !parts[2].isEmpty())
146 *hasFix = (parts[2][0] ==
'A');
148 if (parts.size() > 9 && parts[9].size() == 6) {
149 date = QDate::fromString(QString::fromLatin1(parts[9]), QStringLiteral(
"ddMMyy"));
151 date = date.addYears(100);
154 if (parts.size() > 1 && !parts[1].isEmpty())
155 QLocationUtils::getNmeaTime(parts[1], &time);
157 if (parts.size() > 6 && parts[4].size() == 1 && parts[6].size() == 1) {
160 if (QLocationUtils::getNmeaLatLong(parts[3], parts[4][0], parts[5], parts[6][0], &lat, &lng)) {
161 coord.setLatitude(lat);
162 coord.setLongitude(lng);
168 if (parts.size() > 7 && !parts[7].isEmpty()) {
169 value = parts[7].toDouble(&parsed);
171 info->setAttribute(QGeoPositionInfo::GroundSpeed, qreal(value * 1.852 / 3.6));
173 if (parts.size() > 8 && !parts[8].isEmpty()) {
174 value = parts[8].toDouble(&parsed);
176 info->setAttribute(QGeoPositionInfo::Direction, qreal(value));
178 if (parts.size() > 11 && parts[11].size() == 1
179 && (parts[11][0] ==
'E' || parts[11][0] ==
'W')) {
180 value = parts[10].toDouble(&parsed);
182 if (parts[11][0] ==
'W')
184 info->setAttribute(QGeoPositionInfo::MagneticVariation, qreal(value));
188 if (coord.type() != QGeoCoordinate::InvalidCoordinate)
189 info->setCoordinate(coord);
191 info->setTimestamp(QDateTime(date, time, QTimeZone::UTC));
199 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
203 if (parts.size() > 1 && !parts[1].isEmpty()) {
204 value = parts[1].toDouble(&parsed);
206 info->setAttribute(QGeoPositionInfo::Direction, qreal(value));
208 if (parts.size() > 7 && !parts[7].isEmpty()) {
209 value = parts[7].toDouble(&parsed);
211 info->setAttribute(QGeoPositionInfo::GroundSpeed, qreal(value / 3.6));
220 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(), bv.size()).split(
',');
224 if (parts.size() > 1 && !parts[1].isEmpty())
225 QLocationUtils::getNmeaTime(parts[1], &time);
227 if (parts.size() > 4 && !parts[2].isEmpty() && !parts[3].isEmpty()
228 && parts[4].size() == 4) {
229 int day = parts[2].toInt();
230 int month = parts[3].toInt();
231 int year = parts[4].toInt();
232 if (day > 0 && month > 0 && year > 0)
233 date.setDate(year, month, day);
236 info->setTimestamp(QDateTime(date, time, QTimeZone::UTC));
270QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatelliteSystem(QByteArrayView bv)
272 if (bv.size() < 6 || bv[0] !=
'$' || !hasValidNmeaChecksum(bv))
273 return QGeoSatelliteInfo::Undefined;
275 QByteArrayView key = bv.sliced(1);
278 if (key.startsWith(
"GP"))
279 return QGeoSatelliteInfo::GPS;
282 if (key.startsWith(
"GL"))
283 return QGeoSatelliteInfo::GLONASS;
286 if (key.startsWith(
"GA"))
287 return QGeoSatelliteInfo::GALILEO;
290 if (key.startsWith(
"BD") || key.startsWith(
"GB"))
291 return QGeoSatelliteInfo::BEIDOU;
294 if (key.startsWith(
"GQ") || key.startsWith(
"PQ") || key.startsWith(
"QZ"))
295 return QGeoSatelliteInfo::QZSS;
298 if (key.startsWith(
"GN"))
299 return QGeoSatelliteInfo::Multiple;
301 return QGeoSatelliteInfo::Undefined;
304QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatelliteSystemBySatelliteId(
int satId)
306 if (satId >= 1 && satId <= 32)
307 return QGeoSatelliteInfo::GPS;
309 if (satId >= 65 && satId <= 96)
310 return QGeoSatelliteInfo::GLONASS;
312 if (satId >= 193 && satId <= 200)
313 return QGeoSatelliteInfo::QZSS;
315 if ((satId >= 201 && satId <= 235) || (satId >= 401 && satId <= 437))
316 return QGeoSatelliteInfo::BEIDOU;
318 if (satId >= 301 && satId <= 336)
319 return QGeoSatelliteInfo::GALILEO;
321 return QGeoSatelliteInfo::Undefined;
324bool QLocationUtils::getPosInfoFromNmea(QByteArrayView bv, QGeoPositionInfo *info,
325 double uere,
bool *hasFix)
333 NmeaSentence nmeaType = getNmeaSentenceType(bv);
334 if (nmeaType == NmeaSentenceInvalid)
338 qsizetype idx = bv.indexOf(
'*');
339 QByteArrayView key = idx < 0 ? bv : bv.first(idx);
342 case NmeaSentenceGGA:
343 qlocationutils_readGga(key, info, uere, hasFix);
345 case NmeaSentenceGSA:
346 qlocationutils_readGsa(key, info, uere, hasFix);
348 case NmeaSentenceGLL:
349 qlocationutils_readGll(key, info, hasFix);
351 case NmeaSentenceRMC:
352 qlocationutils_readRmc(key, info, hasFix);
354 case NmeaSentenceVTG:
355 qlocationutils_readVtg(key, info, hasFix);
357 case NmeaSentenceZDA:
358 qlocationutils_readZda(key, info, hasFix);
366QLocationUtils::getSatInfoFromNmea(QByteArrayView bv, QList<QGeoSatelliteInfo> &infos, QGeoSatelliteInfo::SatelliteSystem &system)
369 return QNmeaSatelliteInfoSource::NotParsed;
371 NmeaSentence nmeaType = getNmeaSentenceType(bv);
372 if (nmeaType != NmeaSentenceGSV)
373 return QNmeaSatelliteInfoSource::NotParsed;
377 system = getSatelliteSystem(bv);
381 qsizetype idx = bv.indexOf(
'*');
383 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(),
384 idx < 0 ? bv.size() : idx).split(
',');
386 if (parts.size() <= 3) {
388 return QNmeaSatelliteInfoSource::FullyParsed;
391 const int totalSentences = parts.at(1).toInt(&ok);
394 return QNmeaSatelliteInfoSource::FullyParsed;
397 const int sentence = parts.at(2).toInt(&ok);
400 return QNmeaSatelliteInfoSource::FullyParsed;
403 const int totalSats = parts.at(3).toInt(&ok);
406 return QNmeaSatelliteInfoSource::FullyParsed;
412 const int numSatInSentence = qMin(sentence * 4, totalSats) - (sentence - 1) * 4;
413 if (parts.size() < (4 + numSatInSentence * 4)) {
415 return QNmeaSatelliteInfoSource::FullyParsed;
419 for (
int i = 0; i < numSatInSentence; ++i) {
420 QGeoSatelliteInfo info;
421 info.setSatelliteSystem(system);
422 int prn = parts.at(field++).toInt(&ok);
432 if (ok && (system == QGeoSatelliteInfo::GLONASS)) {
436 info.setSatelliteIdentifier((ok) ? prn : 0);
437 const int elevation = parts.at(field++).toInt(&ok);
438 info.setAttribute(QGeoSatelliteInfo::Elevation, (ok) ? elevation : 0);
439 const int azimuth = parts.at(field++).toInt(&ok);
440 info.setAttribute(QGeoSatelliteInfo::Azimuth, (ok) ? azimuth : 0);
441 const int snr = parts.at(field++).toInt(&ok);
442 info.setSignalStrength((ok) ? snr : -1);
446 if (sentence == totalSentences)
447 return QNmeaSatelliteInfoSource::FullyParsed;
449 return QNmeaSatelliteInfoSource::PartiallyParsed;
452QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatInUseFromNmea(QByteArrayView bv,
453 QList<
int> &pnrsInUse)
456 return QGeoSatelliteInfo::Undefined;
458 NmeaSentence nmeaType = getNmeaSentenceType(bv);
459 if (nmeaType != NmeaSentenceGSA)
460 return QGeoSatelliteInfo::Undefined;
462 auto systemType = getSatelliteSystem(bv);
463 if (systemType == QGeoSatelliteInfo::Undefined)
471 qsizetype idx = bv.indexOf(
'*');
472 QByteArrayView key = idx < 0 ? bv : bv.first(idx);
474 qlocationutils_readGsa(key, pnrsInUse);
485 if (systemType == QGeoSatelliteInfo::GLONASS) {
486 std::for_each(pnrsInUse.begin(), pnrsInUse.end(), [](
int &id) {
492 if ((systemType == QGeoSatelliteInfo::Multiple) && !pnrsInUse.isEmpty()) {
496 auto tempSystemType = getSatelliteSystemBySatelliteId(pnrsInUse.front());
497 if (tempSystemType != QGeoSatelliteInfo::Undefined)
498 systemType = tempSystemType;
504bool QLocationUtils::hasValidNmeaChecksum(QByteArrayView bv)
506 qsizetype asteriskIndex = bv.indexOf(
'*');
508 constexpr qsizetype CSUM_LEN = 2;
509 if (asteriskIndex < 0 || asteriskIndex >= bv.size() - CSUM_LEN)
514 for (qsizetype i = 1; i < asteriskIndex; ++i)
517
518
519
520
522 QByteArrayView checkSumBytes = bv.sliced(asteriskIndex + 1, 2);
524 int checksum = checkSumBytes.toInt(&ok,16);
525 return ok && checksum == result;
528bool QLocationUtils::getNmeaTime(
const QByteArray &bytes, QTime *time)
530 QTime tempTime = QTime::fromString(QString::fromLatin1(bytes),
531 QStringView(bytes.size() > 6 && bytes[6] ==
'.'
535 if (tempTime.isValid()) {
542bool QLocationUtils::getNmeaLatLong(
const QByteArray &latString,
char latDirection,
const QByteArray &lngString,
char lngDirection,
double *lat,
double *lng)
544 if ((latDirection !=
'N' && latDirection !=
'S')
545 || (lngDirection !=
'E' && lngDirection !=
'W')) {
550 bool hasLong =
false;
551 double tempLat = latString.toDouble(&hasLat);
552 double tempLng = lngString.toDouble(&hasLong);
553 if (hasLat && hasLong) {
554 tempLat = qlocationutils_nmeaDegreesToDecimal(tempLat);
555 if (latDirection ==
'S')
557 tempLng = qlocationutils_nmeaDegreesToDecimal(tempLng);
558 if (lngDirection ==
'W')
561 if (isValidLat(tempLat) && isValidLong(tempLng)) {