NMEA pass-through mode

This commit is contained in:
Torsten Harenberg
2025-02-21 14:25:36 +01:00
parent 80f55af864
commit 89c19cf9b8
3 changed files with 152 additions and 103 deletions

171
gpsd.go
View File

@@ -130,24 +130,37 @@ func isNetConnClosedErr(err error) bool {
}
func addClient(client net.Conn) {
_, err := client.Write([]byte("{\"class\":\"VERSION\",\"release\":\"3.25\",\"rev\":\"3.25\",\"proto_major\":3,\"proto_minor\":15}\n"))
if err != nil {
writeDebug(fmt.Sprintf("Error writing to client: %v\n", err), 1)
}
go func() {
defer func() {
client.Close()
removeClient(client)
writeDebug(fmt.Sprintf("GPSd Client disconnected: %v\n", client.RemoteAddr()), 0)
if s.NMEAPassthrough {
go func() {
defer func() {
client.Close()
removeClient(client)
writeDebug(fmt.Sprintf("GPSd Client disconnected: %v\n", client.RemoteAddr()), 0)
}()
rd := bufio.NewScanner(client)
for rd.Scan() {
writeDebug(fmt.Sprintf("GPSd Client received message: %v\n", rd.Text()), 0)
}
}()
writeDebug(fmt.Sprintf("gpsd: starting conversation with %v\n", client.RemoteAddr()), 0)
rd := bufio.NewReader(client)
for {
time.Sleep(100 * time.Millisecond)
// looks like gpsd does not expect \n terminated lines so read what is there from the socket
client.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
if true {
} else {
_, err := client.Write([]byte("{\"class\":\"VERSION\",\"release\":\"3.25\",\"rev\":\"3.25\",\"proto_major\":3,\"proto_minor\":15}\n"))
if err != nil {
writeDebug(fmt.Sprintf("Error writing to client: %v\n", err), 1)
}
go func() {
defer func() {
client.Close()
removeClient(client)
writeDebug(fmt.Sprintf("GPSd Client disconnected: %v\n", client.RemoteAddr()), 0)
}()
writeDebug(fmt.Sprintf("gpsd: starting conversation with %v\n", client.RemoteAddr()), 0)
rd := bufio.NewReader(client)
for {
time.Sleep(100 * time.Millisecond)
// looks like gpsd does not expect \n terminated lines so read what is there from the socket
client.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
buff := make([]byte, 1024)
n, err := rd.Read(buff)
if isNetConnClosedErr(err) {
@@ -197,9 +210,8 @@ func addClient(client net.Conn) {
}
}
}
}()
}()
}
// register the client, so it gets updates from now on
clientMutex.Lock()
clients[client] = struct{}{}
@@ -224,65 +236,76 @@ func publishTPV() {
func readAndBroadcast() {
device := s.DeviceType
for {
nmeaSentence, err := s.GPSStream.DequeueOrWait()
if err != nil {
writeDebug(fmt.Sprintf("Error dequeuing GPS sentence: %v\n", err), 0)
continue
if s.NMEAPassthrough {
for {
nmeaSentence, err := s.GPSStream.DequeueOrWait()
if err != nil {
writeDebug(fmt.Sprintf("Error dequeuing GPS sentence: %v\n", err), 0)
continue
}
broadcastToClients(nmeaSentence)
}
writeDebug(fmt.Sprintf("gpsd: received NMEA: %s", nmeaSentence), 1)
//broadcastToClients(string(nmeaSentence) + "\n")
// NMEA-Satz parsen
sentence, err := nmea.Parse(nmeaSentence)
if err != nil {
writeDebug(fmt.Sprintf("gpsd: error parsing NMEA sentence '%s': %v", nmeaSentence, err), 1)
continue
}
switch s := sentence.(type) {
// RMC (Mindestdaten: Zeit, Position, Kurs, Geschwindigkeit)
case nmea.RMC:
updateTPVFromRMC(s, device)
// GGA (Positions- und Höheninformation)
case nmea.GGA:
updateTPVFromGGA(s, device)
// VTG: Aktualisierung von Kurs und Geschwindigkeit.
case nmea.VTG:
updateTPVFromVTG(s)
// GSV (Satelliten in Sicht)
case nmea.GSV:
sats := make([]SatelliteInfo, 0, len(s.Info))
for _, sat := range s.Info {
sats = append(sats, SatelliteInfo{
PRN: int(sat.SVPRNNumber),
Elevation: int(sat.Elevation),
Azimuth: int(sat.Azimuth),
SNR: int(sat.SNR),
})
} else {
for {
nmeaSentence, err := s.GPSStream.DequeueOrWait()
if err != nil {
writeDebug(fmt.Sprintf("Error dequeuing GPS sentence: %v\n", err), 0)
continue
}
sky := SKY{
Class: "SKY",
Device: device,
Satellites: sats,
}
if jsonOut, err := json.Marshal(sky); err == nil {
broadcastToClients(string(jsonOut))
//fmt.Println(string(jsonOut))
} else {
writeDebug(fmt.Sprintf("gpsd: error serializing SKY object: %v", err), 1)
writeDebug(fmt.Sprintf("gpsd: received NMEA: %s", nmeaSentence), 1)
//broadcastToClients(string(nmeaSentence) + "\n")
// NMEA-Satz parsen
sentence, err := nmea.Parse(nmeaSentence)
if err != nil {
writeDebug(fmt.Sprintf("gpsd: error parsing NMEA sentence '%s': %v", nmeaSentence, err), 1)
continue
}
// GSA (z.B. GPGSA oder auch ohne Talker-ID)
case nmea.GSA:
updateTPVFromGSA(s)
switch s := sentence.(type) {
default:
writeDebug(fmt.Sprintf("unsupported NMEA type: %T", s), 1)
// RMC (Mindestdaten: Zeit, Position, Kurs, Geschwindigkeit)
case nmea.RMC:
updateTPVFromRMC(s, device)
// GGA (Positions- und Höheninformation)
case nmea.GGA:
updateTPVFromGGA(s, device)
// VTG: Aktualisierung von Kurs und Geschwindigkeit.
case nmea.VTG:
updateTPVFromVTG(s)
// GSV (Satelliten in Sicht)
case nmea.GSV:
sats := make([]SatelliteInfo, 0, len(s.Info))
for _, sat := range s.Info {
sats = append(sats, SatelliteInfo{
PRN: int(sat.SVPRNNumber),
Elevation: int(sat.Elevation),
Azimuth: int(sat.Azimuth),
SNR: int(sat.SNR),
})
}
sky := SKY{
Class: "SKY",
Device: device,
Satellites: sats,
}
if jsonOut, err := json.Marshal(sky); err == nil {
broadcastToClients(string(jsonOut))
//fmt.Println(string(jsonOut))
} else {
writeDebug(fmt.Sprintf("gpsd: error serializing SKY object: %v", err), 1)
}
// GSA (z.B. GPGSA oder auch ohne Talker-ID)
case nmea.GSA:
updateTPVFromGSA(s)
default:
writeDebug(fmt.Sprintf("unsupported NMEA type: %T", s), 1)
}
}
}
}