Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.25.0

require (
github.com/TheCacophonyProject/event-reporter/v3 v3.9.0
github.com/TheCacophonyProject/go-config v1.30.1
github.com/TheCacophonyProject/go-config v1.30.2
github.com/TheCacophonyProject/go-utils v0.1.3
github.com/TheCacophonyProject/rpi-net-manager v0.5.6
github.com/TheCacophonyProject/window v0.0.0-20211121225840-66e93100eba1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/TheCacophonyProject/event-reporter/v3 v3.9.0 h1:dtF7n1/9/8poMgKfFlFZ0LAy2EJ743DkZyHuxMWh1E4=
github.com/TheCacophonyProject/event-reporter/v3 v3.9.0/go.mod h1:DE4feSd2eALapCgYFnsAiUnHfO9lhczRth6amCNusaA=
github.com/TheCacophonyProject/go-config v1.4.0/go.mod h1:oARW/N3eJbcewCqB+Jc7TBwuODawwYgpo56UO6yBdKU=
github.com/TheCacophonyProject/go-config v1.30.1 h1:udmdx1qfi5a16RSF+4C1rAUWApCQ/p9ustUKeS6oW8s=
github.com/TheCacophonyProject/go-config v1.30.1/go.mod h1:+9Z/iu1atKlfwfbbyfpUHaDz9quCgo/BvCianA+zSAs=
github.com/TheCacophonyProject/go-config v1.30.2 h1:GpDnYZ38ea7pun8oewP76uB2dsF/nEQJO92MWbZ0Xdg=
github.com/TheCacophonyProject/go-config v1.30.2/go.mod h1:+9Z/iu1atKlfwfbbyfpUHaDz9quCgo/BvCianA+zSAs=
github.com/TheCacophonyProject/go-utils v0.1.3 h1:DSuDeJz7ZM00yQRLsoukWH0fnC+8X8+ziYxOl6l3wEY=
github.com/TheCacophonyProject/go-utils v0.1.3/go.mod h1:jZPUZ4GtYVxnlTtqiYKMFWDT//kmxdbwjLW3HCyCmCE=
github.com/TheCacophonyProject/rpi-net-manager v0.5.6 h1:/80dbwvwhWz4Yh1eBA64/Yzfzw6W0yWSxuDrdUzz9uE=
Expand Down
6 changes: 3 additions & 3 deletions internal/tc2-hat-attiny/battery_autodetect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ func TestSpecificVoltageDetection(t *testing.T) {
description: "Integration test: overlapping range preference through BatteryMonitor",
},
{
name: "6.6V should detect LiFePO4 2 cells",
name: "6.6V should detect Li-Ion 2 cells",
voltage: 6.6,
expectedChemistry: "lifepo4",
expectedChemistry: "li-ion",
expectedCells: 2,
description: "Integration test: LiFePO4 detection through BatteryMonitor",
description: "Integration test: Li-Ion detection through BatteryMonitor",
},
}

Expand Down
44 changes: 21 additions & 23 deletions internal/tc2-hat-comms/at-esl.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import (
)

var (
predictionLockoutNodeRegister int = 5
predictionLockoutNodeRegister int = 5
predictionLockoutMinutesDefault int64 = 30 // default 30mins.
batteryLockoutHoursNodeRegister int = 12
batteryLockoutMinutesNodeRegister int = 13
batteryLockoutMinutesDefault int64 = 180 // default 180mins (3 hours).
batteryLockoutHoursNodeRegister int = 12
batteryLockoutMinutesNodeRegister int = 13
batteryLockoutMinutesDefault int64 = 180 // default 180mins (3 hours).
)

