-
Notifications
You must be signed in to change notification settings - Fork 87
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
Integrated grid square calculations with GPSd #435
base: develop
Are you sure you want to change the base?
Changes from all commits
c616e07
9337dd6
945b1f5
eddc339
2b78798
46fc78d
cd35b94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/pd0mz/go-maidenhead" | ||
"io" | ||
"net" | ||
"sync" | ||
|
@@ -32,11 +33,12 @@ type Positioner interface { | |
|
||
// Position holds geographic positioning data. | ||
type Position struct { | ||
Lat, Lon float64 // Latitude/longitude in degrees. +/- signifies north/south. | ||
Alt float64 // Altitude in meters. | ||
Track float64 // Course over ground, degrees from true north. | ||
Speed float64 // Speed over ground, meters per second. | ||
Time time.Time // Time as reported by the device. | ||
Lat, Lon float64 // Latitude/longitude in degrees. +/- signifies north/south. | ||
Alt float64 // Altitude in meters. | ||
Track float64 // Course over ground, degrees from true north. | ||
Speed float64 // Speed over ground, meters per second. | ||
Time time.Time // Time as reported by the device. | ||
GridSquare string // GridSquare calculated using Maidenhead Locator System | ||
} | ||
|
||
// Conn represents a socket connection to an GPSd daemon. | ||
|
@@ -50,6 +52,12 @@ type Conn struct { | |
closed bool | ||
} | ||
|
||
type GPSdClient interface { | ||
Dial(addr string) (GPSdClient, error) | ||
Watch(enable bool) | ||
Next() (*TPV, error) | ||
} | ||
|
||
// Dial establishes a socket connection to the GPSd daemon. | ||
func Dial(addr string) (*Conn, error) { | ||
tcpConn, err := net.DialTimeout("tcp", addr, 30*time.Second) | ||
|
@@ -235,3 +243,24 @@ func errUnexpected(err error) error { | |
} | ||
return err | ||
} | ||
|
||
// GetGridSquare provides function for getting updated position and calculating the grid square. | ||
func (c *Conn) GetGridSquare() (string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Absolutely! |
||
for { | ||
obj, err := c.NextPos() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var lat = obj.Lat | ||
var lon = obj.Lon | ||
|
||
point := maidenhead.NewPoint(lat, lon) | ||
newGridSquare, err := point.GridSquare() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return newGridSquare, nil | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package gpsd | |
import ( | ||
"encoding/json" | ||
"errors" | ||
"github.com/pd0mz/go-maidenhead" | ||
"time" | ||
) | ||
|
||
|
@@ -27,6 +28,23 @@ type TPV struct { | |
EPX, EPY, EPV json.Number // Lat, Lon, Alt error estimate in meters, 95% confidence. Present if mode is 2 or 3 and DOPs can be calculated from the satellite view. | ||
Track, Speed, Climb json.Number | ||
EPD, EPS, EPC json.Number | ||
GridSquare string `json:"-"` | ||
} | ||
|
||
func (t *TPV) CalculateGridSquare() error { | ||
lat, err := t.Lat.Float64() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
lon, err := t.Lon.Float64() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
point := maidenhead.NewPoint(lat, lon) | ||
t.GridSquare, err = point.GridSquare() | ||
return err | ||
Comment on lines
+31
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is no longer needed I believe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi Martin! You are correct...it is an artifact that I forgot to remove. Thanks! |
||
} | ||
|
||
func (t TPV) Position() Position { | ||
|
@@ -35,8 +53,9 @@ func (t TPV) Position() Position { | |
alt, _ := t.Alt.Float64() | ||
track, _ := t.Track.Float64() | ||
speed, _ := t.Speed.Float64() | ||
gridsquare := t.GridSquare | ||
martinhpedersen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return Position{Lat: lat, Lon: lon, Alt: alt, Track: track, Speed: speed, Time: t.Time} | ||
return Position{Lat: lat, Lon: lon, Alt: alt, Track: track, Speed: speed, Time: t.Time, GridSquare: gridsquare} | ||
} | ||
|
||
func (t TPV) HasFix() bool { return t.Mode > ModeNoFix } | ||
|
@@ -131,7 +150,11 @@ func parseJSONObject(raw []byte) (interface{}, error) { | |
case "TPV": | ||
var tpv TPV | ||
err = json.Unmarshal(raw, &tpv) | ||
return tpv, err | ||
err = tpv.CalculateGridSquare() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return tpv, nil | ||
martinhpedersen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
default: | ||
var m map[string]interface{} | ||
err = json.Unmarshal(raw, &m) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -195,6 +195,7 @@ var fOptions struct { | |
IgnoreBusy bool // Move to connect? | ||
SendOnly bool // Move to connect? | ||
RadioOnly bool | ||
UpdateGrid bool | ||
|
||
Robust bool | ||
MyCall string | ||
|
@@ -214,6 +215,7 @@ func optionsSet() *pflag.FlagSet { | |
set.BoolVarP(&fOptions.SendOnly, "send-only", "s", false, "Download inbound messages later, send only.") | ||
set.BoolVarP(&fOptions.RadioOnly, "radio-only", "", false, "Radio Only mode (Winlink Hybrid RMS only).") | ||
set.BoolVar(&fOptions.IgnoreBusy, "ignore-busy", false, "Don't wait for clear channel before connecting to a node.") | ||
set.BoolVar(&fOptions.UpdateGrid, "gridsquare-update", true, "Automatically update the maidenhead grid square from the gpsd daemon.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use the term locator in the config. Maybe not the best term, but I think we should use the same term both places sice the two are very much related.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi Martin! Yes, I will change to align with the existing terminology. It makes the most sense to keep things consistent. |
||
|
||
defaultMBox := filepath.Join(directories.DataDir(), "mailbox") | ||
defaultFormsPath := filepath.Join(directories.DataDir(), "Standard_Forms") | ||
|
@@ -264,6 +266,7 @@ func main() { | |
debug.Printf("Config file is\t'%s'", fOptions.ConfigPath) | ||
debug.Printf("Log file is \t'%s'", fOptions.LogPath) | ||
debug.Printf("Event log file is\t'%s'", fOptions.EventLogPath) | ||
debug.Printf("Auto gridsquare update is\t'%s'", fOptions.UpdateGrid) | ||
directories.MigrateLegacyDataDir() | ||
|
||
// Graceful shutdown by cancelling background context on interrupt. | ||
|
@@ -387,6 +390,48 @@ func main() { | |
|
||
// Start command execution | ||
cmd.HandleFunc(ctx, args) | ||
|
||
// Go routine for checking the GPS | ||
go func() { | ||
if fOptions.UpdateGrid { | ||
for range time.Tick(time.Minute * 5) { | ||
updateGridIfNeeded() | ||
} | ||
Comment on lines
+397
to
+399
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason for polling once per 5 minute? Wouldn't it be even better to continuously keep the locator field up-to-date by subscribing (Watch(true)) and call Next() in an endless loop? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thought was that while most people will be mostly static...i.e. once they get the first fix from the GPS, they won't need it anymore. However, for true mobile operation, it might be required to get a new grid square at a regular interval. I picked 5 minutes at random for testing, but I think long term, this might be something we allow the user to enable/disable and set the interval as needed. If you were in a moving car for example, 5 minutes might be a good interval, but on a sailboat, it might need be only once every few hours. I'll disable the loop for now. |
||
} | ||
}() | ||
} | ||
|
||
// Update Grid if needed | ||
func updateGridIfNeeded() { | ||
// create connection to gpsd | ||
conn, err := gpsd.Dial(config.GPSd.Addr) | ||
if err != nil { | ||
log.Printf("GPSd daemon: %s", err) | ||
return | ||
} | ||
defer conn.Close() | ||
conn.Watch(true) | ||
|
||
// get next position from gpsd | ||
pos, err := conn.NextPos() | ||
martinhpedersen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
log.Printf("GPSd: %s", err) | ||
return | ||
} | ||
|
||
// get grid square from position | ||
currGridSquare := pos.GridSquare | ||
|
||
if currGridSquare != config.Locator { | ||
// update config | ||
config.Locator = currGridSquare | ||
// write config | ||
if err := WriteConfig(config, fOptions.ConfigPath); err != nil { | ||
log.Printf("Unable to write config: %s", err) | ||
} | ||
Comment on lines
+428
to
+431
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think rewriting the actual config file is the best idea. It is not a lossless operation, as the user might have defined keys and values that are not part of the JSON spec (e.g. "comments"). Custom formatting will also be lost, and possibly also ordering of aliases etc. It also has the potential of corrupting the file if we for some reason the operation fails in the middle of writing (full disk, power loss++). I guess it's useful to retain the "last known" position when restarting the app. Otherwise, the user would have to wait the 5 min interval before the locator field is updated again As I see it, we have two options:
I think the latter might be a better solution, as it guarantees running with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks Martin...I agree the latter would be the better option. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After some refreshing on this...won't the gaps.Dial() and conn.NextPos() already blocking? |
||
// log | ||
log.Printf("Grid square updated to %s", currGridSquare) | ||
} | ||
} | ||
|
||
func configureHandle(ctx context.Context, args []string) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unused? If so, please omit it 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am looking at storing the Locator in the user.json for the multi-user reloader. Like KM4ACK's user swap by config swap
in the PAT Menu 12. GPSd is great and I have one pi feeding 4 pi's locator. only have a 20 minute gap every other month.
in mailbox/CallSign/user.json is (CALLSIGN, Name, TAC), Locator (EL29ER27), WinLink/user (Password), Roll (Owner, Radio Op, Non-Ham), On Duty(00-23), NTS (routing itu-r2, NOAM, USA, TX, STX,) HW ACCESS (Telnet, Wifi2, Wifi5, Ardop, ### Ax25, VaraHF, VaraFM, VaraSat)
Just have to get the Variables loaded at the right time for each Network http connection session or postoffice push/pull.
This lets windows and mac users to get multi-user.... Roll->Multi-user->PostOffice->NTS
Herbert KD5PQJ