Using Reactive extensions for feeding test sensor-readings in Windows Phone

An interesting technology you might have heard of is Reactive Extension (MSDN Rx Site). It’s available for different types of applications, although  you may need to add the necessary assemblies through NuGet. But in WP7.5 it’s included in the SDK. The only thing you need are references to Microsoft.Phone.Reactive and System.Observable.  Let’s start with a very simple scenario : simulating the PositionChanged-event of the GeoCoordinateWatcher. I know that the Mango-emulator allows me to simulate Geographical position, but it’s a nice starting point.

My application consists out of a very simple Page which shows a Bing mapcontrol. I set the Zoomlevel to 10, and we’re set to go. The code is fairly simple :

   1:  private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
   2:  {
   3:      GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();
   4:      watcher.MovementThreshold = 1;
   5:      watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
   6:      
   7:      watcher.Start();
   8:  }
   9:   
  10:  void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
  11:  {
  12:      map1.Dispatcher.BeginInvoke(()=> map1.Center = e.Position.Location);
  13:  }

 

Now let’s try to simulate some changes in our position: Let’s travel from South England to France, passing Dover and Calais. Let’s start by creating a simple function that gives me back an GeoPositionEventArgs every 50 ms :

   1:  #if DEBUG
   2:  static double l = 0;
   3:   
   4:  private static IEnumerable<GeoPositionChangedEventArgs<GeoCoordinate>> GetLocations() 
   5:  {
   6:      while (true)
   7:      {
   8:          System.Threading.Thread.Sleep(50);
   9:          l += .001;
  10:   
  11:          yield return new GeoPositionChangedEventArgs<GeoCoordinate>
  12:              (new GeoPosition<GeoCoordinate>(DateTimeOffset.Now, new GeoCoordinate(51, l)));
  13:      }
  14:  }
  15:  #endif

Now let’s create a Thread that calls this functions, and uses Rx for feeding the eventargs to the eventhandler :

   1:  private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
   2:  {
   3:      GeoCoordinateWatcher watcher = new GeoCoordinateWatcher();
   4:      watcher.MovementThreshold = 1;
   5:      watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
   6:   
   7:      #if DEBUG
   8:      System.Threading.Thread thr = new System.Threading.Thread(() =>
   9:      {
  10:          var test = GetLocations().ToObservable();
  11:          test.Subscribe(ea => watcher_PositionChanged(null, ea));
  12:      });
  13:   
  14:      thr.Start();
  15:      #endif
  16:      
  17:      watcher.Start();
  18:  }

 

The ToObservable-function turns my IEnumerable<T> into an IObservable<T>. The Subscribe-function binds it with the eventhandler. Run the app (in debug-mode, of course), and you’ll see the map slowly moving East passing from England to France.

image

Now let’s try to use the same concept with the Accelerometer. I create a simple page with three TextBlock-controls for displaying the X,Y and Z-components of my accelaration. In here we’ll have to do similar stuff with SensorReadingEventArgs<AccelerometerReading>. In AccelerometerReading I need to set the Acceleration-property which is of type Vector3 (with X,Y,Z). The problem that arrises here is that the setter of Accelartion is declared as internal, so I cannot use this from my code. The solution can be found at Microsoft Patterns and Practices site, which has some recipes for creating alternatives for the sensor-classes (here). Instead of the AccelerometerReading-class I can use AccelerometerSensorReading-class and the SettableAccelerometerSensorReading-class. The name of the latter already describes it : it will allow me to set the Acceleration-property. Let’s have a look at the result :

   1:  private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
   2:  {
   3:      AccelerometerAdapter acc = new AccelerometerAdapter();
   4:      acc.TimeBetweenUpdates = TimeSpan.FromSeconds(1);
   5:   
   6:      acc.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerSensorReading>>(acc_CurrentValueChanged)
   7:      acc.Start();
   8:   
   9:  #if DEBUG
  10:      System.Threading.Thread thr = new System.Threading.Thread(() => 
  11:      {
  12:          var test = GetReadings().ToObservable();
  13:          test.Subscribe(ea => acc_CurrentValueChanged(null, ea));
  14:          });
  15:   
  16:      thr.Start();
  17:  #endif
  18:  }
  19:   
  20:  void acc_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerSensorReading> e)
  21:  {
  22:      ContentPanel.Dispatcher.BeginInvoke(() => 
  23:      {
  24:          textBlock1.Text = e.SensorReading.Acceleration.X.ToString();
  25:          textBlock2.Text = e.SensorReading.Acceleration.Y.ToString();
  26:          textBlock3.Text = e.SensorReading.Acceleration.Z.ToString();
  27:      });            
  28:  }
  29:   
  30:  #if DEBUG
  31:   
  32:  static float f = 0f;
  33:  public static IEnumerable<SensorReadingEventArgs<AccelerometerSensorReading>> GetReadings()
  34:  {
  35:      while (true)
  36:      {
  37:          System.Threading.Thread.Sleep(500);
  38:          var acc = new SettableAccelerometerSensorReading();
  39:          acc.SetAcceleration(new Vector3(f, 0f, 0f));
  40:          f += .1f;
  41:          if (f > Math.PI)
  42:          {
  43:              f = (float)-Math.PI;
  44:          }
  45:          var reading = new SensorReadingEventArgs<AccelerometerSensorReading>();
  46:   
  47:          yield return new SensorReadingEventArgs<AccelerometerSensorReading>() { SensorReading = acc }; 
  48:      }
  49:  }
  50:  #endif

 

And there you go. Also tried it with the virtual Motion-sensor, unfortunately I got stuck there. I’m missing a SettableAttitudeReading-class, and I’ve been trying to create it since a few days, but I’m close to giving up. I guess there’s a reason for not having it in the patterns-library.




Loading