Full Uno code
using Fuse;
using Fuse.Scripting;
using Fuse.Reactive;
using Uno.UX;
using Uno.Compiler.ExportTargetInterop;
using Uno;
[Require("Xcode.Framework","CoreMotion.framework")]
[ForeignInclude(Language.ObjC, "Motion.hh")]
[UXGlobalModule]
public class MotionModule : NativeEventEmitterModule
{
static readonly MotionModule _instance;
extern(iOS) ObjC.Object _motion;
public MotionModule() : base (true, "accelerometerChanged", "gyroscopeChanged", "motionChanged", "magnetometerChanged")
{
if(_instance != null)
return;
_instance = this;
Resource.SetGlobalKey(_instance, "Motion");
AddMember(new NativeFunction("Subscribe", (NativeCallback)Subscribe));
AddMember(new NativeFunction("Unsubscribe", (NativeCallback)Unsubscribe));
if defined(iOS)
_motion = AllocMotion();
}
[Foreign(Language.ObjC)]
extern(iOS) ObjC.Object AllocMotion()
@{
return [Motion alloc];
@}
[Foreign(Language.ObjC)]
public extern(iOS) bool SubscribeAccelerometer(ObjC.Object motion, double interval, Action<string> callback)
@{
[(Motion *)motion getAccelerometerValues: interval withCallback:callback];
return true;
@}
void AccelerometerCallback(string accelerometer)
{
Emit("accelerometerChanged", accelerometer);
}
[Foreign(Language.ObjC)]
public extern(iOS) bool SubscribeGyroscope(ObjC.Object motion, double interval, Action<string> callback)
@{
[(Motion *)motion getGyroValues: interval withCallback:callback];
return true;
@}
void GyroscopeCallback(string gyroscope)
{
Emit("gyroscopeChanged", gyroscope);
}
[Foreign(Language.ObjC)]
public extern(iOS) bool SubscribeMotion(ObjC.Object motion, double interval, Action<string> callback)
@{
[(Motion *)motion getMotionValues: interval withCallback:callback];
return true;
@}
void MotionCallback(string motion)
{
Emit("motionChanged", motion);
}
[Foreign(Language.ObjC)]
public extern(iOS) bool SubscribeMagnetometer(ObjC.Object motion, double interval, Action<string> callback)
@{
[(Motion *)motion getMagnetometerValues: interval withCallback:callback];
return true;
@}
void MagnetometerCallback(string magnetometer)
{
Emit("magnetometerChanged", magnetometer);
}
object Subscribe(Context c, object[] args)
{
if defined(iOS){
if(args.Length != 2)
return false;
bool subscribed = false;
if(args[0].ToString().Contains("accelerometer"))
subscribed = SubscribeAccelerometer(_motion, Marshal.ToDouble(args[1]), AccelerometerCallback);
if(args[0].ToString().Contains("gyroscope"))
subscribed = SubscribeGyroscope(_motion, Marshal.ToDouble(args[1]), GyroscopeCallback);
if(args[0].ToString().Contains("motion"))
subscribed = SubscribeMotion(_motion, Marshal.ToDouble(args[1]), MotionCallback);
if(args[0].ToString().Contains("magnetometer"))
subscribed = SubscribeMagnetometer(_motion, Marshal.ToDouble(args[1]), MagnetometerCallback);
return subscribed;
} else {
debug_log "Motion is only implemented for iOS";
return false;
}
}
object Unsubscribe(Context c, object[] args)
{
if defined(iOS){
if(args.Length == 0)
return false;
bool unsubscribed = false;
/*
if(args[0].Contains("accelerometer"))
unsubscribed = SubscribeAccelerometer(_motion, AccelerometerCallback);
if(args[0].Contains("gyroscope"))
unsubscribed = SubscribeGyroscope(_motion, GyroscopeCallback);
if(args[0].Contains("motion"))
unsubscribed = SubscribeMotion(_motion, MotionCallback);
*/
return unsubscribed;
} else {
debug_log "Motion is only implemented for iOS";
return false;
}
}
}
Full C code (although it should not impact anything)
Motion.hh
#import <CoreMotion/CoreMotion.h>
@interface Motion : NSObject
@property (strong,nonatomic) CMMotionManager *manager;
@property (nonatomic) double previousAttitudeYaw;
- (void) getAccelerometerValues: (double)interval withCallback:(void(^)(NSString*)) callback;
- (void) getGyroValues: (double)interval withCallback:(void(^)(NSString*)) callback;
- (void) getMagnetometerValues: (double)interval withCallback:(void(^)(NSString*)) callback;
- (void) getMotionValues: (double)interval withCallback:(void(^)(NSString*)) callback;
@end
Motion.mm
#import "Motion.hh"
//TODO: add magnetometer support
//http://stackoverflow.com/questions/11711646/why-am-i-getting-0-degrees-from-magneticfield-property-the-whole-time
@implementation Motion
-(id)init {
if (self = [super init]) {
}
return self;
}
/*
//Register for Coremotion notifications
[self.motionActivityManager startActivityUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMotionActivity *activity)
{
NSLog(@"Got a core motion update");
NSLog(@"Current activity date is %f",activity.timestamp);
NSLog(@"Current activity confidence from a scale of 0 to 2 - 2 being best- is: %ld",activity.confidence);
NSLog(@"Current activity type is unknown: %i",activity.unknown);
NSLog(@"Current activity type is stationary: %i",activity.stationary);
NSLog(@"Current activity type is walking: %i",activity.walking);
NSLog(@"Current activity type is running: %i",activity.running);
NSLog(@"Current activity type is automotive: %i",activity.automotive);
}];
*/
//Continously retrieve 3D accelerometer values measured in G:s
- (void) getAccelerometerValues: (double)interval withCallback:(void(^)(NSString*)) callback{
if(self.manager == nil)
self.manager = [[CMMotionManager alloc] init];
if(self.manager.accelerometerAvailable){
self.manager.accelerometerUpdateInterval = interval;
[self.manager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMAccelerometerData *accelerometer, NSError *error)
{
if(error != nil){
callback([NSString stringWithFormat:@"{\
\"error\": {\"description\": %@}}",
error.localizedDescription
]);
return;
}
callback([NSString stringWithFormat:@"{\"acceleration\":{\"x\": %f, \"y\": %f, \"z\": %f}}",
accelerometer.acceleration.x, accelerometer.acceleration.y, accelerometer.acceleration.z
]);
}];
}
}
//Continously retrieve 3D rotation rates measured in angles
- (void) getGyroValues: (double)interval withCallback:(void(^)(NSString*)) callback{
if(self.manager == nil)
self.manager = [[CMMotionManager alloc] init];
if(self.manager.gyroAvailable){
self.manager.gyroUpdateInterval = interval;
[self.manager startGyroUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMGyroData *gyro, NSError *error)
{
if(error != nil){
callback([NSString stringWithFormat:@"{\
\"error\": {\"description\": %@}}",
error.localizedDescription
]);
return;
}
//Return in degrees instead of radians
double degreesFromRadian = 180 / M_PI;
callback([NSString stringWithFormat:@"{\"rotationRate\":{\"x\": %f, \"y\": %f, \"z\": %f}}",
degreesFromRadian * gyro.rotationRate.x, degreesFromRadian * gyro.rotationRate.y, degreesFromRadian * gyro.rotationRate.z
]);
}];
}
}
- (void) getMagnetometerValues: (double)interval withCallback:(void(^)(NSString*)) callback{
if(self.manager == nil)
self.manager = [[CMMotionManager alloc] init];
if(self.manager.magnetometerAvailable){
self.manager.magnetometerUpdateInterval = interval;
[self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMMagnetometerData *magnetometer, NSError *error)
{
if(error != nil){
callback([NSString stringWithFormat:@"{\
\"error\": {\"description\": %@}}",
error.localizedDescription
]);
return;
}
callback([NSString stringWithFormat:@"{\"magneticField\": {\"x\": %f, \"y\": %f, \"z\": %f}}",
magnetometer.magneticField.x, magnetometer.magneticField.y, magnetometer.magneticField.z
]);
}];
}
}
- (void) getMotionValues: (double)interval withCallback:(void(^)(NSString*)) callback{
if(self.manager == nil)
self.manager = [[CMMotionManager alloc] init];
if(self.manager.deviceMotionAvailable){
self.manager.deviceMotionUpdateInterval = interval;
[self.manager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
if(error != nil){
callback([NSString stringWithFormat:@"{\
\"error\": {\"description\": %@}}",
error.localizedDescription
]);
return;
}
//Return in degrees instead of radians
double degreesFromRadian = 180 / M_PI;
CMQuaternion quat = motion.attitude.quaternion;
double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y));
if (self.previousAttitudeYaw == 0) {
self.previousAttitudeYaw = yaw;
}
// kalman filtering
static float q = 0.1; // process noise
static float r = 0.1; // sensor noise
static float p = 0.1; // estimated error
static float k = 0.5; // kalman filter gain
float x = self.previousAttitudeYaw;
p = p + q;
k = p / (p + r);
x = x + k*(yaw - x);
p = (1 - k)*p;
self.previousAttitudeYaw = x;
callback([NSString stringWithFormat:@"{\
\"attitude\": {\"yaw\": %f, \"pitch\": %f, \"roll\": %f}, \
\"rotationRate\": {\"x\": %f, \"y\": %f, \"z\": %f}, \
\"gravity\": {\"x\": %f, \"y\": %f, \"z\": %f}, \
\"magneticField\": {\"accuracy\": %d, \"x\": %f, \"y\": %f, \"z\": %f}, \
\"userAcceleration\": {\"x\": %f, \"y\": %f, \"z\": %f}}",
x * degreesFromRadian, motion.attitude.pitch * degreesFromRadian, motion.attitude.roll * degreesFromRadian,
motion.rotationRate.x, motion.rotationRate.y, motion.rotationRate.z,
motion.gravity.x, motion.gravity.y, motion.gravity.z,
motion.magneticField.accuracy, motion.magneticField.field.x, motion.magneticField.field.y, motion.magneticField.field.z,
motion.userAcceleration.x, motion.userAcceleration.y, motion.userAcceleration.z
]);
}];
}
}
@end