Skip to content

Commit

Permalink
gpsd support
Browse files Browse the repository at this point in the history
  • Loading branch information
kdudkov committed Apr 10, 2024
1 parent a15f00d commit c3ab407
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 41 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# unreleased

FEATURES:

- gpsd support for webclient

# 0.16.4

FEATURES:
Expand Down
2 changes: 1 addition & 1 deletion cmd/webclient/http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func getConfigHandler(app *App) air.Handler {
m := make(map[string]any, 0)
m["version"] = getVersion()
m["uid"] = app.uid
lat, lon := app.pos.Load().Get()
lat, lon := app.pos.Load().GetCoord()
m["lat"] = lat
m["lon"] = lon
m["zoom"] = app.zoom
Expand Down
25 changes: 18 additions & 7 deletions cmd/webclient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/hex"
"flag"
"fmt"
"github.com/kdudkov/goatak/pkg/gpsd"
"log/slog"
"math/rand"
"os"
Expand Down Expand Up @@ -168,6 +169,12 @@ func (app *App) Run(ctx context.Context) {
go app.periodicGetter(ctx1)
go app.myPosSender(ctx1, wg)

if addr := viper.GetString("gpsd"); addr != "" {
c := gpsd.New(addr, app.logger.With("logger", "gpsd"))
go c.Listen(ctx1, func(lat, lon, alt, speed, track float64) {
app.pos.Store(model.NewPosFull(lat, lon, alt, speed, track))
})
}
wg.Wait()
}
}
Expand Down Expand Up @@ -229,9 +236,13 @@ func (app *App) ProcessEvent(msg *cot.CotMessage) {

func (app *App) MakeMe() *cotproto.TakMessage {
ev := cot.BasicMsg(app.typ, app.uid, time.Minute*2)
lat, lon := app.pos.Load().Get()
ev.CotEvent.Lat = lat
ev.CotEvent.Lon = lon
pos := app.pos.Load()

ev.CotEvent.Lat = pos.GetLat()
ev.CotEvent.Lon = pos.GetLon()
ev.CotEvent.Hae = pos.GetAlt()
ev.CotEvent.Ce = pos.GetCe()

ev.CotEvent.Detail = &cotproto.Detail{
Contact: &cotproto.Contact{
Endpoint: "*:-1:stcp",
Expand All @@ -248,12 +259,12 @@ func (app *App) MakeMe() *cotproto.TakMessage {
Version: app.version,
},
Track: &cotproto.Track{
Speed: 0,
Course: 0,
Speed: pos.GetSpeed(),
Course: pos.GetTrack(),
},
PrecisionLocation: &cotproto.PrecisionLocation{
Geopointsrc: "",
Altsrc: "DTED2",
Geopointsrc: "GPS",
Altsrc: "GPS",
},
Status: &cotproto.Status{Battery: 39},
}
Expand Down
11 changes: 7 additions & 4 deletions goatak_client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
server_address: 137.184.101.250:8087:tcp
# local port for web server
web_port: 8080
# gpsd address, usually localhost:2947
gpsd: ""

me:
# your callsign
callsign: TestUser
# your uid. If 'auto' uid is generated
uid: auto
# your unit type
type: a-f-G-U-C
# your team
team: Yellow
# your role
# your team: one of (White, Yellow, Orange, Magenta, Red, Maroon, Purple, Dark Blue, Blue, Cyan, Teal, Green, Dark Green, Brown)
team: White
# your role: one of (HQ, Team Lead, Team Member, K9, Forward Observer, Sniper, Medic, RTO)
role: Team Member
lat: 0
lon: 0
Expand All @@ -21,7 +24,7 @@ ssl:
cert: ""
# password for certificate file
password: atakatak
# login for cert enrollment. If not empty, client do cert enrollment
# login for cert enrollment. If not empty, client does cert enrollment
enroll_user: ""
# password for cert enrollment
enroll_password: ""
Expand Down
152 changes: 152 additions & 0 deletions pkg/gpsd/gpsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package gpsd

import (
"bufio"
"context"
"encoding/json"
"fmt"
"log/slog"
"net"
"time"
)

const (
DefaultAddress = "localhost:2947"
DialTimeout = time.Millisecond * 500
)

type Msg struct {
Class string `json:"class"`
}

type TPVMsg struct {
Class string `json:"class"`
Tag string `json:"tag"`
Device string `json:"device"`
Mode int `json:"mode"`
Time time.Time `json:"time"`
Ept float64 `json:"ept"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
Alt float64 `json:"alt"`
Epx float64 `json:"epx"`
Epy float64 `json:"epy"`
Epv float64 `json:"epv"`
Track float64 `json:"track"`
Speed float64 `json:"speed"`
Climb float64 `json:"climb"`
Epd float64 `json:"epd"`
Eps float64 `json:"eps"`
Epc float64 `json:"epc"`
Eph float64 `json:"eph"`
}

type VERSIONMsg struct {
Class string `json:"class"`
Release string `json:"release"`
Rev string `json:"rev"`
ProtoMajor int `json:"proto_major"`
ProtoMinor int `json:"proto_minor"`
Remote string `json:"remote"`
}

type GpsdClient struct {
addr string
conn net.Conn
logger *slog.Logger
reader *bufio.Reader
}

func New(addr string, logger *slog.Logger) *GpsdClient {
c := &GpsdClient{
addr: DefaultAddress,
conn: nil,
logger: logger,
reader: nil,
}

if addr != "" {
c.addr = addr
}

return c
}

func (c *GpsdClient) connect(ctx context.Context) bool {
timeout := time.Second

for {
conn, err := net.DialTimeout("tcp4", c.addr, DialTimeout)

if err == nil {
c.conn = conn
c.reader = bufio.NewReader(c.conn)
return true
}

c.logger.Error("dial error", "error", err)

select {
case <-time.After(timeout):
case <-ctx.Done():
c.logger.Error("stop connection attempts")
return false
}

if timeout < time.Minute*5 {
timeout = timeout * 2
}
}
}

func (c *GpsdClient) Listen(ctx context.Context, cb func(lat, lon, alt, speed, track float64)) {
for ctx.Err() == nil {
if c.conn == nil {
if !c.connect(ctx) {
return
}
}

line, err := c.reader.ReadString('\n')

if err != nil {
c.logger.Error("error", "error", err)

_ = c.conn.Close()
c.conn = nil
continue
}

data := []byte(line)

var msg Msg

if err = json.Unmarshal(data, &msg); err == nil {
c.logger.Error("JSON decode error", "error", err)
c.logger.Debug("bad json: " + line)
_ = c.conn.Close()
c.conn = nil
continue
}

switch msg.Class {
case "TPV":
var r *TPVMsg
if err1 := json.Unmarshal(data, &r); err1 != nil {
c.logger.Error("JSON decode error", "error", err1)
continue
}

if cb != nil {
cb(r.Lat, r.Lon, r.Alt, r.Speed, r.Track)
}
case "VERSION":
var r *VERSIONMsg
if err1 := json.Unmarshal(data, &r); err1 != nil {
c.logger.Error("JSON decode error", "error", err1)
continue
}
c.logger.Info(fmt.Sprintf("got version %s, rev. %s", r.Release, r.Rev))
}
}
}
85 changes: 75 additions & 10 deletions pkg/model/geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
package model

import (
"github.com/kdudkov/goatak/pkg/cot"
"math"
"sync"
"time"
)

Expand All @@ -30,22 +30,87 @@ func DistBea(lat1, lon1, lat2, lon2 float64) (float64, float64) {
}

type Pos struct {
time time.Time
lat float64
lon float64
speed float64
ce float64
mx sync.RWMutex
Time time.Time
Lat float64
Lon float64
Alt float64
Speed float64
Track float64
Ce float64
}

func NewPos(lat, lon float64) *Pos {
return &Pos{lon: lon, lat: lat, speed: 0, ce: 0, time: time.Now(), mx: sync.RWMutex{}}
return NewPosFull(lat, lon, 0, 0, 0)
}

func (p *Pos) Get() (float64, float64) {
func NewPosFull(lat, lon, alt, speed, track float64) *Pos {
return &Pos{Lon: lon, Lat: lat, Alt: alt, Speed: speed, Track: track, Ce: 0, Time: time.Now()}
}

func msg2pos(msg *cot.CotMessage) *Pos {
return &Pos{
Time: msg.GetSendTime(),
Lat: msg.GetLat(),
Lon: msg.GetLon(),
Alt: msg.GetTakMessage().GetCotEvent().GetHae(),
Ce: msg.GetTakMessage().GetCotEvent().GetCe(),
Speed: msg.GetTakMessage().GetCotEvent().GetDetail().GetTrack().GetSpeed(),
Track: msg.GetTakMessage().GetCotEvent().GetDetail().GetTrack().GetCourse(),
}
}

func (p *Pos) GetCoord() (float64, float64) {
if p == nil {
return 0, 0
}

return p.lat, p.lon
return p.Lat, p.Lon
}

func (p *Pos) GetLat() float64 {
if p == nil {
return 0
}

return p.Lat
}

func (p *Pos) GetLon() float64 {
if p == nil {
return 0
}

return p.Lon
}

func (p *Pos) GetAlt() float64 {
if p == nil {
return 0
}

return p.Alt
}

func (p *Pos) GetSpeed() float64 {
if p == nil {
return 0
}

return p.Speed
}

func (p *Pos) GetTrack() float64 {
if p == nil {
return 0
}

return p.Track
}

func (p *Pos) GetCe() float64 {
if p == nil {
return cot.NotNum
}

return p.Ce
}
Loading

0 comments on commit c3ab407

Please sign in to comment.