driver_nmea0183.c 149 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284
  1. /*
  2. * This file is Copyright 2010 by the GPSD project
  3. * SPDX-License-Identifier: BSD-2-clause
  4. */
  5. #include "../include/gpsd_config.h" // must be before all includes
  6. #include <ctype.h> // for isdigit()
  7. #include <float.h> // for FLT_EVAL_METHOD
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <stdbool.h>
  11. #include <math.h>
  12. #include <string.h>
  13. #include <stdarg.h>
  14. #include <time.h>
  15. #include "../include/gpsd.h"
  16. #include "../include/strfuncs.h"
  17. #include "../include/timespec.h"
  18. /**************************************************************************
  19. *
  20. * Parser helpers begin here
  21. *
  22. **************************************************************************/
  23. /* Allow avoiding long double intermediate values.
  24. *
  25. * On platforms with FLT_EVAL_METHOD >=2 (currently only 32-bit OpenBSD),
  26. * intermediate values may be kept as long doubles. Although this is in
  27. * principle more accurate, it can cause slight differences that lead to
  28. * regression failures. Storing such values in volatile variables avoids
  29. * this. Where the volatile declaration is unnessary (and absent), such
  30. * extra variables are normally optimized out.
  31. */
  32. #if FLT_EVAL_METHOD >= 2
  33. #define FLT_VOLATILE volatile
  34. #else
  35. #define FLT_VOLATILE
  36. #endif // FLT_EVAL_METHOD
  37. /* Common lat/lon decoding for do_lat_lon
  38. *
  39. * This version avoids the use of modf(), which can be slow and also suffers
  40. * from exactness problems. The integer minutes are first extracted and
  41. * corrected for the improper degree scaling, using integer arithmetic.
  42. * Then the fractional minutes are added as a double, and the result is scaled
  43. * to degrees, using multiply which is faster than divide.
  44. *
  45. * Forcing the intermediate minutes value to a double is sufficient to
  46. * avoid regression problems with FLT_EVAL_METHOD>=2.
  47. */
  48. static inline double decode_lat_or_lon(const char *field)
  49. {
  50. long degrees, minutes;
  51. FLT_VOLATILE double full_minutes;
  52. char *cp;
  53. // Get integer "minutes"
  54. minutes = strtol(field, &cp, 10);
  55. // Must have decimal point
  56. if (*cp != '.') return NAN;
  57. // Extract degrees (scaled by 100)
  58. degrees = minutes / 100;
  59. // Rescale degrees to normal factor of 60
  60. minutes -= degrees * (100 - 60);
  61. // Add fractional minutes
  62. full_minutes = minutes + safe_atof(cp);
  63. // Scale to degrees & return
  64. return full_minutes * (1.0 / 60.0);
  65. }
  66. /* process a pair of latitude/longitude fields starting at field index BEGIN
  67. * The input fields look like this:
  68. * field[0]: 4404.1237962
  69. * field[1]: N
  70. * field[2]: 12118.8472460
  71. * field[3]: W
  72. * input format of lat/lon is NMEA style DDDMM.mmmmmmm
  73. * yes, 7 digits of precision past the decimal point from survey grade GPS
  74. *
  75. * Ignoring the complications ellipsoids add:
  76. * 1 minute latitude = 1853 m
  77. * 0.001 minute latitude = 1.853 m
  78. * 0.000001 minute latitude = 0.001853 m = 1.853 mm
  79. * 0.0000001 minute latitude = 0.0001853 m = 0.1853 mm
  80. *
  81. * return: 0 == OK, non zero is failure.
  82. */
  83. static int do_lat_lon(char *field[], struct gps_fix_t *out)
  84. {
  85. double lon;
  86. double lat;
  87. if ('\0' == field[0][0] ||
  88. '\0' == field[1][0] ||
  89. '\0' == field[2][0] ||
  90. '\0' == field[3][0]) {
  91. return 1;
  92. }
  93. lat = decode_lat_or_lon(field[0]);
  94. if ('S' == field[1][0])
  95. lat = -lat;
  96. lon = decode_lat_or_lon(field[2]);
  97. if ('W' == field[3][0])
  98. lon = -lon;
  99. if (0 == isfinite(lat) ||
  100. 0 == isfinite(lon)) {
  101. return 2;
  102. }
  103. out->latitude = lat;
  104. out->longitude = lon;
  105. return 0;
  106. }
  107. /* process an FAA mode character
  108. * return status as in session->newdata.status
  109. */
  110. static int faa_mode(char mode)
  111. {
  112. int newstatus = STATUS_GPS;
  113. switch (mode) {
  114. case '\0': // missing
  115. newstatus = STATUS_UNK;
  116. break;
  117. case 'A': // Autonomous
  118. default:
  119. newstatus = STATUS_GPS;
  120. break;
  121. case 'C': // Quectel unique: Caution
  122. newstatus = STATUS_UNK;
  123. break;
  124. case 'D': // Differential
  125. newstatus = STATUS_DGPS;
  126. break;
  127. case 'E': // Estimated dead reckoning
  128. newstatus = STATUS_DR;
  129. break;
  130. case 'F': // Float RTK
  131. newstatus = STATUS_RTK_FLT;
  132. break;
  133. case 'N': // Data Not Valid
  134. // already handled, for paranoia sake also here
  135. newstatus = STATUS_UNK;
  136. break;
  137. case 'P': // Precise (NMEA 4+)
  138. newstatus = STATUS_DGPS; // sort of DGPS
  139. break;
  140. case 'R': // fixed RTK
  141. newstatus = STATUS_RTK_FIX;
  142. break;
  143. case 'M': // manual input. Same as simulated? Or surveyed?
  144. FALLTHROUGH
  145. case 'S': // simulator
  146. newstatus = STATUS_SIM;
  147. break;
  148. case 'U': // Quectel unique: Unsafe
  149. newstatus = STATUS_UNK;
  150. break;
  151. }
  152. return newstatus;
  153. }
  154. /**************************************************************************
  155. *
  156. * Scary timestamp fudging begins here
  157. *
  158. * Four sentences, GGA and GLL and RMC and ZDA, contain timestamps.
  159. * GGA/GLL/RMC timestamps look like hhmmss.ss, with the trailing .ss,
  160. * or .sss, part optional.
  161. * RMC has a date field, in the format ddmmyy. ZDA has separate fields
  162. * for day/month/year, with a 4-digit year. This means that for RMC we
  163. * must supply a century and for GGA and GLL we must supply a century,
  164. * year, and day. We get the missing data from a previous RMC or ZDA;
  165. * century in RMC is supplied from the daemon's context (initialized at
  166. * startup time) if there has been no previous ZDA.
  167. *
  168. **************************************************************************/
  169. #define DD(s) ((int)((s)[0]-'0')*10+(int)((s)[1]-'0'))
  170. /* sentence supplied ddmmyy, but no century part
  171. *
  172. * return: 0 == OK, greater than zero on failure
  173. */
  174. static int merge_ddmmyy(char *ddmmyy, struct gps_device_t *session)
  175. {
  176. int yy;
  177. int mon;
  178. int mday;
  179. int year;
  180. unsigned i; // NetBSD complains about signed array index
  181. if (NULL == ddmmyy) {
  182. return 1;
  183. }
  184. for (i = 0; i < 6; i++) {
  185. // NetBSD 6 wants the cast
  186. if (0 == isdigit((int)ddmmyy[i])) {
  187. // catches NUL and non-digits
  188. // Telit HE910 can set year to "-1" (1999 - 2000)
  189. GPSD_LOG(LOG_WARN, &session->context->errout,
  190. "NMEA0183: merge_ddmmyy(%s), malformed date\n", ddmmyy);
  191. return 2;
  192. }
  193. }
  194. // check for termination
  195. if ('\0' != ddmmyy[6]) {
  196. // missing NUL
  197. GPSD_LOG(LOG_WARN, &session->context->errout,
  198. "NMEA0183: merge_ddmmyy(%s), malformed date\n", ddmmyy);
  199. return 3;
  200. }
  201. // should be no defects left to segfault DD()
  202. yy = DD(ddmmyy + 4);
  203. mon = DD(ddmmyy + 2);
  204. mday = DD(ddmmyy);
  205. // check for century wrap
  206. if (session->nmea.date.tm_year % 100 == 99 && yy == 0)
  207. gpsd_century_update(session, session->context->century + 100);
  208. year = (session->context->century + yy);
  209. /* 32 bit systems will break in 2038.
  210. * Telix fails on GPS rollover to 2099, which 32 bit system
  211. * can not handle. So wrap at 2080. That way 64 bit systems
  212. * work until 2080, and 2099 gets reported as 1999.
  213. * since GPS epoch started in 1980, allows for old NMEA to work.
  214. */
  215. if (2080 <= year) {
  216. year -= 100;
  217. }
  218. if ( (1 > mon ) || (12 < mon ) ) {
  219. GPSD_LOG(LOG_WARN, &session->context->errout,
  220. "NMEA0183: merge_ddmmyy(%s), malformed month\n", ddmmyy);
  221. return 4;
  222. } else if ( (1 > mday ) || (31 < mday ) ) {
  223. GPSD_LOG(LOG_WARN, &session->context->errout,
  224. "NMEA0183: merge_ddmmyy(%s), malformed day\n", ddmmyy);
  225. return 5;
  226. } else {
  227. GPSD_LOG(LOG_DATA, &session->context->errout,
  228. "NMEA0183: merge_ddmmyy(%s) sets year %d\n",
  229. ddmmyy, year);
  230. session->nmea.date.tm_year = year - 1900;
  231. session->nmea.date.tm_mon = mon - 1;
  232. session->nmea.date.tm_mday = mday;
  233. }
  234. GPSD_LOG(LOG_RAW, &session->context->errout,
  235. "NMEA0183: merge_ddmmyy(%s) %d %d %d\n",
  236. ddmmyy,
  237. session->nmea.date.tm_mon,
  238. session->nmea.date.tm_mday,
  239. session->nmea.date.tm_year);
  240. return 0;
  241. }
  242. /* decode an hhmmss.ss string into struct tm data and nsecs
  243. *
  244. * return: 0 == OK, otherwise failure
  245. */
  246. static int decode_hhmmss(struct tm *date, long *nsec, char *hhmmss,
  247. struct gps_device_t *session)
  248. {
  249. int old_hour = date->tm_hour;
  250. int i;
  251. if (NULL == hhmmss) {
  252. return 1;
  253. }
  254. for (i = 0; i < 6; i++) {
  255. // NetBSD 6 wants the cast
  256. if (0 == isdigit((int)hhmmss[i])) {
  257. // catches NUL and non-digits
  258. GPSD_LOG(LOG_WARN, &session->context->errout,
  259. "NMEA0183: decode_hhmmss(%s), malformed time\n", hhmmss);
  260. return 2;
  261. }
  262. }
  263. // don't check for termination, might have fractional seconds
  264. date->tm_hour = DD(hhmmss);
  265. if (date->tm_hour < old_hour) // midnight wrap
  266. date->tm_mday++;
  267. date->tm_min = DD(hhmmss + 2);
  268. date->tm_sec = DD(hhmmss + 4);
  269. if ('.' == hhmmss[6] &&
  270. // NetBSD 6 wants the cast
  271. 0 != isdigit((int)hhmmss[7])) {
  272. // codacy hates strlen()
  273. int sublen = strnlen(hhmmss + 7, 20);
  274. i = atoi(hhmmss + 7);
  275. *nsec = (long)i * (long)pow(10.0, 9 - sublen);
  276. } else {
  277. *nsec = 0;
  278. }
  279. return 0;
  280. }
  281. /* update from a UTC time
  282. *
  283. * return: 0 == OK, greater than zero on failure
  284. */
  285. static int merge_hhmmss(char *hhmmss, struct gps_device_t *session)
  286. {
  287. int old_hour = session->nmea.date.tm_hour;
  288. int i;
  289. if (NULL == hhmmss) {
  290. return 1;
  291. }
  292. for (i = 0; i < 6; i++) {
  293. // NetBSD 6 wants the cast
  294. if (0 == isdigit((int)hhmmss[i])) {
  295. // catches NUL and non-digits
  296. GPSD_LOG(LOG_WARN, &session->context->errout,
  297. "NMEA0183: merge_hhmmss(%s), malformed time\n", hhmmss);
  298. return 2;
  299. }
  300. }
  301. // don't check for termination, might have fractional seconds
  302. session->nmea.date.tm_hour = DD(hhmmss);
  303. if (session->nmea.date.tm_hour < old_hour) // midnight wrap
  304. session->nmea.date.tm_mday++;
  305. session->nmea.date.tm_min = DD(hhmmss + 2);
  306. session->nmea.date.tm_sec = DD(hhmmss + 4);
  307. session->nmea.subseconds.tv_sec = 0;
  308. if ('.' == hhmmss[6] &&
  309. // NetBSD 6 wants the cast
  310. 0 != isdigit((int)hhmmss[7])) {
  311. // codacy hates strlen()
  312. int sublen = strnlen(hhmmss + 7, 20);
  313. i = atoi(hhmmss + 7);
  314. session->nmea.subseconds.tv_nsec = (long)i *
  315. (long)pow(10.0, 9 - sublen);
  316. } else {
  317. session->nmea.subseconds.tv_nsec = 0;
  318. }
  319. return 0;
  320. }
  321. static void register_fractional_time(const char *tag, const char *fld,
  322. struct gps_device_t *session)
  323. {
  324. if (fld[0] != '\0') {
  325. char ts_buf[TIMESPEC_LEN];
  326. session->nmea.last_frac_time = session->nmea.this_frac_time;
  327. DTOTS(&session->nmea.this_frac_time, safe_atof(fld));
  328. session->nmea.latch_frac_time = true;
  329. GPSD_LOG(LOG_DATA, &session->context->errout,
  330. "NMEA0183: %s: registers fractional time %s\n",
  331. tag,
  332. timespec_str(&session->nmea.this_frac_time, ts_buf,
  333. sizeof(ts_buf)));
  334. }
  335. }
  336. /**************************************************************************
  337. *
  338. * NMEA sentence handling begins here
  339. *
  340. **************************************************************************/
  341. static gps_mask_t processACCURACY(int c UNUSED, char *field[],
  342. struct gps_device_t *session)
  343. {
  344. /*
  345. * $GPACCURACY,961.2*04
  346. *
  347. * ACCURACY,x.x*hh<cr><lf>
  348. *
  349. * The only data field is "accuracy".
  350. * The MT3333 manual just says "The smaller the number is, the be better"
  351. */
  352. gps_mask_t mask = ONLINE_SET;
  353. if ('\0' == field[1][0]) {
  354. // no data
  355. return mask;
  356. }
  357. GPSD_LOG(LOG_DATA, &session->context->errout,
  358. "NMEA0183: $GPACCURACY: %10s.\n", field[1]);
  359. return mask;
  360. }
  361. // BWC - Bearing and Distance to Waypoint - Great Circle
  362. static gps_mask_t processBWC(int count, char *field[],
  363. struct gps_device_t *session)
  364. {
  365. /*
  366. * GPBWC,220516,5130.02,N,00046.34,W,213.8,T,218.0,M,0004.6,N,EGLM*11
  367. *
  368. * 1. UTC Time, hh is hours, mm is minutes, ss.ss is seconds
  369. * 2. Waypoint Latitude
  370. * 3. N = North, S = South
  371. * 4. Waypoint Longitude
  372. * 5. E = East, W = West
  373. * 6. Bearing, degrees True
  374. * 7. T = True
  375. * 8. Bearing, degrees Magnetic
  376. * 9. M = Magnetic
  377. * 10. Distance, Nautical Miles
  378. * 11. N = Nautical Miles
  379. * 12. Waypoint ID
  380. * 13. FAA mode indicator (NMEA 2.3 and later, optional)
  381. * 14. Checksum
  382. *
  383. * Parse this just to get the time, to help the cycle ender
  384. */
  385. gps_mask_t mask = ONLINE_SET;
  386. if ('\0' != field[1][0]) {
  387. if (0 == merge_hhmmss(field[1], session)) {
  388. if (0 == session->nmea.date.tm_year) {
  389. GPSD_LOG(LOG_WARN, &session->context->errout,
  390. "NMEA0183: can't use BWC time until after ZDA or RMC"
  391. " has supplied a year.\n");
  392. } else {
  393. mask = TIME_SET;
  394. }
  395. }
  396. }
  397. if (12 <= count) {
  398. // NMEA 2.3 and later
  399. session->newdata.status = faa_mode(field[12][0]);
  400. }
  401. GPSD_LOG(LOG_DATA, &session->context->errout,
  402. "NMEA0183: BWC: hhmmss=%s faa mode %d\n",
  403. field[1], session->newdata.status);
  404. return mask;
  405. }
  406. /* process xxVTG
  407. * $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K
  408. * $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K,A
  409. *
  410. * where:
  411. * 1,2 054.7,T True track made good (degrees)
  412. * 3,4 034.4,M Magnetic track made good
  413. * 5,6 005.5,N Ground speed, knots
  414. * 7,8 010.2,K Ground speed, Kilometers per hour
  415. * 9 A Mode Indicator (optional)
  416. * see faa_mode() for possible mode values
  417. *
  418. * see also:
  419. * https://gpsd.gitlab.io/gpsd/NMEA.html#_vtg_track_made_good_and_ground_speed
  420. */
  421. static gps_mask_t processVTG(int count,
  422. char *field[],
  423. struct gps_device_t *session)
  424. {
  425. gps_mask_t mask = ONLINE_SET;
  426. if( (field[1][0] == '\0') || (field[5][0] == '\0')){
  427. return mask;
  428. }
  429. // ignore empty/missing field, fix mode of last resort
  430. if ((9 < count) &&
  431. ('\0' != field[9][0])) {
  432. switch (field[9][0]) {
  433. case 'A':
  434. // Autonomous, 2D or 3D fix
  435. FALLTHROUGH
  436. case 'D':
  437. // Differential, 2D or 3D fix
  438. // MODE_SET here causes issues
  439. // mask |= MODE_SET;
  440. break;
  441. case 'E':
  442. // Estimated, DR only
  443. FALLTHROUGH
  444. case 'N':
  445. // Not Valid
  446. // MODE_SET here causes issues
  447. // mask |= MODE_SET;
  448. // nothing to use here, leave
  449. return mask;
  450. default:
  451. // Huh?
  452. break;
  453. }
  454. }
  455. // set true track
  456. session->newdata.track = safe_atof(field[1]);
  457. mask |= TRACK_SET;
  458. // set magnetic variation
  459. if (field[3][0] != '\0'){ // ignore empty fields
  460. session->newdata.magnetic_track = safe_atof(field[3]);
  461. mask |= MAGNETIC_TRACK_SET;
  462. }
  463. session->newdata.speed = safe_atof(field[5]) * KNOTS_TO_MPS;
  464. mask |= SPEED_SET;
  465. GPSD_LOG(LOG_DATA, &session->context->errout,
  466. "NMEA0183: VTG: course(T)=%.2f, course(M)=%.2f, speed=%.2f",
  467. session->newdata.track, session->newdata.magnetic_track,
  468. session->newdata.speed);
  469. return mask;
  470. }
  471. // Recommend Minimum Course Specific GPS/TRANSIT Data
  472. static gps_mask_t processRMC(int count, char *field[],
  473. struct gps_device_t *session)
  474. {
  475. /*
  476. * RMC,225446.33,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E,A*68
  477. * 1 225446.33 Time of fix 22:54:46 UTC
  478. * 2 A Status of Fix:
  479. * A = Autonomous, valid;
  480. * D = Differential, valid;
  481. * V = invalid
  482. * 3,4 4916.45,N Latitude 49 deg. 16.45 min North
  483. * 5,6 12311.12,W Longitude 123 deg. 11.12 min West
  484. * 7 000.5 Speed over ground, Knots
  485. * 8 054.7 Course Made Good, True north
  486. * 9 181194 Date of fix ddmmyy. 18 November 1994
  487. * 10,11 020.3,E Magnetic variation 20.3 deg East
  488. * 12 A FAA mode indicator (NMEA 2.3 and later)
  489. * see faa_mode() for possible mode values
  490. * 13 V Nav Status (NMEA 4.1 and later)
  491. * A=autonomous,
  492. * D=differential,
  493. * E=Estimated,
  494. * M=Manual input mode
  495. * N=not valid,
  496. * S=Simulator,
  497. * V=Valid
  498. * *68 mandatory nmea_checksum
  499. *
  500. * SiRF chipsets don't return either Mode Indicator or magnetic variation.
  501. */
  502. gps_mask_t mask = ONLINE_SET;
  503. char status = field[2][0];
  504. int newstatus;
  505. switch (status) {
  506. default:
  507. // missing
  508. FALLTHROUGH
  509. case 'V':
  510. // Invalid
  511. session->newdata.mode = MODE_NO_FIX;
  512. if ('\0' == field[1][0] ||
  513. '\0' == field[9][0]) {
  514. /* No time available. That breaks cycle end detector
  515. * Force report to bypass cycle detector and get report out.
  516. * To handle Querks (Quectel) like this:
  517. * $GPRMC,,V,,,,,,,,,,N*53
  518. */
  519. memset(&session->nmea.date, 0, sizeof(session->nmea.date));
  520. session->cycle_end_reliable = false;
  521. mask |= REPORT_IS | TIME_SET;
  522. }
  523. mask |= STATUS_SET | MODE_SET;
  524. break;
  525. case 'D':
  526. /* Differential Fix
  527. * STATUS_DGPS set below, after lat/lon check */
  528. FALLTHROUGH
  529. case 'A':
  530. // Valid Fix
  531. /*
  532. * The MTK3301, Royaltek RGM-3800, and possibly other
  533. * devices deliver bogus time values when the navigation
  534. * warning bit is set.
  535. */
  536. if ('\0' != field[1][0] &&
  537. 9 < count &&
  538. '\0' != field[9][0]) {
  539. if (0 == merge_hhmmss(field[1], session) &&
  540. 0 == merge_ddmmyy(field[9], session)) {
  541. // got a good data/time
  542. mask |= TIME_SET;
  543. register_fractional_time(field[0], field[1], session);
  544. }
  545. }
  546. // else, no point to the time only case, no regressions with that
  547. if (0 == do_lat_lon(&field[3], &session->newdata)) {
  548. if ('D' == status) {
  549. newstatus = STATUS_DGPS;
  550. } else {
  551. newstatus = STATUS_GPS;
  552. }
  553. mask |= LATLON_SET;
  554. if (MODE_2D >= session->lastfix.mode) {
  555. /* we have at least a 2D fix
  556. * might cause blinking */
  557. session->newdata.mode = MODE_2D;
  558. mask |= MODE_SET;
  559. } else if (MODE_3D == session->lastfix.mode) {
  560. // keep the 3D, this may be cycle starter
  561. // might cause blinking
  562. session->newdata.mode = MODE_3D;
  563. mask |= MODE_SET;
  564. }
  565. } else {
  566. newstatus = STATUS_UNK;
  567. session->newdata.mode = MODE_NO_FIX;
  568. mask |= MODE_SET;
  569. }
  570. if ('\0' != field[7][0]) {
  571. session->newdata.speed = safe_atof(field[7]) * KNOTS_TO_MPS;
  572. mask |= SPEED_SET;
  573. }
  574. if ('\0' != field[8][0]) {
  575. session->newdata.track = safe_atof(field[8]);
  576. mask |= TRACK_SET;
  577. }
  578. // get magnetic variation
  579. if ('\0' != field[10][0] &&
  580. '\0' != field[11][0]) {
  581. session->newdata.magnetic_var = safe_atof(field[10]);
  582. switch (field[11][0]) {
  583. case 'E':
  584. // no change
  585. break;
  586. case 'W':
  587. session->newdata.magnetic_var = -session->newdata.magnetic_var;
  588. break;
  589. default:
  590. // huh?
  591. session->newdata.magnetic_var = NAN;
  592. break;
  593. }
  594. if (0 == isfinite(session->newdata.magnetic_var) ||
  595. 0.09 >= fabs(session->newdata.magnetic_var)) {
  596. // some GPS set 0.0,E, or 0,w instead of blank
  597. session->newdata.magnetic_var = NAN;
  598. } else {
  599. mask |= MAGNETIC_TRACK_SET;
  600. }
  601. }
  602. if (12 <= count) {
  603. newstatus = faa_mode(field[12][0]);
  604. /* QUectel uses
  605. * S = Safe (s/b Simulated)
  606. * C = Caution (not NMEA)
  607. * U = Unsafe (not NMEA)
  608. * V = Invalid
  609. */
  610. }
  611. /*
  612. * This copes with GPSes like the Magellan EC-10X that *only* emit
  613. * GPRMC. In this case we set mode and status here so the client
  614. * code that relies on them won't mistakenly believe it has never
  615. * received a fix.
  616. */
  617. if (3 < session->gpsdata.satellites_used) {
  618. // 4 sats used means 3D
  619. session->newdata.mode = MODE_3D;
  620. mask |= MODE_SET;
  621. } else if (0 != isfinite(session->gpsdata.fix.altHAE) ||
  622. 0 != isfinite(session->gpsdata.fix.altMSL)) {
  623. /* we probably have at least a 3D fix
  624. * this handles old GPS that do not report 3D */
  625. session->newdata.mode = MODE_3D;
  626. mask |= MODE_SET;
  627. }
  628. session->newdata.status = newstatus;
  629. mask |= STATUS_SET;
  630. }
  631. GPSD_LOG(LOG_DATA, &session->context->errout,
  632. "NMEA0183: RMC: ddmmyy=%s hhmmss=%s lat=%.2f lon=%.2f "
  633. "speed=%.2f track=%.2f mode=%d var=%.1f status=%d\n",
  634. field[9], field[1],
  635. session->newdata.latitude,
  636. session->newdata.longitude,
  637. session->newdata.speed,
  638. session->newdata.track,
  639. session->newdata.mode,
  640. session->newdata.magnetic_var,
  641. session->newdata.status);
  642. return mask;
  643. }
  644. // Geographic position - Latitude, Longitude
  645. static gps_mask_t processGLL(int count, char *field[],
  646. struct gps_device_t *session)
  647. {
  648. /* Introduced in NMEA 3.0.
  649. *
  650. * $GPGLL,4916.45,N,12311.12,W,225444,A,A*5C
  651. *
  652. * 1,2: 4916.46,N Latitude 49 deg. 16.45 min. North
  653. * 3,4: 12311.12,W Longitude 123 deg. 11.12 min. West
  654. * 5: 225444 Fix taken at 22:54:44 UTC
  655. * 6: A Data valid
  656. * 7: A Autonomous mode
  657. * 8: *5C Mandatory NMEA checksum
  658. *
  659. * 1,2 Latitude, N (North) or S (South)
  660. * 3,4 Longitude, E (East) or W (West)
  661. * 5 UTC of position
  662. * 6 A = Active, V = Invalid data
  663. * 7 Mode Indicator
  664. * See faa_mode() for possible mode values.
  665. *
  666. * I found a note at <http://www.secoh.ru/windows/gps/nmfqexep.txt>
  667. * indicating that the Garmin 65 does not return time and status.
  668. * SiRF chipsets don't return the Mode Indicator.
  669. * This code copes gracefully with both quirks.
  670. *
  671. * Unless you care about the FAA indicator, this sentence supplies nothing
  672. * that GPRMC doesn't already. But at least two (Garmin GPS 48 and
  673. * Magellan Triton 400) actually ship updates in GLL that aren't redundant.
  674. *
  675. */
  676. char *status = field[7];
  677. gps_mask_t mask = ONLINE_SET;
  678. if (field[5][0] != '\0') {
  679. if (0 == merge_hhmmss(field[5], session)) {
  680. register_fractional_time(field[0], field[5], session);
  681. if (session->nmea.date.tm_year == 0)
  682. GPSD_LOG(LOG_WARN, &session->context->errout,
  683. "NMEA0183: can't use GLL time until after ZDA or RMC"
  684. " has supplied a year.\n");
  685. else {
  686. mask = TIME_SET;
  687. }
  688. }
  689. }
  690. if ('\0' == field[6][0] ||
  691. 'V' == field[6][0]) {
  692. // Invalid
  693. session->newdata.status = STATUS_UNK;
  694. session->newdata.mode = MODE_NO_FIX;
  695. } else if ('A' == field[6][0] &&
  696. (count < 8 || *status != 'N') &&
  697. 0 == do_lat_lon(&field[1], &session->newdata)) {
  698. int newstatus;
  699. mask |= LATLON_SET;
  700. newstatus = STATUS_GPS;
  701. if (count >= 8) {
  702. newstatus = faa_mode(*status);
  703. }
  704. /*
  705. * This is a bit dodgy. Technically we shouldn't set the mode
  706. * bit until we see GSA, or similar. But it may be later in the
  707. * cycle, some devices like the FV-18 don't send it by default,
  708. * and elsewhere in the code we want to be able to test for the
  709. * presence of a valid fix with mode > MODE_NO_FIX.
  710. */
  711. if (0 != isfinite(session->gpsdata.fix.altHAE) ||
  712. 0 != isfinite(session->gpsdata.fix.altMSL)) {
  713. session->newdata.mode = MODE_3D;
  714. } else if (3 < session->gpsdata.satellites_used) {
  715. // 4 sats used means 3D
  716. session->newdata.mode = MODE_3D;
  717. } else if (MODE_2D > session->gpsdata.fix.mode ||
  718. (0 == isfinite(session->oldfix.altHAE) &&
  719. 0 == isfinite(session->oldfix.altMSL))) {
  720. session->newdata.mode = MODE_2D;
  721. }
  722. session->newdata.status = newstatus;
  723. } else {
  724. session->newdata.status = STATUS_UNK;
  725. session->newdata.mode = MODE_NO_FIX;
  726. }
  727. mask |= STATUS_SET | MODE_SET;
  728. GPSD_LOG(LOG_DATA, &session->context->errout,
  729. "NMEA0183: GLL: hhmmss=%s lat=%.2f lon=%.2f mode=%d status=%d\n",
  730. field[5],
  731. session->newdata.latitude,
  732. session->newdata.longitude,
  733. session->newdata.mode,
  734. session->newdata.status);
  735. return mask;
  736. }
  737. // Geographic position - Latitude, Longitude, and more
  738. static gps_mask_t processGNS(int count UNUSED, char *field[],
  739. struct gps_device_t *session)
  740. {
  741. /* Introduced in NMEA 4.0?
  742. *
  743. * This mostly duplicates RMC, except for the multi GNSS mode
  744. * indicator.
  745. *
  746. * Example. Ignore the line break.
  747. * $GPGNS,224749.00,3333.4268304,N,11153.3538273,W,D,19,0.6,406.110,
  748. * -26.294,6.0,0138,S,*6A
  749. *
  750. * 1: 224749.00 UTC HHMMSS.SS. 22:47:49.00
  751. * 2: 3333.4268304 Latitude DDMM.MMMMM. 33 deg. 33.4268304 min
  752. * 3: N Latitude North
  753. * 4: 12311.12 Longitude 111 deg. 53.3538273 min
  754. * 5: W Longitude West
  755. * 6: D FAA mode indicator
  756. * see faa_mode() for possible mode values
  757. * May be one to six characters.
  758. * Char 1 = GPS
  759. * Char 2 = GLONASS
  760. * Char 3 = Galileo
  761. * Char 4 = BDS
  762. * Char 5 = QZSS
  763. * Char 6 = NavIC (IRNSS)
  764. * 7: 19 Number of Satellites used in solution
  765. * 8: 0.6 HDOP
  766. * 9: 406110 MSL Altitude in meters
  767. * 10: -26.294 Geoid separation in meters
  768. * 11: 6.0 Age of differential corrections, in seconds
  769. * 12: 0138 Differential reference station ID
  770. * 13: S NMEA 4.1+ Navigation status
  771. * S = Safe
  772. * C = Caution
  773. * U = Unsafe
  774. * V = Not valid for navigation
  775. * 8: *6A Mandatory NMEA checksum
  776. *
  777. */
  778. int newstatus;
  779. int satellites_used;
  780. gps_mask_t mask = ONLINE_SET;
  781. if (field[1][0] != '\0') {
  782. if (0 == merge_hhmmss(field[1], session)) {
  783. register_fractional_time(field[0], field[1], session);
  784. if (session->nmea.date.tm_year == 0) {
  785. GPSD_LOG(LOG_WARN, &session->context->errout,
  786. "NMEA0183: can't use GNS time until after ZDA or RMC"
  787. " has supplied a year.\n");
  788. } else {
  789. mask = TIME_SET;
  790. }
  791. }
  792. }
  793. /* FAA mode: not valid, ignore
  794. * Yes, in 2019 a GLONASS only fix may be valid, but not worth
  795. * the confusion */
  796. if ('\0' == field[6][0] || // FAA mode: missing
  797. 'N' == field[6][0]) { // FAA mode: not valid
  798. session->newdata.mode = MODE_NO_FIX;
  799. mask |= MODE_SET;
  800. return mask;
  801. }
  802. /* navigation status, assume S=safe and C=caution are OK */
  803. /* can be missing on valid fix */
  804. if ('U' == field[13][0] || // Unsafe
  805. 'V' == field[13][0]) { // not valid
  806. return mask;
  807. }
  808. satellites_used = atoi(field[7]);
  809. if (0 == do_lat_lon(&field[2], &session->newdata)) {
  810. mask |= LATLON_SET;
  811. session->newdata.mode = MODE_2D;
  812. if ('\0' != field[9][0]) {
  813. // altitude is MSL
  814. session->newdata.altMSL = safe_atof(field[9]);
  815. if (0 != isfinite(session->newdata.altMSL)) {
  816. mask |= ALTITUDE_SET;
  817. if (3 < satellites_used) {
  818. // more than 3 sats used means 3D
  819. session->newdata.mode = MODE_3D;
  820. }
  821. }
  822. // only need geoid_sep if in 3D mode
  823. if ('\0' != field[10][0]) {
  824. session->newdata.geoid_sep = safe_atof(field[10]);
  825. }
  826. // Let gpsd_error_model() deal with geoid_sep and altHAE
  827. }
  828. } else {
  829. session->newdata.mode = MODE_NO_FIX;
  830. mask |= MODE_SET;
  831. }
  832. if (field[8][0] != '\0') {
  833. session->gpsdata.dop.hdop = safe_atof(field[8]);
  834. mask |= DOP_SET;
  835. }
  836. // we ignore all but the leading mode indicator.
  837. newstatus = faa_mode(field[6][0]);
  838. session->newdata.status = newstatus;
  839. mask |= MODE_SET | STATUS_SET;
  840. // get DGPS stuff
  841. if ('\0' != field[11][0] &&
  842. '\0' != field[12][0]) {
  843. // both, or neither
  844. session->newdata.dgps_age = safe_atof(field[11]);
  845. session->newdata.dgps_station = atoi(field[12]);
  846. }
  847. GPSD_LOG(LOG_DATA, &session->context->errout,
  848. "NMEA0183: GNS: hhmmss=%s lat=%.2f lon=%.2f mode=%d status=%d\n",
  849. field[1],
  850. session->newdata.latitude,
  851. session->newdata.longitude,
  852. session->newdata.mode,
  853. session->newdata.status);
  854. return mask;
  855. }
  856. // Global Positioning System Fix Data
  857. static gps_mask_t processGGA(int c UNUSED, char *field[],
  858. struct gps_device_t *session)
  859. {
  860. /*
  861. * GGA,123519,4807.038,N,01131.324,E,1,08,0.9,545.4,M,46.9,M, , *42
  862. * 1 123519 Fix taken at 12:35:19 UTC
  863. * 2,3 4807.038,N Latitude 48 deg 07.038' N
  864. * 4,5 01131.324,E Longitude 11 deg 31.324' E
  865. * 6 1 Fix quality:
  866. * 0 = invalid,
  867. * 1 = GPS,
  868. * u-blox may use 1 for Estimated
  869. * 2 = DGPS,
  870. * 3 = PPS (Precise Position Service),
  871. * 4 = RTK (Real Time Kinematic) with fixed integers,
  872. * 5 = Float RTK,
  873. * 6 = Estimated,
  874. * 7 = Manual,
  875. * 8 = Simulator
  876. * 7 08 Number of satellites in use
  877. * 8 0.9 Horizontal dilution of position
  878. * 9,10 545.4,M Altitude, Meters MSL
  879. * 11,12 46.9,M Height of geoid (mean sea level) above WGS84
  880. * ellipsoid, in Meters
  881. * 13 33 time in seconds since last DGPS update
  882. * usually empty
  883. * 14 1023 DGPS station ID number (0000-1023)
  884. * usually empty
  885. *
  886. * Some GPS, like the SiRFstarV in NMEA mode, send both GPGSA and
  887. * GLGPSA with identical data.
  888. */
  889. gps_mask_t mask = ONLINE_SET;
  890. int newstatus;
  891. char last_last_gga_talker = session->nmea.last_gga_talker;
  892. int fix; // a.k.a Quality flag
  893. int satellites_visible;
  894. session->nmea.last_gga_talker = field[0][1];
  895. if ('\0' == field[6][0]) {
  896. /* no data is no data, assume no fix
  897. * the test/daemon/myguide-3100.log shows lat/lon/alt but
  898. * no status, and related RMC shows no fix. */
  899. fix = -1;
  900. } else {
  901. fix = atoi(field[6]);
  902. }
  903. // Jackson Labs Micro JLT uses nonstadard fix flag, not handled
  904. switch (fix) {
  905. case 0: // no fix
  906. newstatus = STATUS_UNK;
  907. if ('\0' == field[1][0]) {
  908. /* No time available. That breaks cycle end detector
  909. * Force report to bypass cycle detector and get report out.
  910. * To handle Querks (Quectel) like this:
  911. * $GPGGA,,,,,,0,,,,,,,,*66
  912. */
  913. memset(&session->nmea.date, 0, sizeof(session->nmea.date));
  914. session->cycle_end_reliable = false;
  915. mask |= REPORT_IS | TIME_SET;
  916. }
  917. break;
  918. case 1:
  919. // could be 2D, 3D, GNSSDR
  920. newstatus = STATUS_GPS;
  921. break;
  922. case 2: // differential
  923. newstatus = STATUS_DGPS;
  924. break;
  925. case 3:
  926. // GPS PPS, fix valid, could be 2D, 3D, GNSSDR
  927. newstatus = STATUS_PPS_FIX;
  928. break;
  929. case 4: // RTK integer
  930. newstatus = STATUS_RTK_FIX;
  931. break;
  932. case 5: // RTK float
  933. newstatus = STATUS_RTK_FLT;
  934. break;
  935. case 6:
  936. // dead reckoning, could be valid or invalid
  937. newstatus = STATUS_DR;
  938. break;
  939. case 7:
  940. // manual input, surveyed
  941. newstatus = STATUS_TIME;
  942. break;
  943. case 8:
  944. /* simulated mode
  945. * Garmin GPSMAP and Gecko sends an 8, but undocumented why */
  946. newstatus = STATUS_SIM;
  947. break;
  948. case -1:
  949. FALLTHROUGH
  950. default:
  951. newstatus = -1;
  952. break;
  953. }
  954. if (0 <= newstatus) {
  955. session->newdata.status = newstatus;
  956. mask = STATUS_SET;
  957. }
  958. /*
  959. * There are some receivers (the Trimble Placer 450 is an example) that
  960. * don't ship a GSA with mode 1 when they lose satellite lock. Instead
  961. * they just keep reporting GGA and GSA on subsequent cycles with the
  962. * timestamp not advancing and a bogus mode.
  963. *
  964. * On the assumption that GGA is only issued once per cycle we can
  965. * detect this here (it would be nicer to do it on GSA but GSA has
  966. * no timestamp).
  967. *
  968. * SiRFstarV breaks this assumption, sending GGA with different
  969. * talker IDs.
  970. */
  971. if ('\0' != last_last_gga_talker &&
  972. last_last_gga_talker != session->nmea.last_gga_talker) {
  973. // skip the time check
  974. session->nmea.latch_mode = 0;
  975. } else {
  976. session->nmea.latch_mode = strncmp(field[1],
  977. session->nmea.last_gga_timestamp,
  978. sizeof(session->nmea.last_gga_timestamp))==0;
  979. }
  980. if (session->nmea.latch_mode) {
  981. session->newdata.status = STATUS_UNK;
  982. session->newdata.mode = MODE_NO_FIX;
  983. mask |= MODE_SET | STATUS_SET;
  984. GPSD_LOG(LOG_PROG, &session->context->errout,
  985. "NMEA0183: xxGGA: latch mode\n");
  986. } else {
  987. (void)strlcpy(session->nmea.last_gga_timestamp, field[1],
  988. sizeof(session->nmea.last_gga_timestamp));
  989. }
  990. /* satellites_visible is used as an accumulator in xxGSV
  991. * so if we set it here we break xxGSV
  992. * Some GPS, like SiRFstarV NMEA, report per GNSS used
  993. * counts in GPGGA and GLGGA.
  994. * session->gpsdata.satellites_visible = atoi(field[7]);
  995. */
  996. satellites_visible = atoi(field[7]);
  997. if ('\0' == field[1][0]) {
  998. GPSD_LOG(LOG_DATA, &session->context->errout,
  999. "NMEA0183: GGA time missing.\n");
  1000. } else if (0 == merge_hhmmss(field[1], session)) {
  1001. register_fractional_time(field[0], field[1], session);
  1002. if (session->nmea.date.tm_year == 0)
  1003. GPSD_LOG(LOG_WARN, &session->context->errout,
  1004. "NMEA0183: can't use GGA time until after ZDA or RMC"
  1005. " has supplied a year.\n");
  1006. else {
  1007. mask |= TIME_SET;
  1008. }
  1009. }
  1010. if (0 == do_lat_lon(&field[2], &session->newdata)) {
  1011. session->newdata.mode = MODE_2D;
  1012. mask |= LATLON_SET;
  1013. if ('\0' != field[11][0]) {
  1014. session->newdata.geoid_sep = safe_atof(field[11]);
  1015. } else {
  1016. session->newdata.geoid_sep = wgs84_separation(
  1017. session->newdata.latitude, session->newdata.longitude);
  1018. }
  1019. /*
  1020. * SiRF chipsets up to version 2.2 report a null altitude field.
  1021. * See <http://www.sirf.com/Downloads/Technical/apnt0033.pdf>.
  1022. * If we see this, force mode to 2D at most.
  1023. */
  1024. if ('\0' != field[9][0]) {
  1025. // altitude is MSL
  1026. session->newdata.altMSL = safe_atof(field[9]);
  1027. // Let gpsd_error_model() deal with altHAE
  1028. mask |= ALTITUDE_SET;
  1029. /*
  1030. * This is a bit dodgy. Technically we shouldn't set the mode
  1031. * bit until we see GSA. But it may be later in the cycle,
  1032. * some devices like the FV-18 don't send it by default, and
  1033. * elsewhere in the code we want to be able to test for the
  1034. * presence of a valid fix with mode > MODE_NO_FIX.
  1035. *
  1036. * Use satellites_visible as double check on MODE_3D
  1037. */
  1038. if (4 <= satellites_visible) {
  1039. session->newdata.mode = MODE_3D;
  1040. }
  1041. }
  1042. if (3 > satellites_visible) {
  1043. session->newdata.mode = MODE_NO_FIX;
  1044. }
  1045. } else {
  1046. session->newdata.mode = MODE_NO_FIX;
  1047. }
  1048. mask |= MODE_SET;
  1049. // BT-451, and others, send 99.99 for invalid DOPs
  1050. if ('\0' != field[8][0]) {
  1051. double hdop;
  1052. hdop = safe_atof(field[8]);
  1053. if (99.0 > hdop) {
  1054. // why not to newdata?
  1055. session->gpsdata.dop.hdop = hdop;
  1056. mask |= DOP_SET;
  1057. }
  1058. }
  1059. // get DGPS stuff
  1060. if ('\0' != field[13][0] &&
  1061. '\0' != field[14][0]) {
  1062. // both, or neither
  1063. double age;
  1064. int station;
  1065. age = safe_atof(field[13]);
  1066. station = atoi(field[14]);
  1067. if (0.09 < age ||
  1068. 0 < station) {
  1069. // ignore both zeros
  1070. session->newdata.dgps_age = age;
  1071. session->newdata.dgps_station = station;
  1072. }
  1073. }
  1074. GPSD_LOG(LOG_DATA, &session->context->errout,
  1075. "NMEA0183: GGA: hhmmss=%s lat=%.2f lon=%.2f altMSL=%.2f "
  1076. "mode=%d status=%d\n",
  1077. field[1],
  1078. session->newdata.latitude,
  1079. session->newdata.longitude,
  1080. session->newdata.altMSL,
  1081. session->newdata.mode,
  1082. session->newdata.status);
  1083. return mask;
  1084. }
  1085. // GST - GPS Pseudorange Noise Statistics
  1086. static gps_mask_t processGST(int count, char *field[],
  1087. struct gps_device_t *session)
  1088. {
  1089. /*
  1090. * GST,hhmmss.ss,x,x,x,x,x,x,x,*hh
  1091. * 1 UTC time of associated GGA fix
  1092. * 2 Total RMS standard deviation of ranges inputs to the nav solution
  1093. * 3 Standard deviation (meters) of semi-major axis of error ellipse
  1094. * 4 Standard deviation (meters) of semi-minor axis of error ellipse
  1095. * 5 Orientation of semi-major axis of error ellipse (true north degrees)
  1096. * 6 Standard deviation (meters) of latitude error
  1097. * 7 Standard deviation (meters) of longitude error
  1098. * 8 Standard deviation (meters) of altitude error
  1099. * 9 Checksum
  1100. */
  1101. struct tm date;
  1102. timespec_t ts;
  1103. int ret;
  1104. char ts_buf[TIMESPEC_LEN];
  1105. gps_mask_t mask = ONLINE_SET;
  1106. if (0 > count) {
  1107. return mask;
  1108. }
  1109. // since it is NOT current time, do not register_fractional_time()
  1110. // compute start of today
  1111. if (0 < session->nmea.date.tm_year) {
  1112. // Do not bother if no current year
  1113. memset(&date, 0, sizeof(date));
  1114. date.tm_year = session->nmea.date.tm_year;
  1115. date.tm_mon = session->nmea.date.tm_mon;
  1116. date.tm_mday = session->nmea.date.tm_mday;
  1117. /* note this is not full UTC, just HHMMSS.ss
  1118. * this is not the current time,
  1119. * it references another GPA of the same stamp. So do not set
  1120. * any time stamps with it */
  1121. ret = decode_hhmmss(&date, &ts.tv_nsec, field[1], session);
  1122. } else {
  1123. ret = 1;
  1124. }
  1125. if (0 == ret) {
  1126. // convert to timespec_t , tv_nsec already set
  1127. session->gpsdata.gst.utctime.tv_sec = mkgmtime(&date);
  1128. session->gpsdata.gst.utctime.tv_nsec = ts.tv_nsec;
  1129. } else {
  1130. // no idea of UTC time now
  1131. session->gpsdata.gst.utctime.tv_sec = 0;
  1132. session->gpsdata.gst.utctime.tv_nsec = 0;
  1133. }
  1134. session->gpsdata.gst.rms_deviation = safe_atof(field[2]);
  1135. session->gpsdata.gst.smajor_deviation = safe_atof(field[3]);
  1136. session->gpsdata.gst.sminor_deviation = safe_atof(field[4]);
  1137. session->gpsdata.gst.smajor_orientation = safe_atof(field[5]);
  1138. session->gpsdata.gst.lat_err_deviation = safe_atof(field[6]);
  1139. session->gpsdata.gst.lon_err_deviation = safe_atof(field[7]);
  1140. session->gpsdata.gst.alt_err_deviation = safe_atof(field[8]);
  1141. GPSD_LOG(LOG_DATA, &session->context->errout,
  1142. "NMEA0183: GST: utc = %s, rms = %.2f, maj = %.2f, min = %.2f,"
  1143. " ori = %.2f, lat = %.2f, lon = %.2f, alt = %.2f\n",
  1144. timespec_str(&session->gpsdata.gst.utctime, ts_buf,
  1145. sizeof(ts_buf)),
  1146. session->gpsdata.gst.rms_deviation,
  1147. session->gpsdata.gst.smajor_deviation,
  1148. session->gpsdata.gst.sminor_deviation,
  1149. session->gpsdata.gst.smajor_orientation,
  1150. session->gpsdata.gst.lat_err_deviation,
  1151. session->gpsdata.gst.lon_err_deviation,
  1152. session->gpsdata.gst.alt_err_deviation);
  1153. mask = GST_SET | ONLINE_SET;
  1154. return mask;
  1155. }
  1156. // convert NMEA sigid to ublox sigid
  1157. static unsigned char nmea_sigid_to_ubx(unsigned char nmea_sigid)
  1158. {
  1159. unsigned char ubx_sigid = 0;
  1160. // FIXME: need to know gnssid to guess sigid
  1161. switch (nmea_sigid) {
  1162. default:
  1163. FALLTHROUGH
  1164. case 0:
  1165. // missing, assume GPS L1
  1166. ubx_sigid = 0;
  1167. break;
  1168. case 1:
  1169. // L1
  1170. ubx_sigid = 0;
  1171. break;
  1172. case 2:
  1173. // E5, could be 5 or 6.
  1174. ubx_sigid = 5;
  1175. break;
  1176. case 3:
  1177. // B2 or L2, could be 2 or 3.
  1178. ubx_sigid = 2;
  1179. break;
  1180. case 5:
  1181. // L2
  1182. ubx_sigid = 4;
  1183. break;
  1184. case 6:
  1185. // L2CL
  1186. ubx_sigid = 3;
  1187. break;
  1188. case 7:
  1189. // E1, could be 0 or 1.
  1190. ubx_sigid = 0;
  1191. break;
  1192. }
  1193. return ubx_sigid;
  1194. }
  1195. /* Deal with range-mapping attempts to use IDs 1-32 by Beidou, etc.
  1196. *
  1197. * See struct satellite_t in gps.h for ubx and nmea gnssid and svid mappings
  1198. *
  1199. * char *talker -- NMEA talker string
  1200. * int nmea_satnum -- NMEA (All ver) satellite number (kinda the PRN)
  1201. * int nmea_gnssid -- NMEA 4.10 gnssid, if known, otherwise zero
  1202. * unsigned char *ubx_gnssid -- returned u-blox gnssid
  1203. * unsigned char *ubx_svid -- returned u-blox gnssid
  1204. *
  1205. * Return the NMEA 2.x to 4.0 extended PRN
  1206. */
  1207. static int nmeaid_to_prn(char *talker, int nmea_satnum,
  1208. int nmea_gnssid,
  1209. unsigned char *ubx_gnssid,
  1210. unsigned char *ubx_svid)
  1211. {
  1212. /*
  1213. * According to https://github.com/mvglasow/satstat/wiki/NMEA-IDs
  1214. * and u-blox documentation.
  1215. * NMEA IDs can be roughly divided into the following ranges:
  1216. *
  1217. * 1..32: GPS
  1218. * 33..64: Various SBAS systems (EGNOS, WAAS, SDCM, GAGAN, MSAS)
  1219. * 65..96: GLONASS
  1220. * 101..136: Quectel Querk, (not NMEA), seems to be Galileo
  1221. * 152..158: Various SBAS systems (EGNOS, WAAS, SDCM, GAGAN, MSAS)
  1222. * 173..182: IMES
  1223. * 193..202: QZSS (u-blox extended 4.10)
  1224. * 201..264: BeiDou (not NMEA, not u-blox?) Quectel Querk.
  1225. * 301..336: Galileo
  1226. * 401..437: BeiDou
  1227. * null: GLONASS unused
  1228. * 500-509: NavIC (IRNSS) NOT STANDARD!
  1229. *
  1230. * The issue is what to do when GPSes from these different systems
  1231. * fight for IDs in the 1-32 range, as in this pair of Beidou sentences
  1232. *
  1233. * $BDGSV,2,1,07,01,00,000,45,02,13,089,35,03,00,000,37,04,00,000,42*6E
  1234. * $BDGSV,2,2,07,05,27,090,,13,19,016,,11,07,147,*5E
  1235. *
  1236. * Because the PRNs are only used for generating a satellite
  1237. * chart, mistakes here aren't dangerous. The code will record
  1238. * and use multiple sats with the same ID in one skyview; in
  1239. * effect, they're recorded by the order in which they occur
  1240. * rather than by PRN.
  1241. */
  1242. int nmea2_prn = nmea_satnum;
  1243. *ubx_gnssid = 0; // default to ubx_gnssid is GPS
  1244. *ubx_svid = 0; // default to unnknown ubx_svid
  1245. if (1 > nmea_satnum) {
  1246. // uh, oh...
  1247. nmea2_prn = 0;
  1248. } else if (0 < nmea_gnssid) {
  1249. // this switch handles case where nmea_gnssid is known
  1250. switch (nmea_gnssid) {
  1251. default:
  1252. // x = IMES Not defined by NMEA 4.10
  1253. FALLTHROUGH
  1254. case 0:
  1255. // none given, ignore
  1256. nmea2_prn = 0;
  1257. break;
  1258. case 1:
  1259. if (33 > nmea_satnum) {
  1260. // 1 = GPS 1-32
  1261. *ubx_gnssid = 0;
  1262. *ubx_svid = nmea_satnum;
  1263. } else if (65 > nmea_satnum) {
  1264. // 1 = SBAS 33-64
  1265. *ubx_gnssid = 1;
  1266. *ubx_svid = nmea_satnum + 87;
  1267. } else if (137 > nmea_satnum) {
  1268. // 3 = Galileo, 101-136, NOT NMEA. Quectel Querk
  1269. *ubx_gnssid = 3;
  1270. *ubx_svid = nmea_satnum - 100;
  1271. } else if (152 > nmea_satnum) {
  1272. // Huh?
  1273. *ubx_gnssid = 0;
  1274. *ubx_svid = 0;
  1275. nmea2_prn = 0;
  1276. } else if (158 > nmea_satnum) {
  1277. // 1 = SBAS 152-158
  1278. *ubx_gnssid = 1;
  1279. *ubx_svid = nmea_satnum;
  1280. } else if (193 > nmea_satnum) {
  1281. // Huh?
  1282. *ubx_gnssid = 0;
  1283. *ubx_svid = 0;
  1284. nmea2_prn = 0;
  1285. } else if (200 > nmea_satnum) {
  1286. // 1 = QZSS 193-197
  1287. // undocumented u-blox goes to 199
  1288. *ubx_gnssid = 3;
  1289. *ubx_svid = nmea_satnum - 192;
  1290. } else if (265 > nmea_satnum) {
  1291. // 3 = BeiDor, 201-264, NOT NMEA. Quectel Querk
  1292. *ubx_gnssid = 3;
  1293. *ubx_svid = nmea_satnum - 200;
  1294. } else {
  1295. // Huh?
  1296. *ubx_gnssid = 0;
  1297. *ubx_svid = 0;
  1298. nmea2_prn = 0;
  1299. }
  1300. break;
  1301. case 2:
  1302. // 2 = GLONASS 65-96, nul
  1303. *ubx_gnssid = 6;
  1304. if (64 > nmea_satnum) {
  1305. // NMEA 1 - 64
  1306. *ubx_svid = nmea_satnum;
  1307. } else {
  1308. // SiRF quirk 65-96
  1309. // Jackson Labs Micro JLT quirk 65-96
  1310. *ubx_svid = nmea_satnum - 64;
  1311. }
  1312. nmea2_prn = 64 + *ubx_svid;
  1313. break;
  1314. case 3:
  1315. // 3 = Galileo 1-36
  1316. *ubx_gnssid = 2;
  1317. if (100 > nmea_satnum) {
  1318. // NMEA
  1319. *ubx_svid = nmea_satnum;
  1320. } else if (100 < nmea_satnum && 200 > nmea_satnum) {
  1321. // Quectel Querk, NOT NMEA, 101 - 199
  1322. *ubx_svid = nmea_satnum - 100;
  1323. } else if (300 < nmea_satnum && 400 > nmea_satnum) {
  1324. // Jackson Labs quirk, NOT NMEA, 301 - 399
  1325. *ubx_svid = nmea_satnum - 300;
  1326. }
  1327. nmea2_prn = 300 + *ubx_svid; // 301 - 399
  1328. break;
  1329. case 4:
  1330. // 4 - BeiDou 1-37
  1331. *ubx_gnssid = 3;
  1332. if (100 > nmea_satnum) {
  1333. // NMEA 1 - 99
  1334. *ubx_svid = nmea_satnum;
  1335. } else if (200 < nmea_satnum && 300 > nmea_satnum) {
  1336. // Quectel Querk, NOT NMEA, 201 - 299
  1337. *ubx_svid = nmea_satnum - 200;
  1338. } else if (400 < nmea_satnum && 500 > nmea_satnum) {
  1339. // Jackson Labs quirk, NOT NMEA, 401 - 499
  1340. *ubx_svid = nmea_satnum - 400;
  1341. }
  1342. // put it at 400+ where NMEA 4.11 wants it
  1343. nmea2_prn = 400 + *ubx_svid;
  1344. break;
  1345. case 5:
  1346. // 5 - QZSS, 1 - 10, NMEA 4.11
  1347. *ubx_gnssid = 5;
  1348. if (100 > nmea_satnum) {
  1349. // NMEA 1 - 99
  1350. *ubx_svid = nmea_satnum;
  1351. } else {
  1352. // Telit quirk, not NMEA 193 - 199
  1353. *ubx_svid = nmea_satnum - 192;
  1354. }
  1355. // put it at 193 to 199 where NMEA 4.11 wants it
  1356. // huh? space for only 7?
  1357. nmea2_prn = 192 + *ubx_svid;;
  1358. break;
  1359. case 6:
  1360. // 6 - NavIC (IRNSS) 1-15
  1361. *ubx_gnssid = 7;
  1362. *ubx_svid = nmea_satnum;
  1363. nmea2_prn = nmea_satnum + 500; // This is wrong...
  1364. break;
  1365. }
  1366. /* left with NMEA 2.x to NMEA 4.0 satnums
  1367. * use talker ID to disambiguate */
  1368. } else if (32 >= nmea_satnum) {
  1369. *ubx_svid = nmea_satnum;
  1370. switch (talker[0]) {
  1371. case 'G':
  1372. switch (talker[1]) {
  1373. case 'A':
  1374. // Galileo
  1375. nmea2_prn = 300 + nmea_satnum;
  1376. *ubx_gnssid = 2;
  1377. break;
  1378. case 'B':
  1379. // map Beidou IDs 1..37 to 401..437
  1380. *ubx_gnssid = 3;
  1381. nmea2_prn = 400 + nmea_satnum;
  1382. break;
  1383. case 'I':
  1384. // map NavIC (IRNSS) IDs 1..10 to 500 - 509, not NMEA
  1385. *ubx_gnssid = 7;
  1386. nmea2_prn = 500 + nmea_satnum;
  1387. break;
  1388. case 'L':
  1389. // GLONASS GL doesn't seem to do this, better safe than sorry
  1390. nmea2_prn = 64 + nmea_satnum;
  1391. *ubx_gnssid = 6;
  1392. break;
  1393. case 'Q':
  1394. // GQ, QZSS, 1 - 10
  1395. nmea2_prn = 192 + nmea_satnum;
  1396. *ubx_gnssid = 5;
  1397. break;
  1398. case 'N':
  1399. // all of them, but only GPS is 0 < PRN < 33
  1400. FALLTHROUGH
  1401. case 'P':
  1402. // GPS,SBAS,QZSS, but only GPS is 0 < PRN < 33
  1403. FALLTHROUGH
  1404. default:
  1405. // WTF?
  1406. break;
  1407. } // else ??
  1408. break;
  1409. case 'B':
  1410. if (talker[1] == 'D') {
  1411. // map Beidou IDs
  1412. nmea2_prn = 400 + nmea_satnum;
  1413. *ubx_gnssid = 3;
  1414. } // else ??
  1415. break;
  1416. case 'P':
  1417. // Quectel EC25 & EC21 use PQxxx for BeiDou
  1418. if (talker[1] == 'Q') {
  1419. // map Beidou IDs
  1420. nmea2_prn = 400 + nmea_satnum;
  1421. *ubx_gnssid = 3;
  1422. } // else ??
  1423. break;
  1424. case 'Q':
  1425. if (talker[1] == 'Z') {
  1426. // QZSS
  1427. nmea2_prn = 192 + nmea_satnum;
  1428. *ubx_gnssid = 5;
  1429. } // else ?
  1430. break;
  1431. default:
  1432. // huh?
  1433. break;
  1434. }
  1435. } else if (64 >= nmea_satnum) {
  1436. // NMEA-ID (33..64) to SBAS PRN 120-151.
  1437. // SBAS
  1438. *ubx_gnssid = 1;
  1439. *ubx_svid = 87 + nmea_satnum;
  1440. } else if (96 >= nmea_satnum) {
  1441. // GLONASS 65..96
  1442. *ubx_gnssid = 6;
  1443. *ubx_svid = nmea_satnum - 64;
  1444. } else if (120 > nmea_satnum) {
  1445. // Huh?
  1446. *ubx_gnssid = 0;
  1447. *ubx_svid = 0;
  1448. nmea2_prn = 0;
  1449. } else if (158 >= nmea_satnum) {
  1450. // SBAS 120..158
  1451. *ubx_gnssid = 1;
  1452. *ubx_svid = nmea_satnum;
  1453. } else if (173 > nmea_satnum) {
  1454. // Huh?
  1455. *ubx_gnssid = 0;
  1456. *ubx_svid = 0;
  1457. nmea2_prn = 0;
  1458. } else if (182 >= nmea_satnum) {
  1459. // IMES 173..182
  1460. *ubx_gnssid = 4;
  1461. *ubx_svid = nmea_satnum - 172;
  1462. } else if (193 > nmea_satnum) {
  1463. // Huh?
  1464. *ubx_gnssid = 0;
  1465. *ubx_svid = 0;
  1466. nmea2_prn = 0;
  1467. } else if (197 >= nmea_satnum) {
  1468. // QZSS 193..197
  1469. // undocumented u-blox goes to 199
  1470. *ubx_gnssid = 5;
  1471. *ubx_svid = nmea_satnum - 192;
  1472. } else if (201 > nmea_satnum) {
  1473. // Huh?
  1474. *ubx_gnssid = 0;
  1475. *ubx_svid = 0;
  1476. nmea2_prn = 0;
  1477. } else if (237 >= nmea_satnum) {
  1478. // BeiDou, non-standard, some SiRF put BeiDou 201-237
  1479. // $GBGSV,2,2,05,209,07,033,*62
  1480. *ubx_gnssid = 3;
  1481. *ubx_svid = nmea_satnum - 200;
  1482. nmea2_prn += 200; // move up to 400 where NMEA 2.x wants it.
  1483. } else if (301 > nmea_satnum) {
  1484. // Huh?
  1485. *ubx_gnssid = 0;
  1486. *ubx_svid = 0;
  1487. nmea2_prn = 0;
  1488. } else if (356 >= nmea_satnum) {
  1489. // Galileo 301..356
  1490. *ubx_gnssid = 2;
  1491. *ubx_svid = nmea_satnum - 300;
  1492. } else if (401 > nmea_satnum) {
  1493. // Huh?
  1494. *ubx_gnssid = 0;
  1495. *ubx_svid = 0;
  1496. nmea2_prn = 0;
  1497. } else if (437 >= nmea_satnum) {
  1498. // BeiDou
  1499. *ubx_gnssid = 3;
  1500. *ubx_svid = nmea_satnum - 400;
  1501. } else {
  1502. // greater than 437 Huh?
  1503. *ubx_gnssid = 0;
  1504. *ubx_svid = 0;
  1505. nmea2_prn = 0;
  1506. }
  1507. return nmea2_prn;
  1508. }
  1509. // GPS DOP and Active Satellites
  1510. static gps_mask_t processGSA(int count, char *field[],
  1511. struct gps_device_t *session)
  1512. {
  1513. #define GSA_TALKER field[0][1]
  1514. /*
  1515. * eg1. $GPGSA,A,3,,,,,,16,18,,22,24,,,3.6,2.1,2.2*3C
  1516. * eg2. $GPGSA,A,3,19,28,14,18,27,22,31,39,,,,,1.7,1.0,1.3*35
  1517. * NMEA 4.10: $GNGSA,A,3,13,12,22,19,08,21,,,,,,,1.05,0.64,0.83,4*0B
  1518. * 1 = Mode:
  1519. * M=Manual, forced to operate in 2D or 3D
  1520. * A=Automatic, 3D/2D
  1521. * 2 = Mode:
  1522. * 1=Fix not available,
  1523. * 2=2D,
  1524. * 3=3D
  1525. * 3-14 = PRNs of satellites used in position fix (null for unused fields)
  1526. * 15 = PDOP
  1527. * 16 = HDOP
  1528. * 17 = VDOP
  1529. * 18 - NMEA 4.1+ GNSS System ID, u-blox extended
  1530. * 1 = GPS L1C/A, L2CL, L2CM
  1531. * 2 = GLONASS L1 OF, L2 OF
  1532. * 3 = Galileo E1C, E1B, E5 bl, E5 bQ
  1533. * 4 = BeiDou B1I D1, B1I D2, B2I D1, B2I D12
  1534. * 5 = QZSS
  1535. * 6 - NavID (IRNSS)
  1536. *
  1537. * Not all documentation specifies the number of PRN fields, it
  1538. * may be variable. Most doc that specifies says 12 PRNs.
  1539. *
  1540. * The Navior-24 CH-4701 outputs 30 fields, 24 PRNs!
  1541. * GPGSA,A,3,27,23,13,07,25,,,,,,,,,,,,,,,,,,,,07.9,06.0,05.2
  1542. *
  1543. * The Skytraq S2525F8-BD-RTK output both GPGSA and BDGSA in the
  1544. * same cycle:
  1545. * $GPGSA,A,3,23,31,22,16,03,07,,,,,,,1.8,1.1,1.4*3E
  1546. * $BDGSA,A,3,214,,,,,,,,,,,,1.8,1.1,1.4*18
  1547. * These need to be combined like GPGSV and BDGSV
  1548. *
  1549. * Some GPS emit GNGSA. So far we have not seen a GPS emit GNGSA
  1550. * and then another flavor of xxGSA
  1551. *
  1552. * Some Skytraq will emit all GPS in one GNGSA, Then follow with
  1553. * another GNGSA with the BeiDou birds.
  1554. *
  1555. * SEANEXX, SiRFstarIV, and others also do it twice in one cycle:
  1556. * $GNGSA,A,3,31,26,21,,,,,,,,,,3.77,2.55,2.77*1A
  1557. * $GNGSA,A,3,75,86,87,,,,,,,,,,3.77,2.55,2.77*1C
  1558. * seems like the first is GNSS and the second GLONASS
  1559. *
  1560. * u-blox 9 outputs one per GNSS on each cycle. Note the
  1561. * extra last parameter which is NMEA gnssid:
  1562. * $GNGSA,A,3,13,16,21,15,10,29,27,20,,,,,1.05,0.64,0.83,1*03
  1563. * $GNGSA,A,3,82,66,81,,,,,,,,,,1.05,0.64,0.83,2*0C
  1564. * $GNGSA,A,3,07,12,33,,,,,,,,,,1.05,0.64,0.83,3*0A
  1565. * $GNGSA,A,3,13,12,22,19,08,21,,,,,,,1.05,0.64,0.83,4*0B
  1566. * Also note the NMEA 4.0 GLONASS PRN (82) in an NMEA 4.1
  1567. * sentence.
  1568. */
  1569. gps_mask_t mask = ONLINE_SET;
  1570. char last_last_gsa_talker = session->nmea.last_gsa_talker;
  1571. int nmea_gnssid = 0;
  1572. int nmea_sigid = 0;
  1573. int ubx_sigid = 0;
  1574. /*
  1575. * One chipset called the i.Trek M3 issues GPGSA lines that look like
  1576. * this: "$GPGSA,A,1,,,,*32" when it has no fix. This is broken
  1577. * in at least two ways: it's got the wrong number of fields, and
  1578. * it claims to be a valid sentence (A flag) when it isn't.
  1579. * Alarmingly, it's possible this error may be generic to SiRFstarIII.
  1580. */
  1581. if (18 > count) {
  1582. GPSD_LOG(LOG_DATA, &session->context->errout,
  1583. "NMEA0183: xxGSA: malformed, setting ONLINE_SET only.\n");
  1584. } else if (session->nmea.latch_mode) {
  1585. /* last GGA had a non-advancing timestamp; don't trust this GSA */
  1586. GPSD_LOG(LOG_DATA, &session->context->errout,
  1587. "NMEA0183: xxGSA: non-advancing timestamp\n");
  1588. } else {
  1589. int i;
  1590. session->newdata.mode = atoi(field[2]);
  1591. /*
  1592. * The first arm of this conditional ignores dead-reckoning
  1593. * fixes from an Antaris chipset. which returns E in field 2
  1594. * for a dead-reckoning estimate. Fix by Andreas Stricker.
  1595. */
  1596. if ('E' != field[2][0]) {
  1597. mask = MODE_SET;
  1598. }
  1599. GPSD_LOG(LOG_PROG, &session->context->errout,
  1600. "NMEA0183: xxGSA sets mode %d\n", session->newdata.mode);
  1601. if (19 < count) {
  1602. GPSD_LOG(LOG_WARN, &session->context->errout,
  1603. "NMEA0183: xxGSA: count %d too long!\n", count);
  1604. } else {
  1605. double dop;
  1606. /* Just ignore the last fields of the Navior CH-4701 */
  1607. if ('\0' != field[15][0]) {
  1608. dop = safe_atof(field[15]);
  1609. if (99.0 > dop) {
  1610. session->gpsdata.dop.pdop = dop;
  1611. mask |= DOP_SET;
  1612. }
  1613. }
  1614. // BT-451, and others, send 99.99 for invalid DOPs
  1615. if ('\0' != field[16][0]) {
  1616. dop = safe_atof(field[16]);
  1617. if (99.0 > dop) {
  1618. session->gpsdata.dop.hdop = dop;
  1619. mask |= DOP_SET;
  1620. }
  1621. }
  1622. if ('\0' != field[17][0]) {
  1623. dop = safe_atof(field[17]);
  1624. if (99.0 > dop) {
  1625. session->gpsdata.dop.vdop = dop;
  1626. mask |= DOP_SET;
  1627. }
  1628. }
  1629. if (19 == count &&
  1630. '\0' != field[18][0]) {
  1631. // get the NMEA 4.10 sigid
  1632. nmea_sigid = atoi(field[18]);
  1633. // FIXME: ubx_sigid not used yet
  1634. ubx_sigid = nmea_sigid_to_ubx(nmea_sigid);
  1635. }
  1636. }
  1637. /*
  1638. * might have gone from GPGSA to GLGSA/BDGSA
  1639. * or GNGSA to GNGSA
  1640. * in which case accumulate
  1641. */
  1642. if ('\0' == session->nmea.last_gsa_talker ||
  1643. (GSA_TALKER == session->nmea.last_gsa_talker &&
  1644. 'N' != GSA_TALKER) ) {
  1645. session->gpsdata.satellites_used = 0;
  1646. memset(session->nmea.sats_used, 0, sizeof(session->nmea.sats_used));
  1647. GPSD_LOG(LOG_DATA, &session->context->errout,
  1648. "NMEA0183: xxGSA: clear sats_used\n");
  1649. }
  1650. session->nmea.last_gsa_talker = GSA_TALKER;
  1651. switch (session->nmea.last_gsa_talker) {
  1652. case 'A':
  1653. // GA Galileo
  1654. nmea_gnssid = 3;
  1655. session->nmea.seen_gagsa = true;
  1656. break;
  1657. case 'B':
  1658. // GB BeiDou
  1659. FALLTHROUGH
  1660. case 'D':
  1661. // BD BeiDou
  1662. nmea_gnssid = 4;
  1663. session->nmea.seen_bdgsa = true;
  1664. break;
  1665. case 'I':
  1666. // GI IRNSS
  1667. nmea_gnssid = 6;
  1668. session->nmea.seen_gigsa = true;
  1669. break;
  1670. case 'L':
  1671. // GL GLONASS
  1672. nmea_gnssid = 2;
  1673. session->nmea.seen_glgsa = true;
  1674. break;
  1675. case 'N':
  1676. // GN GNSS
  1677. session->nmea.seen_gngsa = true;
  1678. break;
  1679. case 'P':
  1680. // GP GPS
  1681. session->nmea.seen_gpgsa = true;
  1682. break;
  1683. case 'Q':
  1684. // Quectel EC25 & EC21 use PQGSA for QZSS
  1685. FALLTHROUGH
  1686. case 'Z': // QZ QZSS
  1687. // NMEA 4.11 GQGSA for QZSS
  1688. nmea_gnssid = 5;
  1689. session->nmea.seen_qzgsa = true;
  1690. break;
  1691. }
  1692. // the magic 6 here counts the tag, two mode fields, and DOP fields
  1693. for (i = 0; i < count - 6; i++) {
  1694. int prn;
  1695. int nmea_satnum;
  1696. unsigned char ubx_gnssid; // UNUSED
  1697. unsigned char ubx_svid; // UNUSED
  1698. // skip empty fields, otherwise empty becomes prn=200
  1699. nmea_satnum = atoi(field[i + 3]);
  1700. if (1 > nmea_satnum) {
  1701. continue;
  1702. }
  1703. prn = nmeaid_to_prn(field[0], nmea_satnum, nmea_gnssid,
  1704. &ubx_gnssid, &ubx_svid);
  1705. #ifdef __UNUSED__
  1706. // debug
  1707. GPSD_LOG(LOG_ERROR, &session->context->errout,
  1708. "NMEA0183: %s nmeaid_to_prn: nmea_gnssid "
  1709. "%d nmea_satnum %d "
  1710. "ubx_gnssid %d ubx_svid %d nmea2_prn %d\n",
  1711. field[0],
  1712. nmea_gnssid, nmea_satnum, ubx_gnssid, ubx_svid, prn);
  1713. GPSD_LOG(LOG_ERROR, &session->context->errout,
  1714. NMEA0183: "%s count %d\n", field[0], count);
  1715. #endif // __UNUSED__
  1716. if (0 < prn) {
  1717. // check first BEFORE over-writing memory
  1718. if (MAXCHANNELS <= session->gpsdata.satellites_used) {
  1719. /* This should never happen as xxGSA is limited to 12,
  1720. * except for the Navior-24 CH-4701.
  1721. * But it could happen with multiple GSA per cycle */
  1722. break;
  1723. }
  1724. session->nmea.sats_used[session->gpsdata.satellites_used++] =
  1725. (unsigned short)prn;
  1726. }
  1727. }
  1728. mask |= USED_IS;
  1729. GPSD_LOG(LOG_DATA, &session->context->errout,
  1730. "NMEA0183: xxGSA: mode=%d used=%d pdop=%.2f hdop=%.2f "
  1731. " vdop=%.2f "
  1732. "ubx_sigid %d\n",
  1733. session->newdata.mode,
  1734. session->gpsdata.satellites_used,
  1735. session->gpsdata.dop.pdop,
  1736. session->gpsdata.dop.hdop,
  1737. session->gpsdata.dop.vdop, ubx_sigid);
  1738. }
  1739. // assumes GLGSA or BDGSA, if present, is emitted directly after the GPGSA
  1740. if ((session->nmea.seen_bdgsa ||
  1741. session->nmea.seen_gagsa ||
  1742. session->nmea.seen_gigsa ||
  1743. session->nmea.seen_glgsa ||
  1744. session->nmea.seen_gngsa ||
  1745. session->nmea.seen_qzgsa) &&
  1746. GSA_TALKER == 'P') {
  1747. mask = ONLINE_SET;
  1748. } else if ( 'N' != last_last_gsa_talker && 'N' == GSA_TALKER) {
  1749. /* first of two GNGSA
  1750. * if mode == 1 some GPS only output 1 GNGSA, so ship mode always */
  1751. mask = ONLINE_SET | MODE_SET;
  1752. }
  1753. // cast for 32/64 compatibility
  1754. GPSD_LOG(LOG_PROG, &session->context->errout,
  1755. "NMEA0183: xxGSA: mask %#llx\n", (long long unsigned)mask);
  1756. return mask;
  1757. #undef GSA_TALKER
  1758. }
  1759. // xxGSV - GPS Satellites in View
  1760. static gps_mask_t processGSV(int count, char *field[],
  1761. struct gps_device_t *session)
  1762. {
  1763. #define GSV_TALKER field[0][1]
  1764. /*
  1765. * GSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75
  1766. * 1) 2 Number of sentences for full data
  1767. * 2) 1 Sentence 1 of 2
  1768. * 3) 08 Total number of satellites in view
  1769. * 4) 01 Satellite PRN number
  1770. * 5) 40 Elevation, degrees
  1771. * 6) 083 Azimuth, degrees
  1772. * 7) 46 Signal-to-noise ratio in decibels
  1773. * <repeat for up to 4 satellites per sentence>
  1774. * ) NMEA 4.1 signalId
  1775. * ) checksum
  1776. *
  1777. * NMEA 4.1+:
  1778. * $GAGSV,3,1,09,02,00,179,,04,09,321,,07,11,134,11,11,10,227,,7*7F
  1779. * after the satellite block, before the checksum, new field:
  1780. * 7 NMEA Signal ID
  1781. * 1 = GPS L1C/A, BeiDou B1I D1, BeiDou B1I D2, GLONASS L1 OF
  1782. * 2 = Galileo E5 bl, E5 bQ
  1783. * 3 = BeiDou B2I D1, B2I D2
  1784. * 5 = GPS L2 CM
  1785. * 6 = GPS L2 CL
  1786. * 7 = Galileo E1C, E1B
  1787. *
  1788. * Can occur with talker IDs:
  1789. * BD (Beidou),
  1790. * GA (Galileo),
  1791. * GB (Beidou),
  1792. * GI (IRNSS
  1793. * GL (GLONASS),
  1794. * GN (GLONASS, any combination GNSS),
  1795. * GP (GPS, SBAS, QZSS),
  1796. * GQ (QZSS).
  1797. * PQ (QZSS). Quectel Quirk
  1798. * QZ (QZSS).
  1799. *
  1800. * As of April 2019:
  1801. * no gpsd regressions have GNGSV
  1802. * every xxGSV cycle starts with GPGSV
  1803. * xxGSV cycles may be spread over several xxRMC cycles
  1804. *
  1805. * GL may be (incorrectly) used when GSVs are mixed containing
  1806. * GLONASS, GN may be (incorrectly) used when GSVs contain GLONASS
  1807. * only. Usage is inconsistent.
  1808. *
  1809. * In the GLONASS version sat IDs run from 65-96 (NMEA0183
  1810. * standardizes this). At least two GPSes, the BU-353 GLONASS and
  1811. * the u-blox NEO-M8N, emit a GPGSV set followed by a GLGSV set.
  1812. * We have also seen two GPSes, the Skytraq S2525F8-BD-RTK and a
  1813. * SiRF-IV variant, that emit GPGSV followed by BDGSV. We need to
  1814. * combine these.
  1815. *
  1816. * The following shows how the Skytraq S2525F8-BD-RTK output both
  1817. * GPGSV and BDGSV in the same cycle:
  1818. * $GPGSV,4,1,13,23,66,310,29,03,65,186,33,26,43,081,27,16,41,124,38*78
  1819. * $GPGSV,4,2,13,51,37,160,38,04,37,066,25,09,34,291,07,22,26,156,37*77
  1820. * $GPGSV,4,3,13,06,19,301,,31,17,052,20,193,11,307,,07,11,232,27*4F
  1821. * $GPGSV,4,4,13,01,03,202,30*4A
  1822. * $BDGSV,1,1,02,214,55,153,40,208,01,299,*67
  1823. *
  1824. * The driver automatically adapts to either case, but it takes until the
  1825. * second cycle (usually 10 seconds from device connect) for it to
  1826. * learn to expect BSDGV or GLGSV.
  1827. *
  1828. * Some GPS (Garmin 17N) spread the xxGSV over several cycles. So
  1829. * cycles, or cycle time, can not be used to determine start of
  1830. * xxGSV cycle.
  1831. *
  1832. * NMEA 4.1 adds a signal-ID field just before the checksum. First
  1833. * seen in May 2015 on a u-blox M8. It can output 2 sets of GPGSV
  1834. * in one cycle, one for L1C and the other for L2C.
  1835. */
  1836. int n, fldnum;
  1837. unsigned char nmea_sigid = 0;
  1838. int nmea_gnssid = 0;
  1839. unsigned char ubx_sigid = 0;
  1840. if (count <= 3) {
  1841. GPSD_LOG(LOG_WARN, &session->context->errout,
  1842. "NMEA0183: malformed xxGSV - fieldcount %d <= 3\n",
  1843. count);
  1844. gpsd_zero_satellites(&session->gpsdata);
  1845. return ONLINE_SET;
  1846. }
  1847. GPSD_LOG(LOG_PROG, &session->context->errout,
  1848. "NMEA0183: x%cGSV: part %s of %s, last_gsv_talker '%#x' "
  1849. " last_gsv_sigid %u\n",
  1850. GSV_TALKER, field[2], field[1],
  1851. session->nmea.last_gsv_talker,
  1852. session->nmea.last_gsv_sigid);
  1853. /*
  1854. * This check used to be !=0, but we have loosen it a little to let by
  1855. * NMEA 4.1 GSVs with an extra signal-ID field at the end.
  1856. */
  1857. switch (count % 4) {
  1858. case 0:
  1859. /* normal, pre-NMEA 4.10 */
  1860. break;
  1861. case 1:
  1862. /* NMEA 4.10, get the signal ID */
  1863. nmea_sigid = atoi(field[count - 1]);
  1864. ubx_sigid = nmea_sigid_to_ubx(nmea_sigid);
  1865. break;
  1866. default:
  1867. /* bad count */
  1868. GPSD_LOG(LOG_WARN, &session->context->errout,
  1869. "NMEA0183: malformed GPGSV - fieldcount %d %% 4 != 0\n",
  1870. count);
  1871. gpsd_zero_satellites(&session->gpsdata);
  1872. return ONLINE_SET;
  1873. }
  1874. session->nmea.await = atoi(field[1]);
  1875. if ((session->nmea.part = atoi(field[2])) < 1) {
  1876. GPSD_LOG(LOG_WARN, &session->context->errout,
  1877. "NMEA0183: malformed GPGSV - bad part\n");
  1878. gpsd_zero_satellites(&session->gpsdata);
  1879. return ONLINE_SET;
  1880. }
  1881. if (session->nmea.part == 1) {
  1882. /*
  1883. * might have gone from GPGSV to GLGSV/BDGSV/QZGSV,
  1884. * in which case accumulate
  1885. *
  1886. * NMEA 4.1 might have gone from GPGVS,sigid=x to GPGSV,sigid=y
  1887. *
  1888. * session->nmea.last_gsv_talker is zero at cycle start
  1889. */
  1890. if (session->nmea.last_gsv_talker == '\0' ||
  1891. ('P' == GSV_TALKER &&
  1892. 0 == ubx_sigid)) {
  1893. GPSD_LOG(LOG_PROG, &session->context->errout,
  1894. "NMEA0183: x%cGSV: new part %d, last_gsv_talker '%#x', "
  1895. "zeroing\n",
  1896. GSV_TALKER,
  1897. session->nmea.part,
  1898. session->nmea.last_gsv_talker);
  1899. gpsd_zero_satellites(&session->gpsdata);
  1900. }
  1901. session->nmea.last_gsv_talker = GSV_TALKER;
  1902. session->nmea.last_gsv_sigid = ubx_sigid; /* UNUSED */
  1903. switch (GSV_TALKER) {
  1904. case 'A': // GA Galileo
  1905. nmea_gnssid = 3;
  1906. session->nmea.seen_gagsv = true;
  1907. break;
  1908. case 'B': // GB BeiDou
  1909. FALLTHROUGH
  1910. case 'D': // BD BeiDou
  1911. nmea_gnssid = 4;
  1912. session->nmea.seen_bdgsv = true;
  1913. break;
  1914. case 'I': // GI IRNSS
  1915. nmea_gnssid = 6;
  1916. session->nmea.seen_gigsv = true;
  1917. break;
  1918. case 'L': // GL GLONASS
  1919. nmea_gnssid = 2;
  1920. session->nmea.seen_glgsv = true;
  1921. break;
  1922. case 'N': // GN GNSS
  1923. session->nmea.seen_gngsv = true;
  1924. break;
  1925. case 'P': // GP GPS
  1926. session->nmea.seen_gpgsv = true;
  1927. break;
  1928. case 'Q': // GQ, and PQ (Quectel Querk) QZSS
  1929. // Quectel EC25 & EC21 use PQGSA for QZSS
  1930. FALLTHROUGH
  1931. case 'Z': // QZ QZSS
  1932. nmea_gnssid = 5;
  1933. session->nmea.seen_qzgsv = true;
  1934. break;
  1935. default:
  1936. /* uh, what? */
  1937. break;
  1938. }
  1939. }
  1940. for (fldnum = 4; fldnum < count / 4 * 4;) {
  1941. struct satellite_t *sp;
  1942. int nmea_svid;
  1943. if (session->gpsdata.satellites_visible >= MAXCHANNELS) {
  1944. GPSD_LOG(LOG_ERROR, &session->context->errout,
  1945. "NMEA0183: xxGSV: internal error - too many "
  1946. "satellites [%d]!\n",
  1947. session->gpsdata.satellites_visible);
  1948. gpsd_zero_satellites(&session->gpsdata);
  1949. break;
  1950. }
  1951. sp = &session->gpsdata.skyview[session->gpsdata.satellites_visible];
  1952. nmea_svid = atoi(field[fldnum++]);
  1953. if (0 == nmea_svid) {
  1954. /* skip bogus fields */
  1955. continue;
  1956. }
  1957. /* FIXME: this ignores possible NMEA 4.1 Signal ID hint */
  1958. sp->PRN = (short)nmeaid_to_prn(field[0], nmea_svid, nmea_gnssid,
  1959. &sp->gnssid, &sp->svid);
  1960. #ifdef __UNUSED__
  1961. {
  1962. /* debug */
  1963. char ts_buf[TIMESPEC_LEN];
  1964. GPSD_LOG(LOG_ERROR, &session->context->errout,
  1965. "NMEA0183: %s nmeaid_to_prn: nmea_gnssid %d "
  1966. "nmea_satnum %d ubx_gnssid %d ubx_svid %d nmea2_prn %d\n",
  1967. field[0],
  1968. nmea_gnssid, nmea_svid, sp->gnssid, sp->svid, sp->PRN);
  1969. }
  1970. #endif /* __UNUSED__ */
  1971. sp->elevation = (double)atoi(field[fldnum++]);
  1972. sp->azimuth = (double)atoi(field[fldnum++]);
  1973. sp->ss = (double)atoi(field[fldnum++]);
  1974. sp->used = false;
  1975. sp->sigid = ubx_sigid;
  1976. /* sadly NMEA 4.1 does not tell us which sigid (L1, L2) is
  1977. * used. So if the ss is zero, do not mark used */
  1978. if (0 < sp->PRN && 0 < sp->ss) {
  1979. for (n = 0; n < MAXCHANNELS; n++)
  1980. if (session->nmea.sats_used[n] == (unsigned short)sp->PRN) {
  1981. sp->used = true;
  1982. break;
  1983. }
  1984. }
  1985. /*
  1986. * Incrementing this unconditionally falls afoul of chipsets like
  1987. * the Motorola Oncore GT+ that emit empty fields at the end of the
  1988. * last sentence in a GPGSV set if the number of satellites is not
  1989. * a multiple of 4.
  1990. */
  1991. session->gpsdata.satellites_visible++;
  1992. }
  1993. #if __UNUSED
  1994. /* debug code */
  1995. GPSD_LOG(LOG_ERROR, &session->context->errout,
  1996. "NMEA0183: x%cGSV: vis %d bdgsv %d gagsv %d gigsv %d glgsv %d "
  1997. "gngsv %d qpgsv %dqzgsv %d\n",
  1998. GSV_TALKER,
  1999. session->gpsdata.satellites_visible,
  2000. session->nmea.seen_bdgsv,
  2001. session->nmea.seen_gagsv,
  2002. session->nmea.seen_gigsv,
  2003. session->nmea.seen_glgsv,
  2004. session->nmea.seen_gngsv,
  2005. session->nmea.seen_gpgsv,
  2006. session->nmea.seen_qzgsv);
  2007. #endif
  2008. /*
  2009. * Alas, we can't sanity check field counts when there are multiple sat
  2010. * pictures, because the visible member counts *all* satellites - you
  2011. * get a bad result on the second and later SV spans. Note, this code
  2012. * assumes that if any of the special sat pics occur they come right
  2013. * after a stock GPGSV one.
  2014. *
  2015. * FIXME: Add per-talker totals so we can do this check properly.
  2016. */
  2017. if (!(session->nmea.seen_bdgsv ||
  2018. session->nmea.seen_gagsv ||
  2019. session->nmea.seen_gigsv ||
  2020. session->nmea.seen_glgsv ||
  2021. session->nmea.seen_gngsv ||
  2022. session->nmea.seen_qzgsv)) {
  2023. if (session->nmea.part == session->nmea.await
  2024. && atoi(field[3]) != session->gpsdata.satellites_visible) {
  2025. GPSD_LOG(LOG_WARN, &session->context->errout,
  2026. "NMEA0183: xxGSV field 3 value of %d != actual count %d\n",
  2027. atoi(field[3]), session->gpsdata.satellites_visible);
  2028. }
  2029. }
  2030. /* not valid data until we've seen a complete set of parts */
  2031. if (session->nmea.part < session->nmea.await) {
  2032. GPSD_LOG(LOG_PROG, &session->context->errout,
  2033. "NMEA0183: xxGSV: Partial satellite data (%d of %d).\n",
  2034. session->nmea.part, session->nmea.await);
  2035. session->nmea.gsx_more = true;
  2036. return ONLINE_SET;
  2037. }
  2038. session->nmea.gsx_more = false;
  2039. /*
  2040. * This sanity check catches an odd behavior of SiRFstarII receivers.
  2041. * When they can't see any satellites at all (like, inside a
  2042. * building) they sometimes cough up a hairball in the form of a
  2043. * GSV packet with all the azimuth entries 0 (but nonzero
  2044. * elevations). This behavior was observed under SiRF firmware
  2045. * revision 231.000.000_A2.
  2046. */
  2047. for (n = 0; n < session->gpsdata.satellites_visible; n++) {
  2048. if (0 != session->gpsdata.skyview[n].azimuth) {
  2049. goto sane;
  2050. }
  2051. }
  2052. GPSD_LOG(LOG_WARN, &session->context->errout,
  2053. "NMEA0183: xxGSV: Satellite data no good (%d of %d).\n",
  2054. session->nmea.part, session->nmea.await);
  2055. gpsd_zero_satellites(&session->gpsdata);
  2056. return ONLINE_SET;
  2057. sane:
  2058. session->gpsdata.skyview_time.tv_sec = 0;
  2059. session->gpsdata.skyview_time.tv_nsec = 0;
  2060. GPSD_LOG(LOG_DATA, &session->context->errout,
  2061. "NMEA0183: xxGSV: Satellite data OK (%d of %d).\n",
  2062. session->nmea.part, session->nmea.await);
  2063. /* assumes GLGSV or BDGSV group, if present, is emitted after the GPGSV */
  2064. if ((session->nmea.seen_bdgsv ||
  2065. session->nmea.seen_gagsv ||
  2066. session->nmea.seen_gigsv ||
  2067. session->nmea.seen_glgsv ||
  2068. session->nmea.seen_gngsv ||
  2069. session->nmea.seen_qzgsv)
  2070. && GSV_TALKER == 'P')
  2071. return ONLINE_SET;
  2072. #if __UNUSED
  2073. /* debug code */
  2074. GPSD_LOG(LOG_ERROR, &session->context->errout,
  2075. "NMEA0183: x%cGSV: set skyview_time %s frac_time %.2f\n", GSV_TALKER,
  2076. timespec_str(&session->gpsdata.skyview_time, ts_buf, sizeof(ts_buf)),
  2077. session->nmea.this_frac_time);
  2078. #endif
  2079. return SATELLITE_SET;
  2080. #undef GSV_TALKER
  2081. }
  2082. /* Android GNSS super message
  2083. * A stub.
  2084. */
  2085. static gps_mask_t processPGLOR(int c UNUSED, char *field[],
  2086. struct gps_device_t *session)
  2087. {
  2088. /*
  2089. * $PGLOR,0,FIX,....
  2090. * 1 = sentence version (may not be present)
  2091. * 2 = message subtype
  2092. * ....
  2093. *
  2094. * subtypes:
  2095. * $PGLOR,[],AGC - ??
  2096. * $PGLOR,[],CPU - CPU Loading
  2097. * $PGLOR,[],FIN - Request completion status
  2098. * $PGLOR,0,FIX,seconds - Time To Fix
  2099. * $PGLOR,[],FTS - Factory Test Status
  2100. * $PGLOR,[],GFC - GeoFence Fix
  2101. * $PGLOR,[],GLO - ??
  2102. * $PGLOR,[],HLA - Value of HULA sensors
  2103. * $PGLOR,[],IMS - IMES messages
  2104. * $PGLOR,1,LSQ,hhmmss.ss - Least squares GNSS fix
  2105. * $PGLOR,NET - Report network information
  2106. * $PGLOR,[],NEW - Indicate new GPS request
  2107. * $PGLOR,[],PFM - Platform Status
  2108. * $PGLOR,[],PPS - Indicate PPS time corrections
  2109. * $PGLOR,5,PWR i - Power consumption report
  2110. * Only have doc for 5, Quectel uses 4
  2111. * $PGLOR,[],RID - Version Information
  2112. * $PGLOR,2,SAT - GPS Satellite information
  2113. * $PGLOR,[],SIO - Serial I/O status report
  2114. * $PGLOR,[],SPA - Spectrum analyzer results
  2115. * $PGLOR,0,SPD - Speed, Steps, etc.
  2116. * $PGLOR,SPL - ??
  2117. * $PGLOR,[],SPS - ??
  2118. * $PGLOR,10,STA - GLL status
  2119. * $PGLOR,[],SVC - ??
  2120. * $PGLOR,[],SVD - SV Dopplers detected in the false alarm test.
  2121. * $PGLOR,[],SMx - Report GPS Summary Information
  2122. * $PGLOR,[],UNC - ??
  2123. *
  2124. * Are NET and SPL really so different?
  2125. *
  2126. */
  2127. gps_mask_t mask = ONLINE_SET;
  2128. int got_one = 0;
  2129. switch (field[1][0]) {
  2130. case '0':
  2131. if (0 == strncmp("FIX", field[2], 3)) {
  2132. got_one = 1;
  2133. // field 3, time to first fix in seconds
  2134. GPSD_LOG(LOG_DATA, &session->context->errout,
  2135. "NMEA0183: PGLOR: FIX, TTFF %s\n",
  2136. field[3]);
  2137. } else if (0 == strncmp("SPD", field[2], 3)) {
  2138. got_one = 1;
  2139. // field 4, ddmmy.ss UTC
  2140. // field 5, hhmmss.ss UTC
  2141. GPSD_LOG(LOG_DATA, &session->context->errout,
  2142. "NMEA0183: PGLOR: SPD, %s %s UTC\n",
  2143. field[4], field[5]);
  2144. }
  2145. break;
  2146. case '1':
  2147. if (0 == strncmp("LSQ", field[2], 3)) {
  2148. got_one = 1;
  2149. // field 3, hhmmss.ss UTC, only field Quectel supplies
  2150. GPSD_LOG(LOG_DATA, &session->context->errout,
  2151. "NMEA0183: PGLOR: LSQ %s UTC\n",
  2152. field[3]);
  2153. } else if ('0' == field[1][1] && 0 == strncmp("STA", field[2], 3)) {
  2154. // version 10
  2155. got_one = 1;
  2156. // field 3, hhmmss.ss UTC
  2157. // field 7, Position uncertainty meters
  2158. GPSD_LOG(LOG_DATA, &session->context->errout,
  2159. "NMEA0183: PGLOR: STA, UTC %s PosUncer %s\n",
  2160. field[3], field[7]);
  2161. }
  2162. break;
  2163. }
  2164. if (0 != got_one) {
  2165. GPSD_LOG(LOG_DATA, &session->context->errout,
  2166. "NMEA0183: PGLOR: seq %s type %s\n",
  2167. field[1], field[2]);
  2168. }
  2169. return mask;
  2170. }
  2171. /* smart watch sensors
  2172. * A stub.
  2173. */
  2174. static gps_mask_t processPRHS(int c UNUSED, char *field[],
  2175. struct gps_device_t *session)
  2176. {
  2177. /*
  2178. * $PRHS ,type,....
  2179. * type = message type
  2180. *
  2181. * Yes: $PRHS[space],
  2182. *
  2183. * types:
  2184. * $PRHS ,ACC,9.952756,0.37819514,1.3165021,20150305072428436*44
  2185. * $PRHS ,COM,238.09642,16.275442,82.198425,20150305072428824*43
  2186. * $PRHS ,GYR,0.0,0.0,0.0,20150305072428247*4D
  2187. * $PRHS ,LAC,0.23899937,0.009213656,0.02143073,20150305072428437*46
  2188. * $PRHS ,MAG,47.183502,-51.789,-2.7145,20150305072428614*41
  2189. * $PRHS ,ORI,187.86511,-2.1546898,-82.405205,20150305072428614*53
  2190. * $PRHS ,RMC,20150305072427985*55
  2191. *
  2192. */
  2193. gps_mask_t mask = ONLINE_SET;
  2194. GPSD_LOG(LOG_DATA, &session->context->errout,
  2195. "NMEA0183: PRHS: type %s\n",
  2196. field[1]);
  2197. return mask;
  2198. }
  2199. /* Garmin Estimated Position Error */
  2200. static gps_mask_t processPGRME(int c UNUSED, char *field[],
  2201. struct gps_device_t *session)
  2202. {
  2203. /*
  2204. * $PGRME,15.0,M,45.0,M,25.0,M*22
  2205. * 1 = horizontal error estimate
  2206. * 2 = units
  2207. * 3 = vertical error estimate
  2208. * 4 = units
  2209. * 5 = spherical error estimate
  2210. * 6 = units
  2211. * *
  2212. * * Garmin won't say, but the general belief is that these are 50% CEP.
  2213. * * We follow the advice at <http://gpsinformation.net/main/errors.htm>.
  2214. * * If this assumption changes here, it should also change in garmin.c
  2215. * * where we scale error estimates from Garmin binary packets, and
  2216. * * in libgpsd_core.c where we generate $PGRME.
  2217. */
  2218. gps_mask_t mask = ONLINE_SET;
  2219. if ('M' == field[2][0] &&
  2220. 'M' == field[4][0] &&
  2221. 'M' == field[6][0]) {
  2222. session->newdata.epx = session->newdata.epy =
  2223. safe_atof(field[1]) * (1 / sqrt(2))
  2224. * (GPSD_CONFIDENCE / CEP50_SIGMA);
  2225. session->newdata.epv =
  2226. safe_atof(field[3]) * (GPSD_CONFIDENCE / CEP50_SIGMA);
  2227. session->newdata.sep =
  2228. safe_atof(field[5]) * (GPSD_CONFIDENCE / CEP50_SIGMA);
  2229. mask = HERR_SET | VERR_SET | PERR_IS;
  2230. }
  2231. GPSD_LOG(LOG_DATA, &session->context->errout,
  2232. "NMEA0183: PGRME: epx=%.2f epy=%.2f sep=%.2f\n",
  2233. session->newdata.epx,
  2234. session->newdata.epy,
  2235. session->newdata.sep);
  2236. return mask;
  2237. }
  2238. /* Garmin GPS Fix Data Sentence
  2239. *
  2240. * FIXME: seems to happen after cycle ender, so little happens...
  2241. */
  2242. static gps_mask_t processPGRMF(int c UNUSED, char *field[],
  2243. struct gps_device_t *session)
  2244. {
  2245. /*
  2246. * $PGRMF,290,293895,160305,093802,13,5213.1439,N,02100.6511,E,A,2,0,226,2,1*11
  2247. *
  2248. * 1 = GPS week
  2249. * 2 = GPS seconds
  2250. * 3 = UTC Date ddmmyy
  2251. * 4 = UTC time hhmmss
  2252. * 5 = GPS leap seconds
  2253. * 6 = Latitude ddmm.mmmm
  2254. * 7 = N or S
  2255. * 8 = Longitude dddmm.mmmm
  2256. * 9 = E or W
  2257. * 10 = Mode, M = Manual, A = Automatic
  2258. * 11 = Fix type, 0 = No fix, 2 = 2D fix, 2 = 3D fix
  2259. * 12 = Ground Speed, 0 to 1151 km/hr
  2260. * 13 = Course over ground, 0 to 359 degrees true
  2261. * 14 = pdop, 0 to 9
  2262. * 15 = dop, 0 to 9
  2263. */
  2264. gps_mask_t mask = ONLINE_SET;
  2265. timespec_t ts_tow = {0, 0};
  2266. /* Some garmin fail due to GPS Week Roll Over
  2267. * Ignore their UTC date/time, use their GPS week, GPS tow and
  2268. * leap seconds to decide the correct time */
  2269. if (isdigit((int)field[5][0])) {
  2270. session->context->leap_seconds = atoi(field[5]);
  2271. session->context->valid = LEAP_SECOND_VALID;
  2272. }
  2273. if (isdigit((int)field[1][0]) &&
  2274. isdigit((int)field[2][0]) &&
  2275. 0 < session->context->leap_seconds) {
  2276. // have GPS week, tow and leap second
  2277. unsigned short week = atol(field[1]);
  2278. ts_tow.tv_sec = atol(field[2]);
  2279. ts_tow.tv_nsec = 0;
  2280. session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
  2281. mask |= TIME_SET;
  2282. // (long long) cast for 32/64 bit compat
  2283. GPSD_LOG(LOG_SPIN, &session->context->errout,
  2284. "NMEA0183: PGRMF gps time %lld\n",
  2285. (long long)session->newdata.time.tv_sec);
  2286. } else if (0 == merge_hhmmss(field[4], session) &&
  2287. 0 == merge_ddmmyy(field[3], session)) {
  2288. // fall back to UTC if we need and can
  2289. // (long long) cast for 32/64 bit compat
  2290. GPSD_LOG(LOG_SPIN, &session->context->errout,
  2291. "NMEA0183: PGRMF gps time %lld\n",
  2292. (long long)session->newdata.time.tv_sec);
  2293. mask |= TIME_SET;
  2294. }
  2295. if ('A' != field[10][0]) {
  2296. /* Huh? */
  2297. return mask;
  2298. }
  2299. if (0 == do_lat_lon(&field[6], &session->newdata)) {
  2300. mask |= LATLON_SET;
  2301. }
  2302. switch (field[11][0]) {
  2303. default:
  2304. /* Huh? */
  2305. break;
  2306. case '0':
  2307. session->newdata.mode = MODE_NO_FIX;
  2308. mask |= MODE_SET;
  2309. break;
  2310. case '1':
  2311. session->newdata.mode = MODE_2D;
  2312. mask |= MODE_SET;
  2313. break;
  2314. case '2':
  2315. session->newdata.mode = MODE_3D;
  2316. mask |= MODE_SET;
  2317. break;
  2318. }
  2319. session->newdata.speed = safe_atof(field[12]) / MPS_TO_KPH;
  2320. session->newdata.track = safe_atof(field[13]);
  2321. mask |= SPEED_SET | TRACK_SET;
  2322. if ('\0' != field[14][0]) {
  2323. session->gpsdata.dop.pdop = safe_atof(field[14]);
  2324. mask |= DOP_SET;
  2325. }
  2326. if ('\0' != field[15][0]) {
  2327. session->gpsdata.dop.tdop = safe_atof(field[15]);
  2328. mask |= DOP_SET;
  2329. }
  2330. GPSD_LOG(LOG_DATA, &session->context->errout,
  2331. "NMEA0183: PGRMF: pdop %.1f tdop %.1f \n",
  2332. session->gpsdata.dop.pdop,
  2333. session->gpsdata.dop.tdop);
  2334. return mask;
  2335. }
  2336. /* Garmin Map Datum
  2337. *
  2338. * FIXME: seems to happen after cycle ender, so nothing happens...
  2339. */
  2340. static gps_mask_t processPGRMM(int c UNUSED, char *field[],
  2341. struct gps_device_t *session)
  2342. {
  2343. /*
  2344. * $PGRMM,NAD83*29
  2345. * 1 = Map Datum
  2346. */
  2347. gps_mask_t mask = ONLINE_SET;
  2348. if ('\0' != field[1][0]) {
  2349. strlcpy(session->newdata.datum, field[1],
  2350. sizeof(session->newdata.datum));
  2351. }
  2352. GPSD_LOG(LOG_DATA, &session->context->errout,
  2353. "NMEA0183: PGRMM: datum=%.40s\n",
  2354. session->newdata.datum);
  2355. return mask;
  2356. }
  2357. /* Garmin Altitude Information */
  2358. static gps_mask_t processPGRMZ(int c UNUSED, char *field[],
  2359. struct gps_device_t *session)
  2360. {
  2361. /*
  2362. * $PGRMZ,246,f,3*1B
  2363. * 1 = Altitude (probably MSL) in feet
  2364. * 2 = f (feet)
  2365. * 3 = Mode
  2366. * 1 = No Fix
  2367. * 2 = 2D Fix
  2368. * 3 = 3D Fix
  2369. *
  2370. * From: Garmin Proprietary NMEA 0183 Sentences
  2371. * technical Specifications
  2372. * 190-00684-00, Revision C December 2008
  2373. */
  2374. gps_mask_t mask = ONLINE_SET;
  2375. // codacy does not like strlen()
  2376. if ('f' == field[2][0] &&
  2377. 0 < strnlen(field[1], 20)) {
  2378. // have a GPS altitude, must be 3D
  2379. // seems to be altMSL. regressions show this matches GPGGA MSL
  2380. session->newdata.altMSL = atoi(field[1]) * FEET_TO_METERS;
  2381. mask |= (ALTITUDE_SET);
  2382. }
  2383. switch (field[3][0]) {
  2384. default:
  2385. // Huh?
  2386. break;
  2387. case '1':
  2388. session->newdata.mode = MODE_NO_FIX;
  2389. mask |= MODE_SET;
  2390. break;
  2391. case '2':
  2392. session->newdata.mode = MODE_2D;
  2393. mask |= MODE_SET;
  2394. break;
  2395. case '3':
  2396. session->newdata.mode = MODE_3D;
  2397. mask |= MODE_SET;
  2398. break;
  2399. }
  2400. GPSD_LOG(LOG_DATA, &session->context->errout,
  2401. "NMEA0183: PGRMZ: altMSL %.2f mode %d\n",
  2402. session->newdata.altMSL,
  2403. session->newdata.mode);
  2404. return mask;
  2405. }
  2406. // Magellan Status
  2407. static gps_mask_t processPMGNST(int c UNUSED, char *field[],
  2408. struct gps_device_t *session)
  2409. {
  2410. /*
  2411. * $PMGNST,01.75,3,T,816,11.1,-00496,00*43
  2412. * 1 = Firmware version number
  2413. * 2 = Mode (1 = no fix, 2 = 2D fix, 3 = 3D fix)
  2414. * 3 = T if we have a fix
  2415. * 4 = battery percentage left in tenths of a percent
  2416. * 5 = time left on the GPS battery in hours
  2417. * 6 = numbers change (freq. compensation?)
  2418. * 7 = PRN number receiving current focus
  2419. */
  2420. gps_mask_t mask = ONLINE_SET;
  2421. int newmode = atoi(field[3]);
  2422. if ('T' == field[4][0]) {
  2423. switch(newmode) {
  2424. default:
  2425. session->newdata.mode = MODE_NO_FIX;
  2426. break;
  2427. case 2:
  2428. session->newdata.mode = MODE_2D;
  2429. break;
  2430. case 3:
  2431. session->newdata.mode = MODE_3D;
  2432. break;
  2433. }
  2434. } else {
  2435. /* Can report 3D fix, but 'F' for no fix */
  2436. session->newdata.mode = MODE_NO_FIX;
  2437. }
  2438. mask |= MODE_SET;
  2439. GPSD_LOG(LOG_DATA, &session->context->errout,
  2440. "NMEA0183: PMGNST: mode: %d\n",
  2441. session->newdata.mode);
  2442. return mask;
  2443. }
  2444. /* SiRF Estimated Position Errors */
  2445. static gps_mask_t processPSRFEPE(int c UNUSED, char *field[],
  2446. struct gps_device_t *session)
  2447. {
  2448. /*
  2449. * $PSRFEPE,100542.000,A,0.7,6.82,10.69,0.0,180.0*24
  2450. * 1 = UTC Time hhmmss.sss
  2451. * 2 = Status. A = Valid, V = Data not valid
  2452. * 3 = HDOP
  2453. * 4 = EHPE meters (Estimated Horizontal Position Error)
  2454. * 5 = EVPE meters (Estimated Vertical Position Error)
  2455. * 6 = EHVE meters (Estimated Speed Over Ground/Velocity Error)
  2456. * 7 = EHE degrees (Estimated Heading Error)
  2457. *
  2458. * SiRF won't say if these are 1-sigma or what...
  2459. */
  2460. gps_mask_t mask = STATUS_SET;
  2461. /* get time/ valid or not */
  2462. if ('\0' != field[1][0]) {
  2463. if (0 == merge_hhmmss(field[1], session)) {
  2464. register_fractional_time(field[0], field[1], session);
  2465. if (session->nmea.date.tm_year == 0) {
  2466. GPSD_LOG(LOG_WARN, &session->context->errout,
  2467. "NMEA0183: can't use PSRFEPE time until after ZDA "
  2468. "or RMC has supplied a year.\n");
  2469. } else {
  2470. mask |= TIME_SET;
  2471. }
  2472. }
  2473. }
  2474. if ('A' != field[2][0]) {
  2475. /* Huh? */
  2476. return mask;
  2477. }
  2478. if ('\0' != field[3][0]) {
  2479. /* This adds nothing, it just agrees with the gpsd calculation
  2480. * from the skyview. Which is a nice confirmation. */
  2481. session->gpsdata.dop.hdop = safe_atof(field[3]);
  2482. mask |= DOP_SET;
  2483. }
  2484. if ('\0' != field[4][0]) {
  2485. /* EHPE (Estimated Horizontal Position Error) */
  2486. session->newdata.eph = safe_atof(field[4]);
  2487. mask |= HERR_SET;
  2488. }
  2489. if ('\0' != field[5][0]) {
  2490. /* Estimated Vertical Position Error (meters, 0.01 resolution) */
  2491. session->newdata.epv = safe_atof(field[5]);
  2492. mask |= VERR_SET;
  2493. }
  2494. if ('\0' != field[6][0]) {
  2495. /* Estimated Horizontal Speed Error meters/sec */
  2496. session->newdata.eps = safe_atof(field[6]);
  2497. }
  2498. if ('\0' != field[7][0]) {
  2499. /* Estimated Heading Error degrees */
  2500. session->newdata.epd = safe_atof(field[7]);
  2501. }
  2502. GPSD_LOG(LOG_PROG, &session->context->errout,
  2503. "NMEA0183: PSRFEPE: hdop=%.1f eph=%.1f epv=%.1f eps=%.1f "
  2504. "epd=%.1f\n",
  2505. session->gpsdata.dop.hdop,
  2506. session->newdata.eph,
  2507. session->newdata.epv,
  2508. session->newdata.eps,
  2509. session->newdata.epd);
  2510. return mask;
  2511. }
  2512. /* NMEA Map Datum
  2513. *
  2514. * FIXME: seems to happen after cycle ender, so nothing happens...
  2515. */
  2516. static gps_mask_t processDTM(int c UNUSED, char *field[],
  2517. struct gps_device_t *session)
  2518. {
  2519. /*
  2520. * $GPDTM,W84,C*52
  2521. * $GPDTM,xxx,x,xx.xxxx,x,xx.xxxx,x,,xxx*hh<CR><LF>
  2522. * 1 = Local datum code (xxx):
  2523. * W84 – WGS84
  2524. * W72 – WGS72
  2525. * S85 – SGS85
  2526. * P90 – PE90
  2527. * 999 – User defined
  2528. * IHO datum code
  2529. * 2 = Local datum sub code (x)
  2530. * 3 = Latitude offset in minutes (xx.xxxx)
  2531. * 4 = Latitude offset mark (N: +, S: -) (x)
  2532. * 5 = Longitude offset in minutes (xx.xxxx)
  2533. * 6 = Longitude offset mark (E: +, W: -) (x)
  2534. * 7 = Altitude offset in meters. Always null
  2535. * 8 = Datum (xxx):
  2536. * W84 – WGS84
  2537. * W72 – WGS72
  2538. * S85 – SGS85
  2539. * P90 – PE90
  2540. * 999 – User defined
  2541. * IHO datum code
  2542. * 9 = checksum
  2543. */
  2544. int i;
  2545. static struct
  2546. {
  2547. char *code;
  2548. char *name;
  2549. } codes[] = {
  2550. {"W84", "WGS84"},
  2551. {"W72", "WGS72"},
  2552. {"S85", "SGS85"},
  2553. {"P90", "PE90"},
  2554. {"999", "User Defined"},
  2555. {"", ""},
  2556. };
  2557. gps_mask_t mask = ONLINE_SET;
  2558. if ('\0' == field[1][0]) {
  2559. return mask;
  2560. }
  2561. for (i = 0; ; i++) {
  2562. if ('\0' == codes[i].code[0]) {
  2563. /* not found */
  2564. strlcpy(session->newdata.datum, field[1],
  2565. sizeof(session->newdata.datum));
  2566. break;
  2567. }
  2568. if (0 ==strcmp(codes[i].code, field[1])) {
  2569. strlcpy(session->newdata.datum, codes[i].name,
  2570. sizeof(session->newdata.datum));
  2571. break;
  2572. }
  2573. }
  2574. GPSD_LOG(LOG_DATA, &session->context->errout,
  2575. "NMEA0183: xxDTM: datum=%.40s\n",
  2576. session->newdata.datum);
  2577. return mask;
  2578. }
  2579. /* NMEA 3.0 Estimated Position Error */
  2580. static gps_mask_t processGBS(int c UNUSED, char *field[],
  2581. struct gps_device_t *session)
  2582. {
  2583. /*
  2584. * $GPGBS,082941.00,2.4,1.5,3.9,25,,-43.7,27.5*65
  2585. * 1) UTC time of the fix associated with this sentence (hhmmss.ss)
  2586. * 2) Expected error in latitude (meters)
  2587. * 3) Expected error in longitude (meters)
  2588. * 4) Expected error in altitude (meters)
  2589. * 5) PRN of most likely failed satellite
  2590. * 6) Probability of missed detection for most likely failed satellite
  2591. * 7) Estimate of bias in meters on most likely failed satellite
  2592. * 8) Standard deviation of bias estimate
  2593. * 9) NMEA 4.1 GNSS ID
  2594. * 10) NMEA 4.1 Signal ID
  2595. * Checksum
  2596. *
  2597. * Fields 2, 3 and 4 are one standard deviation.
  2598. */
  2599. gps_mask_t mask = ONLINE_SET;
  2600. /* register fractional time for end-of-cycle detection */
  2601. register_fractional_time(field[0], field[1], session);
  2602. /* check that we're associated with the current fix */
  2603. if (session->nmea.date.tm_hour == DD(field[1])
  2604. && session->nmea.date.tm_min == DD(field[1] + 2)
  2605. && session->nmea.date.tm_sec == DD(field[1] + 4)) {
  2606. session->newdata.epy = safe_atof(field[2]);
  2607. session->newdata.epx = safe_atof(field[3]);
  2608. session->newdata.epv = safe_atof(field[4]);
  2609. GPSD_LOG(LOG_DATA, &session->context->errout,
  2610. "NMEA0183: GBS: epx=%.2f epy=%.2f epv=%.2f\n",
  2611. session->newdata.epx,
  2612. session->newdata.epy,
  2613. session->newdata.epv);
  2614. mask = HERR_SET | VERR_SET;
  2615. } else {
  2616. GPSD_LOG(LOG_PROG, &session->context->errout,
  2617. "NMEA0183: second in $GPGBS error estimates doesn't match.\n");
  2618. }
  2619. return mask;
  2620. }
  2621. static gps_mask_t processZDA(int c UNUSED, char *field[],
  2622. struct gps_device_t *session)
  2623. /* Time & Date */
  2624. {
  2625. /*
  2626. * $GPZDA,160012.71,11,03,2004,-1,00*7D
  2627. * 1) UTC time (hours, minutes, seconds, may have fractional subsecond)
  2628. * 2) Day, 01 to 31
  2629. * 3) Month, 01 to 12
  2630. * 4) Year (4 digits)
  2631. * 5) Local zone description, 00 to +- 13 hours
  2632. * 6) Local zone minutes description, apply same sign as local hours
  2633. * 7) Checksum
  2634. *
  2635. * Note: some devices, like the u-blox ANTARIS 4h, are known to ship ZDAs
  2636. * with some fields blank under poorly-understood circumstances (probably
  2637. * when they don't have satellite lock yet).
  2638. */
  2639. gps_mask_t mask = ONLINE_SET;
  2640. int year, mon, mday, century;
  2641. if (field[1][0] == '\0' || field[2][0] == '\0' || field[3][0] == '\0'
  2642. || field[4][0] == '\0') {
  2643. GPSD_LOG(LOG_WARN, &session->context->errout,
  2644. "NMEA0183: ZDA fields are empty\n");
  2645. return mask;
  2646. }
  2647. if (0 != merge_hhmmss(field[1], session)) {
  2648. /* bad time */
  2649. return mask;
  2650. }
  2651. /*
  2652. * We don't register fractional time here because want to leave
  2653. * ZDA out of end-of-cycle detection. Some devices sensibly emit it only
  2654. * when they have a fix, so watching for it can make them look
  2655. * like they have a variable fix reporting cycle.
  2656. */
  2657. year = atoi(field[4]);
  2658. mon = atoi(field[3]);
  2659. mday = atoi(field[2]);
  2660. century = year - year % 100;
  2661. if ( (1900 > year ) || (2200 < year ) ) {
  2662. GPSD_LOG(LOG_WARN, &session->context->errout,
  2663. "NMEA0183: malformed ZDA year: %s\n", field[4]);
  2664. } else if ( (1 > mon ) || (12 < mon ) ) {
  2665. GPSD_LOG(LOG_WARN, &session->context->errout,
  2666. "NMEA0183: malformed ZDA month: %s\n", field[3]);
  2667. } else if ( (1 > mday ) || (31 < mday ) ) {
  2668. GPSD_LOG(LOG_WARN, &session->context->errout,
  2669. "NMEA0183: malformed ZDA day: %s\n", field[2]);
  2670. } else {
  2671. char ts_buf[TIMESPEC_LEN];
  2672. gpsd_century_update(session, century);
  2673. session->nmea.date.tm_year = year - 1900;
  2674. session->nmea.date.tm_mon = mon - 1;
  2675. session->nmea.date.tm_mday = mday;
  2676. if (true == session->context->batteryRTC) {
  2677. // user wants to live dangerously
  2678. session->newdata.time = gpsd_utc_resolve(session);
  2679. GPSD_LOG(LOG_DATA, &session->context->errout,
  2680. "NMEA0183: ZDA badtime %s\n",
  2681. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)));
  2682. }
  2683. mask = TIME_SET;
  2684. }
  2685. return mask;
  2686. }
  2687. static gps_mask_t processHDG(int c UNUSED, char *field[],
  2688. struct gps_device_t *session)
  2689. {
  2690. /*
  2691. * $SDHDG,234.6,,,1.3,E*34
  2692. *
  2693. * $--HDG,h.h,d.d,a,v.v,a*hh<CR><LF>
  2694. * Magnetic sensor heading, degrees
  2695. * Magnetic deviation, degrees E/W
  2696. * Magnetic variation, degrees, E/W
  2697. *
  2698. * 1. To obtain Magnetic Heading:
  2699. * Add Easterly deviation (E) to Magnetic Sensor Reading
  2700. * Subtract Westerly deviation (W) from Magnetic Sensor Reading
  2701. * 2. To obtain True Heading:
  2702. * Add Easterly variation (E) to Magnetic Heading
  2703. * Subtract Westerly variation (W) from Magnetic Heading
  2704. * 3. Variation and deviation fields shall be null fields if unknown.
  2705. */
  2706. gps_mask_t mask = ONLINE_SET;
  2707. double sensor_heading;
  2708. double magnetic_deviation;
  2709. if ('\0' == field[1][0]) {
  2710. // no data
  2711. return mask;
  2712. }
  2713. sensor_heading = safe_atof(field[1]);
  2714. if ((0.0 > sensor_heading) ||
  2715. (360.0 < sensor_heading)) {
  2716. // bad data */
  2717. return mask;
  2718. }
  2719. magnetic_deviation = safe_atof(field[2]);
  2720. if ((0.0 > magnetic_deviation) ||
  2721. (360.0 < magnetic_deviation)) {
  2722. // bad data
  2723. return mask;
  2724. }
  2725. switch (field[2][0]) {
  2726. case 'E':
  2727. sensor_heading += magnetic_deviation;
  2728. break;
  2729. case 'W':
  2730. sensor_heading += magnetic_deviation;
  2731. break;
  2732. default:
  2733. // ignore
  2734. break;
  2735. }
  2736. // good data
  2737. session->newdata.magnetic_track = sensor_heading;
  2738. mask |= MAGNETIC_TRACK_SET;
  2739. // get magnetic variation
  2740. if ('\0' != field[3][0] &&
  2741. '\0' != field[4][0]) {
  2742. session->newdata.magnetic_var = safe_atof(field[3]);
  2743. switch (field[4][0]) {
  2744. case 'E':
  2745. // no change
  2746. mask |= MAGNETIC_TRACK_SET;
  2747. break;
  2748. case 'W':
  2749. session->newdata.magnetic_var = -session->newdata.magnetic_var;
  2750. mask |= MAGNETIC_TRACK_SET;
  2751. break;
  2752. default:
  2753. // huh?
  2754. session->newdata.magnetic_var = NAN;
  2755. break;
  2756. }
  2757. }
  2758. GPSD_LOG(LOG_DATA, &session->context->errout,
  2759. "NMEA0183: $SDHDG heading %lf var %.1f\n",
  2760. session->newdata.magnetic_track,
  2761. session->newdata.magnetic_var);
  2762. return mask;
  2763. }
  2764. static gps_mask_t processHDT(int c UNUSED, char *field[],
  2765. struct gps_device_t *session)
  2766. {
  2767. /*
  2768. * $HEHDT,341.8,T*21
  2769. *
  2770. * HDT,x.x*hh<cr><lf>
  2771. *
  2772. * The only data field is true heading in degrees.
  2773. * The following field is required to be 'T' indicating a true heading.
  2774. * It is followed by a mandatory nmea_checksum.
  2775. */
  2776. gps_mask_t mask = ONLINE_SET;
  2777. double heading;
  2778. if ('\0' == field[1][0]) {
  2779. // no data
  2780. return mask;
  2781. }
  2782. heading = safe_atof(field[1]);
  2783. if ((0.0 > heading) ||
  2784. (360.0 < heading)) {
  2785. // bad data
  2786. return mask;
  2787. }
  2788. // good data
  2789. gps_clear_att(&session->gpsdata.attitude);
  2790. // True heading
  2791. session->gpsdata.attitude.heading = heading;
  2792. mask |= (ATTITUDE_SET);
  2793. GPSD_LOG(LOG_PROG, &session->context->errout,
  2794. "NMEA0183: $HEHDT heading %lf.\n",
  2795. session->gpsdata.attitude.heading);
  2796. return mask;
  2797. }
  2798. static gps_mask_t processDBT(int c UNUSED, char *field[],
  2799. struct gps_device_t *session)
  2800. {
  2801. /*
  2802. * $SDDBT,7.7,f,2.3,M,1.3,F*05
  2803. * 1) Depth below sounder in feet
  2804. * 2) Fixed value 'f' indicating feet
  2805. * 3) Depth below sounder in meters
  2806. * 4) Fixed value 'M' indicating meters
  2807. * 5) Depth below sounder in fathoms
  2808. * 6) Fixed value 'F' indicating fathoms
  2809. * 7) Checksum.
  2810. *
  2811. * In real-world sensors, sometimes not all three conversions are reported.
  2812. */
  2813. gps_mask_t mask = ONLINE_SET;
  2814. if ('\0' != field[3][0]) {
  2815. session->newdata.depth = safe_atof(field[3]);
  2816. mask |= (ALTITUDE_SET);
  2817. } else if ('\0' != field[1][0]) {
  2818. session->newdata.depth = safe_atof(field[1]) * FEET_TO_METERS;
  2819. mask |= (ALTITUDE_SET);
  2820. } else if ('\0' != field[5][0]) {
  2821. session->newdata.depth = safe_atof(field[5]) * FATHOMS_TO_METERS;
  2822. mask |= (ALTITUDE_SET);
  2823. }
  2824. GPSD_LOG(LOG_DATA, &session->context->errout,
  2825. "NMEA0183: mode %d, depth %lf.\n",
  2826. session->newdata.mode,
  2827. session->newdata.depth);
  2828. return mask;
  2829. }
  2830. // $xxTHS -- True Heading and Status
  2831. static gps_mask_t processTHS(int c UNUSED, char *field[],
  2832. struct gps_device_t *session)
  2833. {
  2834. /*
  2835. * $GNTHS,121.15.A*1F<CR><LF>
  2836. * 1 - Heading, degrees True
  2837. * 2 - Mode indicator
  2838. * 'A’ = Autonomous
  2839. * 'E’ = Estimated (dead reckoning)
  2840. * 'M’ = Manual input
  2841. * 'S’ = Simulator
  2842. * 'V’ = Data not valid
  2843. * 3 - Checksum
  2844. */
  2845. gps_mask_t mask = ONLINE_SET;
  2846. double heading;
  2847. if ('\0' == field[1][0] ||
  2848. '\0' == field[2][0]) {
  2849. // no data
  2850. return mask;
  2851. }
  2852. if ('V' == field[2][0]) {
  2853. // invalid data
  2854. // ignore A, E, M and S for now
  2855. return mask;
  2856. }
  2857. heading = safe_atof(field[1]);
  2858. if ((0.0 > heading) ||
  2859. (360.0 < heading)) {
  2860. // bad data
  2861. return mask;
  2862. }
  2863. GPSD_LOG(LOG_PROG, &session->context->errout,
  2864. "NMEA0183: $xxTHS heading %lf mode %s\n",
  2865. heading, field[2]);
  2866. return mask;
  2867. }
  2868. // GPS Text message
  2869. static gps_mask_t processTXT(int count, char *field[],
  2870. struct gps_device_t *session)
  2871. {
  2872. /*
  2873. * $GNTXT,01,01,01,PGRM inv format*2A
  2874. * 1 Number of sentences for full data
  2875. * 1 Sentence 1 of 1
  2876. * 01 Message type
  2877. * 00 - error
  2878. * 01 - warning
  2879. * 02 - notice
  2880. * 07 - user
  2881. * PGRM inv format ASCII text
  2882. *
  2883. * Can occur with talker IDs:
  2884. * BD (Beidou),
  2885. * GA (Galileo),
  2886. * GB (Beidou),
  2887. * GI (IRNSS
  2888. * GL (GLONASS),
  2889. * GN (GLONASS, any combination GNSS),
  2890. * GP (GPS, SBAS, QZSS),
  2891. * GQ (QZSS).
  2892. * PQ (QZSS). Quectel Quirk
  2893. * QZ (QZSS).
  2894. */
  2895. gps_mask_t mask = ONLINE_SET;
  2896. int msgType = 0;
  2897. char *msgType_txt = "Unknown";
  2898. if (5 != count) {
  2899. return mask;
  2900. }
  2901. msgType = atoi(field[3]);
  2902. switch ( msgType ) {
  2903. case 0:
  2904. msgType_txt = "Error";
  2905. break;
  2906. case 1:
  2907. msgType_txt = "Warning";
  2908. break;
  2909. case 2:
  2910. msgType_txt = "Notice";
  2911. break;
  2912. case 7:
  2913. msgType_txt = "User";
  2914. break;
  2915. }
  2916. /* maximum text length unknown, guess 80 */
  2917. GPSD_LOG(LOG_WARN, &session->context->errout,
  2918. "NMEA0183: TXT: %.10s: %.80s\n",
  2919. msgType_txt, field[4]);
  2920. return mask;
  2921. }
  2922. static gps_mask_t processTNTHTM(int c UNUSED, char *field[],
  2923. struct gps_device_t *session)
  2924. {
  2925. /*
  2926. * Proprietary sentence for True North Technologies Magnetic Compass.
  2927. * This may also apply to some Honeywell units since they may have been
  2928. * designed by True North.
  2929. $PTNTHTM,14223,N,169,N,-43,N,13641,2454*15
  2930. HTM,x.x,a,x.x,a,x.x,a,x.x,x.x*hh<cr><lf>
  2931. Fields in order:
  2932. 1. True heading (compass measurement + deviation + variation)
  2933. 2. magnetometer status character:
  2934. C = magnetometer calibration alarm
  2935. L = low alarm
  2936. M = low warning
  2937. N = normal
  2938. O = high warning
  2939. P = high alarm
  2940. V = magnetometer voltage level alarm
  2941. 3. pitch angle
  2942. 4. pitch status character - see field 2 above
  2943. 5. roll angle
  2944. 6. roll status character - see field 2 above
  2945. 7. dip angle
  2946. 8. relative magnitude horizontal component of earth's magnetic field
  2947. *hh mandatory nmea_checksum
  2948. By default, angles are reported as 26-bit integers: weirdly, the
  2949. technical manual says either 0 to 65535 or -32768 to 32767 can
  2950. occur as a range.
  2951. */
  2952. gps_mask_t mask = ONLINE_SET;
  2953. // True heading
  2954. session->gpsdata.attitude.heading = safe_atof(field[1]);
  2955. session->gpsdata.attitude.mag_st = *field[2];
  2956. session->gpsdata.attitude.pitch = safe_atof(field[3]);
  2957. session->gpsdata.attitude.pitch_st = *field[4];
  2958. session->gpsdata.attitude.roll = safe_atof(field[5]);
  2959. session->gpsdata.attitude.roll_st = *field[6];
  2960. session->gpsdata.attitude.dip = safe_atof(field[7]);
  2961. session->gpsdata.attitude.mag_x = safe_atof(field[8]);
  2962. mask |= (ATTITUDE_SET);
  2963. GPSD_LOG(LOG_DATA, &session->context->errout,
  2964. "NMEA0183: $PTNTHTM heading %lf (%c).\n",
  2965. session->gpsdata.attitude.heading,
  2966. session->gpsdata.attitude.mag_st);
  2967. return mask;
  2968. }
  2969. static gps_mask_t processTNTA(int c UNUSED, char *field[],
  2970. struct gps_device_t *session)
  2971. {
  2972. /*
  2973. * Proprietary sentence for iSync GRClok/LNRClok.
  2974. $PTNTA,20000102173852,1,T4,,,6,1,0*32
  2975. 1. Date/time in format year, month, day, hour, minute, second
  2976. 2. Oscillator quality 0:warming up, 1:freerun, 2:disciplined.
  2977. 3. Always T4. Format indicator.
  2978. 4. Interval ppsref-ppsout in [ns]. Blank if no ppsref.
  2979. 5. Fine phase comparator in approx. [ns]. Always close to -500 or
  2980. +500 if not disciplined. Blank if no ppsref.
  2981. 6. iSync Status. 0:warming up or no light, 1:tracking set-up,
  2982. 2:track to PPSREF, 3:synch to PPSREF, 4:Free Run. Track OFF,
  2983. 5:FR. PPSREF unstable, 6:FR. No PPSREF, 7:FREEZE, 8:factory
  2984. used, 9:searching Rb line
  2985. 7. GPS messages indicator. 0:do not take account, 1:take account,
  2986. but no message, 2:take account, partially ok, 3:take account,
  2987. totally ok.
  2988. 8. Transfer quality of date/time. 0:no, 1:manual, 2:GPS, older
  2989. than x hours, 3:GPS, fresh.
  2990. */
  2991. gps_mask_t mask = ONLINE_SET;
  2992. if (0 == strcmp(field[3], "T4")) {
  2993. struct oscillator_t *osc = &session->gpsdata.osc;
  2994. unsigned int quality = atoi(field[2]);
  2995. unsigned int delta = atoi(field[4]);
  2996. unsigned int fine = atoi(field[5]);
  2997. unsigned int status = atoi(field[6]);
  2998. char deltachar = field[4][0];
  2999. osc->running = (0 < quality);
  3000. osc->reference = (deltachar && (deltachar != '?'));
  3001. if (osc->reference) {
  3002. if (500 > delta) {
  3003. osc->delta = fine;
  3004. } else {
  3005. osc->delta = ((delta < 500000000) ? delta : 1000000000 - delta);
  3006. }
  3007. } else {
  3008. osc->delta = 0;
  3009. }
  3010. osc->disciplined = ((quality == 2) && (status == 3));
  3011. mask |= OSCILLATOR_SET;
  3012. GPSD_LOG(LOG_DATA, &session->context->errout,
  3013. "NMEA0183: PTNTA,T4: quality=%s, delta=%s, fine=%s,"
  3014. "status=%s\n",
  3015. field[2], field[4], field[5], field[6]);
  3016. }
  3017. return mask;
  3018. }
  3019. #ifdef OCEANSERVER_ENABLE
  3020. static gps_mask_t processOHPR(int c UNUSED, char *field[],
  3021. struct gps_device_t *session)
  3022. {
  3023. /*
  3024. * Proprietary sentence for OceanServer Magnetic Compass.
  3025. OHPR,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x*hh<cr><lf>
  3026. Fields in order:
  3027. 1. Azimuth
  3028. 2. Pitch Angle
  3029. 3. Roll Angle
  3030. 4. Sensor temp, degrees centigrade
  3031. 5. Depth (feet)
  3032. 6. Magnetic Vector Length
  3033. 7-9. 3 axis Magnetic Field readings x,y,z
  3034. 10. Acceleration Vector Length
  3035. 11-13. 3 axis Acceleration Readings x,y,z
  3036. 14. Reserved
  3037. 15-16. 2 axis Gyro Output, X,y
  3038. 17. Reserved
  3039. 18. Reserved
  3040. *hh mandatory nmea_checksum
  3041. */
  3042. gps_mask_t mask = ONLINE_SET;
  3043. // True heading?
  3044. session->gpsdata.attitude.heading = safe_atof(field[1]);
  3045. session->gpsdata.attitude.pitch = safe_atof(field[2]);
  3046. session->gpsdata.attitude.roll = safe_atof(field[3]);
  3047. session->gpsdata.attitude.temp = safe_atof(field[4]);
  3048. session->gpsdata.attitude.depth = safe_atof(field[5]) * FEET_TO_METERS;
  3049. session->gpsdata.attitude.mag_len = safe_atof(field[6]);
  3050. session->gpsdata.attitude.mag_x = safe_atof(field[7]);
  3051. session->gpsdata.attitude.mag_y = safe_atof(field[8]);
  3052. session->gpsdata.attitude.mag_z = safe_atof(field[9]);
  3053. session->gpsdata.attitude.acc_len = safe_atof(field[10]);
  3054. session->gpsdata.attitude.acc_x = safe_atof(field[11]);
  3055. session->gpsdata.attitude.acc_y = safe_atof(field[12]);
  3056. session->gpsdata.attitude.acc_z = safe_atof(field[13]);
  3057. session->gpsdata.attitude.gyro_x = safe_atof(field[15]);
  3058. session->gpsdata.attitude.gyro_y = safe_atof(field[16]);
  3059. mask |= (ATTITUDE_SET);
  3060. GPSD_LOG(LOG_DATA, &session->context->errout,
  3061. "NMEA0183: Heading %lf.\n", session->gpsdata.attitude.heading);
  3062. return mask;
  3063. }
  3064. #endif // OCEANSERVER_ENABLE
  3065. /* Ashtech sentences take this format:
  3066. * $PASHDR,type[,val[,val]]*CS
  3067. * type is an alphabetic subsentence type
  3068. *
  3069. * Oxford Technical Solutions (OxTS) also uses the $PASHR sentence,
  3070. * but with a very different sentence contents:
  3071. * $PASHR,HHMMSS.SSS,HHH.HH,T,RRR.RR,PPP.PP,aaa.aa,r.rrr,p.ppp,h.hhh,Q1,Q2*CS
  3072. *
  3073. * so field 1 in ASHTECH is always alphabetic and numeric in OXTS
  3074. *
  3075. */
  3076. static gps_mask_t processPASHR(int c UNUSED, char *field[],
  3077. struct gps_device_t *session)
  3078. {
  3079. gps_mask_t mask = ONLINE_SET;
  3080. char ts_buf[TIMESPEC_LEN];
  3081. if (0 == strcmp("ACK", field[1])) {
  3082. // ACK
  3083. GPSD_LOG(LOG_DATA, &session->context->errout, "NMEA0183: PASHR,ACK\n");
  3084. return ONLINE_SET;
  3085. } else if (0 == strcmp("MCA", field[1])) {
  3086. // MCA, raw data
  3087. GPSD_LOG(LOG_DATA, &session->context->errout, "NMEA0183: PASHR,MCA\n");
  3088. return ONLINE_SET;
  3089. } else if (0 == strcmp("NAK", field[1])) {
  3090. // NAK
  3091. GPSD_LOG(LOG_DATA, &session->context->errout, "NMEA0183: PASHR,NAK\n");
  3092. return ONLINE_SET;
  3093. } else if (0 == strcmp("PBN", field[1])) {
  3094. // PBN, position data
  3095. // FIXME: decode this for ECEF
  3096. GPSD_LOG(LOG_DATA, &session->context->errout, "NMEA0183: PASHR,PBN\n");
  3097. return ONLINE_SET;
  3098. } else if (0 == strcmp("POS", field[1])) { // 3D Position
  3099. /* $PASHR,POS,
  3100. *
  3101. * 2: position type:
  3102. * 0 = autonomous
  3103. * 1 = position differentially corrected with RTCM code
  3104. * 2 = position differentially corrected with CPD float solution
  3105. * 3 = position is CPD fixed solution
  3106. */
  3107. mask |= MODE_SET | STATUS_SET | CLEAR_IS;
  3108. if ('\0' == field[2][0]) {
  3109. // empty first field means no 3D fix is available
  3110. session->newdata.status = STATUS_UNK;
  3111. session->newdata.mode = MODE_NO_FIX;
  3112. } else {
  3113. int satellites_used;
  3114. // if we make it this far, we at least have a 3D fix
  3115. session->newdata.mode = MODE_3D;
  3116. if (1 <= atoi(field[2]))
  3117. session->newdata.status = STATUS_DGPS;
  3118. else
  3119. session->newdata.status = STATUS_GPS;
  3120. /* don't use as this breaks the GPGSV counter
  3121. * session->gpsdata.satellites_used = atoi(field[3]); */
  3122. satellites_used = atoi(field[3]);
  3123. if (0 == merge_hhmmss(field[4], session)) {
  3124. register_fractional_time(field[0], field[4], session);
  3125. mask |= TIME_SET;
  3126. }
  3127. if (0 == do_lat_lon(&field[5], &session->newdata)) {
  3128. mask |= LATLON_SET;
  3129. if ('\0' != field[9][0]) {
  3130. // altitude is already WGS 84
  3131. session->newdata.altHAE = safe_atof(field[9]);
  3132. mask |= ALTITUDE_SET;
  3133. }
  3134. }
  3135. session->newdata.track = safe_atof(field[11]);
  3136. session->newdata.speed = safe_atof(field[12]) / MPS_TO_KPH;
  3137. session->newdata.climb = safe_atof(field[13]);
  3138. if ('\0' != field[14][0]) {
  3139. session->gpsdata.dop.pdop = safe_atof(field[14]);
  3140. mask |= DOP_SET;
  3141. }
  3142. if ('\0' != field[15][0]) {
  3143. session->gpsdata.dop.hdop = safe_atof(field[15]);
  3144. mask |= DOP_SET;
  3145. }
  3146. if ('\0' != field[16][0]) {
  3147. session->gpsdata.dop.vdop = safe_atof(field[16]);
  3148. mask |= DOP_SET;
  3149. }
  3150. if ('\0' != field[17][0]) {
  3151. session->gpsdata.dop.tdop = safe_atof(field[17]);
  3152. mask |= DOP_SET;
  3153. }
  3154. mask |= (SPEED_SET | TRACK_SET | CLIMB_SET);
  3155. GPSD_LOG(LOG_DATA, &session->context->errout,
  3156. "NMEA0183: PASHR,POS: hhmmss=%s lat=%.2f lon=%.2f"
  3157. " altHAE=%.f"
  3158. " speed=%.2f track=%.2f climb=%.2f mode=%d status=%d"
  3159. " pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f used=%d\n",
  3160. field[4], session->newdata.latitude,
  3161. session->newdata.longitude, session->newdata.altHAE,
  3162. session->newdata.speed, session->newdata.track,
  3163. session->newdata.climb, session->newdata.mode,
  3164. session->newdata.status, session->gpsdata.dop.pdop,
  3165. session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
  3166. session->gpsdata.dop.tdop, satellites_used);
  3167. }
  3168. } else if (0 == strcmp("RID", field[1])) { // Receiver ID
  3169. (void)snprintf(session->subtype, sizeof(session->subtype) - 1,
  3170. "%s ver %s", field[2], field[3]);
  3171. GPSD_LOG(LOG_DATA, &session->context->errout,
  3172. "NMEA0183: PASHR,RID: subtype=%s mask={}\n",
  3173. session->subtype);
  3174. return mask;
  3175. } else if (0 == strcmp("SAT", field[1])) { // Satellite Status
  3176. struct satellite_t *sp;
  3177. int i, n = session->gpsdata.satellites_visible = atoi(field[2]);
  3178. session->gpsdata.satellites_used = 0;
  3179. for (i = 0, sp = session->gpsdata.skyview;
  3180. sp < session->gpsdata.skyview + n; sp++, i++) {
  3181. sp->PRN = (short)atoi(field[3 + i * 5 + 0]);
  3182. sp->azimuth = (double)atoi(field[3 + i * 5 + 1]);
  3183. sp->elevation = (double)atoi(field[3 + i * 5 + 2]);
  3184. sp->ss = safe_atof(field[3 + i * 5 + 3]);
  3185. sp->used = false;
  3186. if (field[3 + i * 5 + 4][0] == 'U') {
  3187. sp->used = true;
  3188. session->gpsdata.satellites_used++;
  3189. }
  3190. }
  3191. GPSD_LOG(LOG_DATA, &session->context->errout,
  3192. "NMEA0183: PASHR,SAT: used=%d\n",
  3193. session->gpsdata.satellites_used);
  3194. session->gpsdata.skyview_time.tv_sec = 0;
  3195. session->gpsdata.skyview_time.tv_nsec = 0;
  3196. mask |= SATELLITE_SET | USED_IS;
  3197. } else if (0 == strcmp("T", field[3])) { /* Assume OxTS PASHR */
  3198. // FIXME: decode OxTS $PASHDR, time is wrong, breaks cycle order
  3199. if (0 == merge_hhmmss(field[1], session)) {
  3200. // register_fractional_time(field[0], field[1], session);
  3201. // mask |= TIME_SET; confuses cycle order
  3202. }
  3203. // Assume true heading
  3204. session->gpsdata.attitude.heading = safe_atof(field[2]);
  3205. session->gpsdata.attitude.roll = safe_atof(field[4]);
  3206. session->gpsdata.attitude.pitch = safe_atof(field[5]);
  3207. // mask |= ATTITUDE_SET; * confuses cycle order ??
  3208. GPSD_LOG(LOG_DATA, &session->context->errout,
  3209. "NMEA0183: PASHR (OxTS) time %s, heading %lf.\n",
  3210. timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
  3211. session->gpsdata.attitude.heading);
  3212. }
  3213. return mask;
  3214. }
  3215. static gps_mask_t processMWD(int c UNUSED, char *field[],
  3216. struct gps_device_t *session)
  3217. {
  3218. /*
  3219. * xxMWD - Wind direction and speed
  3220. * $xxMWD,x.x,T,x.x,M,x.x,N,x.x,M*hh<cr><lf>
  3221. * Fields in order:
  3222. * 1. wind direction, 0 to 359, True
  3223. * 2. T
  3224. * 3. wind direction, 0 to 359, Magnetic
  3225. * 4. M
  3226. * 5. wind speed, knots
  3227. * 6. N
  3228. * 7. wind speed, meters/sec
  3229. * 8. M
  3230. * *hh mandatory nmea_checksum
  3231. */
  3232. gps_mask_t mask = ONLINE_SET;
  3233. session->newdata.wanglet = safe_atof(field[1]);
  3234. session->newdata.wanglem = safe_atof(field[3]);
  3235. session->newdata.wspeedt = safe_atof(field[7]);
  3236. mask |= NAVDATA_SET;
  3237. GPSD_LOG(LOG_DATA, &session->context->errout,
  3238. "NMEA0183: xxMWD wanglet %.2f wanglem %.2f wspeedt %.2f\n",
  3239. session->newdata.wanglet,
  3240. session->newdata.wanglem,
  3241. session->newdata.wspeedt);
  3242. return mask;
  3243. }
  3244. static gps_mask_t processMWV(int c UNUSED, char *field[],
  3245. struct gps_device_t *session)
  3246. {
  3247. /*
  3248. * xxMWV - Wind speed and angle
  3249. * $xxMWV,x.x,a,x.x,a,A*hh<cr><lf>
  3250. * Fields in order:
  3251. * 1. wind angle, 0 to 359, True
  3252. * 2. R = Relative (apparent), T = Theoretical (calculated)
  3253. * Is T magnetic or true??
  3254. * 3. wind speed
  3255. * 4. wind speed units K/M/N/S
  3256. * 6. A = Valid, V = invalid
  3257. * *hh mandatory nmea_checksum
  3258. */
  3259. gps_mask_t mask = ONLINE_SET;
  3260. if (('R' == field[2][0]) &&
  3261. ('N' == field[4][0]) &&
  3262. ('A' == field[5][0])) {
  3263. // relative, knots, and valid
  3264. session->newdata.wangler = safe_atof(field[1]);
  3265. session->newdata.wspeedr = safe_atof(field[3]) * KNOTS_TO_MPS;
  3266. mask |= NAVDATA_SET;
  3267. }
  3268. GPSD_LOG(LOG_DATA, &session->context->errout,
  3269. "NMEA0183: xxMWV wangler %.2f wspeedr %.2f\n",
  3270. session->newdata.wangler,
  3271. session->newdata.wspeedr);
  3272. return mask;
  3273. }
  3274. static gps_mask_t processMTK3301(int c UNUSED, char *field[],
  3275. struct gps_device_t *session)
  3276. {
  3277. int msg, reason;
  3278. msg = atoi(&(session->nmea.field[0])[4]);
  3279. switch (msg) {
  3280. case 001: // ACK / NACK
  3281. reason = atoi(field[2]);
  3282. if (-1 == atoi(field[1])) {
  3283. GPSD_LOG(LOG_WARN, &session->context->errout,
  3284. "NMEA0183: MTK NACK: unknown sentence\n");
  3285. } else if (3 > reason) {
  3286. const char *mtk_reasons[] = {
  3287. "Invalid",
  3288. "Unsupported",
  3289. "Valid but Failed",
  3290. "Valid success"
  3291. };
  3292. GPSD_LOG(LOG_WARN, &session->context->errout,
  3293. "NMEA0183: MTK NACK: %s, reason: %s\n",
  3294. field[1], mtk_reasons[reason]);
  3295. } else {
  3296. GPSD_LOG(LOG_PROG, &session->context->errout,
  3297. "NMEA0183: MTK ACK: %s\n", field[1]);
  3298. }
  3299. return ONLINE_SET;
  3300. case 424: // PPS pulse width response
  3301. /*
  3302. * Response will look something like: $PMTK424,0,0,1,0,69*12
  3303. * The pulse width is in field 5 (69 in this example). This
  3304. * sentence is poorly documented at:
  3305. * http://www.trimble.com/embeddedsystems/condor-gps-module.aspx?dtID=documentation
  3306. *
  3307. * Packet Type: 324 PMTK_API_SET_OUTPUT_CTL
  3308. * Packet meaning
  3309. * Write the TSIP/antenna/PPS configuration data to the Flash memory.
  3310. * DataField [Data0]:TSIP Packet[on/off]
  3311. * 0 - Disable TSIP output (Default).
  3312. * 1 - Enable TSIP output.
  3313. * [Data1]:Antenna Detect[on/off]
  3314. * 0 - Disable antenna detect function (Default).
  3315. * 1 - Enable antenna detect function.
  3316. * [Data2]:PPS on/off
  3317. * 0 - Disable PPS function.
  3318. * 1 - Enable PPS function (Default).
  3319. * [Data3]:PPS output timing
  3320. * 0 - Always output PPS (Default).
  3321. * 1 - Only output PPS when GPS position is fixed.
  3322. * [Data4]:PPS pulse width
  3323. * 1~16367999: 61 ns~(61x 16367999) ns (Default = 69)
  3324. *
  3325. * The documentation does not give the units of the data field.
  3326. * Andy Walls <andy@silverblocksystems.net> says:
  3327. *
  3328. * "The best I can figure using an oscilloscope, is that it is
  3329. * in units of 16.368000 MHz clock cycles. It may be
  3330. * different for any other unit other than the Trimble
  3331. * Condor. 69 cycles / 16368000 cycles/sec = 4.216 microseconds
  3332. * [which is the pulse width I have observed]"
  3333. *
  3334. * Support for this theory comes from the fact that crystal
  3335. * TXCOs with a 16.368MHZ period are commonly available from
  3336. * multiple vendors. Furthermore, 61*69 = 4209, which is
  3337. * close to the observed cycle time and suggests that the
  3338. * documentation is trying to indicate 61ns units.
  3339. *
  3340. * He continues:
  3341. *
  3342. * "I chose [127875] because to divides 16368000 nicely and the
  3343. * pulse width is close to 1/100th of a second. Any number
  3344. * the user wants to use would be fine. 127875 cycles /
  3345. * 16368000 cycles/second = 1/128 seconds = 7.8125
  3346. * milliseconds"
  3347. */
  3348. /* too short? Make it longer */
  3349. if (atoi(field[5]) < 127875)
  3350. (void)nmea_send(session, "$PMTK324,0,0,1,0,127875");
  3351. return ONLINE_SET;
  3352. case 705: /* return device subtype */
  3353. // Firmware release name and version
  3354. (void)strlcpy(session->subtype, field[1], sizeof(session->subtype));
  3355. (void)strlcat(session->subtype, "-", sizeof(session->subtype));
  3356. // Build ID
  3357. (void)strlcat(session->subtype, field[2], sizeof(session->subtype));
  3358. (void)strlcat(session->subtype, "-", sizeof(session->subtype));
  3359. // Product Model
  3360. (void)strlcat(session->subtype, field[3], sizeof(session->subtype));
  3361. (void)strlcat(session->subtype, "-", sizeof(session->subtype));
  3362. // SDK Version
  3363. (void)strlcat(session->subtype, field[4], sizeof(session->subtype));
  3364. return ONLINE_SET;
  3365. default:
  3366. GPSD_LOG(LOG_PROG, &session->context->errout,
  3367. "NMEA0183: MTK: unknown msg: %d\n", msg);
  3368. return ONLINE_SET; /* ignore */
  3369. }
  3370. }
  3371. /* Recommended Minimum 3D GNSS Data */
  3372. static gps_mask_t processPSTI030(int count, char *field[],
  3373. struct gps_device_t *session)
  3374. {
  3375. /*
  3376. * $PSTI,030,hhmmss.sss,A,dddmm.mmmmmmm,a,dddmm.mmmmmmm,a,x.x,
  3377. x.x,x.x,x.x,ddmmyy,a.x.x,x.x*hh<CR><LF>
  3378. * 1 030 Sentence ID
  3379. * 2 225446.334 Time of fix 22:54:46 UTC
  3380. * 3 A Status of Fix: A = Autonomous, valid;
  3381. * V = invalid
  3382. * 4,5 4916.45,N Latitude 49 deg. 16.45 min North
  3383. * 6,7 12311.12,W Longitude 123 deg. 11.12 min West
  3384. * 8 103.323 Mean Sea Level meters
  3385. * 9 0.00 East Velocity meters/sec
  3386. * 10 0.00 North Velocity meters/sec
  3387. * 11 0.00 Up Velocity meters/sec
  3388. * 12 181194 Date of fix 18 November 1994
  3389. * 13 A FAA mode indicator
  3390. * See faa_mode() for possible mode values.
  3391. * 14 1.2 RTK Age
  3392. * 15 4.2 RTK Ratio
  3393. * 16 *68 mandatory nmea_checksum
  3394. *
  3395. * In private email, SkyTraq says F mode is 10x more accurate
  3396. * than R mode.
  3397. */
  3398. gps_mask_t mask = ONLINE_SET;
  3399. if (16 != count) {
  3400. // FIXME: report runt
  3401. return mask;
  3402. }
  3403. if ('V' == field[3][0] ||
  3404. 'N' == field[13][0]) {
  3405. // nav warning, or FAA not valid, ignore the rest of the data
  3406. session->newdata.status = STATUS_UNK;
  3407. session->newdata.mode = MODE_NO_FIX;
  3408. mask |= MODE_SET | STATUS_SET;
  3409. } else if ('A' == field[3][0]) {
  3410. double east, north, climb;
  3411. // data valid
  3412. if ('\0' != field[2][0] &&
  3413. '\0' != field[12][0]) {
  3414. // good date and time
  3415. if (0 == merge_hhmmss(field[2], session) &&
  3416. 0 == merge_ddmmyy(field[12], session)) {
  3417. mask |= TIME_SET;
  3418. register_fractional_time( "PSTI030", field[2], session);
  3419. }
  3420. }
  3421. if (0 == do_lat_lon(&field[4], &session->newdata)) {
  3422. session->newdata.mode = MODE_2D;
  3423. mask |= LATLON_SET;
  3424. if ('\0' != field[8][0]) {
  3425. /* altitude is MSL */
  3426. session->newdata.altMSL = safe_atof(field[8]);
  3427. mask |= ALTITUDE_SET;
  3428. session->newdata.mode = MODE_3D;
  3429. /* Let gpsd_error_model() deal with geoid_sep and altHAE */
  3430. }
  3431. mask |= MODE_SET;
  3432. }
  3433. /* convert ENU to track
  3434. * this has more precision than GPVTG, GPVTG comes earlier
  3435. * in the cycle */
  3436. east = safe_atof(field[9]); // east velocity m/s
  3437. north = safe_atof(field[10]); // north velocity m/s
  3438. climb = safe_atof(field[11]); // up velocity m/s
  3439. session->newdata.NED.velN = north;
  3440. session->newdata.NED.velE = east;
  3441. session->newdata.NED.velD = -climb;
  3442. mask |= VNED_SET | STATUS_SET;
  3443. session->newdata.status = faa_mode(field[13][0]);
  3444. // FIXME: save RTK Age and RTK Ratio
  3445. }
  3446. GPSD_LOG(LOG_PROG, &session->context->errout,
  3447. "NMEA0183: PSTI,030: ddmmyy=%s hhmmss=%s lat=%.2f lon=%.2f "
  3448. "status=%d, RTK(Age=%s Ratio=%s)\n",
  3449. field[12], field[2],
  3450. session->newdata.latitude,
  3451. session->newdata.longitude,
  3452. session->newdata.status,
  3453. field[14], field[15]);
  3454. return mask;
  3455. }
  3456. /*
  3457. * Skytraq sentences take this format:
  3458. * $PSTI,type[,val[,val]]*CS
  3459. * type is a 2 or 3 digit subsentence type
  3460. *
  3461. * Note: this sentence can be at least 100 chars long.
  3462. * That violates the NMEA 3.01 max of 82.
  3463. *
  3464. */
  3465. static gps_mask_t processPSTI(int count, char *field[],
  3466. struct gps_device_t *session)
  3467. {
  3468. gps_mask_t mask = ONLINE_SET;
  3469. int type = atoi(field[1]);
  3470. if ( 0 != strncmp(session->subtype, "kver ", 5) ) {
  3471. // this is skytraq, but not marked yet, so probe for Skytraq
  3472. // Send MID 0x02, to get back MID 0x80
  3473. (void)gpsd_write(session, "\xA0\xA1\x00\x02\x02\x01\x03\x0d\x0a",9);
  3474. }
  3475. switch (type) {
  3476. case 0:
  3477. if (4 != count) {
  3478. // FIXME: report runt
  3479. break;
  3480. }
  3481. // 1 PPS Timing report ID
  3482. GPSD_LOG(LOG_PROG, &session->context->errout,
  3483. "NMEA0183: PSTI,00: Mode: %s, Length: %s, Quant: %s\n",
  3484. field[2], field[3], field[4]);
  3485. break;
  3486. case 1:
  3487. // Active Antenna Status Report
  3488. GPSD_LOG(LOG_PROG, &session->context->errout,
  3489. "NMEA0183: PSTI,001: Count: %d\n", count);
  3490. break;
  3491. case 5:
  3492. // GPIO 10 event-triggered time & position stamp.
  3493. GPSD_LOG(LOG_PROG, &session->context->errout,
  3494. "NMEA0183: PSTI,005: Count: %d\n", count);
  3495. break;
  3496. case 30:
  3497. // Recommended Minimum 3D GNSS Data
  3498. mask = processPSTI030(count, field, session);
  3499. break;
  3500. case 32:
  3501. if (16 != count) {
  3502. // FIXME: report runt
  3503. break;
  3504. }
  3505. // RTK Baseline
  3506. if (0 == strcmp(field[4], "A")) {
  3507. // Status Valid
  3508. if (field[2][0] != '\0' && field[3][0] != '\0') {
  3509. // good date and time
  3510. if (0 == merge_hhmmss(field[2], session) &&
  3511. 0 == merge_ddmmyy(field[3], session)) {
  3512. mask |= TIME_SET;
  3513. register_fractional_time("PSTI032", field[2], session);
  3514. }
  3515. }
  3516. }
  3517. GPSD_LOG( LOG_PROG,&session->context->errout,
  3518. "NMEA0183: PSTI,032: stat:%s mode: %s E: %s N: %s U:%s L:%s "
  3519. "C:%s\n",
  3520. field[4], field[5],
  3521. field[6], field[7], field[8],
  3522. field[9], field[10]);
  3523. break;
  3524. case 33:
  3525. // RTK RAW Measurement Monitoring Data
  3526. // PX1172RH
  3527. if (27 != count) {
  3528. // FIXME: report runt
  3529. break;
  3530. }
  3531. GPSD_LOG(LOG_PROG, &session->context->errout,
  3532. "NMEA0183: PSTI,033: RTK RAW\n");
  3533. break;
  3534. case 35:
  3535. // RTK Baseline Data of Rover Moving Base Receiver
  3536. // PX1172RH
  3537. if (16 != count) {
  3538. // FIXME: report runt
  3539. break;
  3540. }
  3541. if ('\0' != field[2][0] &&
  3542. '\0' != field[3][0]) {
  3543. // good date and time
  3544. if (0 == merge_hhmmss(field[2], session) &&
  3545. 0 == merge_ddmmyy(field[3], session)) {
  3546. mask |= TIME_SET;
  3547. register_fractional_time( "PSTI035", field[2], session);
  3548. }
  3549. }
  3550. GPSD_LOG(LOG_PROG, &session->context->errout,
  3551. "NMEA0183: PSTI,035: RTK Baseline\n");
  3552. break;
  3553. case 36:
  3554. // Heading, Pitch and Roll Messages of vehicle
  3555. // PX1172RH
  3556. if (8 != count) {
  3557. // FIXME: report runt
  3558. break;
  3559. }
  3560. if ('\0' != field[2][0] &&
  3561. '\0' != field[3][0] &&
  3562. 'N' != field[7][0]) {
  3563. // good date and time
  3564. if (0 == merge_hhmmss(field[2], session) &&
  3565. 0 == merge_ddmmyy(field[3], session)) {
  3566. mask |= TIME_SET;
  3567. register_fractional_time( "PSTI036", field[2], session);
  3568. }
  3569. }
  3570. GPSD_LOG(LOG_PROG, &session->context->errout,
  3571. "NMEA0183: PSTI,036: RTK RAW\n");
  3572. break;
  3573. default:
  3574. GPSD_LOG(LOG_PROG, &session->context->errout,
  3575. "NMEA0183: PSTI,%s: Unknown type, Count: %d\n",
  3576. field[1], count);
  3577. }
  3578. return mask;
  3579. }
  3580. /*
  3581. * Skytraq undocumented debug sentences take this format:
  3582. * $STI,type,val*CS
  3583. * type is a 2 char subsentence type
  3584. * Note: NO checksum
  3585. */
  3586. static gps_mask_t processSTI(int count, char *field[],
  3587. struct gps_device_t *session)
  3588. {
  3589. gps_mask_t mask = ONLINE_SET;
  3590. if ( 0 != strncmp(session->subtype, "kver ", 5) ) {
  3591. // this is skytraq, but marked yet, so probe for Skytraq
  3592. // Send MID 0x02, to get back MID 0x80
  3593. (void)gpsd_write(session, "\xA0\xA1\x00\x02\x02\x01\x03\x0d\x0a",9);
  3594. }
  3595. if ( 0 == strcmp( field[1], "IC") ) {
  3596. /* $STI,IC,error=XX, this is always very bad, but undocumented */
  3597. GPSD_LOG(LOG_ERROR, &session->context->errout,
  3598. "NMEA0183: Skytraq: $STI,%s,%s\n", field[1], field[2]);
  3599. return mask;
  3600. }
  3601. GPSD_LOG(LOG_PROG, &session->context->errout,
  3602. "NMEA0183: STI,%s: Unknown type, Count: %d\n", field[1], count);
  3603. return mask;
  3604. }
  3605. /**************************************************************************
  3606. *
  3607. * Entry points begin here
  3608. *
  3609. **************************************************************************/
  3610. // parse an NMEA sentence, unpack it into a session structure
  3611. gps_mask_t nmea_parse(char *sentence, struct gps_device_t * session)
  3612. {
  3613. typedef gps_mask_t(*nmea_decoder) (int count, char *f[],
  3614. struct gps_device_t * session);
  3615. static struct
  3616. {
  3617. char *name;
  3618. int nf; // minimum number of fields required to parse
  3619. bool cycle_continue; // cycle continuer?
  3620. nmea_decoder decoder;
  3621. } nmea_phrase[NMEA_NUM] = {
  3622. {"PGLOR", 2, false, processPGLOR}, // Android something or other
  3623. {"PGRMB", 0, false, NULL}, // ignore Garmin DGPS Beacon Info
  3624. {"PGRMC", 0, false, NULL}, // ignore Garmin Sensor Config
  3625. {"PGRME", 7, false, processPGRME},
  3626. {"PGRMF", 15, false, processPGRMF}, // Garmin GPS Fix Data
  3627. {"PGRMH", 0, false, NULL}, // ignore Garmin Aviation Height
  3628. {"PGRMI", 0, false, NULL}, // ignore Garmin Sensor Init
  3629. {"PGRMM", 2, false, processPGRMM}, // Garmin Map Datum
  3630. {"PGRMO", 0, false, NULL}, // ignore Garmin Sentence Enable
  3631. {"PGRMT", 0, false, NULL}, // ignore Garmin Sensor Info
  3632. {"PGRMV", 0, false, NULL}, // ignore Garmin 3D Velocity Info
  3633. {"PGRMZ", 4, false, processPGRMZ},
  3634. /*
  3635. * Basic sentences must come after the PG* ones, otherwise
  3636. * Garmins can get stuck in a loop that looks like this:
  3637. *
  3638. * 1. A Garmin GPS in NMEA mode is detected.
  3639. *
  3640. * 2. PGRMC is sent to reconfigure to Garmin binary mode.
  3641. * If successful, the GPS echoes the phrase.
  3642. *
  3643. * 3. nmea_parse() sees the echo as RMC because the talker
  3644. * ID is ignored, and fails to recognize the echo as
  3645. * PGRMC and ignore it.
  3646. *
  3647. * 4. The mode is changed back to NMEA, resulting in an
  3648. * infinite loop.
  3649. */
  3650. {"AAM", 0, false, NULL}, // ignore Waypoint Arrival Alarm
  3651. {"ACCURACY", 1, true, processACCURACY},
  3652. {"ALM", 0, false, NULL}, // ignore GPS Almanac Data
  3653. {"APB", 0, false, NULL}, // ignore Autopilot Sentence B
  3654. {"BOD", 0, false, NULL}, // ignore Bearing Origin to Destination
  3655. // Bearing & Distance to Waypoint, Great Circle
  3656. {"BWC", 12, false, processBWC},
  3657. {"DBT", 7, false, processDBT},
  3658. {"DPT", 0, false, NULL}, // ignore depth
  3659. {"DTM", 2, false, processDTM}, // datum
  3660. {"GBS", 7, false, processGBS},
  3661. {"GGA", 13, false, processGGA},
  3662. {"GLC", 0, false, NULL}, // ignore Geographic Position, LoranC
  3663. {"GLL", 7, true, processGLL},
  3664. {"GNS", 13, false, processGNS},
  3665. {"GRS", 0, false, NULL}, // ignore GNSS Range Residuals
  3666. {"GSA", 18, false, processGSA},
  3667. {"GST", 8, false, processGST},
  3668. {"GSV", 0, false, processGSV},
  3669. // ignore Heading, Deviation and Variation
  3670. {"HDG", 0, false, processHDG},
  3671. {"HDT", 1, false, processHDT},
  3672. {"HWBIAS", 0, false, NULL}, // Unknown HuaWei sentence
  3673. {"MLA", 0, false, NULL}, // ignore GLONASS Almana Data
  3674. {"MSS", 0, false, NULL}, // ignore beacon receiver status
  3675. {"MTW", 0, false, NULL}, // ignore Water Temperature
  3676. {"MWD", 0, false, processMWD}, // Wind Direction and Speed
  3677. {"MWV", 0, false, processMWV}, // Wind Speed and Angle
  3678. #ifdef OCEANSERVER_ENABLE
  3679. {"OHPR", 18, false, processOHPR},
  3680. #endif /* OCEANSERVER_ENABLE */
  3681. {"OSD", 0, false, NULL}, // ignore Own Ship Data
  3682. // general handler for Ashtech
  3683. {"PASHR", 3, false, processPASHR},
  3684. // Jackson Labs proprietary
  3685. {"PJLTS", 11, false, NULL}, // GPSDO status
  3686. {"PJLTV", 4, false, NULL}, // Time and 3D velocity
  3687. {"PMGNST", 8, false, processPMGNST}, // Magellan Status
  3688. {"PMTK", 3, false, processMTK3301},
  3689. // for some reason the parser no longer triggering on leading chars
  3690. {"PMTK001", 3, false, processMTK3301},
  3691. {"PMTK424", 3, false, processMTK3301},
  3692. {"PMTK705", 3, false, processMTK3301},
  3693. {"PMTKCHN", 0, false, NULL}, // MediaTek Channel Status
  3694. {"PRHS ", 2, false, processPRHS}, // smart watch sensors, Yes: space!
  3695. {"PRWIZCH", 0, false, NULL}, // Rockwell Channel Status
  3696. {"PSRFEPE", 7, false, processPSRFEPE}, // SiRF Estimated Errors
  3697. {"PSTI", 2, false, processPSTI}, // $PSTI Skytraq
  3698. // $PSTM ST Micro STA8088xx/STA8089xx/STA8090xx
  3699. {"PSTM", 0, false, NULL},
  3700. {"PTFTTXT", 0, false, NULL}, // unknown uptime
  3701. {"PTKM", 0, false, NULL}, // Robertson RGC12 Gyro
  3702. {"PTNLRHVR", 0, false, NULL}, // Trimble Software Version
  3703. {"PTNLRPT", 0, false, NULL}, // Trimble Serial Port COnfig
  3704. {"PTNLRSVR", 0, false, NULL}, // Trimble Firmware Version
  3705. {"PTNLRZD", 0, false, NULL}, // Extended Time and Date
  3706. {"PTNTA", 8, false, processTNTA},
  3707. {"PTNTHTM", 9, false, processTNTHTM},
  3708. {"PUBX", 0, false, NULL}, // ignore u-blox and Antaris
  3709. {"RLM", 0, false, NULL}, // ignore Return Link Message
  3710. // ignore Recommended Minimum Navigation Info, waypoint
  3711. {"RMB", 0, false, NULL}, // ignore Recommended Min Nav Info
  3712. {"RMC", 8, false, processRMC},
  3713. {"ROT", 0, false, NULL}, // ignore Rate of Turn
  3714. {"RPM", 0, false, NULL}, // ignore Revolutions
  3715. {"RSA", 0, false, NULL}, // ignore Rudder Sensor Angle
  3716. {"RTE", 0, false, NULL}, // ignore Routes
  3717. {"STI", 2, false, processSTI}, // $STI Skytraq
  3718. {"THS", 0, false, processTHS}, // True Heading and Status
  3719. {"TXT", 5, false, processTXT},
  3720. {"VBW", 0, false, NULL}, // ignore Dual Ground/Water Speed
  3721. {"VDO", 0, false, NULL}, // ignore Own Vessel's Information
  3722. {"VDR", 0, false, NULL}, // ignore Set and Drift
  3723. {"VHW", 0, false, NULL}, // ignore Water Speed and Heading
  3724. {"VLW", 0, false, NULL}, // ignore Dual ground/water distance
  3725. {"VTG", 5, false, processVTG},
  3726. {"XDR", 0, false, NULL}, // ignore $HCXDR, IMU?
  3727. {"XTE", 0, false, NULL}, // ignore Cross-Track Error
  3728. {"ZDA", 4, false, processZDA},
  3729. {NULL, 0, false, NULL}, // no more
  3730. };
  3731. int count;
  3732. gps_mask_t mask = 0;
  3733. unsigned i, thistag = 0, lasttag;
  3734. char *p, *e;
  3735. volatile char *t;
  3736. char ts_buf1[TIMESPEC_LEN];
  3737. char ts_buf2[TIMESPEC_LEN];
  3738. bool skytraq_sti = false;
  3739. size_t mlen;
  3740. /*
  3741. * We've had reports that on the Garmin GPS-10 the device sometimes
  3742. * (1:1000 or so) sends garbage packets that have a valid checksum
  3743. * but are like 2 successive NMEA packets merged together in one
  3744. * with some fields lost. Usually these are much longer than the
  3745. * legal limit for NMEA, so we can cope by just tossing out overlong
  3746. * packets. This may be a generic bug of all Garmin chipsets.
  3747. */
  3748. // codacy does not like strlen()
  3749. mlen = strnlen(sentence, NMEA_MAX + 1);
  3750. if (NMEA_MAX < mlen) {
  3751. GPSD_LOG(LOG_WARN, &session->context->errout,
  3752. "NMEA0183: Overlong packet of %zd+ chars rejected.\n",
  3753. mlen);
  3754. return ONLINE_SET;
  3755. }
  3756. // make an editable copy of the sentence
  3757. (void)strlcpy((char *)session->nmea.fieldcopy, sentence,
  3758. sizeof(session->nmea.fieldcopy) - 1);
  3759. // discard the checksum part
  3760. for (p = (char *)session->nmea.fieldcopy;
  3761. (*p != '*') && (*p >= ' ');) {
  3762. ++p;
  3763. }
  3764. if (*p == '*') {
  3765. *p++ = ','; // otherwise we drop the last field
  3766. }
  3767. #ifdef SKYTRAQ_ENABLE_UNUSED
  3768. // $STI is special, no trailing *, or chacksum
  3769. if (0 != strncmp( "STI,", sentence, 4)) {
  3770. skytraq_sti = true;
  3771. *p++ = ','; // otherwise we drop the last field
  3772. }
  3773. #endif
  3774. *p = '\0';
  3775. e = p;
  3776. // split sentence copy on commas, filling the field array
  3777. count = 0;
  3778. t = p; // end of sentence
  3779. p = (char *)session->nmea.fieldcopy + 1; // beginning of tag, 'G' not '$'
  3780. // while there is a search string and we haven't run off the buffer...
  3781. while ((NULL != p) &&
  3782. (p <= t)) {
  3783. session->nmea.field[count] = p; // we have a field. record it
  3784. if ((p = strchr(p, ',')) != NULL) { // search for the next delimiter
  3785. *p = '\0'; // replace it with a NUL
  3786. count++; // bump the counters and continue
  3787. p++;
  3788. }
  3789. }
  3790. // point remaining fields at empty string, just in case
  3791. for (i = (unsigned int)count; i < NMEA_MAX_FLD; i++) {
  3792. session->nmea.field[i] = e;
  3793. }
  3794. // sentences handlers will tell us when they have fractional time
  3795. session->nmea.latch_frac_time = false;
  3796. // GSA and GSV will set this if more in that series to come.
  3797. session->nmea.gsx_more = false;
  3798. #ifdef __UNUSED
  3799. // debug
  3800. GPSD_LOG(0, &session->context->errout,
  3801. "NMEA0183: got %s\n", session->nmea.field[0]);
  3802. #endif // __UNUSED
  3803. // dispatch on field zero, the sentence tag
  3804. for (i = 0; i < NMEA_NUM; ++i) {
  3805. char *s = session->nmea.field[0];
  3806. if (NULL == nmea_phrase[i].name) {
  3807. mask = ONLINE_SET;
  3808. GPSD_LOG(LOG_DATA, &session->context->errout,
  3809. "NMEA0183: Unknown sentence type %s\n",
  3810. session->nmea.field[0]);
  3811. break;
  3812. }
  3813. // strnlen() to shut up codacy
  3814. if (3 == strnlen(nmea_phrase[i].name, 4) &&
  3815. !skytraq_sti) {
  3816. // $STI is special
  3817. s += 2; // skip talker ID
  3818. }
  3819. if (0 == strcmp(nmea_phrase[i].name, s)) {
  3820. if (NULL == nmea_phrase[i].decoder) {
  3821. /* no decoder for this sentence */
  3822. mask = ONLINE_SET;
  3823. GPSD_LOG(LOG_DATA, &session->context->errout,
  3824. "NMEA0183: No decoder for sentence type %s\n",
  3825. session->nmea.field[0]);
  3826. break;
  3827. }
  3828. if (count < nmea_phrase[i].nf) {
  3829. /* sentence to short */
  3830. mask = ONLINE_SET;
  3831. GPSD_LOG(LOG_DATA, &session->context->errout,
  3832. "NMEA0183: Sentence %s too short\n",
  3833. session->nmea.field[0]);
  3834. break;
  3835. }
  3836. mask = (nmea_phrase[i].decoder)(count, session->nmea.field,
  3837. session);
  3838. session->nmea.cycle_continue = nmea_phrase[i].cycle_continue;
  3839. /*
  3840. * Must force this to be nz, as we're going to rely on a zero
  3841. * value to mean "no previous tag" later.
  3842. */
  3843. // FIXME: this fails on Skytrak, $PSTI,xx, many different xx
  3844. thistag = i + 1;
  3845. break;
  3846. }
  3847. }
  3848. /* prevent overaccumulation of sat reports */
  3849. if (!str_starts_with(session->nmea.field[0] + 2, "GSV"))
  3850. session->nmea.last_gsv_talker = '\0';
  3851. if (!str_starts_with(session->nmea.field[0] + 2, "GSA"))
  3852. session->nmea.last_gsa_talker = '\0';
  3853. /* timestamp recording for fixes happens here */
  3854. if ((mask & TIME_SET) != 0) {
  3855. if (0 == session->nmea.date.tm_year &&
  3856. 0 == session->nmea.date.tm_mday) {
  3857. // special case to time zero
  3858. session->newdata.time = (timespec_t){0, 0};
  3859. } else {
  3860. session->newdata.time = gpsd_utc_resolve(session);
  3861. }
  3862. GPSD_LOG(LOG_DATA, &session->context->errout,
  3863. "NMEA0183: %s newtime is %s = "
  3864. "%d-%02d-%02dT%02d:%02d:%02d.%03ldZ\n",
  3865. session->nmea.field[0],
  3866. timespec_str(&session->newdata.time, ts_buf1, sizeof(ts_buf1)),
  3867. 1900 + session->nmea.date.tm_year,
  3868. session->nmea.date.tm_mon + 1,
  3869. session->nmea.date.tm_mday,
  3870. session->nmea.date.tm_hour,
  3871. session->nmea.date.tm_min,
  3872. session->nmea.date.tm_sec,
  3873. session->nmea.subseconds.tv_nsec / 1000000L);
  3874. /*
  3875. * If we have time and PPS is available, assume we have good time.
  3876. * Because this is a generic driver we don't really have enough
  3877. * information for a sharper test, so we'll leave it up to the
  3878. * PPS code to do its own sanity filtering.
  3879. */
  3880. mask |= NTPTIME_IS;
  3881. }
  3882. /*
  3883. * The end-of-cycle detector. This code depends on just one
  3884. * assumption: if a sentence with a timestamp occurs just before
  3885. * start of cycle, then it is always good to trigger a report on
  3886. * that sentence in the future. For devices with a fixed cycle
  3887. * this should work perfectly, locking in detection after one
  3888. * cycle. Most split-cycle devices (Garmin 48, for example) will
  3889. * work fine. Problems will only arise if a a sentence that
  3890. * occurs just before timestamp increments also occurs in
  3891. * mid-cycle, as in the Garmin eXplorist 210; those might jitter.
  3892. */
  3893. GPSD_LOG(LOG_DATA, &session->context->errout,
  3894. "NMEA0183: %s time %s last %s latch %d cont %d\n",
  3895. session->nmea.field[0],
  3896. timespec_str(&session->nmea.this_frac_time, ts_buf1,
  3897. sizeof(ts_buf1)),
  3898. timespec_str(&session->nmea.last_frac_time, ts_buf2,
  3899. sizeof(ts_buf2)),
  3900. session->nmea.latch_frac_time,
  3901. session->nmea.cycle_continue);
  3902. lasttag = session->nmea.lasttag;
  3903. if (session->nmea.gsx_more) {
  3904. // more to come, so ignore for cycle ender
  3905. // appears that GSA and GSV never start a cycle.
  3906. } else if (session->nmea.latch_frac_time) {
  3907. timespec_t ts_delta;
  3908. TS_SUB(&ts_delta, &session->nmea.this_frac_time,
  3909. &session->nmea.last_frac_time);
  3910. if (0.01 < fabs(TSTONS(&ts_delta))) {
  3911. // time changed
  3912. mask |= CLEAR_IS;
  3913. GPSD_LOG(LOG_PROG, &session->context->errout,
  3914. "NMEA0183: %s starts a reporting cycle. lasttag %d\n",
  3915. session->nmea.field[0], lasttag);
  3916. /*
  3917. * Have we seen a previously timestamped NMEA tag?
  3918. * If so, designate as end-of-cycle marker.
  3919. * But not if there are continuation sentences;
  3920. * those get sorted after the last timestamped sentence
  3921. *
  3922. */
  3923. if (0 < lasttag &&
  3924. false == (session->nmea.cycle_enders[lasttag]) &&
  3925. !session->nmea.cycle_continue) {
  3926. session->nmea.cycle_enders[lasttag] = true;
  3927. // we might have a (somewhat) reliable end-of-cycle
  3928. session->cycle_end_reliable = true;
  3929. GPSD_LOG(LOG_PROG, &session->context->errout,
  3930. "NMEA0183: tagged %s as a cycle ender. %u\n",
  3931. nmea_phrase[lasttag - 1].name,
  3932. lasttag);
  3933. }
  3934. }
  3935. } else {
  3936. // ignore multiple sequential, like GSV, GSA
  3937. // extend the cycle to an un-timestamped sentence?
  3938. if (true == session->nmea.cycle_enders[lasttag]) {
  3939. GPSD_LOG(LOG_PROG, &session->context->errout,
  3940. "NMEA0183: %s is just after a cycle ender.\n",
  3941. session->nmea.field[0]);
  3942. }
  3943. if (session->nmea.cycle_continue) {
  3944. GPSD_LOG(LOG_PROG, &session->context->errout,
  3945. "NMEA0183: %s extends the reporting cycle.\n",
  3946. session->nmea.field[0]);
  3947. // change ender
  3948. session->nmea.cycle_enders[lasttag] = false;
  3949. session->nmea.cycle_enders[thistag] = true;
  3950. // have a cycle ender
  3951. session->cycle_end_reliable = true;
  3952. }
  3953. }
  3954. // here's where we check for end-of-cycle
  3955. if ((session->nmea.latch_frac_time ||
  3956. session->nmea.cycle_continue) &&
  3957. (true == session->nmea.cycle_enders[thistag]) &&
  3958. !session->nmea.gsx_more) {
  3959. GPSD_LOG(LOG_PROG, &session->context->errout,
  3960. "NMEA0183: %s ends a reporting cycle.\n",
  3961. session->nmea.field[0]);
  3962. mask |= REPORT_IS;
  3963. }
  3964. if (session->nmea.latch_frac_time) {
  3965. session->nmea.lasttag = thistag;
  3966. }
  3967. /* don't downgrade mode if holding previous fix
  3968. * usually because of xxRMC which does not report 2D/3D */
  3969. if (MODE_SET == (mask & MODE_SET) &&
  3970. MODE_3D == session->gpsdata.fix.mode &&
  3971. MODE_NO_FIX != session->newdata.mode &&
  3972. (0 != isfinite(session->lastfix.altHAE) ||
  3973. 0 != isfinite(session->oldfix.altHAE) ||
  3974. 0 != isfinite(session->lastfix.altMSL) ||
  3975. 0 != isfinite(session->oldfix.altMSL))) {
  3976. session->newdata.mode = session->gpsdata.fix.mode;
  3977. }
  3978. return mask;
  3979. }
  3980. // add NMEA checksum to a possibly terminated sentence
  3981. void nmea_add_checksum(char *sentence)
  3982. {
  3983. unsigned char sum = '\0';
  3984. char c, *p = sentence;
  3985. if ('$' == *p ||
  3986. '!' == *p) {
  3987. p++;
  3988. }
  3989. while (('*' != (c = *p)) &&
  3990. ('\0' != c)) {
  3991. sum ^= c;
  3992. p++;
  3993. }
  3994. *p++ = '*';
  3995. (void)snprintf(p, 5, "%02X\r\n", (unsigned)sum);
  3996. }
  3997. // ship a command to the GPS, adding * and correct checksum
  3998. ssize_t nmea_write(struct gps_device_t *session, char *buf, size_t len UNUSED)
  3999. {
  4000. (void)strlcpy(session->msgbuf, buf, sizeof(session->msgbuf));
  4001. if ('$' == session->msgbuf[0]) {
  4002. (void)strlcat(session->msgbuf, "*", sizeof(session->msgbuf));
  4003. nmea_add_checksum(session->msgbuf);
  4004. } else {
  4005. (void)strlcat(session->msgbuf, "\r\n", sizeof(session->msgbuf));
  4006. }
  4007. // codacy hates strnlen()
  4008. session->msgbuflen = strnlen(session->msgbuf, sizeof(session->msgbuf));
  4009. return gpsd_write(session, session->msgbuf, session->msgbuflen);
  4010. }
  4011. ssize_t nmea_send(struct gps_device_t * session, const char *fmt, ...)
  4012. {
  4013. char buf[BUFSIZ];
  4014. va_list ap;
  4015. va_start(ap, fmt);
  4016. (void)vsnprintf(buf, sizeof(buf) - 5, fmt, ap);
  4017. va_end(ap);
  4018. // codacy hates strnlen()
  4019. return nmea_write(session, buf, strnlen(buf, sizeof(buf)));
  4020. }
  4021. // vim: set expandtab shiftwidth=4