Skip to content

Commit

Permalink
Merge d2c4cad into 2310df0
Browse files Browse the repository at this point in the history
  • Loading branch information
klyve committed Aug 8, 2019
2 parents 2310df0 + d2c4cad commit 4a7ed8e
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 0 deletions.
8 changes: 8 additions & 0 deletions sentence.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type BaseSentence struct {
Fields []string // Array of fields
Checksum string // The Checksum
Raw string // The raw NMEA sentence received
TagBlock TagBlock // NMEA tagblock
}

// Prefix returns the talker and type of message
Expand All @@ -57,6 +58,11 @@ func (s BaseSentence) String() string { return s.Raw }
// parseSentence parses a raw message into it's fields
func parseSentence(raw string) (BaseSentence, error) {
raw = strings.TrimSpace(raw)
tagBlock, raw, err := parseTagBlock(raw)
if err != nil {
return BaseSentence{}, err
}

startIndex := strings.IndexAny(raw, SentenceStart+SentenceStartEncapsulated)
if startIndex != 0 {
return BaseSentence{}, fmt.Errorf("nmea: sentence does not start with a '$' or '!'")
Expand All @@ -77,12 +83,14 @@ func parseSentence(raw string) (BaseSentence, error) {
"nmea: sentence checksum mismatch [%s != %s]", checksum, checksumRaw)
}
talker, typ := parsePrefix(fields[0])

return BaseSentence{
Talker: talker,
Type: typ,
Fields: fields[1:],
Checksum: checksumRaw,
Raw: raw,
TagBlock: tagBlock,
}, nil
}

Expand Down
114 changes: 114 additions & 0 deletions tagblock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package nmea

import (
"fmt"
"regexp"
"strconv"
"strings"
)

const (
// TypeUnixTime unix timestamp, parameter: -c
TypeUnixTime = "c"
// TypeDestinationID destination identification 15char max, parameter: -d
TypeDestinationID = "d"
// TypeGrouping sentence grouping, parameter: -g
TypeGrouping = "g"
// TypeLineCount linecount, parameter: -n
TypeLineCount = "n"
// TypeRelativeTime relative time time, paremeter: -r
TypeRelativeTime = "r"
// TypeSourceID source identification 15char max, paremter: -s
TypeSourceID = "s"
// TypeTextString valid character string, parameter -t
TypeTextString = "t"
)

var (
// tagBlockRegexp matches nmea tag blocks
tagBlockRegexp = regexp.MustCompile(`^(.*)\\(\S+)\\(.*)`)
)

// TagBlock struct
type TagBlock struct {
Head string // *
Time int64 // -c
RelativeTime int64 // -r
Destination string // -d 15 char max
Grouping string // -g nummeric string
LineCount int64 // -n int
Source string // -s 15 char max
Text string // -t Variable length text
}

func parseInt64(raw string) (int64, error) {
i, err := strconv.ParseInt(raw[2:], 10, 32)
if err != nil {
return 0, fmt.Errorf("nmea: tagblock unable to parse uint32 [%s]", raw)
}
return i, nil
}

// parseTagBlock adds support for tagblocks
// https://rietman.wordpress.com/2016/09/17/nemastudio-now-supports-the-nmea-0183-tag-block/
func parseTagBlock(raw string) (TagBlock, string, error) {
matches := tagBlockRegexp.FindStringSubmatch(raw)
if matches == nil {
return TagBlock{}, raw, nil
}

tagBlock := TagBlock{}
raw = matches[3]
tags := matches[2]
tagBlock.Head = matches[1]

sumSepIndex := strings.Index(tags, ChecksumSep)
if sumSepIndex == -1 {
return tagBlock, "", fmt.Errorf("nmea: tagblock does not contain checksum separator")
}

var (
fieldsRaw = tags[0:sumSepIndex]
checksumRaw = strings.ToUpper(tags[sumSepIndex+1:])
checksum = xorChecksum(fieldsRaw)
err error
)

// Validate the checksum
if checksum != checksumRaw {
return tagBlock, "", fmt.Errorf("nmea: tagblock checksum mismatch [%s != %s]", checksum, checksumRaw)
}

items := strings.Split(tags[:sumSepIndex], ",")
for _, item := range items {
if len(item) == 0 {
continue
}
switch item[:1] {
case TypeUnixTime:
tagBlock.Time, err = parseInt64(item)
if err != nil {
return tagBlock, raw, err
}
case TypeDestinationID:
tagBlock.Destination = item[2:]
case TypeGrouping:
tagBlock.Grouping = item[2:]
case TypeLineCount:
tagBlock.LineCount, err = parseInt64(item)
if err != nil {
return tagBlock, raw, err
}
case TypeRelativeTime:
tagBlock.RelativeTime, err = parseInt64(item)
if err != nil {
return tagBlock, raw, err
}
case TypeSourceID:
tagBlock.Source = item[2:]
case TypeTextString:
tagBlock.Text = item[2:]
}
}
return tagBlock, raw, nil
}
121 changes: 121 additions & 0 deletions tagblock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package nmea

