// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build darwin // +build arm arm64 package sensor /* #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework CoreMotion #import void GoIOS_createManager(); void GoIOS_startAccelerometer(float interval); void GoIOS_stopAccelerometer(); void GoIOS_readAccelerometer(int64_t* timestamp, float* vector); void GoIOS_startGyro(float interval); void GoIOS_stopGyro(); void GoIOS_readGyro(int64_t* timestamp, float* vector); void GoIOS_startMagneto(float interval); void GoIOS_stopMagneto(); void GoIOS_readMagneto(int64_t* timestamp, float* vector); void GoIOS_destroyManager(); */ import "C" import ( "fmt" "sync" "time" "unsafe" ) var channels struct { sync.Mutex done [nTypes]chan struct{} } func init() { C.GoIOS_createManager() } // minDelay is the minimum delay allowed. // // From Event Handling Guide for iOS: // // "You can set the reporting interval to be as small as 10 // milliseconds (ms), which corresponds to a 100 Hz update rate, // but most app operate sufficiently with a larger interval." // // There is no need to poll more frequently than once every 10ms. // // https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/motion_event_basics/motion_event_basics.html const minDelay = 10 * time.Millisecond func enable(s Sender, t Type, delay time.Duration) error { channels.Lock() defer channels.Unlock() if channels.done[t] != nil { return fmt.Errorf("sensor: cannot enable; %v sensor is already enabled", t) } channels.done[t] = make(chan struct{}) if delay < minDelay { delay = minDelay } interval := C.float(float64(delay) / float64(time.Second)) switch t { case Accelerometer: C.GoIOS_startAccelerometer(interval) case Gyroscope: C.GoIOS_startGyro(interval) case Magnetometer: C.GoIOS_startMagneto(interval) } go pollSensor(s, t, delay, channels.done[t]) return nil } func disable(t Type) error { channels.Lock() defer channels.Unlock() if channels.done[t] == nil { return fmt.Errorf("sensor: cannot disable; %v sensor is not enabled", t) } close(channels.done[t]) channels.done[t] = nil switch t { case Accelerometer: C.GoIOS_stopAccelerometer() case Gyroscope: C.GoIOS_stopGyro() case Magnetometer: C.GoIOS_stopMagneto() } return nil } func pollSensor(s Sender, t Type, d time.Duration, done chan struct{}) { var lastTimestamp int64 var timestamp C.int64_t var ev [3]C.float for { select { case <-done: return default: tp := (*C.int64_t)(unsafe.Pointer(×tamp)) vp := (*C.float)(unsafe.Pointer(&ev[0])) switch t { case Accelerometer: C.GoIOS_readAccelerometer(tp, vp) case Gyroscope: C.GoIOS_readGyro(tp, vp) case Magnetometer: C.GoIOS_readMagneto(tp, vp) } ts := int64(timestamp) if ts > lastTimestamp { // TODO(jbd): Do we need to convert the values to another unit? // How does iOS units compare to the Android units. s.Send(Event{ Sensor: t, Timestamp: ts, Data: []float64{float64(ev[0]), float64(ev[1]), float64(ev[2])}, }) lastTimestamp = ts time.Sleep(d / 2) } } } } // TODO(jbd): Remove destroy? func destroy() error { C.GoIOS_destroyManager() return nil }