How to: Use Reactive Extensions to Emulate and Filter Accelerometer Data for Windows Phone

1/28/2011

This topic illustrates the use of Reactive Extensions for .NET Framework to emulate and filter accelerometer data.

The Windows Phone accelerometer enables developers to create games and applications with a fun and intuitive user input mechanism. However, creating accelerometer-based applications is difficult for developers that do not have access to a physical device because the Windows Phone emulator that ships with Windows Phone Developer Tools does not have built=in accelerometer emulation. Fortunately the Reactive Extensions framework enables you to add emulation functionality to your application and switch easily from live sensor data to emulated data while you are developing.

It is recommended that you read the topic How to: Get Data From the Accelerometer for Windows Phone for an overview of creating accelerometer-based applications, as this topic will build on that content. Also, How to: Use Reactive Extensions to Emulate and Filter Location Data for Windows Phone illustrates many of the same concepts as this topic. Finally, for overview information on Reactive Extensions, see Reactive Extensions for .NET Overview for Windows Phone.

To Create the Accelerometer Emulator Application

  1. Open a new or existing Windows Phone solution in Visual Studio

  2. From the Project menu in Visual Studio, select Add Reference…. On the .NET tab, select the component name “Microsoft.Devices.Sensors”. Repeat this step again for the Reactive Extensions assemblies, “Microsoft.Phone.Reactive” and “System.Observable”. This application will represent the data for the x, y, and z axes as a three dimensional vector using the Vector3 class in the “Microsoft.Xna.Framework” assembly, so add a reference to this assembly as well.

  3. Add a using directive for the namespace containing the Location Service API and the Microsoft.Xna.Framework namespace. You also need to add a using directive for the System.Threading namespace because the emulated data will be generated on a separate thread.

    using Microsoft.Devices.Sensors;
    using Microsoft.Xna.Framework;
    using System.Threading;
    
    
  4. Add a variable of type Accelerometer inside the MainPage class definition. Also add a Boolean variable useEmulation that will be used to switch between live and emulated location data. You could switch between the two data sources at runtime, but for simplicity, this application will require you to choose the source before compiling.

    public partial class MainPage : PhoneApplicationPage
    {
            Accelerometer accelerometer;
            bool useEmulation = false;
    
  5. In this example, the application uses a button to start the acquisition of either live or emulated data from the accelerometer. The following code shows the click handler for this button. If live data is being used, create an Observable sequence from the accelerometer event stream by using the FromEvent<TEventArgs>(Action<EventHandler<TEventArgs>>, Action<EventHandler<TEventArgs>>) method of the Observable class. The returned object is an Observable sequence of AccelerometerReadingEventArgs objects. The application could subscribe to this sequence as it is, but because there is no public constructor for this class, the emulator part of the application will not be able to create these objects. Instead, represent the data as Vector3 objects. Obtain a sequence of Vector3 objects from the sequence of AccelerometerReadingEventArgs objects by using the from and select operations.

    If the application has been compiled to use emulation, start a new thread on which the data emulation will run.

    private void startButton_Click(object sender, RoutedEventArgs e)
    {
        if (!useEmulation)
        {
            // If emulation is not being used, instantiate the AccelerometerSensor
            accelerometer = new Accelerometer();
    
            // Obtain an Observable sequence using the FromEvent method
            IObservable<IEvent<AccelerometerReadingEventArgs>> accelerometerReadingAsObservable =
                Observable.FromEvent<AccelerometerReadingEventArgs>(
                ev => accelerometer.ReadingChanged += ev,
                ev => accelerometer.ReadingChanged -= ev);
    
            // Use from and select to create a stream of Vector3 from AccelerometerReadingEventArgs stream 
            var vector3FromAccelerometerEventArgs = from args in accelerometerReadingAsObservable
            select new Vector3((float)args.EventArgs.X, (float)args.EventArgs.Y, (float)args.EventArgs.Z);
    
    
            // Subscribe to the Observable sequence of Vector3 objects
            // InvokeAccelerometerReadingChanged will be called whenever a new Vector3 is available in the stream
            vector3FromAccelerometerEventArgs.Subscribe(args => InvokeAccelerometerReadingChanged(args));
    
            // Start the accelerometer
            try
            {
                accelerometer.Start();
            }
            catch (AccelerometerFailedException ex)
            {
                readingTextBlock.Text = "error starting accelerometer";
            }
            accelerometer.Start();
        }
        else
        {
           // Otherwise, start a new thread for emulation
           Thread t = new Thread(StartAccelerometerEmulation);
           t.Start();
        }
    }
    
    
  6. The StartAccelerometerEmulation method of the sample application is called on a new thread from the Start button click handler when emulation is turned on. In this method, the application’s EmulateAccelerometerReading method is called. The return value of this method is an IEnumerable sequence of Vector3 objects which can be converted to an Observable sequence using the ToObservable method. Once the Observable sequence is obtained, subscribe to it using exactly like you did for the live accelerometer data stream.

    private void StartAccelerometerEmulation()
    {
        // Use ToObservable to convert the IEnumerable sequence returned 
        // from EmulateAccelerometerReading to an IObservable sequence
        var emulationDataAsObservable = EmulateAccelerometerReading().ToObservable();
    
        // Use the subscribe method to subscribe. The InvokeAccelerometerReadingChanged
        // handler will be called whenever new Vector3 objects are produced by the emulator
        emulationDataAsObservable.Subscribe(args => InvokeAccelerometerReadingChanged(args));
                
    }
    
    
  7. Next, implement EmulateAccelerometerReading method. This method returns an IEnumerable sequence of Vector3 objects representing the emulated data. This method loops infinitely, generating a new Vector3 object and then sleeping for a moment for each iteration. In this example, the generated data just cycles between -1 and 1 for each axis. At random intervals, a random vector is generated to simulate a sudden forceful move of the device.

    static IEnumerable<Vector3> EmulateAccelerometerReading()
    {
        // Create a random number generator
        Random random = new Random();
    
        // Loop indefinitely
        for (double theta = 0; ; theta+=.1 )
        {
            // Generate a Vector3 in which the values of each axes slowly drift between -1 and 1 and
            // then normalize the vector
            Vector3 reading = new Vector3((float)Math.Sin(theta), (float)Math.Cos(theta * 1.1), (float)Math.Sin(theta * .7));
            reading.Normalize();
                    
            // At random intervals, generate a random spike in the data
            if (random.NextDouble() > .95)
            {
                reading = new Vector3((float)(random.NextDouble() * 3.0 - 1.5),
                 (float)(random.NextDouble() * 3.0 - 1.5),
                 (float)(random.NextDouble() * 3.0 - 1.5));
    
            }
    
            // return the vector and then sleep
            yield return reading;
            Thread.Sleep(100);
        }
    }
    
    
  8. All that is left to do is create the event handler that will be called whenever a new Vector3 object is available from the accelerometer or the emulator. The event handler will use the BeginInvoke method to call another handler on the page’s thread.

    void InvokeAccelerometerReadingChanged(Vector3 data)
    {
        // Call an event handler on the page's thread
        Deployment.Current.Dispatcher.BeginInvoke(() => AccelerometerReadingChanged(data));
    }
    void AccelerometerReadingChanged(Vector3 data)
    {
        // Output the data to a TextBlock on the screen
        readingTextBlock.Text = "x: " + data.X.ToString("0.00") + " y: " + data.Y.ToString("0.00") + " z: " + data.Z.ToString("0.00");
    }
    
    

The application is now complete. By toggling the value of the useEmulation parameter, you can switch between using live accelerometer data and emulated data.

The sample application above converts live accelerometer data and emulated data to Observable sequences, which the application can subscribe to and handle with event handlers. One of the advantages of using Reactive Extensions is that there are many built in operations for filtering and manipulating Observable sequences.

In the click handler for the Start button, the following three lines of code were used to obtain the sequence and subscribe to it.

IObservable<IEvent<AccelerometerReadingAsyncEventArgs>> accelerometerReadingAsObservable =
            Observable.FromEvent<AccelerometerReadingAsyncEventArgs>(
            ev => accelerometer.ReadingChanged += ev,
            ev => accelerometer.ReadingChanged -= ev);

        var vector3FromAccelerometerEventArgs = from args in accelerometerReadingAsObservable
        select new Vector3((float)args.EventArgs.Value.Value.X, (float)args.EventArgs.Value.Value.Y, (float)args.EventArgs.Value.Value.Z);

        vector3FromAccelerometerEventArgs.Subscribe(args => InvokeAccelerometerReadingChanged(args));

With an additional line of code, you can scan the data stream to compare each data value to the previous one, converting the stream from a list of vectors describing the orientation of the phone to a list of vectors describing the difference, or delta, between the phone’s current orientation and its previously measured orientation. Adding another line allows you to select only the values for which the delta is greater than a specified value. This allows you to only receive events when a sudden, forceful movement of the phone is made.

    // Use the Scan method to take compare each element to the previous one
    var deltas = vector3FromAccelerometerEventArgs.Scan(new { last = new Vector3(), delta = new Vector3() },(state, current) =>
      {
        var last = current;
        var delta = new Vector3(current.X - state.last.X, current.Y - state.last.Y, current.Z - state.last.Z);
                                    return new { last, delta };
      }
    );

    // Use from, where, and select, to choose only deltas greater than a specified value
    var largeDeltas = from d in deltas
      where d.delta.Length() > 1.0
      select d.delta;

    // Subscribe to the new Observable sequence
    largeDeltas.Subscribe(args => InvokeAccelerometerReadingChanged(args));

Show: