Skip to content

Added the Gtu7 sensor and serial drivers.#16

Closed
rustyeddy wants to merge 18 commits intomainfrom
gtu7
Closed

Added the Gtu7 sensor and serial drivers.#16
rustyeddy wants to merge 18 commits intomainfrom
gtu7

Conversation

@rustyeddy
Copy link
Copy Markdown
Owner

No description provided.

@rustyeddy rustyeddy self-assigned this Jan 16, 2026
Copilot AI review requested due to automatic review settings January 16, 2026 17:22
@rustyeddy rustyeddy added the enhancement New feature or request label Jan 16, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for the GTU7 GPS sensor module and implements Linux serial port drivers to enable communication with serial devices.

Changes:

  • Adds GTU7 GPS sensor with NMEA sentence parsing (GGA, RMC, VTG) and precedence logic for speed/course data
  • Implements platform-specific serial drivers for Linux with termios configuration and stub implementation for non-Linux platforms
  • Includes basic test coverage for RMC precedence behavior over VTG

Reviewed changes

Copilot reviewed 38 out of 39 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
sensors/gtu7.go Core GTU7 GPS sensor implementation with NMEA parsing logic
sensors/gtu7_test.go Test verifying RMC sentence precedence over VTG for speed/course data
drivers/serial.go Cross-platform serial port interface definitions and configuration validation
drivers/serial_linux.go Linux-specific serial port implementation using termios
drivers/serial_stub.go Stub implementation for unsupported platforms
_archive/gtu7/* Removal of old GTU7 implementation
_archive/examples/* Removal of archived example code

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sensors/gtu7_test.go
Comment on lines +12 to +57
func TestGTU7_PrefersRMCOverVTG(t *testing.T) {
input := `
$GPGGA,160446.00,3340.34121,N,11800.11332,W,2,08,1.20,11.8,M,-33.1,M,,0000*58
$GPVTG,54.70,T,,M,5.50,N,10.19,K,A*00
$GPRMC,160446.00,A,3340.34121,N,11800.11332,W,7.25,123.40,160126,,,A*00
`

gps := NewGTU7(GTU7Config{
Reader: strings.NewReader(input),
})

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

done := make(chan error, 1)
go func() { done <- gps.Run(ctx) }()

// Drain GGA + VTG
for i := 0; i < 2; i++ {
select {
case <-gps.Out():
case <-time.After(time.Second):
require.FailNow(t, "timeout")
}
}

// RMC must override VTG
select {
case fix := <-gps.Out():
cancel()
require.InDelta(t, 7.25, fix.SpeedKnots, 1e-6)
require.InDelta(t, 7.25*0.514444, fix.SpeedMPS, 1e-6)
require.InDelta(t, 123.40, fix.CourseDeg, 1e-6)
require.Equal(t, "A", fix.Status)
require.Equal(t, "160126", fix.Date)
case <-time.After(time.Second):
require.FailNow(t, "timeout waiting for RMC fix")
}

select {
case err := <-done:
require.NoError(t, err)
case <-time.After(time.Second):
require.FailNow(t, "run did not exit")
}
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test covers RMC precedence over VTG but doesn't test other important scenarios such as: parsing errors, handling of empty/malformed NMEA sentences, GGA-only data, behavior when context is cancelled mid-stream, or handling of GNGGA/GNRMC/GNVTG variants. Consider adding more comprehensive test cases to cover these scenarios.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go
Comment on lines +80 to +160
func (g *GTU7) Run(ctx context.Context) error {
defer close(g.out)

var last GPSFix
haveFix := false

// RMC precedence flags
haveRMCSpeed := false
haveRMCCourse := false

sc := bufio.NewScanner(g.r)
for sc.Scan() {
select {
case <-ctx.Done():
return nil
default:
}

line := strings.TrimSpace(sc.Text())
if line == "" {
continue
}

if fix, ok := parseGPGGA(line); ok {
last.Lat = fix.Lat
last.Lon = fix.Lon
last.AltMeters = fix.AltMeters
last.HDOP = fix.HDOP
last.Satellites = fix.Satellites
last.Quality = fix.Quality

haveFix = true
g.emit(last)
continue
}

if fix, ok := parseGPRMC(line); ok {
if !math.IsNaN(fix.Lat) {
last.Lat = fix.Lat
last.Lon = fix.Lon
haveFix = true
}
if !math.IsNaN(fix.SpeedKnots) {
last.SpeedKnots = fix.SpeedKnots
last.SpeedMPS = fix.SpeedMPS
haveRMCSpeed = true
}
if !math.IsNaN(fix.CourseDeg) {
last.CourseDeg = fix.CourseDeg
haveRMCCourse = true
}
if fix.Status != "" {
last.Status = fix.Status
}
if fix.Date != "" {
last.Date = fix.Date
}

if haveFix {
g.emit(last)
}
continue
}

if fix, ok := parseGPVTG(line); ok {
if !math.IsNaN(fix.SpeedKnots) && (!haveRMCSpeed || math.IsNaN(last.SpeedKnots)) {
last.SpeedKnots = fix.SpeedKnots
last.SpeedMPS = fix.SpeedMPS
}
if !math.IsNaN(fix.CourseDeg) && (!haveRMCCourse || math.IsNaN(last.CourseDeg)) {
last.CourseDeg = fix.CourseDeg
}

if haveFix {
g.emit(last)
}
}
}

return sc.Err()
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Run method doesn't clean up the serial port resource. When the serial port is opened (lines 56-60), it implements io.ReadWriteCloser but is stored as io.Reader, losing access to the Close() method. The Run method should close the serial port before returning, similar to how VH400 closes its ADC with a defer (see vh400.go lines 136-140). Consider storing the SerialPort interface instead of io.Reader, or adding a defer that checks if r implements io.Closer and closes it.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go Outdated

return &GTU7{
name: cfg.Name,
out: make(chan GPSFix, 4),
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output channel buffer size is hard-coded to 4 which seems arbitrary and may be insufficient for high-rate GPS data. Most GPS modules output around 1-10 Hz, so a buffer of 4 could fill quickly if the consumer is slow. Consider making this configurable via GTU7Config similar to how VH400Config has a Buf field, or documenting why 4 is appropriate.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go Outdated
Comment on lines +47 to +68
func NewGTU7(cfg GTU7Config) *GTU7 {
if cfg.Factory == nil {
cfg.Factory = drivers.LinuxSerialFactory{}
}

var r io.Reader
if cfg.Reader != nil {
r = cfg.Reader
} else {
port, err := cfg.Factory.OpenSerial(cfg.Serial)
if err != nil {
panic(err)
}
r = port
}

return &GTU7{
name: cfg.Name,
out: make(chan GPSFix, 4),
r: r,
}
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NewGTU7 constructor panics when OpenSerial fails. This violates error handling best practices and makes the API difficult to use safely. Consider returning an error instead of panicking, similar to how other constructors in the codebase handle errors. This would allow callers to decide how to handle initialization failures.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go
Comment on lines +86 to +156
// RMC precedence flags
haveRMCSpeed := false
haveRMCCourse := false

sc := bufio.NewScanner(g.r)
for sc.Scan() {
select {
case <-ctx.Done():
return nil
default:
}

line := strings.TrimSpace(sc.Text())
if line == "" {
continue
}

if fix, ok := parseGPGGA(line); ok {
last.Lat = fix.Lat
last.Lon = fix.Lon
last.AltMeters = fix.AltMeters
last.HDOP = fix.HDOP
last.Satellites = fix.Satellites
last.Quality = fix.Quality

haveFix = true
g.emit(last)
continue
}

if fix, ok := parseGPRMC(line); ok {
if !math.IsNaN(fix.Lat) {
last.Lat = fix.Lat
last.Lon = fix.Lon
haveFix = true
}
if !math.IsNaN(fix.SpeedKnots) {
last.SpeedKnots = fix.SpeedKnots
last.SpeedMPS = fix.SpeedMPS
haveRMCSpeed = true
}
if !math.IsNaN(fix.CourseDeg) {
last.CourseDeg = fix.CourseDeg
haveRMCCourse = true
}
if fix.Status != "" {
last.Status = fix.Status
}
if fix.Date != "" {
last.Date = fix.Date
}

if haveFix {
g.emit(last)
}
continue
}

if fix, ok := parseGPVTG(line); ok {
if !math.IsNaN(fix.SpeedKnots) && (!haveRMCSpeed || math.IsNaN(last.SpeedKnots)) {
last.SpeedKnots = fix.SpeedKnots
last.SpeedMPS = fix.SpeedMPS
}
if !math.IsNaN(fix.CourseDeg) && (!haveRMCCourse || math.IsNaN(last.CourseDeg)) {
last.CourseDeg = fix.CourseDeg
}

if haveFix {
g.emit(last)
}
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The precedence flags haveRMCSpeed and haveRMCCourse are never reset between NMEA sentences. This means once RMC data is received, VTG data will never be used again even if RMC stops providing speed/course data in subsequent sentences. Consider resetting these flags at appropriate points or restructuring the logic to handle per-sentence precedence rather than global state.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go
Comment on lines +162 to +167
func (g *GTU7) emit(f GPSFix) {
select {
case g.out <- f:
default:
}
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The emit method silently drops GPS fixes when the output channel is full (line 165). This is a data loss scenario that could be problematic for applications that need every GPS update. Consider logging a warning or emitting an event when fixes are dropped so applications can detect this condition. Compare with VH400 which uses a configurable buffer and follows a similar pattern.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go
Comment on lines +225 to +260
fix.SpeedMPS = v * 0.514444
}
}

if parts[8] != "" {
if v, err := strconv.ParseFloat(parts[8], 64); err == nil {
fix.CourseDeg = v
}
}

return fix, true
}

func parseGPVTG(line string) (GPSFix, bool) {
line = stripChecksum(line)
parts := strings.Split(line, ",")
if len(parts) < 9 || (parts[0] != "$GPVTG" && parts[0] != "$GNVTG") {
return GPSFix{}, false
}

fix := GPSFix{
SpeedKnots: math.NaN(),
SpeedMPS: math.NaN(),
CourseDeg: math.NaN(),
}

if parts[1] != "" {
if v, err := strconv.ParseFloat(parts[1], 64); err == nil {
fix.CourseDeg = v
}
}

if parts[5] != "" {
if v, err := strconv.ParseFloat(parts[5], 64); err == nil {
fix.SpeedKnots = v
fix.SpeedMPS = v * 0.514444
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conversion factor 0.514444 from knots to meters per second is duplicated on lines 225 and 260. Consider defining this as a named constant at the package level (e.g., const knotsToMPS = 0.514444) to improve maintainability and make the conversion explicit.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread drivers/serial_linux.go
Comment on lines +1 to +120
//go:build linux

package drivers

import (
"fmt"
"os"
"time"

"golang.org/x/sys/unix"
)

// LinuxSerialFactory opens a configured serial port on Linux.
//
// It uses termios to set 8N1 and the requested baud rate.
type LinuxSerialFactory struct{}

func (LinuxSerialFactory) OpenSerial(cfg SerialConfig) (SerialPort, error) {
if err := validateSerialConfig(cfg); err != nil {
return nil, err
}

// Open non-blocking first, configure termios, then clear O_NONBLOCK.
fd, err := unix.Open(cfg.Port, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0)
if err != nil {
return nil, fmt.Errorf("serial: open %s: %w", cfg.Port, err)
}

// Ensure we don't leak the fd if configuration fails.
ok := false
defer func() {
if !ok {
_ = unix.Close(fd)
}
}()

if err := configureTermios(fd, cfg.Baud); err != nil {
return nil, err
}

// Clear O_NONBLOCK so reads behave as expected.
if err := unix.SetNonblock(fd, false); err != nil {
return nil, fmt.Errorf("serial: set blocking %s: %w", cfg.Port, err)
}

f := os.NewFile(uintptr(fd), cfg.Port)
if f == nil {
return nil, fmt.Errorf("serial: os.NewFile returned nil for %s", cfg.Port)
}

ok = true
return &linuxSerialPort{file: f, port: cfg.Port, baud: cfg.Baud}, nil
}

type linuxSerialPort struct {
file *os.File
port string
baud int
}

func (p *linuxSerialPort) Read(b []byte) (int, error) { return p.file.Read(b) }
func (p *linuxSerialPort) Write(b []byte) (int, error) { return p.file.Write(b) }
func (p *linuxSerialPort) Close() error { return p.file.Close() }
func (p *linuxSerialPort) String() string { return fmt.Sprintf("%s@%d", p.port, p.baud) }

func configureTermios(fd int, baud int) error {
t, err := unix.IoctlGetTermios(fd, unix.TCGETS)
if err != nil {
return fmt.Errorf("serial: ioctl TCGETS: %w", err)
}

speed, err := baudToUnix(baud)
if err != nil {
return err
}

// Raw-ish 8N1.
t.Iflag = 0
t.Oflag = 0
t.Lflag = 0
t.Cflag = unix.CS8 | unix.CREAD | unix.CLOCAL

// Disable flow control.
t.Cflag &^= unix.CRTSCTS

// VMIN/VTIME: block until at least 1 byte.
t.Cc[unix.VMIN] = 1
t.Cc[unix.VTIME] = 0

// Set baud.
t.Ispeed = uint32(speed)
t.Ospeed = uint32(speed)

if err := unix.IoctlSetTermios(fd, unix.TCSETS, t); err != nil {
return fmt.Errorf("serial: ioctl TCSETS: %w", err)
}

// Give the line a moment to settle.
time.Sleep(10 * time.Millisecond)
return nil
}

func baudToUnix(baud int) (uint32, error) {
switch baud {
case 4800:
return unix.B4800, nil
case 9600:
return unix.B9600, nil
case 19200:
return unix.B19200, nil
case 38400:
return unix.B38400, nil
case 57600:
return unix.B57600, nil
case 115200:
return unix.B115200, nil
default:
return 0, fmt.Errorf("serial: unsupported baud %d (supported: 4800,9600,19200,38400,57600,115200)", baud)
}
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The serial driver implementation lacks test coverage. The serial_linux.go file contains critical low-level serial port configuration logic including termios settings, baud rate mapping, and file descriptor management, but there are no corresponding test files. Consider adding tests for configuration validation, baud rate conversion, and error handling paths.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go
Comment on lines +72 to +78
func (g *GTU7) Descriptor() devices.Descriptor {
return devices.Descriptor{
Name: g.name,
Kind: "gps",
ValueType: "GPSFix",
}
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Descriptor implementation doesn't include useful metadata fields that are present in other sensors like VH400. Consider adding Tags (e.g., "gps", "navigation", "location") and Attributes (e.g., serial port information) to provide more complete device metadata. This would make the device more discoverable and better documented.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread sensors/gtu7.go
Comment on lines +32 to +39
type GTU7Config struct {
Name string
Serial drivers.SerialConfig
Factory drivers.SerialFactory

// Test injection
Reader io.Reader
}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GTU7Config struct lacks documentation. As an exported configuration type, it should have a doc comment explaining its purpose and providing usage examples. The Reader field is particularly important to document as it's specifically marked for test injection.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #17, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #18, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #19, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #20, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #21, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #22, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #23, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #24, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #25, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #26, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #27, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #28, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #29, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #30, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #31, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #32, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #33, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 16, 2026

@rustyeddy I've opened a new pull request, #34, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 13 commits January 16, 2026 09:59
* Initial plan

* Make GTU7 buffer size configurable via GTU7Config.Buf

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Make GTU7 output channel buffer size configurable

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
* Initial plan

* test: add comprehensive GTU7 test coverage

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Final review completed

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Update sensors/gtu7_test.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
Co-authored-by: Rusty Eddy <rusty@rustyeddy.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Initial plan

* Add proper serial port cleanup in GTU7.Run method

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Improve comment clarity and error message in nopCloser

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Update nopCloser comment for clarity

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Final update to PR description

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Replace custom nopCloser with stdlib io.NopCloser and use io.ReadCloser

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Remove unnecessary nil check in Run defer

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
Co-authored-by: Rusty Eddy <rusty@rustyeddy.com>
* Initial plan

* Fix RMC precedence flags to reset when data is unavailable

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Complete: Fixed RMC precedence flag reset issue

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
Co-authored-by: Rusty Eddy <rusty@rustyeddy.com>
* Initial plan

* Add ReadOnly access to GTU7 Descriptor method

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Add ReadOnly access mode to GTU7 GPS sensor descriptor

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
* Initial plan

* docs: add comprehensive documentation for GPSFix struct

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
* Initial plan

* Add Tags and Attributes to GTU7 Descriptor

- Added Tags: "gps", "navigation", "location"
- Added Attributes: serial port and baud rate information
- Added Access: ReadOnly to match other sensor patterns
- Store GTU7Config in struct to access serial info in Descriptor
- Added comprehensive tests for Descriptor functionality

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Completed: Enhanced GTU7 Descriptor with Tags and Attributes

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
Co-authored-by: Rusty Eddy <rusty@rustyeddy.com>
* Initial plan

* Fix parseLatLon to check strconv.ParseFloat errors

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Improve error message for empty coordinates

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Complete parseLatLon error handling improvements

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Update sensors/gtu7_test.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
Co-authored-by: Rusty Eddy <rusty@rustyeddy.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Initial plan

* Make serial port settle delay configurable with documentation

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Simplify delay handling logic

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Make serial port settle delay configurable

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
* Initial plan

* Change NewGTU7 to return error instead of panicking

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Return error from NewGTU7 instead of panicking

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

* Add test coverage for NewGTU7 error handling

Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustyeddy <2903425+rustyeddy@users.noreply.github.com>
Co-authored-by: Rusty Eddy <rusty@rustyeddy.com>
@rustyeddy rustyeddy closed this Jan 17, 2026
@rustyeddy rustyeddy deleted the gtu7 branch January 17, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants