-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |