Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
GH-52 Add Magnetometer/Gyro/Accelerometer code and samples (#140)
Browse files Browse the repository at this point in the history
* Add Magnetometer code and samples

* Add Tests for Magnetomoter

* Add docs and adjust time intervals to match Android

* Update magnetometer to use events.

* Ensure monitor check is added.

* Cleanup magnetomter vased on feedback

* Tweak data update on ios

* Add accelerometer and gyroscope.

* Update tests

* Update docs with units.

* Cleanup tests

* Gyro calibrated only .

* Add all sensors test page. Ensure Android sensors work across stops

* Use sensor name instead of ID (was api 24+)

* Check for null on sensors

* Update IsMonitoring to match compass

* Make @Redth feel better about IsMonitoring

* Cleanup try/catch for @Redth
  • Loading branch information
jamesmontemagno authored and Redth committed Apr 4, 2018
1 parent 8c69fa1 commit 267edd1
Show file tree
Hide file tree
Showing 53 changed files with 2,698 additions and 35 deletions.
25 changes: 25 additions & 0 deletions Caboodle.Tests/Acceleromter_Tests.cs
@@ -0,0 +1,25 @@
using Microsoft.Caboodle;
using Xunit;

namespace Caboodle.Tests
{
public class Accelerometer_Tests
{
public Accelerometer_Tests()
{
Accelerometer.Stop();
}

[Fact]
public void IsSupported_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Accelerometer.IsSupported);

[Fact]
public void Monitor_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Accelerometer.Start(SensorSpeed.Normal));

[Fact]
public void IsMonitoring_Default_On_NetStandard() =>
Assert.False(Accelerometer.IsMonitoring);
}
}
25 changes: 25 additions & 0 deletions Caboodle.Tests/Gyroscope_Tests.cs
@@ -0,0 +1,25 @@
using Microsoft.Caboodle;
using Xunit;

namespace Caboodle.Tests
{
public class Gyroscope_Tests
{
public Gyroscope_Tests()
{
Gyroscope.Stop();
}

[Fact]
public void IsSupported_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Gyroscope.IsSupported);

[Fact]
public void Monitor_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Gyroscope.Start(SensorSpeed.Normal));

[Fact]
public void IsMonitoring_Default_On_NetStandard() =>
Assert.False(Gyroscope.IsMonitoring);
}
}
25 changes: 25 additions & 0 deletions Caboodle.Tests/Magnetometer_Tests.cs
@@ -0,0 +1,25 @@
using Microsoft.Caboodle;
using Xunit;

namespace Caboodle.Tests
{
public class Magnetometer_Tests
{
public Magnetometer_Tests()
{
Magnetometer.Stop();
}

[Fact]
public void IsSupported_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Magnetometer.IsSupported);

[Fact]
public void Monitor_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Magnetometer.Start(SensorSpeed.Normal));

[Fact]
public void IsMonitoring_Default_On_NetStandard() =>
Assert.False(Magnetometer.IsMonitoring);
}
}
67 changes: 67 additions & 0 deletions Caboodle/Accelerometer/Accelerometer.android.cs
@@ -0,0 +1,67 @@
using Android.Hardware;
using Android.Runtime;

namespace Microsoft.Caboodle
{
public static partial class Accelerometer
{
internal static bool IsSupported =>
Platform.SensorManager?.GetDefaultSensor(SensorType.Accelerometer) != null;

static AccelerometerListener listener;
static Sensor accelerometer;

internal static void PlatformStart(SensorSpeed sensorSpeed)
{
var delay = SensorDelay.Normal;
switch (sensorSpeed)
{
case SensorSpeed.Normal:
delay = SensorDelay.Normal;
break;
case SensorSpeed.Fastest:
delay = SensorDelay.Fastest;
break;
case SensorSpeed.Game:
delay = SensorDelay.Game;
break;
case SensorSpeed.Ui:
delay = SensorDelay.Ui;
break;
}

listener = new AccelerometerListener();
accelerometer = Platform.SensorManager.GetDefaultSensor(SensorType.Accelerometer);
Platform.SensorManager.RegisterListener(listener, accelerometer, delay);
}

internal static void PlatformStop()
{
if (listener == null || accelerometer == null)
{
return;
}

Platform.SensorManager.UnregisterListener(listener, accelerometer);
listener.Dispose();
listener = null;
}
}

internal class AccelerometerListener : Java.Lang.Object, ISensorEventListener
{
public AccelerometerListener()
{
}

public void OnAccuracyChanged(Sensor sensor, [GeneratedEnum] SensorStatus accuracy)
{
}

public void OnSensorChanged(SensorEvent e)
{
var data = new AccelerometerData(e.Values[0], e.Values[1], e.Values[2]);
Accelerometer.OnChanged(data);
}
}
}
53 changes: 53 additions & 0 deletions Caboodle/Accelerometer/Accelerometer.ios.cs
@@ -0,0 +1,53 @@
using System;
using CoreMotion;
using Foundation;

namespace Microsoft.Caboodle
{
public static partial class Accelerometer
{ // Timing intervales to match android sensor speeds in seconds
// https://stackoverflow.com/questions/10044158/android-sensors
internal const double FastestInterval = .02;
internal const double GameInterval = .04;
internal const double UiInterval = .08;
internal const double NormalInterval = .225;

internal static bool IsSupported =>
Platform.MotionManager?.AccelerometerAvailable ?? false;

internal static void PlatformStart(SensorSpeed sensorSpeed)
{
var manager = Platform.MotionManager;
switch (sensorSpeed)
{
case SensorSpeed.Fastest:
manager.AccelerometerUpdateInterval = FastestInterval;
break;
case SensorSpeed.Game:
manager.AccelerometerUpdateInterval = GameInterval;
break;
case SensorSpeed.Normal:
manager.AccelerometerUpdateInterval = NormalInterval;
break;
case SensorSpeed.Ui:
manager.AccelerometerUpdateInterval = UiInterval;
break;
}

manager.StartAccelerometerUpdates(NSOperationQueue.CurrentQueue, DataUpdated);
}

static void DataUpdated(CMAccelerometerData data, NSError error)
{
if (data == null)
return;

var field = data.Acceleration;
var accelData = new AccelerometerData(field.X, field.Y, field.Z);
OnChanged(accelData);
}

internal static void PlatformStop() =>
Platform.MotionManager?.StopAccelerometerUpdates();
}
}
14 changes: 14 additions & 0 deletions Caboodle/Accelerometer/Accelerometer.netstandard.cs
@@ -0,0 +1,14 @@
namespace Microsoft.Caboodle
{
public static partial class Accelerometer
{
internal static bool IsSupported =>
throw new NotImplementedInReferenceAssemblyException();

internal static void PlatformStart(SensorSpeed sensorSpeed) =>
throw new NotImplementedInReferenceAssemblyException();

internal static void PlatformStop() =>
throw new NotImplementedInReferenceAssemblyException();
}
}
102 changes: 102 additions & 0 deletions Caboodle/Accelerometer/Accelerometer.shared.cs
@@ -0,0 +1,102 @@
using System;

namespace Microsoft.Caboodle
{
public static partial class Accelerometer
{
public static event AccelerometerChangedEventHandler ReadingChanged;

public static bool IsMonitoring { get; private set; }

public static void Start(SensorSpeed sensorSpeed)
{
if (!IsSupported)
{
throw new FeatureNotSupportedException();
}

if (IsMonitoring)
{
return;
}

IsMonitoring = true;

UseSyncContext = sensorSpeed == SensorSpeed.Normal || sensorSpeed == SensorSpeed.Ui;
try
{
PlatformStart(sensorSpeed);
}
catch
{
IsMonitoring = false;
throw;
}
}

public static void Stop()
{
if (!IsMonitoring)
{
return;
}

IsMonitoring = false;

try
{
PlatformStop();
}
catch
{
IsMonitoring = true;
throw;
}
}

internal static bool UseSyncContext { get; set; }

internal static void OnChanged(AccelerometerData reading)
=> OnChanged(new AccelerometerChangedEventArgs(reading));

internal static void OnChanged(AccelerometerChangedEventArgs e)
{
if (ReadingChanged == null)
return;

if (UseSyncContext)
{
Platform.BeginInvokeOnMainThread(() => ReadingChanged?.Invoke(e));
}
else
{
ReadingChanged?.Invoke(e);
}
}
}

public delegate void AccelerometerChangedEventHandler(AccelerometerChangedEventArgs e);

public class AccelerometerChangedEventArgs : EventArgs
{
internal AccelerometerChangedEventArgs(AccelerometerData reading) => Reading = reading;

public AccelerometerData Reading { get; }
}

public struct AccelerometerData
{
internal AccelerometerData(double x, double y, double z)
{
AccelerometerX = x;
AccelerometerY = y;
AccelerometerZ = z;
}

public double AccelerometerX { get; }

public double AccelerometerY { get; }

public double AccelerometerZ { get; }
}
}
55 changes: 55 additions & 0 deletions Caboodle/Accelerometer/Accelerometer.uwp.cs
@@ -0,0 +1,55 @@
using Windows.Devices.Sensors;
using WindowsAccelerometer = Windows.Devices.Sensors.Accelerometer;

namespace Microsoft.Caboodle
{
public static partial class Accelerometer
{
// Magic numbers from https://docs.microsoft.com/en-us/uwp/api/windows.devices.sensors.compass.reportinterval#Windows_Devices_Sensors_Compass_ReportInterval
internal const uint FastestInterval = 8;
internal const uint GameInterval = 22;
internal const uint NormalInterval = 33;

static WindowsAccelerometer sensor;

internal static WindowsAccelerometer DefaultSensor =>
WindowsAccelerometer.GetDefault();

internal static bool IsSupported =>
DefaultSensor != null;

internal static void PlatformStart(SensorSpeed sensorSpeed)
{
sensor = DefaultSensor;
var interval = NormalInterval;
switch (sensorSpeed)
{
case SensorSpeed.Fastest:
interval = FastestInterval;
break;
case SensorSpeed.Game:
interval = GameInterval;
break;
}

sensor.ReportInterval = sensor.MinimumReportInterval >= interval ? sensor.MinimumReportInterval : interval;

sensor.ReadingChanged += DataUpdated;
}

static void DataUpdated(object sender, AccelerometerReadingChangedEventArgs e)
{
var reading = e.Reading;
var data = new AccelerometerData(reading.AccelerationX, reading.AccelerationY, reading.AccelerationZ);
OnChanged(data);
}

internal static void PlatformStop()
{
if (sensor == null)
return;

sensor.ReadingChanged -= DataUpdated;
}
}
}

0 comments on commit 267edd1

Please sign in to comment.