-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow prefix with no talker ID #101
Conversation
Talked ID and Sentence type parsing is done here Line 126 in 2740e68
I have somewhat similar PR #97 that tried to work around CRC check (there are devices that do not send CRC) by introducing way to customize parser. NB: it would help with #98 also as it has callback for dealing with tag blocks and thus allowing buffering tags over multiple sentences. I could revive this PR by throwing all breaking changes out ( type SentenceParser struct {
//...
ParseAddress func(rawAddress) (talkerID string, sentence string, err error)
//...
} |
I think the implementation is a little hacky, but the behaviour change makes sense to me. I think splitting the prefix into the talker/type in the BaseSentence was a mistake and we should consider undoing that in v2. @aldas I'm not clear on what your proposed api change would look like. |
In relation for this situation: maybe Lines 133 to 136 in 2740e68
to l := len(s)
if l < 2 { // this will result in error as no sentence has "" (empty) type
return s, ""
} else if l == 3 {
// ugly workaround for sentence which address contains only sentence type
// these are devices with invalid implementation of NMEA0183 protocol, probably not marine related as this kind
// of implementation would not pass certification process.
return "", s
}
// NMEA standard says that (non-proprietary) address consist of 5 bytes where first 2 bytes are Talker ID and last 3 sentence type
return s[:2], s[2:] This is not particularly better or worse from current logic in my opinion as current implementation assumes that length of 1 or 2 is fine and in that case everything belongs to Talker ID in that case. Separating address (first field after Most of the time NMEA0183 is used with serial connection (peer to peer) ala over RS232 but for larger installations - like vessels, where there could be multiple devices that require same input (ala multiple devices need same GPS sentences), in that case you would install NMEA0183 multiplexer/buffer that has inputs from multiple RS232 devices and can multiplex them into single or multiple outputs. TalkerID is little bit naive way to distinguish source of the message and in NMEA2000 protocol this is superseeded by source/destination field as N2K is bus (multiple senders/multiple receivers). anyway, 99% of cases you only care for last 3 characters in address field - i.e. sentence type (I do not consider proprietary messages here where sentence type could be longer that 3) but there are valid cases when library should expose Talker ID to the user. That PR #97 API would be like that: p := nmea.NewSentenceParserWithConfig(nmea.SentenceParserConfig{
ParseAddress: func(address string) (talkerID string, sentence string, err error) {
if len(address) < 3 {
return "", "", errors.New("could not determine talker ID and sentence type from address")
}
if len(address) == 3 { // my workaround
return "", address, nil
}
return address[:2], address[2:], nil
},
CheckCRC: func(rawCRC string, calculatedCRC string, sentence nmea.BaseSentence) error {
return nil // do not check CRC at all
},
})
s, err := p.Parse("$HEROT,-11.23,A*FF") |
I'd prefer if the p := nmea.SentenceParser{
ParseAddress: func(address string) (talkerID string, sentence string, err error) {
if len(address) < 3 {
return "", "", errors.New("could not determine talker ID and sentence type from address")
}
if len(address) == 3 { // my workaround
return "", address, nil
}
return address[:2], address[2:], nil
},
CheckCRC: func(rawCRC string, calculatedCRC string, sentence nmea.BaseSentence) error {
return nil // do not check CRC at all
},
} However, for this PR, I think we can get away with just adding if parser, ok := customParsers[s.Prefix()]; ok {
return parser(s)
} Then it will be up to the nmea.MustRegisterParser("FOOBAR", func(s nmea.BaseSentence) (Sentence, error) {
s.Type = s.Talker + s.Type
s.Talker = ""
return s, nil
}) edit: I also think the prefix |
@icholy, also As I tried to explain - Talker ID depends on type of the hardware. Talker ID is is actually configurable on some devices. NMEA has actually reserved "U[Digit]" ala For example - lets assume that this library does not have "HBT - heartbeat sentence" and user want to add custom parser for it. They use NMEA multiplexer If we would implement it I would still recommend adding another |
@aldas I may have been unclear, but I think we're agreeing. This is what I was suggesting: // try to match the whole prefix first
if parser, ok := customParsers[s.Prefix()]; ok {
return parser(s)
}
// match the type
if parser, ok := customParsers[s.Type]; ok {
return parser(s)
}
If I'm reading this correctly, this will only help if @jdgordon 's custom device sentences have a prefix length of 3. |
aye, you are correct. This would only help with len=3. |
The |
at some point you maybe cant expect library to have workaround line := "$GSENSORD,0.060,0.120,-0.180"
if strings.HasPrefix(line, "$GSENSORD,") {
line = "$XXGSENSORD," + line[10:] // fix talkerid+type
line = line + "*" + nmea.Checksum(line[1:]) // fix CRC
}
s, err := nmea.Parse(line) I am using something similar for my cases with devices missing CRC. but in this case it is little bit harder - the message contains CRC and that is calculated without talkerID. So maybe doing double check is ok, but it still seems such waste :) |
@jdgordon I'm closing this PR in favor of #97 . See my comment at #53 (comment) for more details. |
@icholy no problem. I'm going to continue using my fork in the mean time. Yeah, our devices dont follow the NMEA spec and it doesnt make sense to assume your lib would support it :) cheers |
Hi,
I want to use this NMEA parser for a custom device which doesn't use talker ID's. This change adds a second check to the custom parsers to check for the full
talkerID + prefix
in the registered parsers instead of just theprefix
.This is the bare minimum to make this work, however happy to implement more edge-case handling if desired. I wasnt sure if this should be the first check on the custom parsers or second. Opted for the second to limit the chance of breaking anything.