biscuit

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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 }