driver_nmea0183.c 133 KB

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