@@ -3,65 +3,121 @@ package main
33import (
44 "fmt"
55 "time"
6+ "unsafe"
67
78 "github.com/spf13/cobra"
9+ "github.com/xaionaro-go/ndk/looper"
810 "github.com/xaionaro-go/ndk/sensor"
911)
1012
13+ // sensorEventData extracts x, y, z from an ASensorEvent.
14+ // ASensorEvent layout: version(4) + sensor(4) + type(4) + reserved(4) + timestamp(8) + data[16]float32
15+ // data offset = 24 bytes from the start.
16+ func sensorEventData (event * sensor.SensorEvent ) (x , y , z float32 , timestamp int64 ) {
17+ base := (* byte )(event .Pointer ())
18+ // timestamp at offset 16 (after version+sensor+type+reserved = 4*4 = 16)
19+ timestamp = * (* int64 )(unsafe .Pointer (uintptr (unsafe .Pointer (base )) + 16 ))
20+ // data[0..2] at offset 24
21+ dataPtr := uintptr (unsafe .Pointer (base )) + 24
22+ x = * (* float32 )(unsafe .Pointer (dataPtr ))
23+ y = * (* float32 )(unsafe .Pointer (dataPtr + 4 ))
24+ z = * (* float32 )(unsafe .Pointer (dataPtr + 8 ))
25+ return
26+ }
27+
1128var sensorReadCmd = & cobra.Command {
1229 Use : "read" ,
13- Short : "Read sensor information and poll default sensor" ,
14- Long : `Queries the sensor manager for the default sensor of the given type,
15- prints its static properties, and optionally polls for the specified duration.
16-
17- Note: Real-time sensor data streaming requires looper and event queue
18- integration, which is beyond what a simple CLI poll can provide.
19- This command demonstrates the sensor query API.` ,
30+ Short : "Read real-time sensor data" ,
31+ Long : `Reads real-time sensor events using a looper and event queue.
32+ Prints x, y, z values for each event at the configured sample rate.` ,
2033 RunE : func (cmd * cobra.Command , args []string ) (_err error ) {
2134 sensorType , _ := cmd .Flags ().GetInt32 ("type" )
2235 duration , _ := cmd .Flags ().GetDuration ("duration" )
36+ rate , _ := cmd .Flags ().GetInt32 ("rate" )
2337
24- mgr := sensor .GetInstance ()
25-
26- s := mgr .DefaultSensor (sensorType )
27- if s .Pointer () == nil {
28- return fmt .Errorf ("no default sensor found for type %d" , sensorType )
38+ if duration == 0 {
39+ duration = 5 * time .Second
2940 }
3041
31- fmt .Printf ("sensor info:\n " )
32- fmt .Printf (" name: %s\n " , s .Name ())
33- fmt .Printf (" vendor: %s\n " , s .Vendor ())
34- fmt .Printf (" type: %d (%s)\n " , s .Type (), sensor .Type (s .Type ()))
35- fmt .Printf (" resolution: %g\n " , s .Resolution ())
36- fmt .Printf (" min delay: %d us\n " , s .MinDelay ())
37-
38- fmt .Println ("\n note: real-time sensor data streaming requires looper + event queue integration" )
39-
40- if duration > 0 {
41- fmt .Printf ("\n polling sensor info for %v...\n " , duration )
42- deadline := time .Now ().Add (duration )
43- tick := 0
44- for time .Now ().Before (deadline ) {
45- polled := mgr .DefaultSensor (sensorType )
46- if polled .Pointer () == nil {
47- fmt .Printf (" [%d] sensor no longer available\n " , tick )
48- } else {
49- fmt .Printf (" [%d] %s (type=%d, min_delay=%d us)\n " ,
50- tick , polled .Name (), polled .Type (), polled .MinDelay ())
51- }
52- tick ++
53- time .Sleep (time .Second )
42+ looper .Run (func (lp * looper.Looper ) {
43+ _err = readSensorEvents (lp , sensorType , duration , rate )
44+ })
45+ return
46+ },
47+ }
48+
49+ func readSensorEvents (
50+ lp * looper.Looper ,
51+ sensorType int32 ,
52+ duration time.Duration ,
53+ rateUs int32 ,
54+ ) error {
55+ mgr := sensor .GetInstance ()
56+
57+ s := mgr .DefaultSensor (sensorType )
58+ if s .Pointer () == nil {
59+ return fmt .Errorf ("no default sensor found for type %d" , sensorType )
60+ }
61+
62+ fmt .Printf ("sensor: %s (%s)\n " , s .Name (), sensor .Type (s .Type ()))
63+ fmt .Printf ("vendor: %s\n " , s .Vendor ())
64+ fmt .Printf ("resolution: %g, min delay: %d us\n " , s .Resolution (), s .MinDelay ())
65+
66+ // Create event queue on the current looper thread.
67+ const looperIdent = 1
68+ queue := mgr .CreateEventQueue (
69+ (* sensor .ALooper )(lp .Pointer ()),
70+ looperIdent ,
71+ sensor .ALooper_callbackFunc (nil ), // no callback — we poll manually
72+ nil ,
73+ )
74+ if queue == nil || queue .Pointer () == nil {
75+ return fmt .Errorf ("failed to create sensor event queue" )
76+ }
77+ defer mgr .DestroyEventQueue (queue )
78+
79+ // Enable the sensor on the queue and set the sample rate.
80+ if err := queue .EnableSensor (s ); err != nil {
81+ return fmt .Errorf ("enabling sensor: %w" , err )
82+ }
83+ if err := queue .SetEventRate (s , rateUs ); err != nil {
84+ return fmt .Errorf ("setting event rate: %w" , err )
85+ }
86+
87+ fmt .Printf ("\n streaming for %v (rate=%d us)...\n " , duration , rateUs )
88+
89+ deadline := time .Now ().Add (duration )
90+ count := 0
91+ // Allocate a raw ASensorEvent buffer (104 bytes per event on arm64).
92+ // SensorEvent.ptr must point to valid C memory for GetEvents to write into.
93+ const eventSize = 104 // sizeof(ASensorEvent)
94+ eventBuf := make ([]byte , eventSize )
95+ event := sensor .NewSensorEventFromPointer (unsafe .Pointer (& eventBuf [0 ]))
96+
97+ for time .Now ().Before (deadline ) {
98+ // Poll the looper for events (100ms timeout).
99+ looper .PollOnce (100 * time .Millisecond , nil , nil , nil )
100+
101+ // Drain all available events.
102+ for {
103+ n := queue .GetEvents (event , 1 )
104+ if n <= 0 {
105+ break
54106 }
55- fmt .Printf ("polling complete (%d samples)\n " , tick )
107+ x , y , z , ts := sensorEventData (event )
108+ fmt .Printf (" [%d] t=%d x=%.6f y=%.6f z=%.6f\n " , count , ts , x , y , z )
109+ count ++
56110 }
111+ }
57112
58- return nil
59- },
113+ fmt . Printf ( "received %d events \n " , count )
114+ return nil
60115}
61116
62117func init () {
63- sensorReadCmd .Flags ().Int32 ("type" , int32 (sensor .Accelerometer ), "sensor type ID (1=accelerometer, 2=magnetic, 4=gyroscope, 5=light, ...)" )
64- sensorReadCmd .Flags ().Duration ("duration" , 0 , "how long to poll (0 = just print info)" )
118+ sensorReadCmd .Flags ().Int32 ("type" , int32 (sensor .Accelerometer ), "sensor type ID (1=accelerometer, 2=magnetic, 3=orientation, 4=gyroscope, 5=light, 8=proximity)" )
119+ sensorReadCmd .Flags ().Duration ("duration" , 5 * time .Second , "how long to stream" )
120+ sensorReadCmd .Flags ().Int32 ("rate" , 100000 , "sample period in microseconds (default 100ms)" )
65121
66122 sensorCmd .AddCommand (sensorReadCmd )
67123}
0 commit comments