nmea_parser.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <?php
  2. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3. //class.nmea.parser.php
  4. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  5. // NmeaParser Class
  6. // Version: 1.0
  7. // Author: Terry Griffin
  8. // Based on Code written by: David Boardman
  9. // URL: http://cs.mwsu.edu/~griffin
  10. // Licensed: GNU General Public License (GNU GPL)
  11. //
  12. // Do what you want with the code.
  13. // Filtering not implemented yet....
  14. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  15. class NmeaParser{
  16. private $Nmea; //Nmea Array of data
  17. private $TimeStamp; //Unix time stamp
  18. private $maxHDOP; //max horizontal dilution of precision
  19. private $maxVDOP; //max horizontal dilution of precision
  20. private $CurrentUTC; //Current time stamp to coordinate sentences
  21. function __construct(){
  22. $this->Nmea = array();
  23. $this->TimeStamp= 0;
  24. $this->maxHDOP = 0.0;
  25. $this->maxVDOP = 0.0;
  26. $this->CurrentUTC = 0;
  27. $this->CurrentTime=0;
  28. }
  29. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  30. //SetMinSatellites - Set the minimum satellite parameter to remove / ignore sentences that don't have
  31. //enough satellites for a decent fix.
  32. //
  33. //@param - int $minSats - minimum satellites
  34. //@returns - void
  35. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  36. public function SetMinSatellites($minSats=4){
  37. $this->minSats = $minSats;
  38. }
  39. //Dilution of precision:
  40. //1 Ideal This is the highest possible confidence level to be used for applications demanding the highest possible precision at all times.
  41. //1-2 Excellent At this confidence level, positional measurements are considered accurate enough to meet all but the most sensitive applications.
  42. //2-5 Good Represents a level that marks the minimum appropriate for making business decisions. Positional measurements could be used to make reliable in-route navigation suggestions to the user.
  43. //5-10 Moderate Positional measurements could be used for calculations, but the fix quality could still be improved. A more open view of the sky is recommended.
  44. //10-20 Fair Represents a low confidence level. Positional measurements should be discarded or used only to indicate a very rough estimate of the current location.
  45. //>20 Poor At this level, measurements are inaccurate by as much as 300 meters with a 6 meter accurate device (50 DOP × 6 meters) and should be discarded.
  46. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  47. //SetMaxHdop -
  48. //Set the maximum (smaller value = better) horizontal dilution of precision parameter to
  49. //remove / ignore sentences that don't have enough satellites in the correct location in the sky for a
  50. //decent fix.
  51. //
  52. //@param - int $maxHDOP - Hdop value
  53. //@returns - void
  54. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  55. public function SetMaxHdop($maxHDOP=10){
  56. $this->maxHDOP = $maxHDOP;
  57. }
  58. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  59. //SetMaxVdop -
  60. //Set the maximum (smaller value = better) vertical dilution of precision parameter to
  61. //remove / ignore sentences that don't have enough satellites in the correct location in the sky for a
  62. //decent fix.
  63. //
  64. //@param - int $maxVDOP - Hdop value
  65. //@returns - void
  66. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  67. public function SetMaxVdop($maxVDOP=10){
  68. $this->maxVDOP = $maxVDOP;
  69. }
  70. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  71. //NMEAtoUnixTime - Convert Date and Time to Linux Timestamp
  72. //
  73. //@param - string $time - current utc(hhmmss)
  74. //@param - int $date - current date (mmddyy)
  75. //@returns - int - timestamp
  76. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  77. private function NMEAtoUnixTime($utc,$date){
  78. $h = substr($utc,0,2);
  79. $i = substr($utc,2,2);
  80. $s = substr($utc,4,2);
  81. $d = substr($date,0,2);
  82. $m = substr($date,2,2);
  83. $y = substr($date,4,2);
  84. //list($y,$m,$d) = explode('-',$date);
  85. return mktime($h,$i,$s,$m,$d,$y);
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. //ParseLine - Parse the current line
  89. //
  90. //@param - string $line - current nmea line
  91. //@returns - void
  92. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  93. public function ParseLine($line){
  94. $this->NmeaType = $this->SetNmeaType($line);
  95. switch($this->type){
  96. case "GPGGA": $this->GPGGA($line);break;
  97. case "GPGLL": $this->GPGLL($line);break;
  98. case "GPGSA": $this->GPGSA($line);break;
  99. case "GPGSV": $this->GPGSV($line);break;
  100. case "GPRMC": $this->GPRMC($line);break;
  101. case "GPVTG": $this->GPVTG($line);break;
  102. default: return;
  103. }
  104. }
  105. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  106. //DumpNmea - Returns current Nmea data
  107. //
  108. //@param - void
  109. //@returns - array - Nmea data
  110. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  111. public function DumpNmea(){
  112. return $this->Nmea;
  113. }
  114. function GoodEnough(){
  115. return isset($this->Nmea[$this->CurrentUTC]['date']) && isset($this->Nmea[$this->CurrentUTC]['utc']) && isset($this->Nmea[$this->CurrentUTC]['lat']) && isset($this->Nmea[$this->CurrentUTC]['long']);
  116. }
  117. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  118. //NmeaType - GPGGA,GPGLL,GPGSA,GPGSV,GPRMC,GPVTG
  119. //
  120. //@param - int $NmeaType - what type of nmea sentence is it currently
  121. //@returns - void
  122. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  123. private function SetNmeaType($line){
  124. $this->type = trim(strtoupper(substr($line,1,5)));
  125. return $this->type;
  126. }
  127. //////////////////////////////////////////////////////////////////////////////////////////////////////
  128. //GGA - essential fix data which provide 3D location and accuracy data.
  129. //
  130. // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
  131. //
  132. //Where:
  133. // GGA Global Positioning System Fix Data
  134. // 123519 Fix taken at 12:35:19 UTC
  135. // 4807.038,N Latitude 48 deg 07.038' N
  136. // 01131.000,E Longitude 11 deg 31.000' E
  137. // 1 Fix quality: 0 = invalid
  138. // 1 = GPS fix (SPS)
  139. // 2 = DGPS fix
  140. // 3 = PPS fix
  141. // 4 = Real Time Kinematic
  142. // 5 = Float RTK
  143. // 6 = estimated (dead reckoning) (2.3 feature)
  144. // 7 = Manual input mode
  145. // 8 = Simulation mode
  146. // 08 Number of satellites being tracked
  147. // 0.9 Horizontal dilution of position
  148. // 545.4,M Altitude, Meters, above mean sea level
  149. // 46.9,M Height of geoid (mean sea level) above WGS84
  150. // ellipsoid
  151. // (empty field) time in seconds since last DGPS update
  152. // (empty field) DGPS station ID number
  153. // *47 the checksum data, always begins with *
  154. //////////////////////////////////////////////////////////////////////////////////////////////////////
  155. private function GPGGA($geostr){
  156. $split=explode(",",$geostr);
  157. $this->CurrentUTC = $this->fixUTC($split[1]);
  158. $this->Nmea[$this->CurrentUTC]['type']['GPGGA']=true;
  159. $this->Nmea[$this->CurrentUTC]['utc']=$this->fixUTC($split[1]);
  160. $this->Nmea[$this->CurrentUTC]['lat']=$this->degree2decimal($split[2],$split[3]);
  161. $this->Nmea[$this->CurrentUTC]['ns']=$split[3];
  162. $this->Nmea[$this->CurrentUTC]['long']=$this->degree2decimal($split[4],$split[5]);
  163. $this->Nmea[$this->CurrentUTC]['ew']=$split[5];
  164. $this->Nmea[$this->CurrentUTC]['gpsqual']=$split[6];
  165. $this->Nmea[$this->CurrentUTC]['numsat']=$split[7];
  166. $this->Nmea[$this->CurrentUTC]['hdp']=$split[8];
  167. $this->Nmea[$this->CurrentUTC]['alt']=$split[9];
  168. $this->Nmea[$this->CurrentUTC]['un_alt']=$split[10];
  169. $this->Nmea[$this->CurrentUTC]['geoidal']=$split[11];
  170. $this->Nmea[$this->CurrentUTC]['un_geoidal']=$split[12];
  171. $this->Nmea[$this->CurrentUTC]['dgps']=$split[13];
  172. $this->Nmea[$this->CurrentUTC]['diffstat']=trim($split[14]);
  173. }
  174. //////////////////////////////////////////////////////////////////////////////////////////////////////
  175. // $GPGLL,4916.45,N,12311.12,W,225444,A,*1D
  176. //
  177. //Where:
  178. // GLL Geographic position, Latitude and Longitude
  179. // 4916.46,N Latitude 49 deg. 16.45 min. North
  180. // 12311.12,W Longitude 123 deg. 11.12 min. West
  181. // 225444 Fix taken at 22:54:44 UTC
  182. // A Data Active or V (void)
  183. // *iD checksum data
  184. //////////////////////////////////////////////////////////////////////////////////////////////////////
  185. private function GPGLL($geostr){
  186. $split=explode(",",$geostr);
  187. $this->Nmea[$this->CurrentUTC]['type']['GPGLL']=true;
  188. $this->CurrentUTC = $this->fixUTC($split[3]);
  189. $this->Nmea[$this->CurrentUTC]['utc']=$this->fixUTC($split[3]);
  190. $this->Nmea[$this->CurrentUTC]['status']=$this->dataStatus($split[4]);
  191. }
  192. //////////////////////////////////////////////////////////////////////////////////////////////////////
  193. // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
  194. //
  195. //Where:
  196. // GSA Satellite status
  197. // A Auto selection of 2D or 3D fix (M = manual)
  198. // 3 3D fix - values include: 1 = no fix
  199. // 2 = 2D fix
  200. // 3 = 3D fix
  201. // 04,05... PRNs of satellites used for fix (space for 12)
  202. // 2.5 PDOP (dilution of precision)
  203. // 1.3 Horizontal dilution of precision (HDOP)
  204. // 2.1 Vertical dilution of precision (VDOP)
  205. // *39 the checksum data, always begins with *
  206. //////////////////////////////////////////////////////////////////////////////////////////////////////
  207. private function GPGSA($geostr){
  208. $split=explode(",",$geostr);
  209. $this->Nmea[$this->CurrentUTC]['type']['GPGSA']=true;
  210. $this->Nmea[$this->CurrentUTC]['selectmode']=$split[1];
  211. $this->Nmea[$this->CurrentUTC]['mode']=$split[2];
  212. $this->Nmea[$this->CurrentUTC]['sat1']=$split[3];
  213. $this->Nmea[$this->CurrentUTC]['sat2']=$split[4];
  214. $this->Nmea[$this->CurrentUTC]['sat3']=$split[5];
  215. $this->Nmea[$this->CurrentUTC]['sat4']=$split[6];
  216. $this->Nmea[$this->CurrentUTC]['sat5']=$split[7];
  217. $this->Nmea[$this->CurrentUTC]['sat6']=$split[8];
  218. $this->Nmea[$this->CurrentUTC]['sat7']=$split[9];
  219. $this->Nmea[$this->CurrentUTC]['sat8']=$split[10];
  220. $this->Nmea[$this->CurrentUTC]['sat9']=$split[11];
  221. $this->Nmea[$this->CurrentUTC]['sat10']=$split[12];
  222. $this->Nmea[$this->CurrentUTC]['sat11']=$split[13];
  223. $this->Nmea[$this->CurrentUTC]['sat12']=$split[14];
  224. $this->Nmea[$this->CurrentUTC]['pdop']=$split[15];
  225. $this->Nmea[$this->CurrentUTC]['hdop']=$split[16];
  226. $this->Nmea[$this->CurrentUTC]['vdop']=$split[17];
  227. }
  228. //////////////////////////////////////////////////////////////////////////////////////////////////////
  229. // $GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75
  230. //
  231. //Where:
  232. // GSV Satellites in view
  233. // 2 Number of sentences for full data
  234. // 1 sentence 1 of 2
  235. // 08 Number of satellites in view
  236. //
  237. // 01 Satellite PRN number
  238. // 40 Elevation, degrees
  239. // 083 Azimuth, degrees
  240. // 46 SNR - higher is better
  241. // for up to 4 satellites per sentence
  242. // *75 the checksum data, always begins with *
  243. //////////////////////////////////////////////////////////////////////////////////////////////////////
  244. //*********needs fixing
  245. private function GPGSV($geostr){
  246. $split=explode(",",$geostr);
  247. $this->Nmea[$this->CurrentUTC]['type']['GPGSV']=true;
  248. $this->Nmea[$this->CurrentUTC]['satmessages']=$split[1];
  249. $this->Nmea[$this->CurrentUTC]['messnum']=$split[2];
  250. $this->Nmea[$this->CurrentUTC]['satview']=$split[3];
  251. $this->Nmea[$this->CurrentUTC]['satnum']=$split[4];
  252. $this->Nmea[$this->CurrentUTC]['elevdeg']=$split[5];
  253. $this->Nmea[$this->CurrentUTC]['azimuthdeg']=$split[6];
  254. $this->Nmea[$this->CurrentUTC]['snr']=$split[7];
  255. }
  256. //////////////////////////////////////////////////////////////////////////////////////////////////////
  257. //$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
  258. //
  259. //Where:
  260. // RMC Recommended Minimum sentence C
  261. // 123519 Fix taken at 12:35:19 UTC
  262. // A Status A=active or V=Void.
  263. // 4807.038,N Latitude 48 deg 07.038' N
  264. // 01131.000,E Longitude 11 deg 31.000' E
  265. // 022.4 Speed over the ground in knots
  266. // 084.4 Track angle in degrees True
  267. // 230394 Date - 23rd of March 1994
  268. // 003.1,W Magnetic Variation
  269. // *6A The checksum data, always begins with *
  270. //////////////////////////////////////////////////////////////////////////////////////////////////////
  271. private function GPRMC($geostr){
  272. $split=explode(",",$geostr);
  273. $this->CurrentUTC = $this->fixUTC($split[1]);
  274. $this->Nmea[$this->CurrentUTC]['utc']=$this->fixUTC($split[1]);
  275. $this->Nmea[$this->CurrentUTC]['type']['GPRMC']=true;
  276. $this->Nmea[$this->CurrentUTC]['statusrmc']=$split[2];
  277. $this->Nmea[$this->CurrentUTC]['speed']=$split[7];
  278. $this->Nmea[$this->CurrentUTC]['track']=$split[8];
  279. $this->Nmea[$this->CurrentUTC]['date']=$split[9];
  280. $this->Nmea[$this->CurrentUTC]['magvar']=$split[10];
  281. $this->Nmea[$this->CurrentUTC]['mag_ew']=trim($split[11]);
  282. if($this->CurrentUTC && $split[9])
  283. $this->Nmea[$this->CurrentUTC]['Unix'] = $this->NMEAtoUnixTime($this->CurrentUTC,$split[9]);
  284. }
  285. //////////////////////////////////////////////////////////////////////////////////////////////////////
  286. //VTG - Velocity made good. The gps receiver may use the LC prefix instead of GP if it is emulating Loran output.
  287. //
  288. // $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
  289. //
  290. //where:
  291. // VTG Track made good and ground speed
  292. // 054.7,T True track made good (degrees)
  293. // 034.4,M Magnetic track made good
  294. // 005.5,N Ground speed, knots
  295. // 010.2,K Ground speed, Kilometers per hour
  296. // *48 Checksum
  297. //////////////////////////////////////////////////////////////////////////////////////////////////////
  298. private function GPVTG($geostr){
  299. $split=explode(",",$geostr);
  300. $this->Nmea[$this->CurrentUTC]['type']['GPVTG']=true;
  301. $this->Nmea[$this->CurrentUTC]['trkdeg1']=$split[1];
  302. $this->Nmea[$this->CurrentUTC]['t']=$split[2];
  303. $this->Nmea[$this->CurrentUTC]['trkdeg2']=$split[3];
  304. $this->Nmea[$this->CurrentUTC]['m']=$split[4];
  305. $this->Nmea[$this->CurrentUTC]['spdknots']=$spdk=$split[5];
  306. $this->Nmea[$this->CurrentUTC]['knots']=$split[6];
  307. $this->Nmea[$this->CurrentUTC]['spdkmph']=$split[7];
  308. $this->Nmea[$this->CurrentUTC]['kph']=$split[8];
  309. }
  310. //////////////////////////////////////////////////////////////////////////////////////////////////////
  311. //degree2decimal-
  312. //Convert latitude and longitude in degrees minutes seconds format to decimal format.
  313. //E.g. = 4807.038,N would be 48.12722
  314. //Formula is as follows
  315. // Degrees=Degrees
  316. // .d = M.m/60
  317. // Decimal Degrees=Degrees+.d
  318. //////////////////////////////////////////////////////////////////////////////////////////////////////
  319. private function degree2decimal($deg_coord,$direction,$precision=6){
  320. $degree=(int)($deg_coord/100); //simple way
  321. $minutes= $deg_coord-($degree*100);
  322. $dotdegree=$minutes/60;
  323. $decimal=$degree+$dotdegree;
  324. //South latitudes and West longitudes need to return a negative result
  325. if (($direction=="S") or ($direction=="W"))
  326. {
  327. $decimal=$decimal*(-1);
  328. }
  329. $decimal=number_format($decimal,$precision,'.',''); //truncate decimal to $precision places
  330. return $decimal;
  331. }
  332. //////////////////////////////////////////////////////////////////////////////////////////////////////
  333. //TimeChanged-
  334. //@param int $Time - time value
  335. //@return bool 0,1 (If both time are not equal, then we are processing new nmea data.)
  336. //
  337. //////////////////////////////////////////////////////////////////////////////////////////////////////
  338. private function TimeChanged($Time){
  339. return $this->CurrentTime==$Time;
  340. }
  341. //////////////////////////////////////////////////////////////////////////////////////////////////////
  342. //GetNmeaData-
  343. //@param void
  344. //@return array - nmea data array
  345. //
  346. //////////////////////////////////////////////////////////////////////////////////////////////////////
  347. public function GetNmeaData(){
  348. return $this->Nmea;
  349. }
  350. //////////////////////////////////////////////////////////////////////////////////////////////////////
  351. //fixUTCKey-
  352. //Replaces keys based on UTC with a linux time stamp.
  353. //@param int $UTC - UTC time (time only)
  354. //@param int $Unix - linux timestamp (has date)
  355. //////////////////////////////////////////////////////////////////////////////////////////////////////
  356. private function fixUTCKey($UTC,$Unix){
  357. //Not done
  358. $arr[$newkey] = $arr[$oldkey];
  359. unset($arr[$oldkey]);
  360. }
  361. //////////////////////////////////////////////////////////////////////////////////////////////////////
  362. //cleanUTC-
  363. //If UTC has a decimal in it, get rid of it.
  364. //@param int $UTC - UTC time (time only)
  365. //@return int $UTCfixed -
  366. //////////////////////////////////////////////////////////////////////////////////////////////////////
  367. private function fixUTC($UTC){
  368. //list($Fixed,$Null) = explode('.',$UTC);
  369. return $UTC; //$Fixed;
  370. }
  371. }
  372. ?>