Skip to content

Commit

Permalink
Allow setting what heading sentence to use
Browse files Browse the repository at this point in the history
  • Loading branch information
wlkh committed Jan 28, 2021
1 parent 143cc81 commit 5ebae03
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 81 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ BUILD_FOLDER=build
BINARY=${BUILD_FOLDER}/nmea_ugps

# Pass variables for version number, sha id and build number
VERSION=1.3.0
VERSION=1.4.0
SHA=$(shell git rev-parse --short HEAD)
# Set fallback build num if not set by environment variable
BUILDNUM?=local
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ The application also reads the latitude/longitude of the Locator from the Underw
The application is run on the command line and can be configured via arguments. The arguments are:

```
-d debug
-heading string
Input sentence type to use for heading. Supported: HDM, HDT, THS (default "HDT")
-i string
UDP device and port (host:port) OR serial device (COM7 /dev/ttyUSB1@4800) to listen for NMEA input. (default "0.0.0.0:7777")
-o string
Expand All @@ -33,16 +36,16 @@ The application is run on the command line and can be configured via arguments.
URL of Underwater GPS (default "http://192.168.2.94")
```

Example using UART input from /dev/ttyUSB2 with baud rate 4800 and sending the output via UDP on port 9999 on localhost.
Example using UART input from /dev/ttyUSB2 with baud rate 4800 using THS sentence for heading and sending the output via UDP on port 2947 on localhost.

```
./nmea_ugps_linux_amd64 -i /dev/ttyUSB2@4800 -o 127.0.0.1:9999
./nmea_ugps_linux_amd64 -i /dev/ttyUSB2@4800 -o 127.0.0.1:2947 -heading THS
```

On Windows, the easiest is to create a `start.bat` file, edit it with notepad to the desired settings and then double-click it in Explorer to start it. Example of what the file can look like:

```
nmea_ugps_windows_amd64.exe -i COM1 -o 127.0.0.1:2947
nmea_ugps_windows_amd64.exe -i COM1@4800 -o 127.0.0.1:2947 -heading HDM
pause
```

Expand All @@ -58,9 +61,9 @@ When running the application it typically looks like this:
└──────────────────────────────────────────────────────────────────────────────┘
┌─Input status─────────────────────────────────────────────────────────────────┐
│Supported NMEA sentences received: │
│ * GGA: 5
│ * HDT: 6
│ * THS: 0
│ * Position : GGA: 5
│ * Heading : HDT: 6
│ * Parse error: 0 │
│Sent sucessfully to UGPS: 10 │
│ │
│ │
Expand Down
59 changes: 25 additions & 34 deletions in.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import (
"strings"
"time"

"github.com/waterlinked/go-nmea"
"github.com/tarm/serial"
"github.com/waterlinked/go-nmea"
)

type inputStats struct {
typeGga int
typeHdt int
typeHdm int
typeThs int
isErr bool
errorMsg string
sendOk int
posDesc string
posCount int
headDesc string
unparsableCount int
isErr bool
errorMsg string
sendOk int
}

const missingDataTimeout = 10
Expand All @@ -31,51 +31,41 @@ var (
)

