loc_nmea.cpp 90 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405
  1. /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
  2. *
  3. * Redistribution and use in source and binary forms, with or without
  4. * modification, are permitted provided that the following conditions are
  5. * met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above
  9. * copyright notice, this list of conditions and the following
  10. * disclaimer in the documentation and/or other materials provided
  11. * with the distribution.
  12. * * Neither the name of The Linux Foundation nor the names of its
  13. * contributors may be used to endorse or promote products derived
  14. * from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
  17. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  18. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  20. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  23. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  24. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  25. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  26. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. *
  28. */
  29. #define LOG_NDEBUG 0
  30. #define LOG_TAG "LocSvc_nmea"
  31. #include <loc_nmea.h>
  32. #include <math.h>
  33. #include <log_util.h>
  34. #include <loc_pla.h>
  35. #include <loc_cfg.h>
  36. #define GLONASS_SV_ID_OFFSET 64
  37. #define SBAS_SV_ID_OFFSET (87)
  38. #define QZSS_SV_ID_OFFSET (192)
  39. #define BDS_SV_ID_OFFSET (200)
  40. #define GALILEO_SV_ID_OFFSET (300)
  41. #define NAVIC_SV_ID_OFFSET (400)
  42. #define MAX_SV_COUNT_SUPPORTED_IN_ONE_CONSTELLATION 64
  43. #define MAX_SATELLITES_IN_USE 12
  44. #define MSEC_IN_ONE_WEEK 604800000ULL
  45. #define UTC_GPS_OFFSET_MSECS 315964800000ULL
  46. #define MAX_TAG_BLOCK_GROUP_CODE (99999)
  47. // GNSS system id according to NMEA spec
  48. #define SYSTEM_ID_GPS 1
  49. #define SYSTEM_ID_GLONASS 2
  50. #define SYSTEM_ID_GALILEO 3
  51. #define SYSTEM_ID_BDS 4
  52. #define SYSTEM_ID_QZSS 5
  53. #define SYSTEM_ID_NAVIC 6
  54. //GNSS signal id according to NMEA spec
  55. #define SIGNAL_ID_ALL_SIGNALS 0
  56. #define SIGNAL_ID_GPS_L1CA 1
  57. #define SIGNAL_ID_GPS_L1P 2
  58. #define SIGNAL_ID_GPS_L1M 3
  59. #define SIGNAL_ID_GPS_L2P 4
  60. #define SIGNAL_ID_GPS_L2CM 5
  61. #define SIGNAL_ID_GPS_L2CL 6
  62. #define SIGNAL_ID_GPS_L5I 7
  63. #define SIGNAL_ID_GPS_L5Q 8
  64. #define SIGNAL_ID_GLO_G1CA 1
  65. #define SIGNAL_ID_GLO_G1P 2
  66. #define SIGNAL_ID_GLO_G2CA 3
  67. #define SIGNAL_ID_GLO_G2P 4
  68. #define SIGNAL_ID_GAL_E5A 1
  69. #define SIGNAL_ID_GAL_E5B 2
  70. #define SIGNAL_ID_GAL_E5AB 3
  71. #define SIGNAL_ID_GAL_E6A 4
  72. #define SIGNAL_ID_GAL_E6BC 5
  73. #define SIGNAL_ID_GAL_L1A 6
  74. #define SIGNAL_ID_GAL_L1BC 7
  75. #define SIGNAL_ID_BDS_B1I 1
  76. #define SIGNAL_ID_BDS_B1Q 2
  77. #define SIGNAL_ID_BDS_B1C 3
  78. #define SIGNAL_ID_BDS_B1A 4
  79. #define SIGNAL_ID_BDS_B2A 5
  80. #define SIGNAL_ID_BDS_B2B 6
  81. #define SIGNAL_ID_BDS_B2AB 7
  82. #define SIGNAL_ID_BDS_B3I 8
  83. #define SIGNAL_ID_BDS_B3Q 9
  84. #define SIGNAL_ID_BDS_B3A 0xA
  85. #define SIGNAL_ID_BDS_B2I 0xB
  86. #define SIGNAL_ID_BDS_B2Q 0xC
  87. #define SIGNAL_ID_QZSS_L1CA 1
  88. #define SIGNAL_ID_QZSS_L1CD 2
  89. #define SIGNAL_ID_QZSS_L1CP 3
  90. #define SIGNAL_ID_QZSS_LIS 4
  91. #define SIGNAL_ID_QZSS_L2CM 5
  92. #define SIGNAL_ID_QZSS_L2CL 6
  93. #define SIGNAL_ID_QZSS_L5I 7
  94. #define SIGNAL_ID_QZSS_L5Q 8
  95. #define SIGNAL_ID_QZSS_L6D 9
  96. #define SIGNAL_ID_QZSS_L6E 0xA
  97. #define SIGNAL_ID_NAVIC_L5SPS 1
  98. #define SIGNAL_ID_NAVIC_SSPS 2
  99. #define SIGNAL_ID_NAVIC_L5RS 3
  100. #define SIGNAL_ID_NAVIC_SRS 4
  101. #define SIGNAL_ID_NAVIC_L1SPS 5
  102. typedef struct loc_nmea_sv_meta_s
  103. {
  104. char talker[3];
  105. uint32_t svTypeMask;
  106. uint64_t mask;
  107. uint32_t svCount;
  108. uint32_t totalSvUsedCount;
  109. uint32_t svIdOffset;
  110. uint32_t signalId;
  111. uint32_t systemId;
  112. } loc_nmea_sv_meta;
  113. typedef struct loc_sv_cache_info_s
  114. {
  115. uint64_t gps_used_mask;
  116. uint64_t glo_used_mask;
  117. uint64_t gal_used_mask;
  118. uint64_t qzss_used_mask;
  119. uint64_t bds_used_mask;
  120. uint64_t navic_used_mask;
  121. uint32_t gps_l1_count;
  122. uint32_t gps_l2_count;
  123. uint32_t gps_l5_count;
  124. uint32_t glo_g1_count;
  125. uint32_t glo_g2_count;
  126. uint32_t gal_e1_count;
  127. uint32_t gal_e5_count;
  128. uint32_t gal_e5b_count;
  129. uint32_t qzss_l1_count;
  130. uint32_t qzss_l2_count;
  131. uint32_t qzss_l5_count;
  132. uint32_t bds_b1i_count;
  133. uint32_t bds_b1c_count;
  134. uint32_t bds_b2_count;
  135. uint32_t navic_l5_count;
  136. float hdop;
  137. float pdop;
  138. float vdop;
  139. } loc_sv_cache_info;
  140. static GnssNmeaTypesMask mEnabledNmeaTypes = NMEA_TYPE_ALL;
  141. /*===========================================================================
  142. FUNCTION convert_Lla_to_Ecef
  143. DESCRIPTION
  144. Convert LLA to ECEF
  145. DEPENDENCIES
  146. NONE
  147. RETURN VALUE
  148. NONE
  149. SIDE EFFECTS
  150. N/A
  151. ===========================================================================*/
  152. static void convert_Lla_to_Ecef(const LocLla& plla, LocEcef& pecef)
  153. {
  154. double r;
  155. r = MAJA / sqrt(1.0 - ESQR * sin(plla.lat) * sin(plla.lat));
  156. pecef.X = (r + plla.alt) * cos(plla.lat) * cos(plla.lon);
  157. pecef.Y = (r + plla.alt) * cos(plla.lat) * sin(plla.lon);
  158. pecef.Z = (r * OMES + plla.alt) * sin(plla.lat);
  159. }
  160. /*===========================================================================
  161. FUNCTION convert_WGS84_to_PZ90
  162. DESCRIPTION
  163. Convert datum from WGS84 to PZ90
  164. DEPENDENCIES
  165. NONE
  166. RETURN VALUE
  167. NONE
  168. SIDE EFFECTS
  169. N/A
  170. ===========================================================================*/
  171. static void convert_WGS84_to_PZ90(const LocEcef& pWGS84, LocEcef& pPZ90)
  172. {
  173. double deltaX = DatumConstFromWGS84[0];
  174. double deltaY = DatumConstFromWGS84[1];
  175. double deltaZ = DatumConstFromWGS84[2];
  176. double deltaScale = DatumConstFromWGS84[3];
  177. double rotX = DatumConstFromWGS84[4];
  178. double rotY = DatumConstFromWGS84[5];
  179. double rotZ = DatumConstFromWGS84[6];
  180. pPZ90.X = deltaX + deltaScale * (pWGS84.X + rotZ * pWGS84.Y - rotY * pWGS84.Z);
  181. pPZ90.Y = deltaY + deltaScale * (pWGS84.Y - rotZ * pWGS84.X + rotX * pWGS84.Z);
  182. pPZ90.Z = deltaZ + deltaScale * (pWGS84.Z + rotY * pWGS84.X - rotX * pWGS84.Y);
  183. }
  184. /*===========================================================================
  185. FUNCTION convert_Ecef_to_Lla
  186. DESCRIPTION
  187. Convert ECEF to LLA
  188. DEPENDENCIES
  189. NONE
  190. RETURN VALUE
  191. NONE
  192. SIDE EFFECTS
  193. N/A
  194. ===========================================================================*/
  195. static void convert_Ecef_to_Lla(const LocEcef& pecef, LocLla& plla)
  196. {
  197. double p, r;
  198. double EcefA = C_PZ90A;
  199. double EcefB = C_PZ90B;
  200. double Ecef1Mf;
  201. double EcefE2;
  202. double Mu;
  203. double Smu;
  204. double Cmu;
  205. double Phi;
  206. double Sphi;
  207. double N;
  208. p = sqrt(pecef.X * pecef.X + pecef.Y * pecef.Y);
  209. r = sqrt(p * p + pecef.Z * pecef.Z);
  210. if (r < 1.0) {
  211. plla.lat = 1.0;
  212. plla.lon = 1.0;
  213. plla.alt = 1.0;
  214. }
  215. Ecef1Mf = 1.0 - (EcefA - EcefB) / EcefA;
  216. EcefE2 = 1.0 - (EcefB * EcefB) / (EcefA * EcefA);
  217. if (p > 1.0) {
  218. Mu = atan2(pecef.Z * (Ecef1Mf + EcefE2 * EcefA / r), p);
  219. } else {
  220. if (pecef.Z > 0.0) {
  221. Mu = M_PI / 2.0;
  222. } else {
  223. Mu = -M_PI / 2.0;
  224. }
  225. }
  226. Smu = sin(Mu);
  227. Cmu = cos(Mu);
  228. Phi = atan2(pecef.Z * Ecef1Mf + EcefE2 * EcefA * Smu * Smu * Smu,
  229. Ecef1Mf * (p - EcefE2 * EcefA * Cmu * Cmu * Cmu));
  230. Sphi = sin(Phi);
  231. N = EcefA / sqrt(1.0 - EcefE2 * Sphi * Sphi);
  232. plla.alt = p * cos(Phi) + pecef.Z * Sphi - EcefA * EcefA/N;
  233. plla.lat = Phi;
  234. if ( p > 1.0) {
  235. plla.lon = atan2(pecef.Y, pecef.X);
  236. } else {
  237. plla.lon = 0.0;
  238. }
  239. }
  240. /*===========================================================================
  241. FUNCTION convert_signalType_to_signalId
  242. DESCRIPTION
  243. convert signalType to signal ID
  244. DEPENDENCIES
  245. NONE
  246. RETURN VALUE
  247. value of signal ID
  248. SIDE EFFECTS
  249. N/A
  250. ===========================================================================*/
  251. static uint32_t convert_signalType_to_signalId(GnssSignalTypeMask signalType)
  252. {
  253. uint32_t signalId = SIGNAL_ID_ALL_SIGNALS;
  254. switch (signalType) {
  255. case GNSS_SIGNAL_GPS_L1CA:
  256. case GNSS_SIGNAL_SBAS_L1:
  257. signalId = SIGNAL_ID_GPS_L1CA;
  258. break;
  259. case GNSS_SIGNAL_GPS_L2:
  260. signalId = SIGNAL_ID_GPS_L2CL;
  261. break;
  262. case GNSS_SIGNAL_GPS_L5:
  263. signalId = SIGNAL_ID_GPS_L5Q;
  264. break;
  265. case GNSS_SIGNAL_GLONASS_G1:
  266. signalId = SIGNAL_ID_GLO_G1CA;
  267. break;
  268. case GNSS_SIGNAL_GLONASS_G2:
  269. signalId = SIGNAL_ID_GLO_G2CA;
  270. break;
  271. case GNSS_SIGNAL_GALILEO_E1:
  272. signalId = SIGNAL_ID_GAL_L1BC;
  273. break;
  274. case GNSS_SIGNAL_GALILEO_E5A:
  275. signalId = SIGNAL_ID_GAL_E5A;
  276. break;
  277. case GNSS_SIGNAL_GALILEO_E5B:
  278. signalId = SIGNAL_ID_GAL_E5B;
  279. break;
  280. case GNSS_SIGNAL_QZSS_L1CA:
  281. signalId = SIGNAL_ID_QZSS_L1CA;
  282. break;
  283. case GNSS_SIGNAL_QZSS_L2:
  284. signalId = SIGNAL_ID_QZSS_L2CL;
  285. break;
  286. case GNSS_SIGNAL_QZSS_L5:
  287. signalId = SIGNAL_ID_QZSS_L5Q;
  288. break;
  289. case GNSS_SIGNAL_BEIDOU_B1I:
  290. signalId = SIGNAL_ID_BDS_B1I;
  291. break;
  292. case GNSS_SIGNAL_BEIDOU_B1C:
  293. signalId = SIGNAL_ID_BDS_B1C;
  294. break;
  295. case GNSS_SIGNAL_BEIDOU_B2I:
  296. signalId = SIGNAL_ID_BDS_B2I;
  297. break;
  298. case GNSS_SIGNAL_BEIDOU_B2AI:
  299. case GNSS_SIGNAL_BEIDOU_B2AQ:
  300. signalId = SIGNAL_ID_BDS_B2A;
  301. break;
  302. case GNSS_SIGNAL_NAVIC_L5:
  303. signalId = SIGNAL_ID_NAVIC_L5SPS;
  304. break;
  305. default:
  306. signalId = SIGNAL_ID_ALL_SIGNALS;
  307. }
  308. return signalId;
  309. }
  310. /*===========================================================================
  311. FUNCTION get_sv_count_from_mask
  312. DESCRIPTION
  313. get the sv count from bit mask
  314. DEPENDENCIES
  315. NONE
  316. RETURN VALUE
  317. value of sv count
  318. SIDE EFFECTS
  319. N/A
  320. ===========================================================================*/
  321. static uint32_t get_sv_count_from_mask(uint64_t svMask, int totalSvCount)
  322. {
  323. int index = 0;
  324. uint32_t svCount = 0;
  325. if(totalSvCount > MAX_SV_COUNT_SUPPORTED_IN_ONE_CONSTELLATION) {
  326. LOC_LOGE("total SV count in this constellation %d exceeded limit %d",
  327. totalSvCount, MAX_SV_COUNT_SUPPORTED_IN_ONE_CONSTELLATION);
  328. }
  329. for(index = 0; index < totalSvCount; index++) {
  330. if(svMask & 0x1)
  331. svCount += 1;
  332. svMask >>= 1;
  333. }
  334. return svCount;
  335. }
  336. /*===========================================================================
  337. FUNCTION loc_nmea_sv_meta_init
  338. DESCRIPTION
  339. Init loc_nmea_sv_meta passed in
  340. DEPENDENCIES
  341. NONE
  342. RETURN VALUE
  343. Pointer to loc_nmea_sv_meta
  344. SIDE EFFECTS
  345. N/A
  346. ===========================================================================*/
  347. static loc_nmea_sv_meta* loc_nmea_sv_meta_init(loc_nmea_sv_meta& sv_meta,
  348. loc_sv_cache_info& sv_cache_info,
  349. GnssSvType svType,
  350. GnssSignalTypeMask signalType,
  351. bool needCombine)
  352. {
  353. memset(&sv_meta, 0, sizeof(sv_meta));
  354. sv_meta.svTypeMask = (1 << svType);
  355. switch (svType)
  356. {
  357. case GNSS_SV_TYPE_GPS:
  358. sv_meta.talker[0] = 'G';
  359. sv_meta.talker[1] = 'P';
  360. sv_meta.mask = sv_cache_info.gps_used_mask;
  361. sv_meta.systemId = SYSTEM_ID_GPS;
  362. sv_meta.svTypeMask |= (1 << GNSS_SV_TYPE_SBAS);
  363. switch (signalType) {
  364. case GNSS_SIGNAL_GPS_L1CA:
  365. sv_meta.svCount = sv_cache_info.gps_l1_count;
  366. break;
  367. case GNSS_SIGNAL_GPS_L5:
  368. sv_meta.svCount = sv_cache_info.gps_l5_count;
  369. break;
  370. case GNSS_SIGNAL_GPS_L2:
  371. sv_meta.svCount = sv_cache_info.gps_l2_count;
  372. break;
  373. }
  374. break;
  375. case GNSS_SV_TYPE_GLONASS:
  376. sv_meta.talker[0] = 'G';
  377. sv_meta.talker[1] = 'L';
  378. sv_meta.mask = sv_cache_info.glo_used_mask;
  379. // GLONASS SV ids are from 65-96
  380. sv_meta.svIdOffset = GLONASS_SV_ID_OFFSET;
  381. sv_meta.systemId = SYSTEM_ID_GLONASS;
  382. switch (signalType) {
  383. case GNSS_SIGNAL_GLONASS_G1:
  384. sv_meta.svCount = sv_cache_info.glo_g1_count;
  385. break;
  386. case GNSS_SIGNAL_GLONASS_G2:
  387. sv_meta.svCount = sv_cache_info.glo_g2_count;
  388. break;
  389. }
  390. break;
  391. case GNSS_SV_TYPE_GALILEO:
  392. sv_meta.talker[0] = 'G';
  393. sv_meta.talker[1] = 'A';
  394. sv_meta.mask = sv_cache_info.gal_used_mask;
  395. // GALILEO SV ids are from 301-336, So keep svIdOffset 300
  396. sv_meta.svIdOffset = GALILEO_SV_ID_OFFSET;
  397. sv_meta.systemId = SYSTEM_ID_GALILEO;
  398. switch (signalType) {
  399. case GNSS_SIGNAL_GALILEO_E1:
  400. sv_meta.svCount = sv_cache_info.gal_e1_count;
  401. break;
  402. case GNSS_SIGNAL_GALILEO_E5A:
  403. sv_meta.svCount = sv_cache_info.gal_e5_count;
  404. break;
  405. case GNSS_SIGNAL_GALILEO_E5B:
  406. sv_meta.svCount = sv_cache_info.gal_e5b_count;
  407. break;
  408. }
  409. break;
  410. case GNSS_SV_TYPE_QZSS:
  411. sv_meta.talker[0] = 'G';
  412. sv_meta.talker[1] = 'Q';
  413. sv_meta.mask = sv_cache_info.qzss_used_mask;
  414. // QZSS SV ids are from 193-199. So keep svIdOffset 192
  415. sv_meta.svIdOffset = QZSS_SV_ID_OFFSET;
  416. sv_meta.systemId = SYSTEM_ID_QZSS;
  417. switch (signalType) {
  418. case GNSS_SIGNAL_QZSS_L1CA:
  419. sv_meta.svCount = sv_cache_info.qzss_l1_count;
  420. break;
  421. case GNSS_SIGNAL_QZSS_L2:
  422. sv_meta.svCount = sv_cache_info.qzss_l2_count;
  423. break;
  424. case GNSS_SIGNAL_QZSS_L5:
  425. sv_meta.svCount = sv_cache_info.qzss_l5_count;
  426. break;
  427. }
  428. break;
  429. case GNSS_SV_TYPE_BEIDOU:
  430. sv_meta.talker[0] = 'G';
  431. sv_meta.talker[1] = 'B';
  432. sv_meta.mask = sv_cache_info.bds_used_mask;
  433. // BDS SV ids are from 201-237. So keep svIdOffset 200
  434. sv_meta.svIdOffset = BDS_SV_ID_OFFSET;
  435. sv_meta.systemId = SYSTEM_ID_BDS;
  436. switch (signalType) {
  437. case GNSS_SIGNAL_BEIDOU_B1I:
  438. sv_meta.svCount = sv_cache_info.bds_b1i_count;
  439. break;
  440. case GNSS_SIGNAL_BEIDOU_B1C:
  441. sv_meta.svCount = sv_cache_info.bds_b1c_count;
  442. break;
  443. case GNSS_SIGNAL_BEIDOU_B2AI:
  444. sv_meta.svCount = sv_cache_info.bds_b2_count;
  445. break;
  446. }
  447. break;
  448. case GNSS_SV_TYPE_NAVIC:
  449. sv_meta.talker[0] = 'G';
  450. sv_meta.talker[1] = 'I';
  451. sv_meta.mask = sv_cache_info.navic_used_mask;
  452. // NAVIC SV ids are from 401-414. So keep svIdOffset 400
  453. sv_meta.svIdOffset = NAVIC_SV_ID_OFFSET;
  454. sv_meta.systemId = SYSTEM_ID_NAVIC;
  455. switch (signalType) {
  456. case GNSS_SIGNAL_NAVIC_L5:
  457. sv_meta.svCount = sv_cache_info.navic_l5_count;
  458. break;
  459. }
  460. break;
  461. default:
  462. LOC_LOGE("NMEA Error unknow constellation type: %d", svType);
  463. return NULL;
  464. }
  465. sv_meta.signalId = convert_signalType_to_signalId(signalType);
  466. sv_meta.totalSvUsedCount =
  467. get_sv_count_from_mask(sv_cache_info.gps_used_mask,
  468. GPS_SV_PRN_MAX - GPS_SV_PRN_MIN + 1) +
  469. get_sv_count_from_mask(sv_cache_info.glo_used_mask,
  470. GLO_SV_PRN_MAX - GLO_SV_PRN_MIN + 1) +
  471. get_sv_count_from_mask(sv_cache_info.gal_used_mask,
  472. GAL_SV_PRN_MAX - GAL_SV_PRN_MIN + 1) +
  473. get_sv_count_from_mask(sv_cache_info.qzss_used_mask,
  474. QZSS_SV_PRN_MAX - QZSS_SV_PRN_MIN + 1) +
  475. get_sv_count_from_mask(sv_cache_info.bds_used_mask,
  476. BDS_SV_PRN_MAX - BDS_SV_PRN_MIN + 1) +
  477. get_sv_count_from_mask(sv_cache_info.navic_used_mask,
  478. NAVIC_SV_PRN_MAX - NAVIC_SV_PRN_MIN + 1);
  479. if (needCombine &&
  480. (sv_cache_info.gps_used_mask ? 1 : 0) +
  481. (sv_cache_info.glo_used_mask ? 1 : 0) +
  482. (sv_cache_info.gal_used_mask ? 1 : 0) +
  483. (sv_cache_info.qzss_used_mask ? 1 : 0) +
  484. (sv_cache_info.bds_used_mask ? 1 : 0) +
  485. (sv_cache_info.navic_used_mask ? 1 : 0) > 1)
  486. {
  487. // If GPS, GLONASS, Galileo, QZSS, BDS etc. are combined
  488. // to obtain the reported position solution,
  489. // talker shall be set to GN, to indicate that
  490. // the satellites are used in a combined solution
  491. sv_meta.talker[0] = 'G';
  492. sv_meta.talker[1] = 'N';
  493. }
  494. return &sv_meta;
  495. }
  496. /*===========================================================================
  497. FUNCTION loc_nmea_put_checksum
  498. DESCRIPTION
  499. Generate NMEA sentences generated based on position report
  500. DEPENDENCIES
  501. NONE
  502. RETURN VALUE
  503. Total length of the nmea sentence
  504. SIDE EFFECTS
  505. N/A
  506. ===========================================================================*/
  507. static int loc_nmea_put_checksum(char *pNmea, int maxSize, bool isTagBlock)
  508. {
  509. uint8_t checksum = 0;
  510. int length = 0;
  511. int checksumLength = 0;
  512. if(NULL == pNmea)
  513. return 0;
  514. pNmea++; //skip the $ or / for Tag Block
  515. while (*pNmea != '\0')
  516. {
  517. checksum ^= *pNmea++;
  518. length++;
  519. }
  520. if (isTagBlock) {
  521. // length now contains tag block sentence string length not including / sign.
  522. checksumLength = snprintf(pNmea, (maxSize-length-1), "*%02X\\", checksum);
  523. } else {
  524. // length now contains nmea sentence string length not including $ sign.
  525. checksumLength = snprintf(pNmea, (maxSize-length-1), "*%02X\r\n", checksum);
  526. }
  527. // total length of nmea sentence is length of nmea sentence inc $ sign plus
  528. // length of checksum (+1 is to cover the $ character in the length).
  529. return (length + checksumLength + 1);
  530. }
  531. /*===========================================================================
  532. FUNCTION loc_nmea_generate_GSA
  533. DESCRIPTION
  534. Generate NMEA GSA sentences generated based on position report
  535. Currently below sentences are generated:
  536. - $GPGSA : GPS DOP and active SVs
  537. - $GLGSA : GLONASS DOP and active SVs
  538. - $GAGSA : GALILEO DOP and active SVs
  539. - $GNGSA : GNSS DOP and active SVs
  540. DEPENDENCIES
  541. NONE
  542. RETURN VALUE
  543. Number of SVs used
  544. SIDE EFFECTS
  545. N/A
  546. ===========================================================================*/
  547. static uint32_t loc_nmea_generate_GSA(const GpsLocationExtended &locationExtended,
  548. char* sentence,
  549. int bufSize,
  550. loc_nmea_sv_meta* sv_meta_p,
  551. std::vector<std::string> &nmeaArraystr,
  552. bool isTagBlockGroupingEnabled)
  553. {
  554. if (!sentence || bufSize <= 0 || !sv_meta_p)
  555. {
  556. LOC_LOGE("NMEA Error invalid arguments.");
  557. return 0;
  558. }
  559. char* pMarker = sentence;
  560. int lengthRemaining = bufSize;
  561. int length = 0;
  562. int lengthTagBlock = 0;
  563. uint32_t svUsedCount = 0;
  564. uint32_t svUsedList[64] = {0};
  565. uint32_t sentenceCount = 0;
  566. uint32_t sentenceNumber = 1;
  567. size_t svNumber = 1;
  568. static uint32_t code = 1;
  569. char fixType = '\0';
  570. const char* talker = sv_meta_p->talker;
  571. uint32_t svIdOffset = sv_meta_p->svIdOffset;
  572. uint64_t mask = sv_meta_p->mask;
  573. if (!(sv_meta_p->svTypeMask & (1 << GNSS_SV_TYPE_GLONASS))) {
  574. svIdOffset = 0;
  575. }
  576. for (uint8_t i = 1; mask > 0 && svUsedCount < 64; i++)
  577. {
  578. if (mask & 1)
  579. svUsedList[svUsedCount++] = i + svIdOffset;
  580. mask = mask >> 1;
  581. }
  582. if (svUsedCount == 0) {
  583. return 0;
  584. } else {
  585. sentenceNumber = 1;
  586. sentenceCount = svUsedCount / 12 + (svUsedCount % 12 != 0);
  587. svNumber = 1;
  588. }
  589. while (sentenceNumber <= sentenceCount) {
  590. pMarker = sentence;
  591. lengthRemaining = bufSize;
  592. if (svUsedCount > 12 && isTagBlockGroupingEnabled) {
  593. lengthTagBlock = snprintf(pMarker, lengthRemaining, "\\g:%d-%d-%d", sentenceNumber,
  594. sentenceCount, code);
  595. if (MAX_TAG_BLOCK_GROUP_CODE == code) {
  596. code = 1;
  597. }
  598. lengthTagBlock = loc_nmea_put_checksum(sentence, bufSize, true);
  599. pMarker += lengthTagBlock;
  600. lengthRemaining -= lengthTagBlock;
  601. }
  602. if (sv_meta_p->totalSvUsedCount == 0)
  603. fixType = '1'; // no fix
  604. else if (sv_meta_p->totalSvUsedCount <= 3)
  605. fixType = '2'; // 2D fix
  606. else
  607. fixType = '3'; // 3D fix
  608. // Start printing the sentence
  609. // Format: $--GSA,a,x,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,p.p,h.h,v.v,s*cc
  610. // a : Mode : A : Automatic, allowed to automatically switch 2D/3D
  611. // x : Fixtype : 1 (no fix), 2 (2D fix), 3 (3D fix)
  612. // xx : 12 SV ID
  613. // p.p : Position DOP (Dilution of Precision)
  614. // h.h : Horizontal DOP
  615. // v.v : Vertical DOP
  616. // s : GNSS System Id
  617. // cc : Checksum value
  618. length = snprintf(pMarker, lengthRemaining, "$%sGSA,A,%c,", talker, fixType);
  619. if (length < 0 || length >= lengthRemaining) {
  620. LOC_LOGE("NMEA Error in string formatting");
  621. return 0;
  622. }
  623. pMarker += length;
  624. lengthRemaining -= length;
  625. // Add 12 satellite IDs
  626. for (uint8_t i = 0; i < 12; i++, svNumber++)
  627. {
  628. if (svNumber <= svUsedCount)
  629. length = snprintf(pMarker, lengthRemaining, "%02d,", svUsedList[svNumber - 1]);
  630. else
  631. length = snprintf(pMarker, lengthRemaining, ",");
  632. if (length < 0 || length >= lengthRemaining) {
  633. LOC_LOGE("NMEA Error in string formatting");
  634. return 0;
  635. }
  636. pMarker += length;
  637. lengthRemaining -= length;
  638. }
  639. // Add the position/horizontal/vertical DOP values
  640. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) {
  641. length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f,",
  642. locationExtended.pdop,
  643. locationExtended.hdop,
  644. locationExtended.vdop);
  645. } else if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_EXT_DOP) {
  646. length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f,",
  647. locationExtended.extDOP.PDOP,
  648. locationExtended.extDOP.HDOP,
  649. locationExtended.extDOP.VDOP);
  650. } else { // no dop
  651. length = snprintf(pMarker, lengthRemaining, ",,,");
  652. }
  653. pMarker += length;
  654. lengthRemaining -= length;
  655. // system id
  656. length = snprintf(pMarker, lengthRemaining, "%d", sv_meta_p->systemId);
  657. pMarker += length;
  658. lengthRemaining -= length;
  659. /* Sentence is ready, add checksum and broadcast */
  660. length = loc_nmea_put_checksum(sentence + lengthTagBlock, bufSize - lengthTagBlock, false);
  661. nmeaArraystr.push_back(sentence);
  662. sentenceNumber++;
  663. if (!isTagBlockGroupingEnabled) {
  664. break;
  665. }
  666. }
  667. if (svUsedCount > 12 && isTagBlockGroupingEnabled) {
  668. code++;
  669. }
  670. return svUsedCount;
  671. }
  672. /*===========================================================================
  673. FUNCTION loc_nmea_generate_GSV
  674. DESCRIPTION
  675. Generate NMEA GSV sentences generated based on sv report
  676. Currently below sentences are generated:
  677. - $GPGSV: GPS Satellites in View
  678. - $GLGSV: GLONASS Satellites in View
  679. - $GAGSV: GALILEO Satellites in View
  680. DEPENDENCIES
  681. NONE
  682. RETURN VALUE
  683. NONE
  684. SIDE EFFECTS
  685. N/A
  686. ===========================================================================*/
  687. static void loc_nmea_generate_GSV(const GnssSvNotification &svNotify,
  688. char* sentence,
  689. int bufSize,
  690. loc_nmea_sv_meta* sv_meta_p,
  691. std::vector<std::string> &nmeaArraystr)
  692. {
  693. if (!sentence || bufSize <= 0)
  694. {
  695. LOC_LOGE("NMEA Error invalid argument.");
  696. return;
  697. }
  698. char* pMarker = sentence;
  699. int lengthRemaining = bufSize;
  700. int length = 0;
  701. int sentenceCount = 0;
  702. int sentenceNumber = 1;
  703. size_t svNumber = 1;
  704. const char* talker = sv_meta_p->talker;
  705. uint32_t svIdOffset = sv_meta_p->svIdOffset;
  706. int svCount = sv_meta_p->svCount;
  707. if (svCount <= 0)
  708. {
  709. LOC_LOGV("No SV in view for talker ID:%s, signal ID:%X", talker, sv_meta_p->signalId);
  710. return;
  711. }
  712. if ((1 << GNSS_SV_TYPE_GLONASS) & sv_meta_p->svTypeMask) {
  713. svIdOffset = 0;
  714. }
  715. svNumber = 1;
  716. sentenceNumber = 1;
  717. sentenceCount = svCount / 4 + (svCount % 4 != 0);
  718. while (sentenceNumber <= sentenceCount)
  719. {
  720. pMarker = sentence;
  721. lengthRemaining = bufSize;
  722. length = snprintf(pMarker, lengthRemaining, "$%sGSV,%d,%d,%02d",
  723. talker, sentenceCount, sentenceNumber, svCount);
  724. if (length < 0 || length >= lengthRemaining)
  725. {
  726. LOC_LOGE("NMEA Error in string formatting");
  727. return;
  728. }
  729. pMarker += length;
  730. lengthRemaining -= length;
  731. for (int i=0; (svNumber <= svNotify.count) && (i < 4); svNumber++)
  732. {
  733. GnssSignalTypeMask signalType = svNotify.gnssSvs[svNumber-1].gnssSignalTypeMask;
  734. if (0 == signalType) {
  735. // If no signal type in report, it means default L1,G1,E1,B1I
  736. switch (svNotify.gnssSvs[svNumber - 1].type)
  737. {
  738. case GNSS_SV_TYPE_GPS:
  739. signalType = GNSS_SIGNAL_GPS_L1CA;
  740. break;
  741. case GNSS_SV_TYPE_GLONASS:
  742. signalType = GNSS_SIGNAL_GLONASS_G1;
  743. break;
  744. case GNSS_SV_TYPE_GALILEO:
  745. signalType = GNSS_SIGNAL_GALILEO_E1;
  746. break;
  747. case GNSS_SV_TYPE_QZSS:
  748. signalType = GNSS_SIGNAL_QZSS_L1CA;
  749. break;
  750. case GNSS_SV_TYPE_BEIDOU:
  751. signalType = GNSS_SIGNAL_BEIDOU_B1I;
  752. break;
  753. case GNSS_SV_TYPE_SBAS:
  754. signalType = GNSS_SIGNAL_SBAS_L1;
  755. break;
  756. case GNSS_SV_TYPE_NAVIC:
  757. signalType = GNSS_SIGNAL_NAVIC_L5;
  758. break;
  759. default:
  760. LOC_LOGE("NMEA Error unknow constellation type: %d",
  761. svNotify.gnssSvs[svNumber - 1].type);
  762. continue;
  763. }
  764. }
  765. if ((sv_meta_p->svTypeMask & (1 << svNotify.gnssSvs[svNumber - 1].type)) &&
  766. sv_meta_p->signalId == convert_signalType_to_signalId(signalType))
  767. {
  768. svIdOffset = sv_meta_p->svIdOffset;
  769. if (GNSS_SV_TYPE_SBAS == svNotify.gnssSvs[svNumber - 1].type) {
  770. svIdOffset = SBAS_SV_ID_OFFSET;
  771. }
  772. if (GNSS_SV_TYPE_GLONASS == svNotify.gnssSvs[svNumber - 1].type &&
  773. GLO_SV_PRN_SLOT_UNKNOWN == svNotify.gnssSvs[svNumber - 1].svId) {
  774. length = snprintf(pMarker, lengthRemaining, ",,%02d,%03d,",
  775. (int)(0.5 + svNotify.gnssSvs[svNumber - 1].elevation), //float to int
  776. (int)(0.5 + svNotify.gnssSvs[svNumber - 1].azimuth)); //float to int
  777. } else {
  778. length = snprintf(pMarker, lengthRemaining, ",%02d,%02d,%03d,",
  779. svNotify.gnssSvs[svNumber - 1].svId - svIdOffset,
  780. (int)(0.5 + svNotify.gnssSvs[svNumber - 1].elevation), //float to int
  781. (int)(0.5 + svNotify.gnssSvs[svNumber - 1].azimuth)); //float to int
  782. }
  783. if (length < 0 || length >= lengthRemaining)
  784. {
  785. LOC_LOGE("NMEA Error in string formatting");
  786. return;
  787. }
  788. pMarker += length;
  789. lengthRemaining -= length;
  790. if (svNotify.gnssSvs[svNumber - 1].cN0Dbhz > 0)
  791. {
  792. length = snprintf(pMarker, lengthRemaining, "%02d",
  793. (int)(0.5 + svNotify.gnssSvs[svNumber - 1].cN0Dbhz)); //float to int
  794. if (length < 0 || length >= lengthRemaining)
  795. {
  796. LOC_LOGE("NMEA Error in string formatting");
  797. return;
  798. }
  799. pMarker += length;
  800. lengthRemaining -= length;
  801. }
  802. i++;
  803. }
  804. }
  805. // append signalId
  806. length = snprintf(pMarker, lengthRemaining, ",%X", sv_meta_p->signalId);
  807. pMarker += length;
  808. lengthRemaining -= length;
  809. length = loc_nmea_put_checksum(sentence, bufSize, false);
  810. nmeaArraystr.push_back(sentence);
  811. sentenceNumber++;
  812. } //while
  813. }
  814. /*===========================================================================
  815. FUNCTION loc_nmea_generate_DTM
  816. DESCRIPTION
  817. Generate NMEA DTM sentences generated based on position report
  818. DEPENDENCIES
  819. NONE
  820. RETURN VALUE
  821. NONE
  822. SIDE EFFECTS
  823. N/A
  824. ===========================================================================*/
  825. static void loc_nmea_generate_DTM(const LocLla &ref_lla,
  826. const LocLla &local_lla,
  827. char *talker,
  828. char *sentence,
  829. int bufSize)
  830. {
  831. char* pMarker = sentence;
  832. int lengthRemaining = bufSize;
  833. int length = 0;
  834. int datum_type;
  835. char ref_datum[4] = {'W', '8', '4', '\0'};
  836. char local_datum[4] = {0};
  837. double lla_offset[3] = {0};
  838. char latHem, longHem;
  839. double latMins, longMins;
  840. datum_type = loc_get_datum_type();
  841. switch (datum_type) {
  842. case LOC_GNSS_DATUM_WGS84:
  843. local_datum[0] = 'W';
  844. local_datum[1] = '8';
  845. local_datum[2] = '4';
  846. break;
  847. case LOC_GNSS_DATUM_PZ90:
  848. local_datum[0] = 'P';
  849. local_datum[1] = '9';
  850. local_datum[2] = '0';
  851. break;
  852. default:
  853. break;
  854. }
  855. length = snprintf(pMarker , lengthRemaining , "$%sDTM,%s,," , talker, local_datum);
  856. if (length < 0 || length >= lengthRemaining) {
  857. LOC_LOGE("NMEA Error in string formatting");
  858. return;
  859. }
  860. pMarker += length;
  861. lengthRemaining -= length;
  862. lla_offset[0] = local_lla.lat - ref_lla.lat;
  863. lla_offset[1] = fmod(local_lla.lon - ref_lla.lon, 360.0);
  864. if (lla_offset[1] < -180.0) {
  865. lla_offset[1] += 360.0;
  866. } else if ( lla_offset[1] > 180.0) {
  867. lla_offset[1] -= 360.0;
  868. }
  869. lla_offset[2] = local_lla.alt - ref_lla.alt;
  870. if (lla_offset[0] >= 0.0) {
  871. latHem = 'N';
  872. } else {
  873. latHem = 'S';
  874. lla_offset[0] *= -1.0;
  875. }
  876. latMins = fmod(lla_offset[0] * 60.0, 60.0);
  877. if (lla_offset[1] < 0.0) {
  878. longHem = 'W';
  879. lla_offset[1] *= -1.0;
  880. }else {
  881. longHem = 'E';
  882. }
  883. longMins = fmod(lla_offset[1] * 60.0, 60.0);
  884. length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,%.3lf,",
  885. (uint8_t)floor(lla_offset[0]), latMins, latHem,
  886. (uint8_t)floor(lla_offset[1]), longMins, longHem, lla_offset[2]);
  887. if (length < 0 || length >= lengthRemaining) {
  888. LOC_LOGE("NMEA Error in string formatting");
  889. return;
  890. }
  891. pMarker += length;
  892. lengthRemaining -= length;
  893. length = snprintf(pMarker , lengthRemaining , "%s" , ref_datum);
  894. if (length < 0 || length >= lengthRemaining) {
  895. LOC_LOGE("NMEA Error in string formatting");
  896. return;
  897. }
  898. pMarker += length;
  899. lengthRemaining -= length;
  900. length = loc_nmea_put_checksum(sentence, bufSize, false);
  901. }
  902. /*===========================================================================
  903. FUNCTION get_utctime_with_leapsecond_transition
  904. DESCRIPTION
  905. This function returns true if the position report is generated during
  906. leap second transition period. If not, then the utc timestamp returned
  907. will be set to the timestamp in the position report. If it is,
  908. then the utc timestamp returned will need to take into account
  909. of the leap second transition so that proper calendar year/month/date
  910. can be calculated from the returned utc timestamp.
  911. DEPENDENCIES
  912. NONE
  913. RETURN VALUE
  914. true: position report is generated in leap second transition period.
  915. SIDE EFFECTS
  916. N/A
  917. ===========================================================================*/
  918. static bool get_utctime_with_leapsecond_transition(
  919. const UlpLocation &location,
  920. const GpsLocationExtended &locationExtended,
  921. const LocationSystemInfo &systemInfo,
  922. LocGpsUtcTime &utcPosTimestamp)
  923. {
  924. bool inTransition = false;
  925. // position report is not generated during leap second transition,
  926. // we can use the UTC timestamp from position report as is
  927. utcPosTimestamp = location.gpsLocation.timestamp;
  928. // Check whether we are in leap second transition.
  929. // If so, per NMEA spec, we need to display the extra second in format of 23:59:60
  930. // with year/month/date not getting advanced.
  931. if ((locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_GPS_TIME) &&
  932. ((systemInfo.systemInfoMask & LOCATION_SYS_INFO_LEAP_SECOND) &&
  933. (systemInfo.leapSecondSysInfo.leapSecondInfoMask &
  934. LEAP_SECOND_SYS_INFO_LEAP_SECOND_CHANGE_BIT))) {
  935. const LeapSecondChangeInfo &leapSecondChangeInfo =
  936. systemInfo.leapSecondSysInfo.leapSecondChangeInfo;
  937. const GnssSystemTimeStructType &gpsTimestampLsChange =
  938. leapSecondChangeInfo.gpsTimestampLsChange;
  939. uint64_t gpsTimeLsChange = gpsTimestampLsChange.systemWeek * MSEC_IN_ONE_WEEK +
  940. gpsTimestampLsChange.systemMsec;
  941. uint64_t gpsTimePosReport = locationExtended.gpsTime.gpsWeek * MSEC_IN_ONE_WEEK +
  942. locationExtended.gpsTime.gpsTimeOfWeekMs;
  943. // we are only dealing with positive leap second change, as negative
  944. // leap second change has never occurred and should not occur in future
  945. if (leapSecondChangeInfo.leapSecondsAfterChange >
  946. leapSecondChangeInfo.leapSecondsBeforeChange) {
  947. // leap second adjustment is always 1 second at a time. It can happen
  948. // every quarter end and up to four times per year.
  949. if ((gpsTimePosReport >= gpsTimeLsChange) &&
  950. (gpsTimePosReport < (gpsTimeLsChange + 1000))) {
  951. inTransition = true;
  952. utcPosTimestamp = gpsTimeLsChange + UTC_GPS_OFFSET_MSECS -
  953. leapSecondChangeInfo.leapSecondsBeforeChange * 1000;
  954. // we substract 1000 milli-seconds from UTC timestmap in order to calculate the
  955. // proper year, month and date during leap second transtion.
  956. // Let us give an example, assuming leap second transition is scheduled on 2019,
  957. // Dec 31st mid night. When leap second transition is happening,
  958. // instead of outputting the time as 2020, Jan, 1st, 00 hour, 00 min, and 00 sec.
  959. // The time need to be displayed as 2019, Dec, 31st, 23 hour, 59 min and 60 sec.
  960. utcPosTimestamp -= 1000;
  961. }
  962. }
  963. }
  964. return inTransition;
  965. }
  966. /*===========================================================================
  967. FUNCTION loc_nmea_get_fix_quality
  968. DESCRIPTION
  969. This function obtains the fix quality for GGA sentence, mode indicator
  970. for RMC and VTG sentence based on nav solution mask and tech mask in
  971. the postion report.
  972. DEPENDENCIES
  973. NONE
  974. Output parameter
  975. ggaGpsQuality: gps quality field in GGA sentence
  976. rmcModeIndicator: mode indicator field in RMC sentence
  977. vtgModeIndicator: mode indicator field in VTG sentence
  978. SIDE EFFECTS
  979. N/A
  980. ===========================================================================*/
  981. static void loc_nmea_get_fix_quality(const UlpLocation & location,
  982. const GpsLocationExtended & locationExtended,
  983. bool custom_gga_fix_quality,
  984. char ggaGpsQuality[3],
  985. char & rmcModeIndicator,
  986. char & vtgModeIndicator,
  987. char gnsModeIndicator[7]) {
  988. ggaGpsQuality[0] = '0'; // 0 means no fix
  989. rmcModeIndicator = 'N'; // N means no fix
  990. vtgModeIndicator = 'N'; // N means no fix
  991. memset(gnsModeIndicator, 'N', 6); // N means no fix
  992. gnsModeIndicator[6] = '\0';
  993. do {
  994. if (!(location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)){
  995. break;
  996. }
  997. // NOTE: Order of the check is important
  998. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_NAV_SOLUTION_MASK) {
  999. if (LOC_NAV_MASK_PPP_CORRECTION & locationExtended.navSolutionMask) {
  1000. ggaGpsQuality[0] = '2'; // 2 means DGPS fix
  1001. rmcModeIndicator = 'P'; // P means precise
  1002. vtgModeIndicator = 'P'; // P means precise
  1003. if (locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask ? 1 : 0)
  1004. gnsModeIndicator[0] = 'P'; // P means precise
  1005. if (locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask ? 1 : 0)
  1006. gnsModeIndicator[1] = 'P'; // P means precise
  1007. if (locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask ? 1 : 0)
  1008. gnsModeIndicator[2] = 'P'; // P means precise
  1009. if (locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask ? 1 : 0)
  1010. gnsModeIndicator[3] = 'P'; // P means precise
  1011. if (locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask ? 1 : 0)
  1012. gnsModeIndicator[4] = 'P'; // P means precise
  1013. if (locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask ? 1 : 0)
  1014. gnsModeIndicator[5] = 'P'; // P means precise
  1015. break;
  1016. } else if (LOC_NAV_MASK_RTK_FIXED_CORRECTION & locationExtended.navSolutionMask){
  1017. ggaGpsQuality[0] = '4'; // 4 means RTK Fixed fix
  1018. rmcModeIndicator = 'R'; // use R (RTK fixed)
  1019. vtgModeIndicator = 'D'; // use D (differential) as
  1020. // no RTK fixed defined for VTG in NMEA 183 spec
  1021. if (locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask ? 1 : 0)
  1022. gnsModeIndicator[0] = 'R'; // R means RTK fixed
  1023. if (locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask ? 1 : 0)
  1024. gnsModeIndicator[1] = 'R'; // R means RTK fixed
  1025. if (locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask ? 1 : 0)
  1026. gnsModeIndicator[2] = 'R'; // R means RTK fixed
  1027. if (locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask ? 1 : 0)
  1028. gnsModeIndicator[3] = 'R'; // R means RTK fixed
  1029. if (locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask ? 1 : 0)
  1030. gnsModeIndicator[4] = 'R'; // R means RTK fixed
  1031. if (locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask ? 1 : 0)
  1032. gnsModeIndicator[5] = 'R'; // R means RTK fixed
  1033. break;
  1034. } else if (LOC_NAV_MASK_RTK_CORRECTION & locationExtended.navSolutionMask){
  1035. ggaGpsQuality[0] = '5'; // 5 means RTK float fix
  1036. rmcModeIndicator = 'F'; // F means RTK float fix
  1037. vtgModeIndicator = 'D'; // use D (differential) as
  1038. // no RTK float defined for VTG in NMEA 183 spec
  1039. if (locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask ? 1 : 0)
  1040. gnsModeIndicator[0] = 'F'; // F means RTK float fix
  1041. if (locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask ? 1 : 0)
  1042. gnsModeIndicator[1] = 'F'; // F means RTK float fix
  1043. if (locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask ? 1 : 0)
  1044. gnsModeIndicator[2] = 'F'; // F means RTK float fix
  1045. if (locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask ? 1 : 0)
  1046. gnsModeIndicator[3] = 'F'; // F means RTK float fix
  1047. if (locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask ? 1 : 0)
  1048. gnsModeIndicator[4] = 'F'; // F means RTK float fix
  1049. if (locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask ? 1 : 0)
  1050. gnsModeIndicator[5] = 'F'; // F means RTK float fix
  1051. break;
  1052. } else if (LOC_NAV_MASK_DGNSS_CORRECTION & locationExtended.navSolutionMask){
  1053. ggaGpsQuality[0] = '2'; // 2 means DGPS fix
  1054. rmcModeIndicator = 'D'; // D means differential
  1055. vtgModeIndicator = 'D'; // D means differential
  1056. if (locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask ? 1 : 0)
  1057. gnsModeIndicator[0] = 'D'; // D means differential
  1058. if (locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask ? 1 : 0)
  1059. gnsModeIndicator[1] = 'D'; // D means differential
  1060. if (locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask ? 1 : 0)
  1061. gnsModeIndicator[2] = 'D'; // D means differential
  1062. if (locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask ? 1 : 0)
  1063. gnsModeIndicator[3] = 'D'; // D means differential
  1064. if (locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask ? 1 : 0)
  1065. gnsModeIndicator[4] = 'D'; // D means differential
  1066. if (locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask ? 1 : 0)
  1067. gnsModeIndicator[5] = 'D'; // D means differential
  1068. break;
  1069. } else if (LOC_NAV_MASK_SBAS_CORRECTION_IONO & locationExtended.navSolutionMask){
  1070. ggaGpsQuality[0] = '2'; // 2 means DGPS fix
  1071. rmcModeIndicator = 'D'; // D means differential
  1072. vtgModeIndicator = 'D'; // D means differential
  1073. if (locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask ? 1 : 0)
  1074. gnsModeIndicator[0] = 'D'; // D means differential
  1075. if (locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask ? 1 : 0)
  1076. gnsModeIndicator[1] = 'D'; // D means differential
  1077. if (locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask ? 1 : 0)
  1078. gnsModeIndicator[2] = 'D'; // D means differential
  1079. if (locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask ? 1 : 0)
  1080. gnsModeIndicator[3] = 'D'; // D means differential
  1081. if (locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask ? 1 : 0)
  1082. gnsModeIndicator[4] = 'D'; // D means differential
  1083. if (locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask ? 1 : 0)
  1084. gnsModeIndicator[5] = 'D'; // D means differential
  1085. break;
  1086. }
  1087. }
  1088. // NOTE: Order of the check is important
  1089. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_POS_TECH_MASK) {
  1090. if (LOC_POS_TECH_MASK_SATELLITE & locationExtended.tech_mask){
  1091. ggaGpsQuality[0] = '1'; // 1 means GPS
  1092. rmcModeIndicator = 'A'; // A means autonomous
  1093. vtgModeIndicator = 'A'; // A means autonomous
  1094. if (locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask ? 1 : 0)
  1095. gnsModeIndicator[0] = 'A'; // A means autonomous
  1096. if (locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask ? 1 : 0)
  1097. gnsModeIndicator[1] = 'A'; // A means autonomous
  1098. if (locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask ? 1 : 0)
  1099. gnsModeIndicator[2] = 'A'; // A means autonomous
  1100. if (locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask ? 1 : 0)
  1101. gnsModeIndicator[3] = 'A'; // A means autonomous
  1102. if (locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask ? 1 : 0)
  1103. gnsModeIndicator[4] = 'A'; // A means autonomous
  1104. if (locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask ? 1 : 0)
  1105. gnsModeIndicator[5] = 'A'; // A means autonomous
  1106. break;
  1107. } else if ((LOC_POS_TECH_MASK_SENSORS & locationExtended.tech_mask) ||
  1108. (LOC_POS_TECH_MASK_PROPAGATED & locationExtended.tech_mask)){
  1109. ggaGpsQuality[0] = '6'; // 6 means estimated (dead reckoning)
  1110. rmcModeIndicator = 'E'; // E means estimated (dead reckoning)
  1111. vtgModeIndicator = 'E'; // E means estimated (dead reckoning)
  1112. memset(gnsModeIndicator, 'E', 6); // E means estimated (dead reckoning)
  1113. break;
  1114. }
  1115. }
  1116. } while (0);
  1117. do {
  1118. // check for customized nmea enabled or not
  1119. // with customized GGA quality enabled
  1120. // PPP fix w/o sensor: 59, PPP fix w/ sensor: 69
  1121. // DGNSS/SBAS correction fix w/o sensor: 2, w/ sensor: 62
  1122. // RTK fixed fix w/o sensor: 4, w/ sensor: 64
  1123. // RTK float fix w/o sensor: 5, w/ sensor: 65
  1124. // SPE fix w/o sensor: 1, and w/ sensor: 61
  1125. // Sensor dead reckoning fix: 6
  1126. if (true == custom_gga_fix_quality) {
  1127. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_NAV_SOLUTION_MASK) {
  1128. // PPP fix w/o sensor: fix quality will now be 59
  1129. // PPP fix w sensor: fix quality will now be 69
  1130. if (LOC_NAV_MASK_PPP_CORRECTION & locationExtended.navSolutionMask) {
  1131. if ((locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_POS_TECH_MASK) &&
  1132. (LOC_POS_TECH_MASK_SENSORS & locationExtended.tech_mask)) {
  1133. ggaGpsQuality[0] = '6';
  1134. ggaGpsQuality[1] = '9';
  1135. } else {
  1136. ggaGpsQuality[0] = '5';
  1137. ggaGpsQuality[1] = '9';
  1138. }
  1139. break;
  1140. }
  1141. }
  1142. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_POS_TECH_MASK) {
  1143. if (LOC_POS_TECH_MASK_SENSORS & locationExtended.tech_mask){
  1144. char ggaQuality_copy = ggaGpsQuality[0];
  1145. ggaGpsQuality[0] = '6'; // 6 sensor assisted
  1146. // RTK fixed fix w/ sensor: fix quality will now be 64
  1147. // RTK float fix w/ sensor: 65
  1148. // DGNSS and/or SBAS correction fix and w/ sensor: 62
  1149. // GPS fix without correction and w/ sensor: 61
  1150. if ((LOC_NAV_MASK_RTK_FIXED_CORRECTION & locationExtended.navSolutionMask)||
  1151. (LOC_NAV_MASK_RTK_CORRECTION & locationExtended.navSolutionMask)||
  1152. (LOC_NAV_MASK_DGNSS_CORRECTION & locationExtended.navSolutionMask)||
  1153. (LOC_NAV_MASK_SBAS_CORRECTION_IONO & locationExtended.navSolutionMask)||
  1154. (LOC_POS_TECH_MASK_SATELLITE & locationExtended.tech_mask)) {
  1155. ggaGpsQuality[1] = ggaQuality_copy;
  1156. break;
  1157. }
  1158. }
  1159. }
  1160. }
  1161. } while (0);
  1162. LOC_LOGv("gps quality: %s, rmc mode indicator: %c, vtg mode indicator: %c",
  1163. ggaGpsQuality, rmcModeIndicator, vtgModeIndicator);
  1164. }
  1165. /*===========================================================================
  1166. FUNCTION loc_nmea_generate_pos
  1167. DESCRIPTION
  1168. Generate NMEA sentences generated based on position report
  1169. Currently below sentences are generated within this function:
  1170. - $GPGSA : GPS DOP and active SVs
  1171. - $GLGSA : GLONASS DOP and active SVs
  1172. - $GAGSA : GALILEO DOP and active SVs
  1173. - $GNGSA : GNSS DOP and active SVs
  1174. - $--VTG : Track made good and ground speed
  1175. - $--RMC : Recommended minimum navigation information
  1176. - $--GGA : Time, position and fix related data
  1177. DEPENDENCIES
  1178. NONE
  1179. RETURN VALUE
  1180. 0
  1181. SIDE EFFECTS
  1182. N/A
  1183. ===========================================================================*/
  1184. void loc_nmea_generate_pos(const UlpLocation &location,
  1185. const GpsLocationExtended &locationExtended,
  1186. const LocationSystemInfo &systemInfo,
  1187. unsigned char generate_nmea,
  1188. bool custom_gga_fix_quality,
  1189. std::vector<std::string> &nmeaArraystr,
  1190. int& indexOfGGA,
  1191. bool isTagBlockGroupingEnabled)
  1192. {
  1193. ENTRY_LOG();
  1194. indexOfGGA = -1;
  1195. LocGpsUtcTime utcPosTimestamp = 0;
  1196. bool inLsTransition = false;
  1197. inLsTransition = get_utctime_with_leapsecond_transition
  1198. (location, locationExtended, systemInfo, utcPosTimestamp);
  1199. time_t utcTime(utcPosTimestamp/1000);
  1200. struct tm result;
  1201. tm * pTm = gmtime_r(&utcTime, &result);
  1202. if (NULL == pTm) {
  1203. LOC_LOGE("gmtime failed");
  1204. return;
  1205. }
  1206. char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0};
  1207. char sentence_DTM[NMEA_SENTENCE_MAX_LENGTH] = {0};
  1208. char sentence_RMC[NMEA_SENTENCE_MAX_LENGTH] = {0};
  1209. char sentence_GNS[NMEA_SENTENCE_MAX_LENGTH] = {0};
  1210. char sentence_GGA[NMEA_SENTENCE_MAX_LENGTH] = {0};
  1211. char* pMarker = sentence;
  1212. int lengthRemaining = sizeof(sentence);
  1213. int length = 0;
  1214. int utcYear = pTm->tm_year % 100; // 2 digit year
  1215. int utcMonth = pTm->tm_mon + 1; // tm_mon starts at zero
  1216. int utcDay = pTm->tm_mday;
  1217. int utcHours = pTm->tm_hour;
  1218. int utcMinutes = pTm->tm_min;
  1219. int utcSeconds = pTm->tm_sec;
  1220. int utcMSeconds = (location.gpsLocation.timestamp)%1000;
  1221. int datum_type = loc_get_datum_type();
  1222. LocEcef ecef_w84;
  1223. LocEcef ecef_p90;
  1224. LocLla lla_w84;
  1225. LocLla lla_p90;
  1226. LocLla ref_lla;
  1227. LocLla local_lla;
  1228. memset(&ecef_w84, 0, sizeof(ecef_w84));
  1229. memset(&ecef_p90, 0, sizeof(ecef_p90));
  1230. memset(&lla_w84, 0, sizeof(lla_w84));
  1231. memset(&lla_p90, 0, sizeof(lla_p90));
  1232. memset(&ref_lla, 0, sizeof(ref_lla));
  1233. memset(&local_lla, 0, sizeof(local_lla));
  1234. if (inLsTransition) {
  1235. // During leap second transition, we need to display the extra
  1236. // leap second of hour, minute, second as (23:59:60)
  1237. utcHours = 23;
  1238. utcMinutes = 59;
  1239. utcSeconds = 60;
  1240. // As UTC timestamp is freezing during leap second transition,
  1241. // retrieve milli-seconds portion from GPS timestamp.
  1242. utcMSeconds = locationExtended.gpsTime.gpsTimeOfWeekMs % 1000;
  1243. }
  1244. loc_sv_cache_info sv_cache_info = {};
  1245. if (GPS_LOCATION_EXTENDED_HAS_GNSS_SV_USED_DATA & locationExtended.flags) {
  1246. sv_cache_info.gps_used_mask =
  1247. locationExtended.gnss_sv_used_ids.gps_sv_used_ids_mask;
  1248. sv_cache_info.glo_used_mask =
  1249. locationExtended.gnss_sv_used_ids.glo_sv_used_ids_mask;
  1250. sv_cache_info.gal_used_mask =
  1251. locationExtended.gnss_sv_used_ids.gal_sv_used_ids_mask;
  1252. sv_cache_info.bds_used_mask =
  1253. locationExtended.gnss_sv_used_ids.bds_sv_used_ids_mask;
  1254. sv_cache_info.qzss_used_mask =
  1255. locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask;
  1256. sv_cache_info.navic_used_mask =
  1257. locationExtended.gnss_sv_used_ids.navic_sv_used_ids_mask;
  1258. }
  1259. if (generate_nmea) {
  1260. char talker[3] = {'G', 'P', '\0'};
  1261. uint32_t svUsedCount = 0;
  1262. uint32_t count = 0;
  1263. loc_nmea_sv_meta sv_meta;
  1264. if (mEnabledNmeaTypes & NMEA_TYPE_GSA) {
  1265. // -------------------
  1266. // ---$GPGSA/$GNGSA---
  1267. // -------------------
  1268. count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence),
  1269. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GPS,
  1270. GNSS_SIGNAL_GPS_L1CA, true), nmeaArraystr, isTagBlockGroupingEnabled);
  1271. if (count > 0)
  1272. {
  1273. svUsedCount += count;
  1274. talker[0] = sv_meta.talker[0];
  1275. talker[1] = sv_meta.talker[1];
  1276. }
  1277. // -------------------
  1278. // ---$GLGSA/$GNGSA---
  1279. // -------------------
  1280. count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence),
  1281. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GLONASS,
  1282. GNSS_SIGNAL_GLONASS_G1, true), nmeaArraystr, isTagBlockGroupingEnabled);
  1283. if (count > 0)
  1284. {
  1285. svUsedCount += count;
  1286. talker[0] = sv_meta.talker[0];
  1287. talker[1] = sv_meta.talker[1];
  1288. }
  1289. // -------------------
  1290. // ---$GAGSA/$GNGSA---
  1291. // -------------------
  1292. count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence),
  1293. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GALILEO,
  1294. GNSS_SIGNAL_GALILEO_E1, true), nmeaArraystr, isTagBlockGroupingEnabled);
  1295. if (count > 0)
  1296. {
  1297. svUsedCount += count;
  1298. talker[0] = sv_meta.talker[0];
  1299. talker[1] = sv_meta.talker[1];
  1300. }
  1301. // ----------------------------
  1302. // ---$GBGSA/$GNGSA (BEIDOU)---
  1303. // ----------------------------
  1304. count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence),
  1305. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_BEIDOU,
  1306. GNSS_SIGNAL_BEIDOU_B1I, true), nmeaArraystr, isTagBlockGroupingEnabled);
  1307. if (count > 0)
  1308. {
  1309. svUsedCount += count;
  1310. talker[0] = sv_meta.talker[0];
  1311. talker[1] = sv_meta.talker[1];
  1312. }
  1313. // --------------------------
  1314. // ---$GQGSA/$GNGSA (QZSS)---
  1315. // --------------------------
  1316. count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence),
  1317. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_QZSS,
  1318. GNSS_SIGNAL_QZSS_L1CA, true), nmeaArraystr, isTagBlockGroupingEnabled);
  1319. if (count > 0)
  1320. {
  1321. svUsedCount += count;
  1322. talker[0] = sv_meta.talker[0];
  1323. talker[1] = sv_meta.talker[1];
  1324. }
  1325. // --------------------------
  1326. // ---$GIGSA/$GNGSA (NavIC)---
  1327. // --------------------------
  1328. count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence),
  1329. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_NAVIC,
  1330. GNSS_SIGNAL_NAVIC_L5, true), nmeaArraystr, isTagBlockGroupingEnabled);
  1331. if (count > 0)
  1332. {
  1333. svUsedCount += count;
  1334. talker[0] = sv_meta.talker[0];
  1335. talker[1] = sv_meta.talker[1];
  1336. }
  1337. // if svUsedCount is 0, it means we do not generate any GSA sentence yet.
  1338. // in this case, generate an empty GSA sentence
  1339. if (svUsedCount == 0) {
  1340. strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,,", sizeof(sentence));
  1341. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1342. nmeaArraystr.push_back(sentence);
  1343. }
  1344. }
  1345. char ggaGpsQuality[3] = {'0', '\0', '\0'};
  1346. char rmcModeIndicator = 'N';
  1347. char vtgModeIndicator = 'N';
  1348. char gnsModeIndicator[7] = {'N', 'N', 'N', 'N', 'N', 'N', '\0'};
  1349. loc_nmea_get_fix_quality(location, locationExtended, custom_gga_fix_quality,
  1350. ggaGpsQuality, rmcModeIndicator, vtgModeIndicator, gnsModeIndicator);
  1351. // -------------------
  1352. // ------$--VTG-------
  1353. // -------------------
  1354. if (mEnabledNmeaTypes & NMEA_TYPE_VTG) {
  1355. pMarker = sentence;
  1356. lengthRemaining = sizeof(sentence);
  1357. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_BEARING)
  1358. {
  1359. float magTrack = location.gpsLocation.bearing;
  1360. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV)
  1361. {
  1362. magTrack = location.gpsLocation.bearing - locationExtended.magneticDeviation;
  1363. if (magTrack < 0.0)
  1364. magTrack += 360.0;
  1365. else if (magTrack > 360.0)
  1366. magTrack -= 360.0;
  1367. }
  1368. length = snprintf(pMarker, lengthRemaining, "$%sVTG,%.1lf,T,%.1lf,M,",
  1369. talker, location.gpsLocation.bearing, magTrack);
  1370. }
  1371. else
  1372. {
  1373. length = snprintf(pMarker, lengthRemaining, "$%sVTG,,T,,M,", talker);
  1374. }
  1375. if (length < 0 || length >= lengthRemaining)
  1376. {
  1377. LOC_LOGE("NMEA Error in string formatting");
  1378. return;
  1379. }
  1380. pMarker += length;
  1381. lengthRemaining -= length;
  1382. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_SPEED)
  1383. {
  1384. float speedKnots = location.gpsLocation.speed * (3600.0/1852.0);
  1385. float speedKmPerHour = location.gpsLocation.speed * 3.6;
  1386. length = snprintf(pMarker, lengthRemaining, "%.1lf,N,%.1lf,K,",
  1387. speedKnots, speedKmPerHour);
  1388. }
  1389. else
  1390. {
  1391. length = snprintf(pMarker, lengthRemaining, ",N,,K,");
  1392. }
  1393. if (length < 0 || length >= lengthRemaining)
  1394. {
  1395. LOC_LOGE("NMEA Error in string formatting");
  1396. return;
  1397. }
  1398. pMarker += length;
  1399. lengthRemaining -= length;
  1400. length = snprintf(pMarker, lengthRemaining, "%c", vtgModeIndicator);
  1401. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1402. nmeaArraystr.push_back(sentence);
  1403. lla_w84.lat = location.gpsLocation.latitude / 180.0 * M_PI;
  1404. lla_w84.lon = location.gpsLocation.longitude / 180.0 * M_PI;
  1405. lla_w84.alt = location.gpsLocation.altitude;
  1406. convert_Lla_to_Ecef(lla_w84, ecef_w84);
  1407. convert_WGS84_to_PZ90(ecef_w84, ecef_p90);
  1408. convert_Ecef_to_Lla(ecef_p90, lla_p90);
  1409. ref_lla.lat = location.gpsLocation.latitude;
  1410. ref_lla.lon = location.gpsLocation.longitude;
  1411. ref_lla.alt = location.gpsLocation.altitude;
  1412. switch (datum_type) {
  1413. case LOC_GNSS_DATUM_WGS84:
  1414. local_lla.lat = location.gpsLocation.latitude;
  1415. local_lla.lon = location.gpsLocation.longitude;
  1416. local_lla.alt = location.gpsLocation.altitude;
  1417. break;
  1418. case LOC_GNSS_DATUM_PZ90:
  1419. local_lla.lat = lla_p90.lat / M_PI * 180.0;
  1420. local_lla.lon = lla_p90.lon / M_PI * 180.0;
  1421. local_lla.alt = lla_p90.alt;
  1422. break;
  1423. default:
  1424. break;
  1425. }
  1426. }
  1427. // -------------------
  1428. // ------$--DTM-------
  1429. // -------------------
  1430. if (mEnabledNmeaTypes & NMEA_TYPE_DTM) {
  1431. loc_nmea_generate_DTM(ref_lla, local_lla, talker, sentence_DTM, sizeof(sentence_DTM));
  1432. }
  1433. // -------------------
  1434. // ------$--RMC-------
  1435. // -------------------
  1436. if (mEnabledNmeaTypes & NMEA_TYPE_RMC) {
  1437. pMarker = sentence_RMC;
  1438. lengthRemaining = sizeof(sentence_RMC);
  1439. bool validFix = ((0 != sv_cache_info.gps_used_mask) ||
  1440. (0 != sv_cache_info.glo_used_mask) ||
  1441. (0 != sv_cache_info.gal_used_mask) ||
  1442. (0 != sv_cache_info.qzss_used_mask) ||
  1443. (0 != sv_cache_info.bds_used_mask));
  1444. if (validFix) {
  1445. length = snprintf(pMarker, lengthRemaining, "$%sRMC,%02d%02d%02d.%02d,A,",
  1446. talker, utcHours, utcMinutes, utcSeconds, utcMSeconds/10);
  1447. } else {
  1448. length = snprintf(pMarker, lengthRemaining, "$%sRMC,%02d%02d%02d.%02d,V,",
  1449. talker, utcHours, utcMinutes, utcSeconds, utcMSeconds/10);
  1450. }
  1451. if (length < 0 || length >= lengthRemaining)
  1452. {
  1453. LOC_LOGE("NMEA Error in string formatting");
  1454. return;
  1455. }
  1456. pMarker += length;
  1457. lengthRemaining -= length;
  1458. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)
  1459. {
  1460. double latitude = ref_lla.lat;
  1461. double longitude = ref_lla.lon;
  1462. char latHemisphere;
  1463. char lonHemisphere;
  1464. double latMinutes;
  1465. double lonMinutes;
  1466. if (latitude > 0)
  1467. {
  1468. latHemisphere = 'N';
  1469. }
  1470. else
  1471. {
  1472. latHemisphere = 'S';
  1473. latitude *= -1.0;
  1474. }
  1475. if (longitude < 0)
  1476. {
  1477. lonHemisphere = 'W';
  1478. longitude *= -1.0;
  1479. }
  1480. else
  1481. {
  1482. lonHemisphere = 'E';
  1483. }
  1484. latMinutes = fmod(latitude * 60.0, 60.0);
  1485. lonMinutes = fmod(longitude * 60.0, 60.0);
  1486. length = snprintf(pMarker, lengthRemaining,
  1487. "%02d%09.6lf,%c,%03d%09.6lf,%c,",
  1488. (uint8_t)floor(latitude), latMinutes, latHemisphere,
  1489. (uint8_t)floor(longitude), lonMinutes, lonHemisphere);
  1490. }
  1491. else
  1492. {
  1493. length = snprintf(pMarker, lengthRemaining, ",,,,");
  1494. }
  1495. if (length < 0 || length >= lengthRemaining)
  1496. {
  1497. LOC_LOGE("NMEA Error in string formatting");
  1498. return;
  1499. }
  1500. pMarker += length;
  1501. lengthRemaining -= length;
  1502. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_SPEED)
  1503. {
  1504. float speedKnots = location.gpsLocation.speed * (3600.0/1852.0);
  1505. length = snprintf(pMarker, lengthRemaining, "%.1lf,", speedKnots);
  1506. }
  1507. else
  1508. {
  1509. length = snprintf(pMarker, lengthRemaining, ",");
  1510. }
  1511. if (length < 0 || length >= lengthRemaining)
  1512. {
  1513. LOC_LOGE("NMEA Error in string formatting");
  1514. return;
  1515. }
  1516. pMarker += length;
  1517. lengthRemaining -= length;
  1518. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_BEARING)
  1519. {
  1520. length = snprintf(pMarker, lengthRemaining, "%.1lf,",
  1521. location.gpsLocation.bearing);
  1522. }
  1523. else
  1524. {
  1525. length = snprintf(pMarker, lengthRemaining, ",");
  1526. }
  1527. if (length < 0 || length >= lengthRemaining)
  1528. {
  1529. LOC_LOGE("NMEA Error in string formatting");
  1530. return;
  1531. }
  1532. pMarker += length;
  1533. lengthRemaining -= length;
  1534. length = snprintf(pMarker, lengthRemaining, "%2.2d%2.2d%2.2d,",
  1535. utcDay, utcMonth, utcYear);
  1536. if (length < 0 || length >= lengthRemaining)
  1537. {
  1538. LOC_LOGE("NMEA Error in string formatting");
  1539. return;
  1540. }
  1541. pMarker += length;
  1542. lengthRemaining -= length;
  1543. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV)
  1544. {
  1545. float magneticVariation = locationExtended.magneticDeviation;
  1546. char direction;
  1547. if (magneticVariation < 0.0)
  1548. {
  1549. direction = 'W';
  1550. magneticVariation *= -1.0;
  1551. }
  1552. else
  1553. {
  1554. direction = 'E';
  1555. }
  1556. length = snprintf(pMarker, lengthRemaining, "%.1lf,%c,",
  1557. magneticVariation, direction);
  1558. }
  1559. else
  1560. {
  1561. length = snprintf(pMarker, lengthRemaining, ",,");
  1562. }
  1563. if (length < 0 || length >= lengthRemaining)
  1564. {
  1565. LOC_LOGE("NMEA Error in string formatting");
  1566. return;
  1567. }
  1568. pMarker += length;
  1569. lengthRemaining -= length;
  1570. length = snprintf(pMarker, lengthRemaining, "%c", rmcModeIndicator);
  1571. pMarker += length;
  1572. lengthRemaining -= length;
  1573. // hardcode Navigation Status field to 'V'
  1574. length = snprintf(pMarker, lengthRemaining, ",%c", 'V');
  1575. length = loc_nmea_put_checksum(sentence_RMC, sizeof(sentence_RMC), false);
  1576. }
  1577. // -------------------
  1578. // ------$--GNS-------
  1579. // -------------------
  1580. if (mEnabledNmeaTypes & NMEA_TYPE_GNS) {
  1581. pMarker = sentence_GNS;
  1582. lengthRemaining = sizeof(sentence_GNS);
  1583. length = snprintf(pMarker, lengthRemaining, "$%sGNS,%02d%02d%02d.%02d,",
  1584. talker, utcHours, utcMinutes, utcSeconds, utcMSeconds/10);
  1585. if (length < 0 || length >= lengthRemaining)
  1586. {
  1587. LOC_LOGE("NMEA Error in string formatting");
  1588. return;
  1589. }
  1590. pMarker += length;
  1591. lengthRemaining -= length;
  1592. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)
  1593. {
  1594. double latitude = ref_lla.lat;
  1595. double longitude = ref_lla.lon;
  1596. char latHemisphere;
  1597. char lonHemisphere;
  1598. double latMinutes;
  1599. double lonMinutes;
  1600. if (latitude > 0)
  1601. {
  1602. latHemisphere = 'N';
  1603. }
  1604. else
  1605. {
  1606. latHemisphere = 'S';
  1607. latitude *= -1.0;
  1608. }
  1609. if (longitude < 0)
  1610. {
  1611. lonHemisphere = 'W';
  1612. longitude *= -1.0;
  1613. }
  1614. else
  1615. {
  1616. lonHemisphere = 'E';
  1617. }
  1618. latMinutes = fmod(latitude * 60.0, 60.0);
  1619. lonMinutes = fmod(longitude * 60.0, 60.0);
  1620. length = snprintf(pMarker, lengthRemaining,
  1621. "%02d%09.6lf,%c,%03d%09.6lf,%c,",
  1622. (uint8_t)floor(latitude), latMinutes, latHemisphere,
  1623. (uint8_t)floor(longitude), lonMinutes, lonHemisphere);
  1624. }
  1625. else
  1626. {
  1627. length = snprintf(pMarker, lengthRemaining, ",,,,");
  1628. }
  1629. if (length < 0 || length >= lengthRemaining)
  1630. {
  1631. LOC_LOGE("NMEA Error in string formatting");
  1632. return;
  1633. }
  1634. pMarker += length;
  1635. lengthRemaining -= length;
  1636. length = snprintf(pMarker, lengthRemaining, "%s,", gnsModeIndicator);
  1637. pMarker += length;
  1638. lengthRemaining -= length;
  1639. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) {
  1640. length = snprintf(pMarker, lengthRemaining, "%02d,%.1f,",
  1641. svUsedCount, locationExtended.hdop);
  1642. } else if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_EXT_DOP) {
  1643. length = snprintf(pMarker, lengthRemaining, "%02d,%.1f,",
  1644. svUsedCount, locationExtended.extDOP.HDOP);
  1645. }
  1646. else { // no hdop
  1647. length = snprintf(pMarker, lengthRemaining, "%02d,,",
  1648. svUsedCount);
  1649. }
  1650. if (length < 0 || length >= lengthRemaining)
  1651. {
  1652. LOC_LOGE("NMEA Error in string formatting");
  1653. return;
  1654. }
  1655. pMarker += length;
  1656. lengthRemaining -= length;
  1657. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL)
  1658. {
  1659. length = snprintf(pMarker, lengthRemaining, "%.1lf,",
  1660. locationExtended.altitudeMeanSeaLevel);
  1661. }
  1662. else
  1663. {
  1664. length = snprintf(pMarker, lengthRemaining, ",");
  1665. }
  1666. if (length < 0 || length >= lengthRemaining)
  1667. {
  1668. LOC_LOGE("NMEA Error in string formatting");
  1669. return;
  1670. }
  1671. pMarker += length;
  1672. lengthRemaining -= length;
  1673. if ((location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_ALTITUDE) &&
  1674. (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL))
  1675. {
  1676. length = snprintf(pMarker, lengthRemaining, "%.1lf,",
  1677. ref_lla.alt - locationExtended.altitudeMeanSeaLevel);
  1678. }
  1679. else
  1680. {
  1681. length = snprintf(pMarker, lengthRemaining, ",");
  1682. }
  1683. if (length < 0 || length >= lengthRemaining)
  1684. {
  1685. LOC_LOGE("NMEA Error in string formatting");
  1686. return;
  1687. }
  1688. pMarker += length;
  1689. lengthRemaining -= length;
  1690. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DGNSS_DATA_AGE)
  1691. {
  1692. length = snprintf(pMarker, lengthRemaining, "%.1f,",
  1693. (float)locationExtended.dgnssDataAgeMsec / 1000);
  1694. }
  1695. else
  1696. {
  1697. length = snprintf(pMarker, lengthRemaining, ",");
  1698. }
  1699. if (length < 0 || length >= lengthRemaining)
  1700. {
  1701. LOC_LOGE("NMEA Error in string formatting");
  1702. return;
  1703. }
  1704. pMarker += length;
  1705. lengthRemaining -= length;
  1706. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DGNSS_REF_STATION_ID)
  1707. {
  1708. length = snprintf(pMarker, lengthRemaining, "%04d",
  1709. locationExtended.dgnssRefStationId);
  1710. if (length < 0 || length >= lengthRemaining)
  1711. {
  1712. LOC_LOGE("NMEA Error in string formatting");
  1713. return;
  1714. }
  1715. pMarker += length;
  1716. lengthRemaining -= length;
  1717. }
  1718. // hardcode Navigation Status field to 'V'
  1719. length = snprintf(pMarker, lengthRemaining, ",%c", 'V');
  1720. pMarker += length;
  1721. lengthRemaining -= length;
  1722. length = loc_nmea_put_checksum(sentence_GNS, sizeof(sentence_GNS), false);
  1723. }
  1724. // -------------------
  1725. // ------$--GGA-------
  1726. // -------------------
  1727. if (mEnabledNmeaTypes & NMEA_TYPE_GGA) {
  1728. pMarker = sentence_GGA;
  1729. lengthRemaining = sizeof(sentence_GGA);
  1730. length = snprintf(pMarker, lengthRemaining, "$%sGGA,%02d%02d%02d.%02d,",
  1731. talker, utcHours, utcMinutes, utcSeconds, utcMSeconds/10);
  1732. if (length < 0 || length >= lengthRemaining)
  1733. {
  1734. LOC_LOGE("NMEA Error in string formatting");
  1735. return;
  1736. }
  1737. pMarker += length;
  1738. lengthRemaining -= length;
  1739. if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)
  1740. {
  1741. double latitude = ref_lla.lat;
  1742. double longitude = ref_lla.lon;
  1743. char latHemisphere;
  1744. char lonHemisphere;
  1745. double latMinutes;
  1746. double lonMinutes;
  1747. if (latitude > 0)
  1748. {
  1749. latHemisphere = 'N';
  1750. }
  1751. else
  1752. {
  1753. latHemisphere = 'S';
  1754. latitude *= -1.0;
  1755. }
  1756. if (longitude < 0)
  1757. {
  1758. lonHemisphere = 'W';
  1759. longitude *= -1.0;
  1760. }
  1761. else
  1762. {
  1763. lonHemisphere = 'E';
  1764. }
  1765. latMinutes = fmod(latitude * 60.0, 60.0);
  1766. lonMinutes = fmod(longitude * 60.0, 60.0);
  1767. length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,",
  1768. (uint8_t)floor(latitude), latMinutes, latHemisphere,
  1769. (uint8_t)floor(longitude), lonMinutes, lonHemisphere);
  1770. }
  1771. else
  1772. {
  1773. length = snprintf(pMarker, lengthRemaining, ",,,,");
  1774. }
  1775. if (length < 0 || length >= lengthRemaining)
  1776. {
  1777. LOC_LOGE("NMEA Error in string formatting");
  1778. return;
  1779. }
  1780. pMarker += length;
  1781. lengthRemaining -= length;
  1782. // Number of satellites in use, 00-12
  1783. if (svUsedCount > MAX_SATELLITES_IN_USE)
  1784. svUsedCount = MAX_SATELLITES_IN_USE;
  1785. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP)
  1786. {
  1787. length = snprintf(pMarker, lengthRemaining, "%s,%02d,%.1f,",
  1788. ggaGpsQuality, svUsedCount, locationExtended.hdop);
  1789. } else if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_EXT_DOP) {
  1790. length = snprintf(pMarker, lengthRemaining, "%s,%02d,%.1f,",
  1791. ggaGpsQuality, svUsedCount, locationExtended.extDOP.HDOP);
  1792. }
  1793. else
  1794. { // no hdop
  1795. length = snprintf(pMarker, lengthRemaining, "%s,%02d,,",
  1796. ggaGpsQuality, svUsedCount);
  1797. }
  1798. if (length < 0 || length >= lengthRemaining)
  1799. {
  1800. LOC_LOGE("NMEA Error in string formatting");
  1801. return;
  1802. }
  1803. pMarker += length;
  1804. lengthRemaining -= length;
  1805. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL)
  1806. {
  1807. length = snprintf(pMarker, lengthRemaining, "%.1lf,M,",
  1808. locationExtended.altitudeMeanSeaLevel);
  1809. }
  1810. else
  1811. {
  1812. length = snprintf(pMarker, lengthRemaining, ",,");
  1813. }
  1814. if (length < 0 || length >= lengthRemaining)
  1815. {
  1816. LOC_LOGE("NMEA Error in string formatting");
  1817. return;
  1818. }
  1819. pMarker += length;
  1820. lengthRemaining -= length;
  1821. if ((location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_ALTITUDE) &&
  1822. (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL))
  1823. {
  1824. length = snprintf(pMarker, lengthRemaining, "%.1lf,M,",
  1825. ref_lla.alt - locationExtended.altitudeMeanSeaLevel);
  1826. }
  1827. else
  1828. {
  1829. length = snprintf(pMarker, lengthRemaining, ",,");
  1830. }
  1831. if (length < 0 || length >= lengthRemaining)
  1832. {
  1833. LOC_LOGE("NMEA Error in string formatting");
  1834. return;
  1835. }
  1836. pMarker += length;
  1837. lengthRemaining -= length;
  1838. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DGNSS_DATA_AGE)
  1839. {
  1840. length = snprintf(pMarker, lengthRemaining, "%.1f,",
  1841. (float)locationExtended.dgnssDataAgeMsec / 1000);
  1842. }
  1843. else
  1844. {
  1845. length = snprintf(pMarker, lengthRemaining, ",");
  1846. }
  1847. if (length < 0 || length >= lengthRemaining)
  1848. {
  1849. LOC_LOGE("NMEA Error in string formatting");
  1850. return;
  1851. }
  1852. pMarker += length;
  1853. lengthRemaining -= length;
  1854. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DGNSS_REF_STATION_ID)
  1855. {
  1856. length = snprintf(pMarker, lengthRemaining, "%04d",
  1857. locationExtended.dgnssRefStationId);
  1858. if (length < 0 || length >= lengthRemaining)
  1859. {
  1860. LOC_LOGE("NMEA Error in string formatting");
  1861. return;
  1862. }
  1863. pMarker += length;
  1864. lengthRemaining -= length;
  1865. }
  1866. length = loc_nmea_put_checksum(sentence_GGA, sizeof(sentence_GGA), false);
  1867. }
  1868. // ------$--DTM-------
  1869. nmeaArraystr.push_back(sentence_DTM);
  1870. // ------$--RMC-------
  1871. nmeaArraystr.push_back(sentence_RMC);
  1872. if(LOC_GNSS_DATUM_PZ90 == datum_type) {
  1873. // ------$--DTM-------
  1874. nmeaArraystr.push_back(sentence_DTM);
  1875. }
  1876. // ------$--GNS-------
  1877. nmeaArraystr.push_back(sentence_GNS);
  1878. if(LOC_GNSS_DATUM_PZ90 == datum_type) {
  1879. // ------$--DTM-------
  1880. nmeaArraystr.push_back(sentence_DTM);
  1881. }
  1882. // ------$--GGA-------
  1883. nmeaArraystr.push_back(sentence_GGA);
  1884. indexOfGGA = static_cast<int>(nmeaArraystr.size() - 1);
  1885. }
  1886. //Send blank NMEA reports for non-final fixes
  1887. else {
  1888. if (mEnabledNmeaTypes & NMEA_TYPE_GSA) {
  1889. strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,,", sizeof(sentence));
  1890. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1891. nmeaArraystr.push_back(sentence);
  1892. }
  1893. if (mEnabledNmeaTypes & NMEA_TYPE_VTG) {
  1894. strlcpy(sentence, "$GPVTG,,T,,M,,N,,K,N", sizeof(sentence));
  1895. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1896. nmeaArraystr.push_back(sentence);
  1897. }
  1898. if (mEnabledNmeaTypes & NMEA_TYPE_DTM) {
  1899. strlcpy(sentence, "$GPDTM,,,,,,,,", sizeof(sentence));
  1900. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1901. nmeaArraystr.push_back(sentence);
  1902. }
  1903. if (mEnabledNmeaTypes & NMEA_TYPE_RMC) {
  1904. strlcpy(sentence, "$GPRMC,,V,,,,,,,,,,N,V", sizeof(sentence));
  1905. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1906. nmeaArraystr.push_back(sentence);
  1907. }
  1908. if (mEnabledNmeaTypes & NMEA_TYPE_GNS) {
  1909. strlcpy(sentence, "$GPGNS,,,,,,N,,,,,,,V", sizeof(sentence));
  1910. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1911. nmeaArraystr.push_back(sentence);
  1912. }
  1913. if (mEnabledNmeaTypes & NMEA_TYPE_GGA) {
  1914. strlcpy(sentence, "$GPGGA,,,,,,0,,,,,,,,", sizeof(sentence));
  1915. length = loc_nmea_put_checksum(sentence, sizeof(sentence), false);
  1916. nmeaArraystr.push_back(sentence);
  1917. }
  1918. }
  1919. EXIT_LOG(%d, 0);
  1920. }
  1921. /*===========================================================================
  1922. FUNCTION loc_nmea_generate_sv
  1923. DESCRIPTION
  1924. Generate NMEA sentences generated based on sv report
  1925. DEPENDENCIES
  1926. NONE
  1927. RETURN VALUE
  1928. 0
  1929. SIDE EFFECTS
  1930. N/A
  1931. ===========================================================================*/
  1932. void loc_nmea_generate_sv(const GnssSvNotification &svNotify,
  1933. std::vector<std::string> &nmeaArraystr)
  1934. {
  1935. ENTRY_LOG();
  1936. char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0};
  1937. loc_sv_cache_info sv_cache_info = {};
  1938. //Count GPS SVs for saparating GPS from GLONASS and throw others
  1939. for (uint32_t svOffset = 0; svOffset < svNotify.count; svOffset++) {
  1940. if ((GNSS_SV_TYPE_GPS == svNotify.gnssSvs[svOffset].type) ||
  1941. (GNSS_SV_TYPE_SBAS == svNotify.gnssSvs[svOffset].type))
  1942. {
  1943. if (GNSS_SIGNAL_GPS_L5 == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) {
  1944. sv_cache_info.gps_l5_count++;
  1945. } else if (GNSS_SIGNAL_GPS_L2 == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) {
  1946. sv_cache_info.gps_l2_count++;
  1947. } else {
  1948. // GNSS_SIGNAL_GPS_L1CA, GNSS_SIGNAL_SBAS_L1 or default
  1949. // If no signal type in report, it means default L1
  1950. sv_cache_info.gps_l1_count++;
  1951. }
  1952. }
  1953. else if (GNSS_SV_TYPE_GLONASS == svNotify.gnssSvs[svOffset].type)
  1954. {
  1955. if (GNSS_SIGNAL_GLONASS_G2 == svNotify.gnssSvs[svOffset].gnssSignalTypeMask){
  1956. sv_cache_info.glo_g2_count++;
  1957. } else {
  1958. // GNSS_SIGNAL_GLONASS_G1 or default
  1959. // If no signal type in report, it means default G1
  1960. sv_cache_info.glo_g1_count++;
  1961. }
  1962. }
  1963. else if (GNSS_SV_TYPE_GALILEO == svNotify.gnssSvs[svOffset].type)
  1964. {
  1965. if(GNSS_SIGNAL_GALILEO_E5A == svNotify.gnssSvs[svOffset].gnssSignalTypeMask){
  1966. sv_cache_info.gal_e5_count++;
  1967. } else if (GNSS_SIGNAL_GALILEO_E5B == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) {
  1968. sv_cache_info.gal_e5b_count++;
  1969. } else {
  1970. // GNSS_SIGNAL_GALILEO_E1 or default
  1971. // If no signal type in report, it means default E1
  1972. sv_cache_info.gal_e1_count++;
  1973. }
  1974. }
  1975. else if (GNSS_SV_TYPE_QZSS == svNotify.gnssSvs[svOffset].type)
  1976. {
  1977. if (GNSS_SIGNAL_QZSS_L5 == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) {
  1978. sv_cache_info.qzss_l5_count++;
  1979. } else if (GNSS_SIGNAL_QZSS_L2 == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) {
  1980. sv_cache_info.qzss_l2_count++;
  1981. } else {
  1982. // GNSS_SIGNAL_QZSS_L1CA or default
  1983. // If no signal type in report, it means default L1
  1984. sv_cache_info.qzss_l1_count++;
  1985. }
  1986. }
  1987. else if (GNSS_SV_TYPE_BEIDOU == svNotify.gnssSvs[svOffset].type)
  1988. {
  1989. // cache the used in fix mask, as it will be needed to send $PQGSA
  1990. // during the position report
  1991. if (GNSS_SV_OPTIONS_USED_IN_FIX_BIT ==
  1992. (svNotify.gnssSvs[svOffset].gnssSvOptionsMask &
  1993. GNSS_SV_OPTIONS_USED_IN_FIX_BIT))
  1994. {
  1995. setSvMask(sv_cache_info.bds_used_mask, svNotify.gnssSvs[svOffset].svId);
  1996. }
  1997. if ((GNSS_SIGNAL_BEIDOU_B2AI == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) ||
  1998. (GNSS_SIGNAL_BEIDOU_B2AQ == svNotify.gnssSvs[svOffset].gnssSignalTypeMask)) {
  1999. sv_cache_info.bds_b2_count++;
  2000. } else if (GNSS_SIGNAL_BEIDOU_B1C == svNotify.gnssSvs[svOffset].gnssSignalTypeMask) {
  2001. sv_cache_info.bds_b1c_count++;
  2002. } else {
  2003. // GNSS_SIGNAL_BEIDOU_B1I or default
  2004. // If no signal type in report, it means default B1I
  2005. sv_cache_info.bds_b1i_count++;
  2006. }
  2007. }
  2008. else if (GNSS_SV_TYPE_NAVIC == svNotify.gnssSvs[svOffset].type)
  2009. {
  2010. // GNSS_SIGNAL_NAVIC_L5 is the only signal type for NAVIC
  2011. sv_cache_info.navic_l5_count++;
  2012. }
  2013. }
  2014. loc_nmea_sv_meta sv_meta;
  2015. if (mEnabledNmeaTypes & NMEA_TYPE_GPGSV) {
  2016. // ---------------------
  2017. // ------$GPGSV:L1CA----
  2018. // ---------------------
  2019. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2020. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GPS,
  2021. GNSS_SIGNAL_GPS_L1CA, false), nmeaArraystr);
  2022. // ---------------------
  2023. // ------$GPGSV:L5------
  2024. // ---------------------
  2025. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2026. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GPS,
  2027. GNSS_SIGNAL_GPS_L5, false), nmeaArraystr);
  2028. // ---------------------
  2029. // ------$GPGSV:L2------
  2030. // ---------------------
  2031. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2032. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GPS,
  2033. GNSS_SIGNAL_GPS_L2, false), nmeaArraystr);
  2034. }
  2035. if (mEnabledNmeaTypes & NMEA_TYPE_GLGSV) {
  2036. // ---------------------
  2037. // ------$GLGSV:G1------
  2038. // ---------------------
  2039. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2040. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GLONASS,
  2041. GNSS_SIGNAL_GLONASS_G1, false), nmeaArraystr);
  2042. // ---------------------
  2043. // ------$GLGSV:G2------
  2044. // ---------------------
  2045. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2046. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GLONASS,
  2047. GNSS_SIGNAL_GLONASS_G2, false), nmeaArraystr);
  2048. }
  2049. if (mEnabledNmeaTypes & NMEA_TYPE_GAGSV) {
  2050. // ---------------------
  2051. // ------$GAGSV:E1------
  2052. // ---------------------
  2053. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2054. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GALILEO,
  2055. GNSS_SIGNAL_GALILEO_E1, false), nmeaArraystr);
  2056. // -------------------------
  2057. // ------$GAGSV:E5A---------
  2058. // -------------------------
  2059. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2060. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GALILEO,
  2061. GNSS_SIGNAL_GALILEO_E5A, false), nmeaArraystr);
  2062. // -------------------------
  2063. // ------$GAGSV:E5B---------
  2064. // -------------------------
  2065. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2066. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_GALILEO,
  2067. GNSS_SIGNAL_GALILEO_E5B, false), nmeaArraystr);
  2068. }
  2069. if (mEnabledNmeaTypes & NMEA_TYPE_GQGSV) {
  2070. // -----------------------------
  2071. // ------$GQGSV (QZSS):L1CA-----
  2072. // -----------------------------
  2073. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2074. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_QZSS,
  2075. GNSS_SIGNAL_QZSS_L1CA, false), nmeaArraystr);
  2076. // -----------------------------
  2077. // ------$GQGSV (QZSS):L5-------
  2078. // -----------------------------
  2079. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2080. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_QZSS,
  2081. GNSS_SIGNAL_QZSS_L5, false), nmeaArraystr);
  2082. // -----------------------------
  2083. // ------$GQGSV (QZSS):L2-------
  2084. // -----------------------------
  2085. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2086. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_QZSS,
  2087. GNSS_SIGNAL_QZSS_L2, false), nmeaArraystr);
  2088. }
  2089. if (mEnabledNmeaTypes & NMEA_TYPE_GBGSV) {
  2090. // -----------------------------
  2091. // ------$GBGSV (BEIDOU:B1I)----
  2092. // -----------------------------
  2093. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2094. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_BEIDOU,
  2095. GNSS_SIGNAL_BEIDOU_B1I, false), nmeaArraystr);
  2096. // -----------------------------
  2097. // ------$GBGSV (BEIDOU:B1C)----
  2098. // -----------------------------
  2099. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2100. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_BEIDOU,
  2101. GNSS_SIGNAL_BEIDOU_B1C, false), nmeaArraystr);
  2102. // -----------------------------
  2103. // ------$GBGSV (BEIDOU:B2AI)---
  2104. // -----------------------------
  2105. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2106. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_BEIDOU,
  2107. GNSS_SIGNAL_BEIDOU_B2AI, false), nmeaArraystr);
  2108. }
  2109. // -----------------------------
  2110. // ------$GIGSV (NAVIC:L5)------
  2111. // -----------------------------
  2112. if (mEnabledNmeaTypes & NMEA_TYPE_GIGSV) {
  2113. loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence),
  2114. loc_nmea_sv_meta_init(sv_meta, sv_cache_info, GNSS_SV_TYPE_NAVIC,
  2115. GNSS_SIGNAL_NAVIC_L5, false), nmeaArraystr);
  2116. }
  2117. EXIT_LOG(%d, 0);
  2118. }
  2119. /*===========================================================================
  2120. FUNCTION loc_nmea_config_output_types
  2121. DESCRIPTION
  2122. Configure the NMEA sentence types that will be generated.
  2123. DEPENDENCIES
  2124. NONE
  2125. RETURN VALUE
  2126. NONE
  2127. SIDE EFFECTS
  2128. N/A
  2129. ===========================================================================*/
  2130. void loc_nmea_config_output_types(GnssNmeaTypesMask enabledNmeaTypes) {
  2131. mEnabledNmeaTypes = enabledNmeaTypes;
  2132. }