2025-05-14 17:30:14 +00:00
|
|
|
;; each sentence is introduced by $ and terminated by *
|
|
|
|
;; first two chars are "talker id"
|
|
|
|
;; 3-5 are the message type
|
|
|
|
;; fields are comma-delimited
|
|
|
|
|
2025-05-17 23:23:32 +00:00
|
|
|
;; we can parse the message based only on type (i.e. the same type is
|
|
|
|
;; always parsed the same way no matter who the talker)
|
2025-05-14 17:30:14 +00:00
|
|
|
|
|
|
|
;; outline: split message into lines, extract values between
|
|
|
|
;; $ and * as talker, type, fields
|
|
|
|
;; then call the parser for the type
|
|
|
|
|
|
|
|
(import-macros { : define-tests : expect : expect= } :assert)
|
|
|
|
|
|
|
|
(fn split-line [line]
|
|
|
|
(let [(talker msgtype rest) (string.match line "$(..)(...)(.+)*")
|
|
|
|
fields (icollect [k _ (string.gmatch rest ",([^,]*)")] k)]
|
|
|
|
{ : talker :message-type msgtype :fields fields }))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(expect=
|
|
|
|
(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")
|
|
|
|
{
|
|
|
|
:talker "GL"
|
|
|
|
:message-type "GSV"
|
|
|
|
:fields ["2" "1" "07" "78" "47" "295" "34" "77" "35" "208" "19" "88"
|
|
|
|
"36" "267" "30" "87" "67" "025" "27" "1"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
(expect=
|
|
|
|
(split-line "$GNGNS,111134.00,5131.348976,N,00005.551003,W,AAAANN,19,0.7,20.3,47.0,,,V*0C\n")
|
|
|
|
{
|
|
|
|
:talker "GN"
|
|
|
|
:message-type "GNS"
|
|
|
|
:fields ["111134.00" ; utc
|
|
|
|
"5131.348976" "N"
|
|
|
|
"00005.551003" "W"
|
|
|
|
"AAAANN"
|
|
|
|
"19" ; number of satellites
|
|
|
|
"0.7" ; hdop
|
|
|
|
"20.3" ; height
|
|
|
|
"47.0" ; geoidal separation
|
|
|
|
"" "" ; differential data age, reference station
|
|
|
|
"V" ; not valid for navigation (?)
|
|
|
|
]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2025-05-17 23:20:55 +00:00
|
|
|
(fn try-number [s]
|
|
|
|
(if (= s "")
|
|
|
|
nil
|
|
|
|
(tonumber s)))
|
2025-05-14 17:30:14 +00:00
|
|
|
|
|
|
|
(local
|
|
|
|
msg-types
|
|
|
|
{
|
2025-05-17 23:22:37 +00:00
|
|
|
:GGA
|
|
|
|
(fn [fields]
|
|
|
|
(let [[utc lat latsign lon lonsign
|
|
|
|
fix-quality
|
|
|
|
total-space-vehicles
|
|
|
|
hdop
|
|
|
|
altitude _
|
|
|
|
geoidal-separation _
|
|
|
|
_ _ ] fields
|
|
|
|
latf (if (= lat "")
|
|
|
|
nil
|
|
|
|
(* (tonumber lat) 0.01 (case latsign :N 1 :S -1)))
|
|
|
|
lonf (if (= lon "")
|
|
|
|
nil
|
|
|
|
(* (tonumber lon) 0.01 (case lonsign :E 1 :W -1)))]
|
|
|
|
{ :lat latf
|
|
|
|
:lon lonf
|
|
|
|
: fix-quality
|
|
|
|
:total-space-vehicles (try-number total-space-vehicles)
|
|
|
|
:hdop (try-number hdop)
|
|
|
|
:altitude (try-number altitude)
|
|
|
|
:geoidal-separation (try-number geoidal-separation)
|
|
|
|
}))
|
|
|
|
|
|
|
|
:RMC
|
|
|
|
(fn [[utc valid lat latsign lon lonsign
|
|
|
|
knots bearing-true date
|
|
|
|
magn-var magn-var-sign
|
|
|
|
mode nav-status &as fields]]
|
|
|
|
(let [latf (if (= lat "")
|
|
|
|
nil
|
|
|
|
(* (tonumber lat) 0.01 (case latsign :N 1 :S -1)))
|
|
|
|
lonf (if (= lon "")
|
|
|
|
nil
|
|
|
|
(* (tonumber lon) 0.01 (case lonsign :E 1 :W -1)))
|
|
|
|
magnetic-variation (if (= magn-var "")
|
|
|
|
nil
|
|
|
|
(* (tonumber magn-var)
|
|
|
|
(case magn-var-sign :E 1 :W -1)))]
|
|
|
|
{ : utc
|
|
|
|
: valid
|
|
|
|
:lat latf
|
|
|
|
:lon lonf
|
|
|
|
:speed-knots (try-number knots)
|
|
|
|
:bearing-true (try-number bearing-true)
|
|
|
|
: date
|
|
|
|
: magnetic-variation
|
|
|
|
: mode
|
|
|
|
}))
|
|
|
|
|
2025-05-14 17:30:14 +00:00
|
|
|
:GNS
|
|
|
|
(fn [fields]
|
|
|
|
(let [[utc lat latsign lon lonsign mode total-space-vehicles
|
2025-05-17 23:20:55 +00:00
|
|
|
hdop altitude geoidal-separation _ _ nav-status] fields
|
|
|
|
latf (if (= lat "")
|
|
|
|
nil
|
|
|
|
(* (tonumber lat) 0.01 (case latsign :N 1 :S -1)))
|
|
|
|
lonf (if (= lon "")
|
|
|
|
nil
|
|
|
|
(* (tonumber lon) 0.01 (case lonsign :E 1 :W -1)))]
|
|
|
|
{ :lat latf
|
|
|
|
:lon lonf
|
2025-05-14 17:30:14 +00:00
|
|
|
: mode
|
2025-05-17 23:20:55 +00:00
|
|
|
:total-space-vehicles (try-number total-space-vehicles)
|
|
|
|
:hdop (try-number hdop)
|
|
|
|
:altitude (try-number altitude)
|
|
|
|
:geoidal-separation (try-number geoidal-separation)
|
2025-05-14 17:30:14 +00:00
|
|
|
: nav-status
|
|
|
|
}
|
|
|
|
))
|
2025-05-17 23:20:55 +00:00
|
|
|
|
2025-05-14 17:30:14 +00:00
|
|
|
:VTG
|
|
|
|
(fn [[bearing-true _ bearing-mag _ knots _ kmh _ mode]]
|
|
|
|
{
|
2025-05-17 23:20:55 +00:00
|
|
|
:bearing-true (try-number bearing-true)
|
|
|
|
:bearing-magnetic (try-number bearing-mag)
|
|
|
|
:speed-knots (try-number knots)
|
|
|
|
:speed-kmh (try-number kmh)
|
2025-05-14 17:30:14 +00:00
|
|
|
: mode
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
(fn parse-line [line]
|
|
|
|
(let [{ : message-type : talker : fields &as split} (split-line line)
|
|
|
|
parser (. msg-types message-type)]
|
|
|
|
(if parser
|
|
|
|
(doto (parser fields)
|
|
|
|
(tset :message-type message-type)
|
|
|
|
(tset :talker talker))
|
|
|
|
split)))
|
|
|
|
|
|
|
|
(expect=
|
|
|
|
(parse-line
|
|
|
|
"$GNGNS,111134.00,5131.348976,N,00005.551003,W,AAAANN,19,0.7,20.3,47.0,,,V*0C\n")
|
|
|
|
{:altitude 20.3
|
|
|
|
:geoidal-separation 47
|
|
|
|
:hdop 0.7
|
|
|
|
:lat 51.31348976
|
|
|
|
:lon -0.05551003
|
|
|
|
:message-type "GNS"
|
|
|
|
:mode "AAAANN"
|
|
|
|
:nav-status "V"
|
|
|
|
:talker "GN"
|
|
|
|
:total-space-vehicles 19})
|
|
|
|
|
|
|
|
(expect=
|
|
|
|
(parse-line
|
|
|
|
"$GNVTG,263.3,T,266.5,M,1.6,N,2.9,K,A*32\n")
|
|
|
|
{
|
|
|
|
:talker :GN
|
|
|
|
:message-type :VTG
|
|
|
|
:bearing-true 263.3
|
|
|
|
:bearing-magnetic 266.5
|
|
|
|
:speed-knots 1.6
|
|
|
|
:speed-kmh 2.9
|
|
|
|
:mode :A
|
|
|
|
})
|
2025-05-17 23:23:32 +00:00
|
|
|
|
|
|
|
{ :parse parse-line }
|