Saturday, February 12, 2011

The skewed accelerometer

What can you do if your WP7 accelerometer is not correctly calibrated? As I found out, there seems to be no OS mechanism to calibrate the accelerometer. So if you have a production device you will have to return it for repair or replacement. But what do you do if you have a loaner preproduction device like I do? Well, you have to fix the accelerometer the only way you can: through software.

A correctly calibrated accelerometer should return the vector (0, 0, -1) when you place your device with the screen up on a flat surface (like your office floor). If you have a skewed accelerometer like I do, then you should be able to compensate the difference to the reference vector, once you know what your device is reporting when placed on the floor with the screen up.

To do just that, I wrote a very simple Silverlight application for WP7 that polls the accelerometer every second and displays the vector components, both the sampled value and an average value in order to smooth out the natural variations in the sampling.

You can get the sample application with source code here. After running this app on the floor, I noticed that the Y component of the accelerometer vector was slightly skewed to -0.15, while all other components reported the expected values. After adding 0.15 to the Y component, I got back a calibrated accelerometer.

Now I can get back to my XNA gravity ball code and make it work like it should.

Friday, February 11, 2011

MediaElement and downloaded sound

In a nutshell: when you download a sound file to play on a MediaElement, make sure you set the AutoPlay property to True if you call SetSource with the download stream. This stream does not support seeks, so it must play immediately. If you want to store the sound to play it later, store it in a MemoryStream (it supports seeks). I found this out the hard way...

Tuesday, February 8, 2011

The Tabukiniberu bug

This was first presented to me as the "Marshall islands bug": our Silverlight application was opening the Google map in the middle of the Pacific, where it should be showing the mid-Atlantic. Looking closely to the map, I found this extraordinary name: Tabukiniberu, an island in the Pacific near the Equator (the Marshall islands are farther to the north). So what on Earth (literally) was my code doing to spin half the globe and show this (hopefully) beautiful island?

The SL4 app I've been working for in the last 6 months uses both Bing and Google maps AJAX controls due to the different quality of the map content. Google maps are way better on some regions where our business is growing, like Africa and Eastern Europe. On the code side, Bing seems to be better.

When there is nothing to display on the map, the application usually centers on point (0, 0) with a zoom level of 2, providing the "classic" (for my European patterns) wold view with Europe on the top right and the Americas to the left. When there is something to display, the code pans and zooms in to the area of interest so that all pushpins are shown. This particular bug occurs in Google maps code when you call fitBounds on an empty LatLngBounds variable (I mean, initialized but without any added bounds). Instead of showing the mid-Atlantic, you are thrown on a 180 degree spin around the globe to a place very close to Tabukiniberu. Well, at least on the map...

Monday, January 31, 2011

Back to mobile!

I'm back to writing mobile code. After seeing renewed my MVP status, I was able to get my hands on a loaner developer Windows Phone 7 device. I have to personally thank Cristina Gonzales Herrero from Microsoft Spain for both  (MVP and the phone).

Having a WP7 phone on my hands was just the excuse I needed to get back to mobile development. The first hurdle I had to overcome was the App Hub registration. My first attempt was to use my own company account to enroll but the registration process requires two people from the company. So what happens to companies of one like mine? Well, I turned back and took the individual path only to be greeted by payment form (yes, you have to pay USD $99 to deploy to the phone), that assumed I was a US citizen. After talking to some friends, I understood that the only way to go about this was to create a live.com.pt (I live in Portugal) account, but, before I could do that, I had to change my language preferences on IE. You see, I just cannot use a development machine with a Portuguese OS. The keyboard is OK, but not the OS "translations". I started developing on computers that could hardly "speak Portuguese" so I got used to the English language for development, and this also includes the language preferences on the browser. Incidentally, if you don't have Portuguese as your first browser language selection, you cannot create a live.com.pt account. Sigh! This should me much easier, Microsoft!

After going through all these hurdles, I managed to get my developer account and wrote my first mini-app: an XNA application that explores the device's accelerometer. It merely uses the accelerometer information to move a ball around the screen as you tilt the device (something that you have already seen). The accelerometer input in not treated in any special way so the movement you get is sluggish, just as if the ball is moving through molasses...

You can get the first sample from here.

The next version of the code will include more realistic ball movement, so there is some kinetics a dynamics stuff to review.

To help me in writing this tiny piece of code, I read the following articles:


Wednesday, January 19, 2011

Persisting Silverlight DataGrid sort descriptions

I came across this issue recently: how to persist Silverlight DataGrid sort descriptions between changes of the ItemsSource property? The DataGrid on version 4.0 does not expose a direct interface to manipulate the column sort descriptions although it somehow manages them internally. If you want to explicitly manage the column sort descriptions, you must use a PagedCollectionView container, through the SortDescriptions collection.

If you feed a PagedCollectionView to a DataGrid and then let the user change the column sort descriptions through the user interface (by clicking the column headers), the SortDescriptions collection will store the user changes. After realizing this, I devised a very simple strategy to persist the column sort descriptions whenever I needed to refresh the DataGrid:

  • If the data source is a PagedViewCollection, I just make a copy of the old SortDescriptions collection and apply it to the new PagedViewCollection object.
  • If the data source is just an IEnumerable, I create a new PagedViewCollection to wrap it and do the same as the previous point.

To make my life a bit easier, I created a very simple helper class with two private members:


private DataGrid dataGrid = null;
private PagedCollectionView pagedView = null;

The constructor simply stores the target DataGrid object reference:

public PersistDataGridSort(DataGrid dataGrid)
{
    this.dataGrid = dataGrid;
}

The bulk of the work is done in the SetItemsSource function that takes an object as parameter. This must be either an IEnumerable or a PagedCollectionView:

public void SetItemsSource(object collection)
{
    SortDescriptionCollection sortDescriptions = null;

    if(collection is PagedCollectionView)
    {
        if(pagedView != null)
        {
            sortDescriptions = new SortDescriptionCollection();

            foreach(SortDescription sortDescr in pagedView.SortDescriptions)
            {
                sortDescriptions.Add(sortDescr);
            }
        }
        pagedView = collection as PagedCollectionView;
        pagedView.SortDescriptions.Clear();
    }
    else if(collection is IEnumerable)
    {
        IEnumerable enumerable = collection as IEnumerable;

        if(pagedView != null)
            sortDescriptions = pagedView.SortDescriptions;
        pagedView = new PagedCollectionView(enumerable);
    }
    else
        throw new ArgumentException("Collection must be either a PagedCollectionView or an IEnumerable.");

    // Set the data grid's source
    dataGrid.ItemsSource = pagedView;

    // Reinstate the old sort descriptions
    if(sortDescriptions != null)
    {
        foreach(SortDescription sortDescr in sortDescriptions)
            pagedView.SortDescriptions.Add(sortDescr);
    }
}

To use this class, just create an instance using the target DataGrid and set its data source by calling this class' SetItemsSource method. Have fun!