import (
"testing"

"github.com/stretchr/testify/assert"
)

var tagblocktests = []struct {
name string
raw string
err string
msg TagBlock
}{
{

name: "Test NMEA tag block",
raw: "\\s:Satelite_1,c:1553390539*62\\!AIVDM,1,1,,A,13M@ah0025QdPDTCOl`K6`nV00Sv,0*52",
msg: TagBlock{
Time: 1553390539,
Source: "Satelite_1",
},
},
{

name: "Test NMEA tag block with head",
raw: "UdPbC?\\s:satelite,c:1564827317*25\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
msg: TagBlock{
Time: 1564827317,
Source: "satelite",
Head: "UdPbC?",
},
},
{

name: "Test unknown tag",
raw: "UdPbC?\\x:NorSat_1,c:1564827317*42\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
msg: TagBlock{
Time: 1564827317,
Source: "",
Head: "UdPbC?",
},
},
{

name: "Test all input types",
raw: "UdPbC?\\s:satelite,c:1564827317,r:1553390539,d:ara,g:bulk,n:13,t:helloworld*3F\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
msg: TagBlock{
Time: 1564827317,
RelativeTime: 1553390539,
Destination: "ara",
Grouping: "bulk",
Source: "satelite",
Head: "UdPbC?",
Text: "helloworld",
LineCount: 13,
},
},
{

name: "Test empty tag in tagblock",
raw: "UdPbC?\\s:satelite,,r:1553390539,d:ara,g:bulk,n:13,t:helloworld*68\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
msg: TagBlock{
Time: 0,
RelativeTime: 1553390539,
Destination: "ara",
Grouping: "bulk",
Source: "satelite",
Head: "UdPbC?",
Text: "helloworld",
LineCount: 13,
},
//err: "nmea: tagblock checksum mismatch [25 != 49]",
},
{

name: "Test Invalid checksum",
raw: "UdPbC?\\s:satelite,c:1564827317*49\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
err: "nmea: tagblock checksum mismatch [25 != 49]",
},
{

name: "Test no checksum",
raw: "UdPbC?\\s:satelite,c:156482731749\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
err: "nmea: tagblock does not contain checksum separator",
},
{

name: "Test invalid timestamp",
raw: "UdPbC?\\s:satelite,c:gjadslkg*30\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
err: "nmea: tagblock unable to parse uint32 [c:gjadslkg]",
},
{

name: "Test invalid linecount",
raw: "UdPbC?\\s:satelite,n:gjadslkg*3D\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
err: "nmea: tagblock unable to parse uint32 [n:gjadslkg]",
},
{

name: "Test invalid relative time",
raw: "UdPbC?\\s:satelite,r:gjadslkg*21\\!AIVDM,1,1,,A,19NSRaP02A0fo91kwnaMKbjR08:J,0*15",
err: "nmea: tagblock unable to parse uint32 [r:gjadslkg]",
},
}

func TestTagBlock(t *testing.T) {
for _, tt := range tagblocktests {
t.Run(tt.name, func(t *testing.T) {
m, err := Parse(tt.raw)
if tt.err != "" {
assert.Error(t, err)
assert.EqualError(t, err, tt.err)
} else {
assert.NoError(t, err)
vdm := m.(VDMVDO)
assert.Equal(t, tt.msg, vdm.BaseSentence.TagBlock)
}
})
}
}

0 comments on commit 4a7ed8e

Please sign in to comment.