github.com/adwpc/xmobile@v0.0.0-20231212131043-3f9720cf0e99/exp/sensor/darwin_armx.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build darwin && (arm || arm64)
     6  
     7  package sensor
     8  
     9  /*
    10  #cgo CFLAGS: -x objective-c
    11  #cgo LDFLAGS: -framework CoreMotion
    12  
    13  #import <stdlib.h>
    14  
    15  void GoIOS_createManager();
    16  
    17  void GoIOS_startAccelerometer(float interval);
    18  void GoIOS_stopAccelerometer();
    19  void GoIOS_readAccelerometer(int64_t* timestamp, float* vector);
    20  
    21  void GoIOS_startGyro(float interval);
    22  void GoIOS_stopGyro();
    23  void GoIOS_readGyro(int64_t* timestamp, float* vector);
    24  
    25  void GoIOS_startMagneto(float interval);
    26  void GoIOS_stopMagneto();
    27  void GoIOS_readMagneto(int64_t* timestamp, float* vector);
    28  
    29  void GoIOS_destroyManager();
    30  */
    31  import "C"
    32  import (
    33  	"fmt"
    34  	"sync"
    35  	"time"
    36  	"unsafe"
    37  )
    38  
    39  var channels struct {
    40  	sync.Mutex
    41  	done [nTypes]chan struct{}
    42  }
    43  
    44  func init() {
    45  	C.GoIOS_createManager()
    46  }
    47  
    48  // minDelay is the minimum delay allowed.
    49  //
    50  // From Event Handling Guide for iOS:
    51  //
    52  // "You can set the reporting interval to be as small as 10
    53  // milliseconds (ms), which corresponds to a 100 Hz update rate,
    54  // but most app operate sufficiently with a larger interval."
    55  //
    56  // There is no need to poll more frequently than once every 10ms.
    57  //
    58  // https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/motion_event_basics/motion_event_basics.html
    59  
    60  const minDelay = 10 * time.Millisecond
    61  
    62  // enable enables the sensor t on sender. A non-nil sender is
    63  // required before calling enable.
    64  func enable(t Type, delay time.Duration) error {
    65  	channels.Lock()
    66  	defer channels.Unlock()
    67  
    68  	if channels.done[t] != nil {
    69  		return fmt.Errorf("sensor: cannot enable; %v sensor is already enabled", t)
    70  	}
    71  	channels.done[t] = make(chan struct{})
    72  
    73  	if delay < minDelay {
    74  		delay = minDelay
    75  	}
    76  	interval := C.float(float64(delay) / float64(time.Second))
    77  
    78  	switch t {
    79  	case Accelerometer:
    80  		C.GoIOS_startAccelerometer(interval)
    81  	case Gyroscope:
    82  		C.GoIOS_startGyro(interval)
    83  	case Magnetometer:
    84  		C.GoIOS_startMagneto(interval)
    85  	}
    86  	go pollSensor(t, delay, channels.done[t])
    87  	return nil
    88  }
    89  
    90  func disable(t Type) error {
    91  	channels.Lock()
    92  	defer channels.Unlock()
    93  
    94  	if channels.done[t] == nil {
    95  		return fmt.Errorf("sensor: cannot disable; %v sensor is not enabled", t)
    96  	}
    97  	close(channels.done[t])
    98  	channels.done[t] = nil
    99  
   100  	switch t {
   101  	case Accelerometer:
   102  		C.GoIOS_stopAccelerometer()
   103  	case Gyroscope:
   104  		C.GoIOS_stopGyro()
   105  	case Magnetometer:
   106  		C.GoIOS_stopMagneto()
   107  	}
   108  	return nil
   109  }
   110  
   111  func pollSensor(t Type, d time.Duration, done chan struct{}) {
   112  	var lastTimestamp int64
   113  
   114  	var timestamp C.int64_t
   115  	var ev [3]C.float
   116  
   117  	for {
   118  		select {
   119  		case <-done:
   120  			return
   121  		default:
   122  			tp := (*C.int64_t)(unsafe.Pointer(&timestamp))
   123  			vp := (*C.float)(unsafe.Pointer(&ev[0]))
   124  
   125  			switch t {
   126  			case Accelerometer:
   127  				C.GoIOS_readAccelerometer(tp, vp)
   128  			case Gyroscope:
   129  				C.GoIOS_readGyro(tp, vp)
   130  			case Magnetometer:
   131  				C.GoIOS_readMagneto(tp, vp)
   132  			}
   133  			ts := int64(timestamp)
   134  			if ts > lastTimestamp {
   135  				// TODO(jbd): Do we need to convert the values to another unit?
   136  				// How does iOS units compare to the Android units.
   137  				sender.Send(Event{
   138  					Sensor:    t,
   139  					Timestamp: ts,
   140  					Data:      []float64{float64(ev[0]), float64(ev[1]), float64(ev[2])},
   141  				})
   142  				lastTimestamp = ts
   143  				time.Sleep(d / 2)
   144  			}
   145  		}
   146  	}
   147  }
   148  
   149  // TODO(jbd): Remove destroy?
   150  func destroy() error {
   151  	C.GoIOS_destroyManager()
   152  	return nil
   153  }