// parseNMEA takes a string and return true if new data, else false
func parseNMEA(data []byte) (bool, error) {
func parseNMEA(data []byte, h headingParser) (bool, error) {
line := strings.TrimSpace(string(data))

s, err := nmea.Parse(line)
if err != nil {
return false, err
debugPrintf("Parse err: %s (%s)", err, line)
stats.unparsableCount++
return false, nil
}

switch m := s.(type) {
case nmea.GPGGA:
//debugPrintf("GGA: Lat/lon : %s %s\n", nmea.FormatGPS(m.Latitude), nmea.FormatGPS(m.Longitude))
debugPrintf("GGA: Lat/lon : %s %s\n", nmea.FormatGPS(m.Latitude), nmea.FormatGPS(m.Longitude))

fix, err := strconv.ParseFloat(m.FixQuality, 64)
if err != nil {
log.Printf("GGA invalid fix quality: %s -> %v\n", m.FixQuality, err)
debugPrintf("GGA invalid fix quality: %s -> %v\n", m.FixQuality, err)
fix = 0
}
latest.Lat = m.Latitude
latest.Lon = m.Longitude
latest.NumSats = float64(m.NumSatellites)
latest.FixQuality = fix
latest.Hdop = m.HDOP
stats.typeGga++
return true, nil

case nmea.GPHDT:
//debugPrintf("HDT: Heading : %f\n", m.Heading)
latest.Orientation = m.Heading
stats.typeHdt++
return true, nil
case nmea.HDM:
//debugPrintf("HDM: Heading : %f\n", m.Heading)
latest.Orientation = m.Heading
stats.typeHdm++
return true, nil
case nmea.THS:
//debugPrintf("THS: Heading : %f\n", m.Heading)
latest.Orientation = m.Heading
stats.typeThs++
//stats.typeGga++
stats.posCount++
stats.posDesc = fmt.Sprintf("GGA: %d", stats.posCount)
return true, nil
}
return false, nil
success, err := h.parseNMEA(s)
stats.headDesc = h.String()
return success, err
}

func inputUDPLoop(listen string, msg chan externalMaster, inStatsCh chan inputStats) {
func inputUDPLoop(listen string, hParser headingParser, msg chan externalMaster, inStatsCh chan inputStats) {
udpAddr, err := net.ResolveUDPAddr("udp4", listen)
if err != nil {
log.Fatal(err)
Expand Down Expand Up @@ -105,7 +95,7 @@ func inputUDPLoop(listen string, msg chan externalMaster, inStatsCh chan inputSt
continue
}

gotUpdate, err := parseNMEA(buffer[:n])
gotUpdate, err := parseNMEA(buffer[:n], hParser)
if err != nil {
stats.errorMsg = fmt.Sprintf("%v", err)
stats.isErr = true
Expand All @@ -120,7 +110,7 @@ func inputUDPLoop(listen string, msg chan externalMaster, inStatsCh chan inputSt
}
}

func inputSerialLoop(s *serial.Port, msg chan externalMaster, inStatsCh chan inputStats) {
func inputSerialLoop(s *serial.Port, hParser headingParser, msg chan externalMaster, inStatsCh chan inputStats) {

scanner := bufio.NewReader(s)
for {
Expand All @@ -131,7 +121,7 @@ func inputSerialLoop(s *serial.Port, msg chan externalMaster, inStatsCh chan inp
inStatsCh <- stats
continue
}
gotUpdate, err := parseNMEA(line)
gotUpdate, err := parseNMEA(line, hParser)

if err != nil {
stats.errorMsg = fmt.Sprintf("%v", err)
Expand Down Expand Up @@ -160,6 +150,7 @@ func inputLoop(masterCh chan externalMaster, inputStatusCh chan inputStats) {
if err == nil {
stats.sendOk++
} else {
debugPrintf("%v", err)
stats.isErr = true
stats.errorMsg = fmt.Sprintf("%v", err)
inputStatusCh <- stats
Expand Down
70 changes: 70 additions & 0 deletions in_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"fmt"

"github.com/waterlinked/go-nmea"
)

// headingParser is the interface parsing heading input
type headingParser interface {
// parseNMEA takes a nmea.Sentence and return true if new data, else false
parseNMEA(sentence nmea.Sentence) (bool, error)
// String returns a string representing the current status
String() string
}

type hdmParser struct {
count int
}
type hdtParser struct {
count int
}
type thsParser struct {
count int
}

func (p *hdmParser) parseNMEA(sentence nmea.Sentence) (bool, error) {
switch m := sentence.(type) {
case nmea.HDM:
debugPrintf("HDM: Heading : %f\n", m.Heading)
latest.Orientation = m.Heading
p.count++
return true, nil
}
return false, nil
}

func (p hdmParser) String() string {
return fmt.Sprintf("HDM: %d", p.count)
}

func (p *hdtParser) parseNMEA(sentence nmea.Sentence) (bool, error) {
switch m := sentence.(type) {
case nmea.GPHDT:
debugPrintf("HDT: Heading : %f\n", m.Heading)
latest.Orientation = m.Heading
p.count++
return true, nil
}
return false, nil
}

func (p hdtParser) String() string {
return fmt.Sprintf("HDT: %d", p.count)
}

func (p *thsParser) parseNMEA(sentence nmea.Sentence) (bool, error) {
switch m := sentence.(type) {
case nmea.THS:
debugPrintf("THS: Heading : %f\n", m.Heading)
latest.Orientation = m.Heading
p.count++
return true, nil
}
return false, nil
}

func (p thsParser) String() string {
return fmt.Sprintf("THS: %d", p.count)
}
Loading

0 comments on commit 5ebae03

Please sign in to comment.