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(
"GI"))
299 return QGeoSatelliteInfo::IRNSS;
304 if (key.startsWith(
"GN"))
305 return QGeoSatelliteInfo::Multiple;
307 return QGeoSatelliteInfo::Undefined;
310QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatelliteSystemBySatelliteId(
int satId)
312 if (satId >= 1 && satId <= 32)
313 return QGeoSatelliteInfo::GPS;
315 if (satId >= 33 && satId <= 64)
316 return QGeoSatelliteInfo::SBAS;
318 if (satId >= 65 && satId <= 96)
319 return QGeoSatelliteInfo::GLONASS;
321 if (satId >= 193 && satId <= 200)
322 return QGeoSatelliteInfo::QZSS;
324 if ((satId >= 201 && satId <= 235) || (satId >= 401 && satId <= 437))
325 return QGeoSatelliteInfo::BEIDOU;
327 if (satId >= 301 && satId <= 336)
328 return QGeoSatelliteInfo::GALILEO;
330 return QGeoSatelliteInfo::Undefined;
333bool QLocationUtils::getPosInfoFromNmea(QByteArrayView bv, QGeoPositionInfo *info,
334 double uere,
bool *hasFix)
342 NmeaSentence nmeaType = getNmeaSentenceType(bv);
343 if (nmeaType == NmeaSentenceInvalid)
347 qsizetype idx = bv.indexOf(
'*');
348 QByteArrayView key = idx < 0 ? bv : bv.first(idx);
351 case NmeaSentenceGGA:
352 qlocationutils_readGga(key, info, uere, hasFix);
354 case NmeaSentenceGSA:
355 qlocationutils_readGsa(key, info, uere, hasFix);
357 case NmeaSentenceGLL:
358 qlocationutils_readGll(key, info, hasFix);
360 case NmeaSentenceRMC:
361 qlocationutils_readRmc(key, info, hasFix);
363 case NmeaSentenceVTG:
364 qlocationutils_readVtg(key, info, hasFix);
366 case NmeaSentenceZDA:
367 qlocationutils_readZda(key, info, hasFix);
375QLocationUtils::getSatInfoFromNmea(QByteArrayView bv, QList<QGeoSatelliteInfo> &infos, QGeoSatelliteInfo::SatelliteSystem &system)
378 return QNmeaSatelliteInfoSource::NotParsed;
380 NmeaSentence nmeaType = getNmeaSentenceType(bv);
381 if (nmeaType != NmeaSentenceGSV)
382 return QNmeaSatelliteInfoSource::NotParsed;
386 system = getSatelliteSystem(bv);
390 qsizetype idx = bv.indexOf(
'*');
392 const QList<QByteArray> parts = QByteArray::fromRawData(bv.data(),
393 idx < 0 ? bv.size() : idx).split(
',');
395 if (parts.size() <= 3) {
397 return QNmeaSatelliteInfoSource::FullyParsed;
400 const int totalSentences = parts.at(1).toInt(&ok);
403 return QNmeaSatelliteInfoSource::FullyParsed;
406 const int sentence = parts.at(2).toInt(&ok);
409 return QNmeaSatelliteInfoSource::FullyParsed;
412 const int totalSats = parts.at(3).toInt(&ok);
415 return QNmeaSatelliteInfoSource::FullyParsed;
421 const int numSatInSentence = qMin(sentence * 4, totalSats) - (sentence - 1) * 4;
422 if (parts.size() < (4 + numSatInSentence * 4)) {
424 return QNmeaSatelliteInfoSource::FullyParsed;
428 for (
int i = 0; i < numSatInSentence; ++i) {
429 QGeoSatelliteInfo info;
430 info.setSatelliteSystem(system);
431 int prn = parts.at(field++).toInt(&ok);
441 if (ok && (system == QGeoSatelliteInfo::GLONASS)) {
449 if (ok && system == QGeoSatelliteInfo::Undefined)
450 info.setSatelliteSystem(getSatelliteSystemBySatelliteId(prn));
452 info.setSatelliteIdentifier((ok) ? prn : 0);
453 const int elevation = parts.at(field++).toInt(&ok);
454 info.setAttribute(QGeoSatelliteInfo::Elevation, (ok) ? elevation : 0);
455 const int azimuth = parts.at(field++).toInt(&ok);
456 info.setAttribute(QGeoSatelliteInfo::Azimuth, (ok) ? azimuth : 0);
457 const int snr = parts.at(field++).toInt(&ok);
458 info.setSignalStrength((ok) ? snr : -1);
462 if (sentence == totalSentences)
463 return QNmeaSatelliteInfoSource::FullyParsed;
465 return QNmeaSatelliteInfoSource::PartiallyParsed;
468QGeoSatelliteInfo::SatelliteSystem QLocationUtils::getSatInUseFromNmea(QByteArrayView bv,
469 QList<
int> &pnrsInUse)
472 return QGeoSatelliteInfo::Undefined;
474 NmeaSentence nmeaType = getNmeaSentenceType(bv);
475 if (nmeaType != NmeaSentenceGSA)
476 return QGeoSatelliteInfo::Undefined;
478 auto systemType = getSatelliteSystem(bv);
479 if (systemType == QGeoSatelliteInfo::Undefined)
487 qsizetype idx = bv.indexOf(
'*');
488 QByteArrayView key = idx < 0 ? bv : bv.first(idx);
490 qlocationutils_readGsa(key, pnrsInUse);
501 if (systemType == QGeoSatelliteInfo::GLONASS) {
502 std::for_each(pnrsInUse.begin(), pnrsInUse.end(), [](
int &id) {
508 if ((systemType == QGeoSatelliteInfo::Multiple) && !pnrsInUse.isEmpty()) {
512 auto tempSystemType = getSatelliteSystemBySatelliteId(pnrsInUse.front());
513 if (tempSystemType != QGeoSatelliteInfo::Undefined)
514 systemType = tempSystemType;
520bool QLocationUtils::hasValidNmeaChecksum(QByteArrayView bv)
522 qsizetype asteriskIndex = bv.indexOf(
'*');
524 constexpr qsizetype CSUM_LEN = 2;
525 if (asteriskIndex < 0 || asteriskIndex >= bv.size() - CSUM_LEN)
530 for (qsizetype i = 1; i < asteriskIndex; ++i)
533
534
535
536
538 QByteArrayView checkSumBytes = bv.sliced(asteriskIndex + 1, 2);
540 int checksum = checkSumBytes.toInt(&ok,16);
541 return ok && checksum == result;
544bool QLocationUtils::getNmeaTime(
const QByteArray &bytes, QTime *time)
546 QTime tempTime = QTime::fromString(QString::fromLatin1(bytes),
547 QStringView(bytes.size() > 6 && bytes[6] ==
'.'
551 if (tempTime.isValid()) {
558bool QLocationUtils::getNmeaLatLong(
const QByteArray &latString,
char latDirection,
const QByteArray &lngString,
char lngDirection,
double *lat,
double *lng)
560 if ((latDirection !=
'N' && latDirection !=
'S')
561 || (lngDirection !=
'E' && lngDirection !=
'W')) {
566 bool hasLong =
false;
567 double tempLat = latString.toDouble(&hasLat);
568 double tempLng = lngString.toDouble(&hasLong);
569 if (hasLat && hasLong) {
570 tempLat = qlocationutils_nmeaDegreesToDecimal(tempLat);
571 if (latDirection ==
'S')
573 tempLng = qlocationutils_nmeaDegreesToDecimal(tempLng);
574 if (lngDirection ==
'W')
577 if (isValidLat(tempLat) && isValidLong(tempLng)) {