nmea.fnl (4658B)
1 ;; each sentence is introduced by $ and terminated by * 2 ;; first two chars are "talker id" 3 ;; 3-5 are the message type 4 ;; fields are comma-delimited 5 6 ;; we can parse the message based only on type (i.e. the same type is 7 ;; always parsed the same way no matter who the talker) 8 9 ;; outline: split message into lines, extract values between 10 ;; $ and * as talker, type, fields 11 ;; then call the parser for the type 12 13 (import-macros { : define-tests : expect : expect= } :assert) 14 15 (fn split-line [line] 16 (let [(talker msgtype rest) (string.match line "$(..)(...)(.+)*") 17 fields (icollect [k _ (string.gmatch rest ",([^,]*)")] k)] 18 { : talker :message-type msgtype :fields fields })) 19 20 21 22 (expect= 23 (split-line "$GLGSV,2,1,07,78,47,295,34,77,35,208,19,88,36,267,30,87,67,025,27,1*74\n") 24 { 25 :talker "GL" 26 :message-type "GSV" 27 :fields ["2" "1" "07" "78" "47" "295" "34" "77" "35" "208" "19" "88" 28 "36" "267" "30" "87" "67" "025" "27" "1" 29 ] 30 } 31 ) 32 33 (expect= 34 (split-line "$GNGNS,111134.00,5131.348976,N,00005.551003,W,AAAANN,19,0.7,20.3,47.0,,,V*0C\n") 35 { 36 :talker "GN" 37 :message-type "GNS" 38 :fields ["111134.00" ; utc 39 "5131.348976" "N" 40 "00005.551003" "W" 41 "AAAANN" 42 "19" ; number of satellites 43 "0.7" ; hdop 44 "20.3" ; height 45 "47.0" ; geoidal separation 46 "" "" ; differential data age, reference station 47 "V" ; not valid for navigation (?) 48 ] 49 } 50 ) 51 52 (fn try-number [s] 53 (if (= s "") 54 nil 55 (tonumber s))) 56 57 (fn parse-coordinate [value sign] 58 (if (= value "") 59 nil 60 (let [(deg min) (string.match value "(..)(.+)")] 61 (* 62 (+ (tonumber deg) 63 (/ (tonumber min) 60)) 64 (case sign :N 1 :E 1 :S -1 :W -1))))) 65 66 (local 67 msg-types 68 { 69 :GGA 70 (fn [fields] 71 (let [[utc lat latsign lon lonsign 72 fix-quality 73 total-space-vehicles 74 hdop 75 altitude _ 76 geoidal-separation _ 77 _ _ ] fields 78 ] 79 { :lat (parse-coordinate lat latsign) 80 :lon (parse-coordinate lon lonsign) 81 : fix-quality 82 :total-space-vehicles (try-number total-space-vehicles) 83 :hdop (try-number hdop) 84 :altitude (try-number altitude) 85 :geoidal-separation (try-number geoidal-separation) 86 })) 87 88 :RMC 89 (fn [[utc valid lat latsign lon lonsign 90 knots bearing-true date 91 magn-var magn-var-sign 92 mode nav-status &as fields]] 93 (let [magnetic-variation (if (= magn-var "") 94 nil 95 (* (tonumber magn-var) 96 (case magn-var-sign :E 1 :W -1)))] 97 { : utc 98 : valid 99 :lat (parse-coordinate lat latsign) 100 :lon (parse-coordinate lon lonsign) 101 :speed-knots (try-number knots) 102 :bearing-true (try-number bearing-true) 103 : date 104 : magnetic-variation ;;; XXX probably wrong 105 : mode 106 })) 107 108 :GNS 109 (fn [fields] 110 (let [[utc lat latsign lon lonsign mode total-space-vehicles 111 hdop altitude geoidal-separation _ _ nav-status] fields] 112 { :lat (parse-coordinate lat latsign) 113 :lon (parse-coordinate lon lonsign) 114 : mode 115 :total-space-vehicles (try-number total-space-vehicles) 116 :hdop (try-number hdop) 117 :altitude (try-number altitude) 118 :geoidal-separation (try-number geoidal-separation) 119 : nav-status 120 } 121 )) 122 123 :VTG 124 (fn [[bearing-true _ bearing-mag _ knots _ kmh _ mode]] 125 { 126 :bearing-true (try-number bearing-true) 127 :bearing-magnetic (try-number bearing-mag) 128 :speed-knots (try-number knots) 129 :speed-kmh (try-number kmh) 130 : mode 131 } 132 ) 133 }) 134 135 136 (fn parse-line [line] 137 (let [{ : message-type : talker : fields &as split} (split-line line) 138 parser (. msg-types message-type)] 139 (if parser 140 (doto (parser fields) 141 (tset :message-type message-type) 142 (tset :talker talker)) 143 split))) 144 145 ;; XXX this is wrong. 5131.348976 is 51 degrees 31.348976 minutes 146 147 (expect= 148 (parse-line 149 "$GNGNS,111134.00,5131.348976,N,00005.551003,W,AAAANN,19,0.7,20.3,47.0,,,V*0C\n") 150 {:altitude 20.3 151 :geoidal-separation 47.0 152 :hdop 0.7 153 :lat 51.522482933333 154 :lon -0.092516716666667 155 :message-type "GNS" 156 :mode "AAAANN" 157 :nav-status "V" 158 :talker "GN" 159 :total-space-vehicles 19}) 160 161 (expect= 162 (parse-line 163 "$GNVTG,263.3,T,266.5,M,1.6,N,2.9,K,A*32\n") 164 { 165 :talker :GN 166 :message-type :VTG 167 :bearing-true 263.3 168 :bearing-magnetic 266.5 169 :speed-knots 1.6 170 :speed-kmh 2.9 171 :mode :A 172 }) 173 174 { :parse parse-line }