awk script to parse NMEA 0183 messages

GPS (and other marine equipment) send information in simple NMEA sentences that can easily be parsed by awk. This is a sample script that parses GPS RMC, GSV, GGA, GSA, VTG, GLL and TXT messages.

 cat nmea.awk
 /\$G[ABLNP]?TXT/ {print substr($5,1,index($5,"*")-1)}
 /\$G[ABLNP]?RMC/ {if ($3 == "A") print substr($2,1,2) ":" substr($2,3,2) ":" substr($2,5,2) "Z. " substr($4,1,2) "\xc2\xb0" substr($4,3,2) "\x27" int(substr($4,6,4)*0.006) "\x22" $5 " " substr($6,1,3) "\xc2\xb0" substr($6,4,2) "\x27" int(substr($6,7,4)*0.006) "\x22" $7 ". SOG " $8 "knt. COG " $9 "\xc2\xb0"}
 /\$G[ABLNP]?GSV/ {s = substr($1,2,2); sprint s; if (s == "GP") g = "GPS"; if (s == "GA") g = "Galileo"; if (s == "GB") g = "BeiDou"; if (s == "GL") g = "GLONASS"; print g " msg: " $3 " of " $2 " Total SVs: " $4; for (i=0;i<4;i++) print " SV id: " $(5+4*i) " - elev: " $(6+4*i) "\xc2\xb0, azim: " $(7+4*i) "\xc2\xb0, signal/noise: " substr($(8+4*i),1,2) " dBHz"}
 /\$G[ABLNP]?GGA/ {if ($7 == 1) print substr($2,1,2) ":" substr($2,3,2) ":" substr($2,5,2) "Z. " substr($3,1,2) "\xc2\xb0" substr($3,3,2) "\x27" int(substr($3,6,4)*0.006) "\x22" $4 " " substr($5,1,3) "\xc2\xb0" substr($5,4,2) "\x27" int(substr($5,7,4)*0.006) "\x22" $6 " SVs: " $8 ". hDOP: " $9 ". Alt " $10 $11 " above MSL. Geoid separation " $12 $13}
 /\$G[ABLNP]?GSA/ {if ($3 == 3) print "SV IDs: "; for (i=0;i < 12;i++) if ($(4+i) != "") printf "%d, ", $(4+i); print " pDOP: " $16 ". hDOP: " $17 ". vDOP: " substr($18,1,index($18,"*")-1)}
 /\$G[ABLNP]?VTG/ {if (substr($10,1,1) == "A") print "COGt: " $2 "\xc2\xb0" $3 " COGm: " $4 "\xc2\xb0" $5 " SOGn: " $6 "knts SOGk " $8 "kph "}
 /\$G[ABLNP]?GLL/ {if ($7 == "A") print substr($2,1,2) "\xc2\xb0" substr($2,3,2) "\x27" int(substr($2,6,4)*0.006) "\x22" $3 " " substr($4,1,3) "\xc2\xb0" substr($4,4,2) "\x27" int(substr($4,7,4)*0.006) "\x22" $5 " " substr($6,1,2) ":" substr($6,3,2) ":" substr($6,5,2) "Z"}

Here is an example of unparsed miniterm output:

 miniterm /dev/ttyUSB0 4800
 $GPRMC,081651.000,A,3648.7158,S,17438.8613,E,0.03,75.95,120320,,,A*40
 $GPGGA,081652.000,3648.7158,S,17438.8614,E,1,09,1.0,18.7,M,26.4,M,,0000*7F
 $GPGSA,A,3,01,31,14,22,32,04,26,03,16,,,,2.0,1.0,1.7*34
 $GPRMC,081652.000,A,3648.7158,S,17438.8614,E,0.04,118.14,120320,,,A*70
 $GPGGA,081653.000,3648.7158,S,17438.8613,E,1,09,1.0,18.9,M,26.4,M,,0000*77
 $GPGSA,A,3,01,31,14,22,32,04,26,03,16,,,,2.0,1.0,1.7*34
 $GPGSV,3,1,11,31,69,153,40,22,62,262,37,14,48,107,42,26,47,027,31*7A
 $GPGSV,3,2,11,03,43,230,40,32,27,088,27,16,24,007,29,01,20,290,29*76
 $GPGSV,3,3,11,04,17,245,28,25,13,140,,10,,031,*41

Pipe the serial output above via awk text processor using “,” as a field separator and the awk script above as -f argument:

 miniterm /dev/ttyUSB0 4800|awk -F "," -f nmea.awk
 08:29:16Z. 36°48'43"S 174°38'51"E. SOG 0.01knt. COG 228.82°
 08:29:17Z. 36°48'43"S 174°38'51"E SVs: 09. hDOP: 1.0. Alt 10.4M above MSL. Geoid separation 26.4M
 SV IDs:
 1, 31, 14, 22, 32, 4, 26, 3, 16,  pDOP: 1.9. hDOP: 1.0. vDOP 1.6
 08:29:17Z. 36°48'43"S 174°38'51"E. SOG 0.04knt. COG 141.61°
 08:29:18Z. 36°48'43"S 174°38'51"E SVs: 09. hDOP: 1.0. Alt 10.3M above MSL. Geoid separation 26.4M
 SV IDs:
 1, 31, 14, 22, 32, 4, 26, 3, 16,  pDOP: 1.9. hDOP: 1.0. vDOP 1.6
 Msg: 1 of 3 Total SVs: 11
  SV id: 31 - elev: 64°, azim: 148°, signal/noise: 41 dBHz
  SV id: 22 - elev: 63°, azim: 275°, signal/noise: 40 dBHz
  SV id: 26 - elev: 53°, azim: 032°, signal/noise: 40 dBHz
  SV id: 03 - elev: 47°, azim: 235°, signal/noise: 41 dBHz
 Msg: 2 of 3 Total SVs: 11
  SV id: 14 - elev: 44°, azim: 100°, signal/noise: 30 dBHz
  SV id: 16 - elev: 30°, azim: 008°, signal/noise: 39 dBHz
  SV id: 32 - elev: 24°, azim: 083°, signal/noise: 35 dBHz
  SV id: 04 - elev: 21°, azim: 242°, signal/noise: 30 dBHz
 Msg: 3 of 3 Total SVs: 11
  SV id: 01 - elev: 17°, azim: 294°, signal/noise: 17 dBHz
  SV id: 25 - elev: 09°, azim: 142°, signal/noise:  dBHz
  SV id: 29 - elev: °, azim: 108°, signal/noise: 21 dBHz
  SV id:  - elev: °, azim: °, signal/noise:  dBHz