-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsensor_darwin.go
More file actions
119 lines (98 loc) · 2.75 KB
/
sensor_darwin.go
File metadata and controls
119 lines (98 loc) · 2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//go:build darwin
// +build darwin
package main
import (
"fmt"
"time"
"github.com/taigrr/apple-silicon-accelerometer/detector"
"github.com/taigrr/apple-silicon-accelerometer/sensor"
"github.com/taigrr/apple-silicon-accelerometer/shm"
)
// sensorReady is closed once shared memory is created and the sensor
// worker is about to enter the CFRunLoop.
var sensorReady = make(chan struct{})
// sensorErr receives any error from the sensor worker.
var sensorErr = make(chan error, 1)
func init() {
newPlatformSensor = newMacOSSensor
}
type macOSSensor struct {
accelRing *shm.Ring
lastAccelTotal uint64
det *detector.Detector
}
// newMacOSSensor creates a macOS sensor (threshold ignored on macOS)
func newMacOSSensor(threshold int) (platformSensor, error) {
// Create shared memory for accelerometer data.
accelRing, err := shm.CreateRing(shm.NameAccel)
if err != nil {
return nil, fmt.Errorf("creating accel shm: %w", err)
}
// Start the sensor worker in a background goroutine.
go func() {
close(sensorReady)
err := sensor.Run(sensor.Config{
AccelRing: accelRing,
Restarts: 0,
})
if err != nil {
sensorErr <- err
}
}()
// Wait for sensor to be ready.
select {
case <-sensorReady:
case err := <-sensorErr:
accelRing.Close()
accelRing.Unlink()
return nil, fmt.Errorf("sensor worker failed: %w", err)
case <-time.After(5 * time.Second):
accelRing.Close()
accelRing.Unlink()
return nil, fmt.Errorf("sensor timeout")
}
// Give the sensor a moment to start producing data.
time.Sleep(100 * time.Millisecond)
return &macOSSensor{
accelRing: accelRing,
det: detector.New(),
}, nil
}
func (s *macOSSensor) Read() (eventDetected bool, severity string, amplitude float64, err error) {
select {
case err := <-sensorErr:
return false, "", 0, fmt.Errorf("sensor worker failed: %w", err)
default:
}
tNow := float64(time.Now().UnixNano()) / 1e9
samples, newTotal := s.accelRing.ReadNew(s.lastAccelTotal, shm.AccelScale)
s.lastAccelTotal = newTotal
maxBatch := 200
if len(samples) > maxBatch {
samples = samples[len(samples)-maxBatch:]
}
nSamples := len(samples)
for idx, sample := range samples {
tSample := tNow - float64(nSamples-idx-1)/float64(s.det.FS)
s.det.Process(sample.X, sample.Y, sample.Z, tSample)
}
newEventIdx := len(s.det.Events)
if newEventIdx > 0 {
ev := s.det.Events[newEventIdx-1]
if ev.Severity == "CHOC_MAJEUR" || ev.Severity == "CHOC_MOYEN" || ev.Severity == "MICRO_CHOC" {
return true, ev.Severity, ev.Amplitude, nil
}
}
return false, "", 0, nil
}
func (s *macOSSensor) Close() error {
if s.accelRing != nil {
s.accelRing.Close()
s.accelRing.Unlink()
}
return nil
}
func checkPlatformRequirements() error {
// macOS requires root for IOKit HID access
return nil
}