type ATESLMessenger struct {
Expand All @@ -37,8 +37,8 @@ type ATESLLastBattery struct {
Lockout int64
}

var atesLastPrediction = ATESLLastPrediction{ Lockout: predictionLockoutMinutesDefault }
var atesLastBattery = ATESLLastBattery{ Lockout: batteryLockoutMinutesDefault }
var atesLastPrediction = ATESLLastPrediction{Lockout: predictionLockoutMinutesDefault}
var atesLastBattery = ATESLLastBattery{Lockout: batteryLockoutMinutesDefault}

func processATESL(config *CommsConfig, testClassification *TestClassification, eventChannel chan event) error {
messenger := ATESLMessenger{
Expand Down Expand Up @@ -104,8 +104,8 @@ func (a ATESLMessenger) processBatteryEvent(b batteryEvent, l *ATESLLastBattery)
log.Error("Error sending battery reading:", err)
return err
}
l.Voltage = b.Voltage // Remember the voltage reading
l.When = time.Now() // Remember when we detected it
l.Voltage = b.Voltage // Remember the voltage reading
l.When = time.Now() // Remember when we detected it

// Now let's check the event lockout
l.Lockout = getBatteryEventLockout(a.BaudRate)
Expand All @@ -118,7 +118,7 @@ func (a ATESLMessenger) processTrackingEvent(t trackingEvent, l *ATESLLastPredic
// TODO: tracking events can be buffered - so the clip age make be significant
// if a tacking event was older than our lockout - perhaps we should send it on
//
// The ESL API also assumes all events are 'now' so we also need to extend that to be able to provide
// The ESL API also assumes all events are 'now' so we also need to extend that to be able to provide
// the age in seconds/minutes if the age is over some reasonable threshold - maybe 2-5mins.

lastPrediction := time.Since(l.When).Minutes()
Expand Down Expand Up @@ -147,7 +147,7 @@ func (a ATESLMessenger) processTrackingEvent(t trackingEvent, l *ATESLLastPredic
target = true
targetConfidence = 50 // limit to 50% to avoid too much noise

// Special handler for any - with confidence setting
// Special handler for any - with confidence setting
} else if _, found := a.TrapSpecies["any"]; found {

// We can do without false-positives, not quite any :)
Expand All @@ -158,7 +158,7 @@ func (a ATESLMessenger) processTrackingEvent(t trackingEvent, l *ATESLLastPredic
target = true
targetConfidence = a.TrapSpecies["any"]

// If we have specific species let's check for specific confidence levels oer species
// If we have specific species let's check for specific confidence levels oer species
} else if _, found := a.TrapSpecies[t.What]; found {
target = true
targetConfidence = a.TrapSpecies[t.What]
Expand Down Expand Up @@ -262,13 +262,13 @@ func getRegisteryData(baudRate int, reg int) int64 {
regCmd := "m00"

// Currently limited to the first 'page' of registery data (m00)
cmd := append([]byte("AT+XCMD=" + regCmd), calcCRC16([]byte(regCmd))...)
cmd := append([]byte("AT+XCMD="+regCmd), calcCRC16([]byte(regCmd))...)
log.Infof("get reg %d, data via command %v", reg, cmd)

response, _ := sendATCommand(string(cmd), baudRate)

// Let's clean-up the output - trim any unwanted charaters
if idx := bytes.Index(response, []byte(regCmd + "\r\n\r\n")); idx != -1 {
if idx := bytes.Index(response, []byte(regCmd+"\r\n\r\n")); idx != -1 {
response = response[idx:]
} else {
// fallback: not found, just log and continue
Expand Down Expand Up @@ -338,24 +338,22 @@ func getPredictionEventLockout(baudRate int) int64 {
}

/*
Battery event lockout mins
Time in minutes to have an battery event lockout; default 180mins (3 hours).
Read the 12 (hrs) + 13 (mins) node registery to get the value

Battery event lockout mins
Time in minutes to have an battery event lockout; default 180mins (3 hours).
Read the 12 (hrs) + 13 (mins) node registery to get the value

3hours = 'w1203’
30min = 'w131e’

3hours = 'w1203’
30min = 'w131e’
*/
func getBatteryEventLockout(baudRate int) int64 {

hours := getRegisteryData(baudRate, batteryLockoutHoursNodeRegister)
mins := getRegisteryData(baudRate, batteryLockoutMinutesNodeRegister)
mins := getRegisteryData(baudRate, batteryLockoutMinutesNodeRegister)

battery_lockout_minutes := hours * 60 + mins
battery_lockout_minutes := hours*60 + mins
if battery_lockout_minutes <= 0 {
log.Infof("Battery lockout time not set - using default (%d)", batteryLockoutMinutesDefault)
battery_lockout_minutes = batteryLockoutMinutesDefault
battery_lockout_minutes = batteryLockoutMinutesDefault
}

log.Infof("Battery lockout time = %d (mins)", battery_lockout_minutes)
Expand Down
76 changes: 38 additions & 38 deletions internal/tc2-hat-comms/service-monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
package comms

import (
"time"
"strconv"
"strings"
"time"

"github.com/TheCacophonyProject/tc2-hat-controller/tracks"
"github.com/godbus/dbus/v5"
)

type models struct {
Id int32
Id int32
Labels []string
}

var (
animalsList = models{Id: 1, Labels: []string{"bird","cat","deer","dog","false-positive","hedgehog","human","kiwi","leporidae","mustelid","penguin","possum","rodent","sheep","vehicle","wallaby"}}
fpModelLabels = models{Id: 1004, Labels: []string{"animal","false-positive"}}
animalsList = models{Id: 1, Labels: []string{"bird", "cat", "deer", "dog", "false-positive", "hedgehog", "human", "kiwi", "leporidae", "mustelid", "penguin", "possum", "rodent", "sheep", "vehicle", "wallaby"}}
fpModelLabels = models{Id: 1004, Labels: []string{"animal", "false-positive"}}
)

type event interface {
Expand All @@ -37,7 +37,7 @@ type trackingEvent struct {
BlankRegion bool
Tracking bool
LastPredictionFrame int32
ClipAgeSeconds int32
ClipAgeSeconds int32
}

func (t trackingEvent) isEvent() {}
Expand All @@ -49,8 +49,8 @@ type batteryEvent struct {
}

func addTrackingReprocessedEvents(eventsChan chan event) error {
targetSignalName := "org.cacophony.thermalrecorder.TrackingReprocessed"
return addTrackingEventsForSignal(eventsChan, targetSignalName)
targetSignalName := "org.cacophony.thermalrecorder.TrackingReprocessed"
return addTrackingEventsForSignal(eventsChan, targetSignalName)
}

func addTrackingEvents(eventsChan chan event) error {
Expand Down Expand Up @@ -116,34 +116,34 @@ func addTrackingEventsForSignal(eventsChan chan event, targetSignalName string)

// Match the track model output to our now models
switch modelIdType := signal.Body[11].(type) {
case int32:
modelId = signal.Body[11].(int32)
// Reprocessed events have a "post-" id prefix
case string:
modelIdStr := strings.TrimPrefix(signal.Body[11].(string), "post-")
val64, err := strconv.ParseInt(modelIdStr, 10, 32)
if err != nil {
log.Warnf("Failed to parse the model id[%v]: %v", modelIdStr, err)
continue
}
modelId = int32(val64)
default:
log.Warnf("Model id unexpected type %v .. skipping", modelIdType)
case int32:
modelId = signal.Body[11].(int32)
// Reprocessed events have a "post-" id prefix
case string:
modelIdStr := strings.TrimPrefix(signal.Body[11].(string), "post-")
val64, err := strconv.ParseInt(modelIdStr, 10, 32)
if err != nil {
log.Warnf("Failed to parse the model id[%v]: %v", modelIdStr, err)
continue
}
modelId = int32(val64)
default:
log.Warnf("Model id unexpected type %v .. skipping", modelIdType)
continue
}

// Get the labels for the model used in the prediction
switch modelId {
case fpModelLabels.Id:
modelLabels = fpModelLabels.Labels
case animalsList.Id:
modelLabels = animalsList.Labels
default:
log.Warnf("Model id key not known %v [%v, %v]", modelId, fpModelLabels.Id, animalsList.Id)
continue
}
case fpModelLabels.Id:
modelLabels = fpModelLabels.Labels
case animalsList.Id:
modelLabels = animalsList.Labels
default:
log.Warnf("Model id key not known %v [%v, %v]", modelId, fpModelLabels.Id, animalsList.Id)
continue
}

// Loop through our track species and get the model scores
// Loop through our track species and get the model scores
species := tracks.Species{}
for i, v := range modelLabels {
species[v] = signal.Body[2].([]int32)[i]
Expand All @@ -156,11 +156,11 @@ func addTrackingEventsForSignal(eventsChan chan event, targetSignalName string)
// See if we have a clip end time
clipAgeSeconds := int32(0)
if len(signal.Body) >= 13 {
ts := signal.Body[12].(float64);
ts := signal.Body[12].(float64)
now := time.Now()
target := time.Unix(int64(ts), int64((ts-float64(int64(ts)))*1e9),)
target := time.Unix(int64(ts), int64((ts-float64(int64(ts)))*1e9))

clipAgeSeconds = int32(now.Sub(target).Seconds());
clipAgeSeconds = int32(now.Sub(target).Seconds())
log.Debugf("Clip is %d seconds old", clipAgeSeconds)
}

Expand Down Expand Up @@ -285,12 +285,12 @@ func getThumbnail(clip_id int32, track_id int32) [][]uint16 {
}

switch frame := t_call.Body[0].(type) {
case [][]uint16:
// Access row/col
log.Debugf("Thubnail (clip id: %d, track_id: %d) is: %d×%d", clip_id, track_id, len(frame), len(frame[0]))
return t_call.Body[0].([][]uint16)
default:
log.Warnf("GetThumbnail returned an unexpected 2D type: %T", frame)
case [][]uint16:
// Access row/col
log.Debugf("Thubnail (clip id: %d, track_id: %d) is: %d×%d", clip_id, track_id, len(frame), len(frame[0]))
return t_call.Body[0].([][]uint16)
default:
log.Warnf("GetThumbnail returned an unexpected 2D type: %T", frame)
}
return nil
}
5 changes: 3 additions & 2 deletions test/lifepo_battery_readings.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
2025-05-12 11:36:57, 0.0, 6.5267673, 0.0
2025-05-12 11:36:57, 0.0, 6.3267673, 0.0
2025-05-12 11:36:57, 0.0, 6.5267673, 0.0
2025-05-12 11:39:37, 0.0, 6.5098147, 0.0
2025-05-12 11:39:37, 0.0, 6.5098147, 0.0
Expand Down Expand Up @@ -37,4 +37,5 @@
2025-06-13 22:33:49, 0.0, 6.1707616, 0.0
2025-06-14 07:29:25, 0.0, 5.9503765, 0.0
2025-06-26 01:27:12, 0.0, 6.1538095, 0.0
2025-06-27 03:24:18, 0.0, 5.9164724, 0.0
2025-06-27 03:24:18, 0.0, 5.9164724, 0.0
2025-06-27 04:24:18, 0.0, 5.3164724, 0.0