Mar 9 2014

Astrophotography with Windows Phone Part 1: The Hardware Interface

Category: MobileJoel Ivory Johnson @ 08:49

Warning in this post I'm interfacing my telescope to a custom circuit. While I am totally comfortable doing this myself do so at your own risk. The CGEM mounts sell for well over 1,000 USD. If you try to follow what I did and burn out your telescope's mout you are on your own. 

If you haven't already I encourage you to check out my previous post to get the full context of what this post is about. To summarize I'm making a solution for controlling my telescope from my Windows Phone and this post describes the hardware interface.  The phone is going to communicate with the telescope over Bluetooth. The Bluetooth tranceiver from the post on controlling the car with the phone is being repurposed in this post. I'm constructing this circuit on a breadboard. If you would like to follow along with this project but don't want to assemble a bluetooth/serial adapter yourself a fully assembled circuit can be purchased at online retailers such as Amazon.com for 50-80 USD. I've not used these receivers myself and can't provide any information on changing settings on them.

For those familiar with my telescope you may also be aware of a Wi-Fi adapter sold under the name SkyQ Link. Why am I not using it? The reason is simple, there is no information available on interacting with the SkyQ. It seems to be exclusively used by Celestron applications. 

The telescope uses a serial communications standard known as RS-232 for communicating with a computer. The SkyQ Link, GPS receiver, and other accessories communicate with the telescope's mount through one of it's many serial ports.Not all of these serial ports are the same and use different voltage levels. The 6 conductor RJ-12 jacks tend to use TTL voltage levels. The jack on the bottom of the telescope's hand controller uses RS-232.  The RS-232 standard (Created in the 1960s) defines voltages levels, timing, and some other details. To convert the output from the bluetooth tranceiver to levels that the telescop's serial port expects the MAX 232 chip is being used. Celestron ships a 4 conductor RJ-22 to 9-pin D cable that comes with the telescope for interfacing to a computer. This is the connection that we'll be using. 

The circuit is composed of 3 major components; the Bluetooth transceiver, the RS-232 adapter, and a power regulator. The power regulator is somewhat optional. The other two components will run off of any voltage between 3.3 volts and 5 volts. So 3 battery cells would meet needs. But by using a power supply I'm opening up the possibility of other power supplies. The power regulator (known as a 7805 regulator) produces an output voltage that is at least 1.7 volts lower than the input, but not more than 5 volts. If it receives a 5 volt input the 1.7 volt drop will put the output at 3.3 volts which is within toleration of what it needed. If the input voltage is 6.7 volts or higher the 7805 outputs 5 volts and burns off the extra energy as heat. In general you can put up to 12 volts through the 7805. Higher voltages are possible, but they may require a heat sink or additional cooling to prevent overheating. 

 

Powered by a cell phone charger that I received the last time I met with the Windows Phone team at Microsoft (Thanks Desiree!). Because of the voltage regulator there are many other power source options that you could use such as a 9 volt battery. With the extremely simple hardware construction done we can start the first part of communication with the telescope. For now we are only going to be concerned with retrieving state information from the telescope. 

To make sure the circit works I wrote some throw-away code to communicate with the telescope mount. This code is not going to tell the mount to do anything; there's some math that we need to cover before we start to do that. Instead we will query the telescope's current state. I'll ask it for the time, whether or not it's aligned, and the model number. I've made an enumerated type to represent commands. These commands can be queued up to be executed. I also made a few bindable properties to hold the state information.

public enum Command
{
    Nop,
    GetModel,
    IsAlignmentComplete,
    IsGotoInProgress,
    GetVersion,
    GetYear,
    GetDate,
    GetTime,
    GetLongitude,
    GetLatitude,            
};

Command _currentCommand = Command.Nop;
Queue _commandQueue = new Queue();

DateTimeOffset _mountDate = DateTimeOffset.MinValue;
public DateTimeOffset MountDate
{
    get { return _mountDate; }
    set
    {
        if(value!=_mountDate)
        {
            _mountDate = value;
            RaisePropertyChanged("MountDate");
        }
    }
}

string _mountModel;
public string MountModel
{
    get { return _mountModel; }
    set
    {
        if(_mountModel!=value)
        {
            _mountModel = value;
            RaisePropertyChanged("MountModel");
        }
    }
}

bool _isAlignmentComplete;
public bool IsAlignmentComplete
{
    get { return _isAlignmentComplete; }
    set
    {
        if(_isAlignmentComplete!=value)
        {
            _isAlignmentComplete = value;
            RaisePropertyChanged("IsAlignmentComplete");
        }
    }
}

These commands are dequeued one at a time, sent to the telescope, the response is processed, then on to the next command.

void SendNextCommand()
{

    if(_commandQueue.Count>0)
    {
        _currentCommand= _commandQueue.Dequeue();
        switch (_currentCommand)
        {
            case Command.GetModel:
                Write("m");
                break;
            case Command.GetTime:
                Write("h");
                break;
            case Command.IsAlignmentComplete: Write("J");
                break;
        }
    }
}

The telescope doesn't necessarily send the response back in a single chunk. This means that I will need to accumulate the responses until I have enough to assemble a complete response from the parts received.

async private void WaitForData(StreamSocket socket)
{
    try
    {
        byte[] bytes = new byte[128];
        await socket.InputStream.ReadAsync(bytes.AsBuffer(), (uint)bytes.Length, InputStreamOptions.Partial);
        bytes = bytes.TakeWhile((v, index) => bytes.Skip(index).Any(w => w != 0x00)).ToArray();
        System.Buffer.BlockCopy(
            bytes, 0, _responseBuffer, _responseBufferPosition, 
            Math.Min(bytes.Length, _responseBuffer.Length-_responseBufferPosition)
        );
        _responseBufferPosition += bytes.Length;
                
        //If we wanted to do any preprocessing with the message this is the place to do it. 
        //I've currently got no preprocessing needs, so I'm just passing it on to whoever
        //may be listening. 
        OnMessageReceived(bytes);
        ProcessResponse();
    }
    catch
    {
    }
    finally
    {
        WaitForData(socket);
    }
}

void ProcessResponse()
{
    byte delimiter  = 0x23;//#
    int delimiterPosition = -1;
    for(int i=0;i<_responseBufferPosition;++i)
    {
        if (_responseBuffer[i] == (byte)delimiter)
        {
            delimiterPosition = i;
            break;
        }
    }
    if(delimiterPosition>-1)
    {
        //copy out the message
        byte[] response = new byte[delimiterPosition];
        System.Buffer.BlockCopy(_responseBuffer,0,response,0,delimiterPosition);
        //shift the data in the response buffer
        System.Buffer.BlockCopy(_responseBuffer,delimiterPosition+1,_responseBuffer,0,_responseBuffer.Length -( delimiterPosition+1));
        _responseBufferPosition -= delimiterPosition+1;

        switch(_currentCommand)
        {
            case Command.GetModel: ProcessGetModel(response);break;
            case Command.IsAlignmentComplete: ProcessIsAlignmentComplete(response); break;
            case Command.GetTime: ProcessGetTime(response); break;
            default: break;
        }
        SendNextCommand();
    }
}

A different method is defined to process each type of response that comes back. I originally thought about returning the responses as strings. But many of the responses are not composed of printable characters. So I've left the responses as byte arrays.

void ProcessGetModel(byte[] response)
{
    if(response.Length==1)
    {
        switch(response[0])
        {
            case 1: MountModel = "GPS Series"; break;
            case 3: MountModel = "i-Series"; break;
            case 4: MountModel = "i-Series SE"; break;
            case 5: MountModel = "CGE"; break;
            case 6: MountModel = "Advanced GT"; break;
            case 7: MountModel = "SLT"; break;
            case 9: MountModel = "CPC"; break;
            case 10: MountModel = "GT"; break;
            case 11: MountModel = "4/5 SE"; break;
            case 12: MountModel = "6/8 SE"; break;
            case 14: MountModel = "CGEM?"; break; //Need to find documentation to back this up
        }
    }
}
void ProcessIsAlignmentComplete(byte[] response)
{
    if(response.Length==1)
    {
        IsAlignmentComplete = (response[0] > 0);
    }
    else
    {
        IsAlignmentComplete = false;
    }
}

void ProcessGetTime(byte[] response)
{
    if((response.Length==8))
    {
        int hour = response[0];
        int minutes = response[1];
        int seconds = response[2];
        int month = response[3];
        int day = response[4];
        int year = 2000 + response[5];
        int gmtOffset = (response[6] < 128) ? response[6] : 256 - response[6];
        bool daylightSavings = (response[7] != 0);

        DateTimeOffset dateTime = new DateTimeOffset(
            year, month, day, hour, minutes, seconds,  TimeSpan.FromHours(gmtOffset + (daylightSavings?-1:0))
         );
        MountDate = dateTime;

    }
}

I started a program that queued up and executed commands. The responses were pleasing.  When I first started the program I had forgotten to align the telescope. I was trying to figure out what was wrong before I realized the response was correct. I aligned the telescope and started to get back the expected response. The date returned was also correct. For the telescope mount model I got back an unexpected result. For my telescope the response was the byte value 14. The protocol document tha tI have from Celestron doesn't list a value for 14. But the document that I've got also doesn't mention my model. So it appears the document simply has not been updated to account for my telescope. This is fine and an important learning experience; it's possible that the code could one day run against a telescope mount that hasn't yet been created. 

Now that I've successfully been able to communicate with the telescope mount it's time to talk about the math behind what needs to be done. I'll detail most of the math in my next entry.  If you want to follow along a good place to start is learning about Sidereal time. It's going to be a foundational piece of information for what I plan to do. 

 

 

Tags: , ,

Mar 3 2014

Astrophotography with Windows Phone Part 0 : Introduction

Category: Joel Ivory Johnson @ 09:11

Last week I did a post on using a Windows Phone to control an RC car. I'm applying some of the information in that post to another project. You may have seen one of the astronomy applications that let's some one point their phone at the sky and identify a star or planet with it. I'm making the same type of application with the diference being that the one I'm making will also be controlling a telescope mount. The communication between the phone and the mount will occur over the same Bluetooth Tranceiver that I made iin the post about the car. The first iteration of this project will omit the Arduino. Telescope mounts from Celestron and Meade already contain microcontrollers so I'll start off by communicatin with what is built into the mount. At a later point I will need to reintroduce the Arduino to control other hardware (such as a camera). 

This project a targeting a special audiance; those interested in astronomy that already have a telescope with a computerized mount. Telescopes like this tend to be rather pricy. If you don't already have one I would not suggest picking up a telescope for the sake of experimenting with this project. Some of the less expensive telescops are available for around 500 USD. I'm using the CGEM 800 which at the time of this writing cost about 4 times more than that. For those that already have a computerized telescope if your telescope is another name brand (such as Meade) then you'll need to make adjustments to the code for the protocol that your telescope uses. The telescope that I'm using is on a right ascension mount. If yours is not you will also need to make adjustments to convert right ascension coordinates to altitude-azimuth coordinates. Once again I will be covering the math for this, but I don't have an alt-az telescope for testing. 

In the next post I'll introduce the hardware used to allow the Windows Phone and the Telescope to communicate with each other. A simple program will be introduced that will read and display telescope state; it won't control anything. In the next post after this I discuss the math of what needs to be done. The heavy lifting will all be explained here. After discussing all the math that is needed the third post will present the code in which the phone takes control of the telescope. 

There are also a few things that I won't be covering here. For this program I will only be tracking objects outside of our solar system. The sun and planets will be left out of this program. Unlike the non-solar stars the planets have this habit of wandering around the celestial sphere. They also influence each other's paths (especially Jupiter).  A discussion of the movement of the planets is worthy of it's own series of post. 

Next time: Astrophotography with Windows Phone Part 1: The Hardware Interface

Tags: ,

Feb 12 2014

Controlling an RC car with a Windows Phone

Category: C# | MobileJoel Ivory Johnson @ 02:47

Introduction

RCCarCode.zip (2.36 mb)

alt download site

Earlier in the 2013 year while working with the Emerging Experiences group at Razorfish I had the opportunity to do some work with the Netduino, Arduino, and several other technologies that seem to play a significant role in the Maker Movement. Among other things I had converted an RC car to be controlled over Bluetooth with a Windows Phone. A few months ago someone saw a video of the car and asked me how I did it. The comments section on a video wasn't large enough to contain what I thought to be a sufficient response, so I'm writing this article instead.

Recognizing that there's a spectrum of backgrounds and experience levels that one may have when approaching this I've decided to start over from square one and make some changes along the way. The original RC to Bluetooth conversion was done with a Netduino Plus 2. This time around I will be using the Arduino because it is more ubiquitous and generally less expensive. For this revision of the car I'm staying away from the other improvements that could be added for the sake of simplicity. I'm also keeping my focus narrow and staying on the path to get the car up and running. Right now the goal is to cover what is minimally sufficient to get the car running with an understanding of what is going on. In later writings I plan to make improvements on the UI of the control application and add more capabilities to the car.

Prerequisites

To understand this article you'll need to have an understanding of basic digital electronics and programming. You don't need to know or remember the difference between CMOS and TTL gates to understand whats being written here. I'm staying as basic as I can. You will want to have familiarity with one of the C based languages (C, C++, C#) to understand what is being written within this article. I'm using Windows Phone 8 for this first revision of the article but I may return to add Android and iOS in later revisions.

What is the Arduino

I'm going to borrow from a description of the Arduino that I gave before. The Arduino is a single board computer based on the 32-bit Amtel ARM processor. The development environment for it is free (C++ based) It has the support of a large community of hardware and software developers ranging from professionals to hobbiest. From the software side there’s a number of existing solution components that are included in the development software or are available for download. From the hardware side you’ll find a wide range of hardware additions and accessories that can be added to a solution by plugging them into the Arduino.

While there are several forms that are recognizable as an Arduino there are new variants being released be various companies and individuals all the time. Information on constructing them is readily available and the required parts are easily acquired. Most of the Arduinos available available preconstructed will take on a common form and have a name that indicates it's configuration. To avoid the inundating number a variants in the Arduino I'm going to only consider Arduino boards that are available through The Arduino site at the time of this writing.

Any of these can be called an Arduino

How is one Arduino Different from Another?

Just as PCs are available with different configurations (different amounts of storage, ports, so on) so are Arduinos. The differences tend to be in processor speed, the number of different types of ports, or other peripherials. Here are some of the hardware components that may be on an Arduino. Note that note all Arduinos will have all of these available. You can find a table of some of the available Arduino configurations on the Arduino site.

Input/Output Voltages

Many of the peripherals that you connect to are going to expect (or produce) input and output signals that are either 3.3 volts or 5 volts. Some devices can work either either, some cannot. So you'll want to make sure that the Arduino that you use is mated with peripherals that have agreable signal voltages. Some Arduinos will have a switch that will let you change whether it is operating in the 3.3 volt range or the 5.5 volt range. If you want to interface two devices that operate on different voltages without risking damage additional circuits will be needed.

The Seeeduino Arduino Mega Adk that I'm using runs in 3.3V or 5V mode. But I must remember to ensure the switch is set to the needed voltage.

Processor Clock Rates

The Arduino boards available presently range in speed from 8 MHz up to 84 MHz. 16 MHz is the most typical.

USB Ports

Most of the Arduino boards have a USB port for programming and for getting information back from the Arduino. When connected to your computer it will appear as another Serial port. So you could use any terminal software that uses a serial port for interacting with the Arduino. Some of the Arduinos can emulate other devices such as keyboards or mice. Some Arduinos also have a USB host port that will allow it to interact with other USB peripherals.

Digital Port

Quickly glancing at the Arduinos that I have on my desk right now the number of ports that they have range from a little more than 5 to a little under 50. The digital ports can act as both outputs or inputs. When running in output mode they can programmatically be set to either a high or low voltage. When operating as an input a program can see if something connected to the Arduino is outputting a high or a low voltage. This extremely basic ability is an important building block for other capabilities. Turning on a light or motor or even communicating with other devices can be done with basic operations over a digital port. Some of the digital ports also support interrupts. So you could associate that port with a routine that automatically gets called if the ports status changes.

Analog Port

If you need to set a pin's output to something between high and low then the analog port is what you'll want to use. This can be used to control the speed of a motor instead of turning it on or off, or to control the brightness of a light. When used as an input an analog port can be used to detect the position of a potentiometer or the intensity of light on a sensor .

PWM Port

The PWM ports are able to output square waves. In general the signals outputted on these ports will all have the same frequency. But the pulse width of the wave can be varied. The following two waves have the same frequency but the widths of the waves differ.

The percentage of time that the wave spends in its high state is also called the wave's duty cycle. When a PWM signal has a 0% duty cycle it is always off. When it has a 100% duty cycle it is always on. PWM signals can be used to control the speed of a motor or the brightness of a lights.

Communications Port

While it's possible to communicate with other hardware through careful timing of turning a port on and off you will generally not want to (or need to) do this yourself. The Arduino supports some of the standard communications protocols used by many other circuits and sensors that you may want to interact with. All of these communications ports work serially; they will send or receive a message one bit at a time.

  • Serial Port - Connects a single device to another. Uses two lines for communication, one for sending, another for receiving. Devices that use these ports will have a set speed at which they can send and receive bits (baud rate). Two devices connected with a serial port must agree on the speed at which they will transmit information (baud rate, ranging from 300 baud to 57600 baud) and how a sequesnce is to be terminated (number of stop bits). Devices using serial port communication are able to track time passed so that they can write to or read from the communications lines at the correct rate. Serial ports on an Arduino can either be implemented through hardware or software. Only specific pins can be used for hardware serial ports, but any two pins can be used for the software serial port. However the software serial port cannot send and receive at the same time while the hardware serial ports can.
  • I2C - Connects a device to multiple other devices. Like the serial port the I2C port uses two lines from the Arduino. But unlike the serial port (which connects the arduino to only one other device) the I2C lines can be connected to multiple devices. Each device connect to the I2C ports has an address. When interacting with one of the devices the Arduino must send the address of the device that it wishes to respond and only that device will respond. An I2C address is composed of either 7 or 10 bits. So either 128 or 1024 devices could be connected to an I2C lines. Timing for communication is controlled through a clock line (usually labeled SCL) and data, whether transmitted or received, travels over a single data line (labeled SDA). Unlike the Serial Port (which allows data to travel in both directions at the same time) I2C communication is one direction at a time. But the I2C bus speed on an Arduino is at 100 KHz.
  • SPI - connects one device to multiple othe rdevices. SPI communication uses four lines; one for clock synchronization, one for data from the master device, one for all the devices that may respond, and a device selection line. Instead of transmitting an address the selection line for the device that is to respond must be driven high (each device will have its own line). The speed of the SPI bus is going to depend on the clock speed of the Arduino and a clock divider value (possible values will depend on the Arduino board being used). The default values on most Arduinos will result in a communications rate of 4 MHz.

What peripherals are Available for the Arduino

You can connect Arudinos to a wide range of products even if those products were not designed with the Arduino in mind. Your ability to do that is going to depend on your knowledge level with the Arduino. But what of the products that were made with the Arduino in mind or can be interfaced with litter effort? There are a number of products that can be used by aligning some pins and stacking them on top of the Arduino. Peripherals like this are referred to as "shields."

  • Wi-Fi and Ethernet Shields - host and consume simple web services or control your project over a network ( 1,2 )
  • GPRS Radio - have your circuite send text messages, make and receive calls, or access the Internet on the go (1)
  • Memory Shields - Read from and write to memory cards (a)
  • Bluetooth - One of the many ways that you can communicate with the Arduino wirelessly. One of the best options for devices controled by other mobile devices. (1).

There are other devices that while not made with the Arduino in mind interface to it with little effort because they implement a simple or common interface

  • Bluetooth
  • Servos
  • Relays
  • LCD Displays

Getting Started

Let's start working towards a "hello world". For the Arduino this will mean using it to turn on a light. You'll need the following

  • Computer (PC, Mac, Linux) with a free USB port
  • An Arduino board
  • Light emiting Diode
  • Resistor (about 220 Ohms)
  • Arduino IDE

Software Installation

The software is available for Linux, OS X, and Windows. I'm using the Windowa version which can be installed in one of two ways. The method I would suggest is downloading the "installer" version of the download. Like many other Windows installations the only thing the installer requires you to do is click on a "next" button a few times. The Zip version of the download will let you unzip the IDE to the arbitrary place of your choosing. After the installation has completed connect the Arduino board to your computer with a USB cable. Arduino's usually ship with a program preloaded that blinks a light on the board. Once plugged into the USB you should see the light on the Arduino strt to blink and the drivers should install.

It's possible that you might not be prompted for the drivers. I've connected the Arduino Mega to my computer and it wasn't recognized (unlike the other Arduinos it uses a different hardware component for it's connection). It originally showed up in the device manager as an unrecognized device. The drivers  can be found in the Arduino application folder. 

 

 

Creating a New Program

Start the Arduino IDE. By default the Arduino IDE will open blank and ready for you to start writing code. If you wanted to start another blank project selecting New from the File menu will open a new instance of the IDE. If you are using version 1.5.5 of the IDE (a beta at the time of this writing) then the window will open with two empty functions defined. If not then the window will be completely blank. If it's blank trype the following code.

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

We are going to compile this code and upload it to the board. We'll need to tell the IDE what board that we have and which com port that it's attached to. Under the Tools menu there is a Board submenu that contains the Arduino Board models supported by the IDE. Select the board name that matches yours. You'll need to do the same thing for the port. If you don't know which port your Arduino is using unplug it and access the menu again to see which port name has disappeared from the listing. The one that has been removed is the one that your Arduino board is using.

In the toolbar there is a button that you can use to check to code for syntax errors (a check mark) and a button that can be used to upload the compiled program to the Arduino (an arrow).

Click on the arrow. If all goes well at the bottom of the IDE you will receive a message that says "Upload Successful" and the light should stop blinking. Let's update the program so that we can blink the light ourselves. To do this we need to configure a pin as an output pin. Many times you can use the same pins across different Arduino boards. It's a good practive to define the pins being used in a #define section or as a variable instead of using the literal pin number. If it never needs to be changed then it can be changed in one place. Pin 13 is usually connected to a led. Writing a HIGH value to it will turn the led on. So let's defined the pint that we are using to control the light as 13.

#define LIGHT_PIN 13

Then we write a HIGH or LOW value to it. Initialization steps are done in the setup function. Pin 13 could either be an input or output pin. We need to set it as an output pin using the pinMode function. When the device first starts (or is restart) it first runs the code in the setup() function. It will then run the code in the loop function until the device is turned off. In the loop we want to turn the light on, wait for a second (1000 ms), turn the light off, and then wait for another second.

#define LIGHT_PIN 13

void setup() {
    //set PIN 13 as a pin that we can write to
    pinMode(LIGHT_PIN, OUTPUT); 
}

void loop() {
    digitalWrite(LIGHT_PIN, HIGH);
    delay(1000);
    digitalWrite(LIGHT_PIN, LOW);
    delay(1000);
}

Deploy the application again. After a few seconds the light should start to blink. Congradulations! You've completed your first Arduino program. Before moving on let's make a small change. Instead of just turning it on and off let's adjust the brightness so that it fades in and out. For this we'll need to start adding external circuitry. For this next part you'll need a LED (light emitting diode), a resistor with a value in the range of 300 Ω to 1 KΩ value, and a breadboard. The resistor is for lowering the current that goes through the LED to ensure that we don't burn anything out with too much current. We are going to adjust the brightness of a led by using the analogWrite() function to change the amount of power supplied to the ligh. Pin 13 on many Arduinos doesn't support the analogWrite() function.

You need to look up which pins on the Arduino that you are using can be used for PWM output. Go to the Arduino Products Page and click on the model for the Arduino that matches the one you have. On the page that opens search the page for "Input and Output" and look for the line in that section about PWM. The pin numbers that you can use will be listed there. Below is the information for some of the arduino boards

Board Model Available Pins Resolution
Arduino Uno 3, 5, 6, 9, 10, and 11 8 bit
Due 2-13 8-12 bit
Mega ADK/Mega 2560 2-13, 44-46 8-bit
Yún 3, 5, 6, 9, 10, 11, and 13 8 bit
Nano 3, 5, 6, 9, 10, 11 8 bit
See analogWriteResolution() for information on changing resolution.

About using a Bread Board

If you've used a bread boardbefore you can skip this section. Breadboards allow you to prototype a circuit more quickly and reduce the amount of wires that you would need to construct the same circuite without a breadboard. Underneath the holes in the breadboard are conductors that electrically connect components. The The conductors all run paralle to each other and are usually perpendicular to the length of the breadboard. The exception being the powerlines which run down the length of the board. There's a depression down the center of  the board. The conductors do not cross this depression. Any components that are inserted on the same row (the rows are numbered) that are on the same side of the depression are connected. jumper wires need to be plugged in to connect components that are not on the same row and the same side of the divide.

Closeup of a breadboard. The red outline mars sets of holes that have a common conductor underneath.

Wiring the LED to the Arduino

LEDs will only work if current passes through them in the correct direction. There are two wires on the LED that are of different length. The longer of the two wires must be connected to a positive source while the shorter to a negative. Use a jumper to connect one of the pins labeled GND  to a row of wires on the bread board. Plug the LED into the board so that the shorter wire is in the same row as the GND wire. Connect the resistor into the board so that one of the wires is in the same row as the long wire of the LED and the other end is in an unoccupied row of the breadboard. Using a second jumper wire to connect the other end of the resistor to the pin that you are using for PWM.

Analog Code

Once the circuit is wired we can write the code to control the brightness of the led. When we wrote the previous program it was necessary to use use pinMode to set the pin to output mode before controlling it. It isn't necessary to do this when using analogWrite on a pin. We'll use the PWM port in 8-bit mode (which is much more widely supported than 10-bit mode). The variable containing the value to be written to the pin is going to start at 0, count up to 255, and then back down to zero. Each new value will be sent to the PWM pin. The result is that the LED will grow bright and then dim in cycles.

//Change the following to the pin number that
//matches the one that you are using. 

#define LIGHT_PIN 13

int brightness = 0;
int delta = 1;


void setup() {
}

void loop() {
    analogWrite(LIGHT_PIN, brightness);    
    brightness+= delta;
    if(brightness == 255)
      delta = -1;
    else if(brightness == 0)
      delta = 1;    
    delay(10);
}

Input and Output with the Serial Port

The serial port can be used to communicate with peripherals connected to the Arduino or to communicate back to the computer. There will be 1 to 4 hardware serial ports on your Arduino board. They will be named Serial, Serial1, Serial2, and Serial3. Each of these ports (if present in Arduino board) will be assigned to specific sets of pins. You can also instantiate software serial ports. Software serial ports can be assigned to any two pins. The hardware and software serial ports appear similar in capabilities. But the software serial port can only send or receive at any given point in time but can't do both simultaniously. The hardware serial serial port can do both at the same time.

The port Serial is connected to the computer. Communication between your computer and the Arduino occurs through this port when your programs are uploaded to the Arduino. You can also make your own applications on a computer that interact with a connected Arduino by opening the appropriate port. The computer and the Arduino will need to agree on a baud rate (data transfer speed).

Method Description
int available() Gets the number of bytes available for reading
void begin(baudRate) Sets the baud rate for the serial port and opens it for reading and writing.
void print(value) closes the port and disables the pins for communication.
void print(value) Writes a string to the port. Numbers and floats printed to the port will be converted to strings first.
char read() reads a byte from the serial port
readBytes(buffer, length) Reads characters from the serial port into a buffer
void write(byte) Writes a byte value to the serial port

The Arduino IDE has an interface called the Serial Monitor (can be opened by pressing CTRL+Shift+M or from the menu path Tools->Serial Monitor) that can be used to see the values coming back from the serial port or being written to it. For a simple demonstration we'll have the arduino count from one to one hundred and output it to the serial port.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  for(int i=0;i<=100;++i)
  {
    Serial.println(i);    
    delay(100);
  }
}

After you upload the program open the serial port monitor. You'll see the stream of numbers coming back.  Here I want to point out that there's a difference in behavior in how the Arduino Yun, Micro  and Arduingo Leonardo behave when compared to the other Arduinos. Many of the other Arduinos use a chip that is separate from the main processor for handling communication between the computer and the Arduino's processor. When you open the Serial Port Monitor on these boards the device will reset. So you'll be able to catch all of the information that the devices outputs from begining to end. On the Leonardo, Micro and the Yun the serial communication is not being handled by a separate chip. The Amtega processor in these devices have a built in USB interface that presents itself as a serial port and can also present itself as a mouse, mouse, and serial port. While the serial connection can remain open on the other devices when they are reset if the processor is reset on the Yun, Mico, or Leonardo the virtual serial port would disappear while it restarts and the computer's connection to it will be lost. So these devices do not reset when you open a serial connection from your computer and the first few numbers that are written will not show up in the serial monitor. You could have the processor wait until the serial port is open before it starts writing numbers with a slight modification to the program. The statement if(Serial) wrapped around the code we just wrote to prevent it from executing until the serial port is open.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  if(Serial)
  {
    for(int i=0;i<=100;++i)
    {
      Serial.println(i);  
    delay(200);  
    }
  }
}

All the programs we have done until now have been non-interactive. We are about to do our first interactive program. In this one the Arduino will listen for text to come in over the serial port and will turn the light on or off in response to it. The program will also send a small bit of feedback on what it's doing. In the updated program the delta variable is going to default to zero. So the light's brightness will not start changing as soon as the program starts up. Instead when this program received the character '1' on the Serial object it will change delta to one so that the brightness will grow until it reaches its maximum value (then delta is reset back to 0 to prevent the brightness level from changing). When the program receives the value '0' it will set delta to -1 until the brightness variable reaches its minimum value. All other characters received through the Serial object will be ignored. When the program does receive a value that it's processing it will print back a message that will show up on the computer. Here is the source code to the updated program.

#define LIGHT_PIN 13

int brightness = 0;
int delta = 0;


void setup() {
  Serial.begin(9600);
}

void loop() {
    if(Serial.available())
    {
      char c = Serial.read();
      if((c == '1')&&(brightness!=255))
      {
        delta = 1;
        Serial.println("Turning Light On");
      }
      else if((c=='0')&&(brightness!=0))
      {
        delta = -1;
        Serial.println("Turning Light Off");
      }
    }
    brightness+= delta;
    analogWrite(LIGHT_PIN, brightness);    

    if(brightness == 255)
      delta = 0;
    else if(brightness == 0)
      delta = 0;    
    delay(10);
}

Run the program and open the Serial Port Monitor. From the monitor type the value 1 and press enter. You'll receive a confirmation back and will see the light's brightness begin to grow. Send it a 0 and you'll see the light's brightness diminish until it's extinguished.

Servos

There is only one more concept to cover before we are ready to interface to the car. And that is the concept of the servo. Servo is short for servomechanism. The term can refer to any device that uses negative feedback to correct its performance in achieving its built in function. Most of the time when some one mentions a servo in the context of an Arduino the person is refering to a servomotor. Servomotors receive a signal that coresponds to a desired position. The servo will rotate its shaft until the desired position is reached and will hold its position. RC (Remote Control) servos receive the position signal as a PWM signal. While you could directly send the PWM value for a desired position using analogWrite it is usually easier to use the <Servo.h> library instead. It will take care of the mappings between the angular position that we want and the value that would need to be sent through analogWrite() for that position. Just as analogWrite() will only work on certain ports the <Servo.h> library will only work on certain ports. Also note that with the exception of the the Arduino Mega use of the <Servo.h> library will disable the use of analogWrite() on ports 9 and 10, even if you are not using those ports to control a servo.

Method Description
void attach(pin, [max, min]) Attach the servo variable to a pin. One of the overloaded versions of this method can be used to adjust the minimum and maximum pulse width.
void write(angle) Writes a value to the servo setting the shaft position accordingly.
int read() Returns the last value written to the servo.
void detach() Detaches the servo object from the pin.

RC servos have three wires. Two of the wires will be for power. A red wire must be connected to a positive power source and the black or brown wire must be connected to the ground (GND). The third wire is connected to the PWM source. If you are going to drive several motors you will want to have the motors connected to a power source that can handle enough current. But we can connect a small servo to the Arduino without much concern.

This program will be much like the previous one. Only instead of controlling brightness we will be controlling the position of the motor. Servos generally have a range of 180 degrees. The minimum value is 0, the maximum value is 180, and middle value is 90. The exact range of your servo may vary. Don't try to drive the servo past its physical limit. Doing so can cause the servo to overheat. Confession: I burned out somewhat expensive servo this way when I was first programming with on.

In the previous program I used a delta variable to increment or decrement the brightness value. While you could do this for the Servo it is not necessary unless you need to control the rate of the turn. For light brightness jumping from one value to another will result in what looks like a simultanious change in brightness. The servo moves at a limited speed. Jumping from one value to another will result in in the servo changing position over time. There's no instantanious transition in position. So in this program we will not worry about incrementing the position over time and will just jump to the position that we need. Because there are positions between absolute left and absolute right in which we will have interest this program will respond to values between 0-9 instead of just 0 and 1.

//Change the following to the pin number that
//matches the one that you are using. 
#include <Servo.h>

#define MOTOR_PIN 13

Servo steeringServo;

void setup() {
  Serial.begin(9600);
  steeringServo.attach(MOTOR_PIN);
}

void loop() {
  if(Serial.available())
  {
    char c = Serial.read();
    switch(c)
    {
      case '0': steeringServo.write(0); break;
      case '1': steeringServo.write(20); break;
      case '2': steeringServo.write(40); break;
      case '3': steeringServo.write(60); break;
      case '4': steeringServo.write(80); break;
      case '5': steeringServo.write(100); break;
      case '6': steeringServo.write(120); break;
      case '7': steeringServo.write(140); break;
      case '8': steeringServo.write(160); break;
      case '9': steeringServo.write(180); break;      
      default:
      break;
    }
  }
  else
    delay(100);
}

Connect a servo to your Arduino. The red wire should be connected to the pin labeled 5V and the black/brown wire should be connected to one of the pins labeled GND. The remaining wire is to be connected to the PWM pin that you are using. Run the program and open the serial monitor. As you are sending the values keep an eye on your servo to ensure it's not trying to go past it's physical range. If it does immediately return it back to a position that is within its range.

Controlling the Drive Motor

Something to keep in mind before you start activating the drive motor on your vehicle. Make sure you lift the drive wheels so that they are not touching anything. Last thing you'd want to happen is for the car to take off at full speed while it's still connected to your computer or on a desk. Depending on your car you may be able to do this by turning it upside down. I took some camera rig parts that I had laying around and assembled them to support the vehicle.

RC car supported by camera rig pieces.

The drive motor on the car that I'm uses uses an ESC (Electronic Speed Control) unit. The ESC does a couple of things. It contains a power regulator that's outputting 5 volts from the 7.2 voltage from the battery and it activates the drive motor. The 5 volt output is also routed to the steering servo and the radio receiver/control circuit. We are unplugging the car's receiver and connecting the Arduino in it's place. If we use the same program that we used to control the servo but connect the arduino to the ESC instead of the servo we can use it to control the motor's speed. We are going to wire the two together a little differently this time. The ESC is going to need to be connected to it's own power source (the car's battery). There Arduino would not be able to power the drive motor. For this test you do not need to connect the red wire. If you want to connect the red wire anyway connect it to the VIN pin so that the voltage from the ESC is passed through the Arduino's voltage regulator.

After you are done with the wiring run the program again. This time you should be able to make the car's drive motor go in forward and reverse. If your ESC behaves as mine does if you try to go from forward to reverse without going through the neutral position the ESC will interpret it to mean you want the vehicle to brake. So you may need to select the neutral position before you change directions.

The ESC that I am using is the Dynamite Tazer 15T. Despite documentation that is available on the Internet (PDF) this ESC does not allow any calibration. If you are using another esc calibration may be required so that the esc knows what speeds />directions to map to the range of values that it needs to work with. Since I don't have an ESC that supports calibration I won't be able to address that myself within this document.

The Dynamite Tazer 15T

Bluetooth Communication

There are a number of Bluetooth solutions available that will work with the Arduino. Some are available as shields. I didn't want to use a shield as a solution since it would restrict which pins that I could use for bluetooth communication. I used one that needed to have its pins connected to the arduino individually. Most of these will have 4 connections that you'll care about. Two of them are for power. They will need to be connected to the 5 volt line and the GND. The other two lines are for data to be sent and data received. We are also finally to the point where we are going to make use of the Windows Phone. Before we get started with coding go ahead and connect your power and GND to power it up and pair your phone with it. On the bluetooth device that I'm using the pairing code is 1234. You'll need to check with the documentation for the Bluetooth circuit that you are using to know what code to use.

Picture of the Bluetooth Module that I am using

Picture of the Bluetooth Module that I am using

You'll need to write code that will do a a few things

  1. Get a list of the paired bluetooth devices
  2. Select the paired device to be used for communication
  3. Open streams to the device for listening and sending information

For getting a list of the Bluetooth devices paired with the phone Microsoft has provided the PeerFinder

class. You'll need to add the Proximity permission to your application to use this class. The list of devices paired with the phone can be retrieved with two lines of code.

PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
var pairedDevices = await PeerFinder.FindAllPeersAsync();

For the sake of improving the user's experience you would probably want to give your Bluetooth receiver a distinct name that the application could look for to distinguish it from the other Bluetooth devices. On many of the Bluetooth receivers it's possible to change the name that the device uses. On the one that I have handy I've noticed that the company that produces it seems to have explicitly stricken out the portion of the documentation that explained how to change my adapter's ID (probably appropriate, as the device isn't responding to name change commands). I'm going to stick with the name that seems to be burned into my device, linvor. Check the documentation in your Bluetooth adapter to see what name you need to use.

The pairedDevices collection in the above code will contain PeerInformation instances. Grab the one that represents your device and use it to create a new Socket object. The Socket will be used for reading from and writing to the Bluetooth adapter.

_socket = new StreamSocket();
await _socket.ConnectAsync(_selectedDevice.HostName, "1");

Now we can send and receive messages between the Windows Phone and the car circuit. Read and write operations are asynchronous. Using the InputStream member of the socket we can initiate a read operation. If there is nothing to read the call will [perceivably] wait until there is data available to be read (the nuances of what really goes on while interesting is beyond the scope of this document). Once the bytes are available and read I'm converting them to a string for later processing.

byte[] bytes = new byte[128];
await socket.InputStream.ReadAsync(bytes.AsBuffer(),(uint)bytes.Length, InputStreamOptions.Partial);
bytes = bytes.TakeWhile((v, index) => bytes.Skip(index).Any(w => w != 0x00)).ToArray();
string str = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
OnMessageReceived(str);

For this first iteration of the project the car doesn't need to send back any vital data. It's only going to be listening for instructions for what it should be doing. For now the only instructions to send are of what the state of the throttle and the steering servo should be. I'm sending the commands in strings that look like a REST request.

public void TransmitState()
{
    if ((_communicationController != null)&&(_communicationController.IsConnected))
    {
        _communicationController.Write(String.Format("/car/throttle/{0}\r\n", ThrottleValue));
        _communicationController.Write(String.Format("/car/steering/{0}\r\n", SteeringValue));
    }
}

With that the basics of what need to happen on the Windows Phone side for communicating with the car have been covered. There is still work to on deciding on a user interface in the application. Let's go back to the car to implement the code for responding to the received commands.

Don't Let the Car Get Away!

Since we are approaching a point at which the car is almost prepared to be sat down on the ground to be driven there's something I want to address. You must be prepared to respond to a loss of a connection. If you don't the car could end up continuing to do the last thing that it was doing until it gets a connection again. If the last thing it was doing was going ahead full throttle chances are you won't be able to reestablish a connection without sprinting after the car. There are a couple of solutions that come to mind to handle this. On many of the Bluetooth receivers that I've seen there is an additional line that connects to a led that will change its blinking sequence to indicate whether or not the Bluetooth receiver has a connection. It's possible to connect to this and put the car in a neutral state if it indicates that the Bluetooth connection has dropped. Another method (the one that I will go with) is to keep track of how long it's been since an instruction was received. If more than a certain amount of time passes and no instructions are received stop the car.

The millis() function returns how much time has passed since the Arduino was started in milliseconds. Every time an instruction is received we can save the value returned by this function and use it as a reference for knowing how long it's been since an instruction was received. With each execution of the main loop the the millis() function is called again and compared to the saved value. The difference between the two values is the amount of time passed since the last instruction. Once the difference has become greater than a certain amount we know it's been a while since an instruction was received. The car will be put in a neutral state. If an instruction is received before this condition occurs then the saved value would have been updated so the difference between the saved time value and the current time value would not be great enough to reach the expiration period.

Parsing the Commands

Parsing is easy since all the commands are being sent in what looks like a rest request. Each segment of the path in the REST like request is associated with a method. Each time an instruction is received it is passed to ProcessCommand(string) which will parse out the first segment and use it to decide which method to call next. Right now all of the instructions will start with /car/. The root level method in the processing call stack only checks to see if an instruction starts this way and does nothing if the command is something else.

void ProcessCommand(String command)
{
  int startPosition, endPosition;
  String segment;

  startPosition =  command.indexOf('/', 0);
  endPosition = command.indexOf('/', startPosition+1);
 
  if((startPosition>-1) && (endPosition == -1))
  {
      segment = command.substring(startPosition + 1, endPosition - startPosition+1);
      startPosition = endPosition + 1;
  }
  if(segment == "car")
     ProcessCarCommand(command.substring(endPosition));
}

The ProcessCarCommand() method is similar. It is testing to see if the next part of the instruction is "throttle" or "steering" and called the ProcessThrottle() or ProcessSteering() methods respectively.

void ProcessCarCommand(String command)
{
  int startPosition, endPosition;
  String segment;

  startPosition =  command.indexOf('/', 0);
  endPosition = command.indexOf('/', startPosition+1);

  if((startPosition>-1) && (endPosition>-1))
  {
       segment = command.substring(startPosition + 1, endPosition - startPosition);//.
  }

  if(segment=="throttle")
     ProcessThrottleCommand(command.substring(endPosition));
  else if (segment=="steering")
    ProcessSteeringCommand(command.substring(endPosition));
}

The methods that follow are finally getting to where the rubber meets the road; they are controlling the steering and throttle lines. Their implementation is almost identical. They strip the numeric value off the end of the command (which will be between the values of -1 and 1). The value of 1 for the throttle represents full throttle forward, -1 would be for full reverse, and 0 for neutral. Similarly -1 for the steering is full left, 1 is full right, and 0 is straight ahead. These values are being scaled and shifted before being sent to the Servo object. The continuous value range of -1 to +1 needs to be remapped to the the values 0 to 180. The Arduino function library already contains a function for doing this appropriatenamed map. It takes 5 arguments. The first is the value to be remapped. The next two are the lower and uper range for the value. The fourth and fifth are the range to which the value needs to be mapped. For our specific case the call will look like map(value,-1,1,0,180). The mapped value is then written to the servo object for either the steering or throttle. The code for the two functions follows.

float StringToFloat(String str)
{
  char buf[str.length()];
  str.toCharArray(buf, str.length());
  float retVal = atof(buf);
  return retVal; 
}
void ProcessThrottleCommand(String command)
{
  if(command.length() < 2) return;
                         float newthrottlevalue=StringToFloat(command.substring(1));
                         if ((newthrottlevalue < -1)&&(newThrottleValue > 1))
   return;
   int servoValue =(int) map(newThrottleValue,-1,1,0,180); 
   throttleESC.write( servoValue );
}
void ProcessSteeringCommand(String command)
{
  Serial.println("Processing Steering Command");
  if(command.length() < 2) return;
                         float newsteeringvalue=StringToFloat(command.substring(1));
                         if((newsteeringvalue < -1) && (newSteeringValue > 1))
    return;
  int servoValue = map(newSteeringValue,-1,1,0,180);
  steeringServo.write( servoValue );
}

Control Interface

All the code needed for sending control signals to the car and to have the car interpret and act on them has been presented. I skipped over discussing a user interface for controlling the car though. Some of the other phone controlled systems I've seen use the accelrometer for control. This is a matter of personal preference, but I don't like the accelerometer based approaches. When playing games on my phone the method of control that prefer second is the virtual joystick with a dead zone that is centered on whereever one's finger happen come in contact with the screen. My most preferred method of control is a physical controller.

I made an extremely simple virtual joystick for the phone in the form of a user control. When some one first place their finger down on the control that point becomes the new center point for the dead zone. Sliding ones finger will move the virtual joystick out of the dead zone and be registered as a direction. Lifting one's finger off of the control will set the virtual joystick back to it's neutral position.

Second best isn't bad, but we can do better. I've got a Moga bluetooth game controller that is compatible with Windows Phone. So I've added support for that too. Normally I would have the code organized so that there are a number of logical joysticks (whether physical or virtual) and then some mechanism for deciding which one of the joysticks will be listened to. I'm avoiding that organization in this article for the sake of simplicity. The code handling input from the Moga is in the same class as the virtual joy stick and there's absolutely no resulution implemented for what to do if some one decides to use both the touch screen and the game controller at the same time.

Moga Bluetooth controller.

There's an SDK at Moga's web site that contains an assembly (Moga.Windows.Phone) that simplifies interaction with the Moga controller. There are two modes of interaction with the controller, polling and listening. In polling mode every time the code wants to know the state of the controller it will need to query the controller. In listening mode as the state of the controller changes methods on your code are called through an interface indicating what has changed. Xaml based programs will usually want to use the listening mode while DirectX based programs will use polling. To use listening mode my VirtualJoyStick class implements the IControllerListener interface. More development information on the Moga series of controllers can be found on their site.

IControllerListener methods
Method Description
void OnMotionEvent(MotionEvent e) One of the analog inputs on the controller has changed. This includes both thumb sticks and the the analog triggers.
void OnStateEvent(StateEvent e) Indicates the connection and battery state of the controller.
void OnKeyEvent(KeyEvent e) The pressed state of one of the buttons has changed.

Remember what I said earlier about responding to the potential loss of the connection between the phone and the car? That also applies to the connection between the phone and the game controller. If the connection to the controller drops (battery goes dead, or the user walks away from the phone) the code on the phone needs to be prepared to start transmitting a neutral state. Otherwise the phone will continue to transmit what ever the last known state of the game controller indicated. One of the IControllerListener events is used for this, OnStateEvent(StateEvent e).

public void OnStateEvent(StateEvent e)
{
    if (e.StateKey == ControllerState.Connection)
    {
        if(e.StateValue == ControllerResult.Disconnected)
        {
            SetNeutralState();
        }
    }
}

I've decided to use the left thumb stick for steering and the right thumb stick for the throttle. I'm ignoring the buttons all together.

public void OnMotionEvent(MotionEvent e)
{
    this.Dispatcher.BeginInvoke(() => { 
        if(e.Axis==Axis.Z)
        {
            Y = e.AxisValue;
            OnVirtualJoystickUpdated();
        }
        else if(e.Axis == Axis.X)
        {
            X = e.AxisValue;
            OnVirtualJoystickUpdated();
        }
    });
}

Loose Connections

I've said next to nothing about the physical assembly of the finished product. In part I've done this because it's not my final design. Dependind on the car that you have you may be able to screw the Arduino in place on the vehicle. You are not going to want to use loose wires to connect the servo and ESC to the Arduino though; with just a little bit of strees the wires would come loose. Instead check out your local electronics store or other provider to see what prototype shields are available. Using one of those you may be able to securly hold the connections in place through either soldering or with screws.

What's Next

Writing additional code so that the car can be controlled by an Android phone isn't much effort. The same concepts carry over. When time allows I may update this article or add another one to explain what needs to be done to control the car with an Android phone. Also keep in mind that my personal goal is to make the car more autonomous. This means that the car must be able to know something about it's environment and position. In the next update I plan to walk through the various sensors that will be attached to the car and how to use them. These include GPS, distance sensors, and motion sensors such as an accelerometer and gyrometer/rate sensor. Because of the low distance range on bluetooth radios I'll be using a different radio (Wi-Fi) so that more distance can be put between the phone and the car. I may eventually move up to adding a GSM radio.

Revision History

  • 2014 February 12 - Initial Publication

 

RCCarCode.zip (2.36 mb)

alt download site

Tags: , ,

Sep 14 2012

Sidereal Time Calculator

Category: MobileJoel Ivory Johnson @ 04:12
Download Code (1.15 MB)
   

Introduction

There are a number of applications available on various mobile devices that allow you to aim your device at the sky and identify various heavenly bodies. I find the ability of identifying a body based on a user's location and the device's orientation fascinating. I find it even more fascinating when I see physical hardware acting on this information; the telescope I have uses GPS (to get my location and the current time) and uses this information to automatically move the telescope to the orientation needed to see selected body.

I now have a pretty good understanding of how it works. A significant amount of the calculations involved are based on time and another part is based on coordinate conversion. With the right time conversions you'll have enough information to get the orientation of the stars. (Getting the orientation of the planets and moon requires a little more work, but the stars have no apparent motion with respect to the solar system). I only want to talk about time for now, as there's more than enough information on time to fill an article.

Table of Contents

Terms and Time Units

Time is usually described in terms of some cyclic process or event where the units of time are from counting the cycles/events. This could be from the vibration of a crystal, the passing of some celestial, or some other event. Historically the apparent motion of the sun and moon have been used as the periodic event around which our time system was based. We've all used the units of time that are derived from these events; hours, minutes, seconds, years, and months along with the terms AM, PM, AD, and BC, and degrees. Let's dissect the physical events behind these terms.

Roman Calendar

The Roman calendar is said to have been invented by Romulus (the founder of Rome) around 753 years BC. This calendar had 10 months with the vernal equinox being the first month. The calendar had 304 days plus an additional number of winter days that stretched from December to the following month that were not part of any month on the calendar.

Julian Calendar

The Julian calendar is a modified version of the Roman Calendar. It has 365 days divided into 12 months. Once every 4 years a leap day is added.  It sounds very much like the civil calendar that we use today with the exception we don't have leap days on years divisible by 400 but not a millennium. With enough time solar and seasons events would begin to creep to other parts of the calendar. This was corrected with the Gregorian calendar.

Gregorian Calendar, Astronomical Year, and Julian Dates

The Gregorian calendar (also called the Western Calendar or Civil Calendar) is the name of the calendar that most of the world knows, loves, and uses today. The namesake for the calendar is Pope Gregory XIII. The number of times the earth rotates during its orbit around the sun is 365 times plus some fractional units (approximately 0.2524).   The Julian calendar made an attempt to correct this by introducing a leap year every 4 years. This contribution slowed the rate at which the seasons migrated on the calendar but didn't stop it all together. Pope Gregory XIII's contribution to the calendar was to not have a leap year if the year was divisible by 400 and is a  millennial year. The last day of the Julian calendar was Thursday 4 October 1582. The day that came after this was the first day of the Gregorian calendar; Friday 15 October 1582. The date range of 5-14 October (inclusive) doesn't exist - something that needs to be remembered for time conversions that cross this boundary.

Julian Date

Another commonly used calendar is counting the number of days since noon of 1 January 4713 B.C. This is also called a Julian date. Noon of 1 January 4713 is Julian date 0. Midnight between 1 January 4713 and 2 January 4714 is Julian date 0.5. Note that the time of day is a part of the Julian date as a fractional unit. For more recent dates the number used to express the Julian date is over 2,400,000 million. To avoid dealing with unnecessarily large numbers there's also the Modified Julian Date (MJD) which counts the number of days since midnight 17 November 1858. Note that MJD starts at midnight while JD starts at noon. So the time units in these two date expressions will have a difference of 0.5 for the digits after the decimal point. You may also hear of a Julian Date Number, which is just the integer portion of the Julian Date.  Dates of this form are of special significance to astronomical calculations.

AD, BC, and Astronomical Year

One of the oddities about the system of tracking years is that there is no year zero. The first year of the calendar , based on the reckoned conception of Jesus Christ of the Christian religion, is 1 AD (AD = Anno Domino, Latin for "Year of the lord", also written as "CE" for Common Era). The year immediately proceeding this is 1 BC (BC = Before Christ. Sometimes written as BCE for Before Common Era). When doing astronomical calculations no one wants to deal with the lack of the zero. So there is also the concept of the Astronomical Year. Astronomical years for the most part align with our current system of tracking years. So 1984 AD is also the Astronomical Year 1984. The difference is apparent when you look at years in the BC range. 1 BC is the astronomical year 0. 2 BC is the astronomical year -1, and so on.

Solar Day

A solar day is the period over which the sun apparently moves on a path and returns to it's starting point. I say apparent because while we know this phenomenon is from the earths rotation. But the movement of the sun is still described in geocentric terms (sun rise, sun set, so on). The sun's path varies slightly from one day to another so it doesn't really return to it's starting point. So the meridian is used as the starting line. The meridian is the imaginary circle around the globe stretching from the north pole to the south pole. The  sun and other bodies reach their highest point in the sky at the meridian and then go from climbing to declining. This line is also used to divide a day in half. Once the sun goes past the meridian the time is labeled as post meridian (P.M.). When it passes this line on the opposite side of the globe we say that it is in it's before meridian. The Latin word for before is "ante", so it is referred to as A.M. (for Ante Meridian). When the sun is on the meridian it is at its highest point in the sky. This is called "solar noon." Solar noon doesn't necessarily occur at the same instance in which the local time is 12:00 PM. There are slight variations in the time at which the sun reaches this point that we tend to ignore with civil clocks.

The pathway that the sun travels around an observer us usually divided into 24 units . Note that if you divide the 360 degrees of a circle by 24 you get 15. These 24 equal units are called hours (in other words, one hour of rotation is 15 degrees). These 15 degrees may also be divided into 60 units (minutes). A minute of rotation is 15/60, or 0.25 of a degree. As you may have guessed the next level of division is to divide a minute into 60 equal parts (the second) which contains 0.26/60 of a degree. An inference that you can make from using hours, minutes, and seconds (HMS) are a rotational unit is that for every hour that passes you can approximate the rotational distance that a celestial body in the night sky will travel over a unit of time; in a 2 hour period an object will travel 60 degrees. I say approximate because if you measure the distance with a high precision you'll see that the sun and moon appear to move by a slightly different amount than 60 degrees over this time period. For casual observations this difference won't be noticeable.

Sidereal Day

If you use the sun as your reference for rotation distance it appears that the earth takes 24 hours to make one full rotation. This isn't quite correct though. The earth moves about 1 degree on its orbit around the sun each day. So the sun shouldn't be used for an accurate measurement of how far the earth has rotated. Any other star will do though. The other stars are far enough away such that their apparent position is the same regardless of where the earth is on its orbit.

Difference in sidereal and solar day.

Choose a star (other than our sun, any star will do). Every time the earth rotates that star will reach the meridian. if you used a wall clock to measure the amount of time it takes for a star to reach the meridian again you'll find it isn't quite 24 hours. It is 23 hours 56 minutes and 4 seconds. Days measured using this method are sidereal days. Because these days are a bit less than 24 hours the amount of solar days in a sidereal year is about 366.25 instead of 365.25. Since a sidereal day is shorter than a solar day on any given solar day there will exist a range of sidereal times that occur twice within the solar day.

Time zones

Our universal time system is based on the time at Greenwich. Greenwich is on the zero longitude. Observations of celestial events on it's meridian was once the foundation of our timing system. It's meridian is also called the prime meridian. As a matter of convenience we also have the concept of local time, which is derived by adding some number of minutes and hours to the time at Greenwich. The earth is divided into 40 regions that share local time. These areas, or time zones, usually have a time difference by some interval of hours from the time at Greenwich (GMT). there are some time zones that are also offset by some hour interval plus 30 minutes. The difference from the most positive offset to the most negative offset is 26 hours. On average the difference between time zones can be inferred by their longitude (recall that one hour is 15 degrees of rotation). However the time zone lines are not straight. Rather than divide small geographic areas into several time zones the time zone borders will coincide with the borders of that geographical region.

The Earth's Celestia Movements

The stars are perceivably in a fixed position. For some one that wants to be extremely technical the stars are moving at speeds that we would find to be incredibly fast relative to our position or relative to the galaxy in which they rotate. But they are so far away that their movement is imperceptible to us, allowing us to treat them as stationary bodies over short periods measured in hundreds of years. There are a few factors that impact the orientation of the stars with respect to an observer on the earth.

Of these movements the one that has the most immediate impact on an observer is the rotation of the earth. Its impacts are directly observable through the apparent path that the sun, moon, and other bodies travel through our sky. If you are looking at a body with a telescope the movement becomes more apparent unless you have a motorized telescope that automatically adjust; as you look at a body it will drift out of the view of your telescope within a minute or less. This is the movement with which I am most concerned.

The Earth advances about 1 degree per day as it travels around the sun. With each day that passes the part of the celestial sphere that becomes unobservable due to competing light with the sun will slightly shift. This will mean that some stars will not be visible during certain parts of the year. While their direction can still be determined with the exception of an eclipse you won't be able to observer these stars during the day. Also note that this impacts the time of sun rise, sun set, and the number of hours in a day in which sunlight is visible (there's less hours of daylight in the winter). For now I'm not particularly concerned with what hours a star will be visible during my general case scenarios. Since I only use my telescope when time and weather unexpectedly permit I don't do much fore planning. If you've got interest in this I would suggest first explore the definitions on the various definitions of twilight (ex: civil vs. nautical vs. astronomical).

The third movement occurs over the course of about 25,700 years. It causes a slight circular drift of the direction in which the earth's rotational axis is pointing.  It can be addressed through a time dependent coordinate space adjustment. But I don't want to talk about coordinate conversions in this post. Just in case you are curious, the Earth's shift of the direction of it's rotational axis is about 1 degree every 71 years, so we can ignore this movement for now and it won't have a significant impact on our results.

Local Sidereal Time

Because of the continually varying orientation of the earth with respect to the sun we don't want to use a solar day for calculations of where stars are located with respect to the earth. The sidereal time is what we want. To get the sidereal time we need to know the Julian date. We'll get the Julian date from the civil (Gregorian) date. I've made a set of extensions for getting these dates. In calculating the Gregorian date you will need to be able to calculate how far we are into a day in decimal format. 12:00 Noon would be 0.5 into a day, 18:00 is 0.75 into a day, and so on. These can be easily calculated from a date or a time.

 static double ToFractionalDay(this TimeSpan sourceTime)
{
    return sourceTime.TotalHours / 24d;
}

 static double ToFractionalDay(this DateTime sourceDate)
 {
     return sourceDate.TimeOfDay.ToFractionalDay();
 }

These are written as extension methods because I find the calling syntax to be cleaner.  Now that we know how far we are into a day we can use that information to calculate the Julian date.

public static double ToJulianDate(this DateTime  sourceDate)
{
    double y, m, c;
    if (sourceDate.Month <= 2)
    {
        y = sourceDate.Year - 1;
        m = sourceDate.Month + 2;
    }
    else
    {
        y = sourceDate.Year;
        m = sourceDate.Month;
    }

    double leapDayCount  = (sourceDate > GregorianReformDate) ? (2 - Math.Floor(y / 100) + Math.Floor(y/400) ) : 0;
    if (sourceDate.Year < 0)
        c = (int)(365.25 * (double)sourceDate.Year - 0.75);
    else
        c = (int)(365.25 * (double)sourceDate.Year);
    double d = Math.Floor(30.6001 * (m + 1));
    var retVal = leapDayCount +c+ d + sourceDate.Day + 1720994.5;
    return retVal + sourceDate.ToFractionalDay();;
}

There's something I've not mentioned. All of these calculations are centric to the 0 longitude and are based on the GMT time zone without daylight savings. If you want to adjust the results to figure out the orientation of your time zone with respect to the rest of the observable universe you'll need to make an adjustment for your longitude. If your longitude is to the west of GMT express it with a negative number, otherwise use a positive number. Divide this number by 15 and add it to the sidereal time.  I live 84 degrees west of the 0 longitude. So to get the local sidereal time I do the following.

localSiderealTimeClock.CurrentTime = DateTime.Now.ToUniversalTime().ToSiderealTime().Add(TimeSpan.FromHours(-84d/15d));

The local sidereal time describes your rotational displacement from the direction of the vernal equinox (♈). While there's no up in space the direction formed by drawing a line from the sun to the earth while it is in the vernal equinox is the foundation of a couple of celestial coordinate systems (Ecliptic, which is based on the earth's orbit around the sun and equitorial which is based on the earth's rotation).

Correcting Variance's in the User's Clock

User's both intentionally and unintentionally may have their clocks set to an incorrect time. One way of avoiding problems from this is to make use of NTP (Network Time Protocol). I've written on obtaining NTP time before. You can read about it here.  While it is possible to continually poll an NTP source for the time I only grab it once every few minutes. When I get the NTP time the difference between the user's close and the NTP time source is saved and added to the value that comes from the user's clock. The expectation is that between refreshes for the NTP time the user's clock will reliably count seconds without any significant drift (if it doesn't, then the user needs a new device!).

NtpClient _ntpClient;
TimeSpan _ntpOffset;
DateTime _lastNtpRefresh = DateTime.MinValue;
TimeSpan _ntpRefreshPeriod = TimeSpan.FromMinutes(1);

public MainViewModel()
{
    _ntpClient = new NtpClient();
    _ntpClient.TimeReceived += new EventHandler<NtpClient.TimeReceivedEventArgs>(_ntpClient_TimeReceived);
    //Default the difference to zero and provisionally assume the user's
    //clock is correct until we receive information of otherwise
    _ntpOffset = TimeSpan.Zero;
}

void _ntpClient_TimeReceived(object sender, NtpClient.TimeReceivedEventArgs e)
{
    _lastNtpRefresh = DateTime.Now;
    DateTime NtpTime = e.CurrentTime;
    // NTP time is always in universal time, so we need to adjust the system clock 
    // to universal before getting the time offset. 
    _ntpOffset = NtpTime.Subtract(DateTime.Now.ToUniversalTime());
}

//Use thie method to get time adjusted for NTP offset.
DateTime GetDate()
{  
     return DateTime.Now.Add(_ntpOffset);
}

Displaying the Time

If you've looked at clocks that show the time in more than one time zone chances are the numbers shown for minutes and seconds were the same for most of the time zones. This isn't the case when looking at both civil time and sidereal time. The seconds will be out of sync. Because of personal preference (I simply find this displeasing) I'm updating the seconds simultaneously. I've made two controls for displaying the time; an analog clock and a digital clock. Both can display the time in 12 hour or 24 hour format.

 

Digital display of sidereal clock.

Displaying the 24 hour time with an analog clock may be new to many. I took a look at several 24-hour analog clocks in images online. Some started with midnight at the top of the clock and others with midnight at the bottom. I decided on having the midnight (0) hour at the bottom. This places noon at the top of the clock. Displaying 24-hour time in sidereal format is something that I'm still playing with though. While I have a circular gauge-like clock in place I'm going to change this from a user control to a templated control and expose new options on how it's to be rendered. (hints of the forthcoming changes are visible in the source code).

 
Display of analog class
 
Options screen

Help Files

In experimenting with something else I've included a Help HTML file for the application. The help file is stored in the application as content but unpackaged the first time the application is run. To prevent the unnecessary unpacking of files every time the application run it checks to see if a file already exists before unpacking it.

public class ContentUnpacker
{
   
    static string[] ContentFileList = { "About.html", "Sidereal.png", "appTimes.png", "settings.png" }; 
    public static void UnpackAllFiles()
    {
        IsolatedStorageFile sourceArchive = IsolatedStorageFile.GetUserStoreForApplication();
        if (!sourceArchive.DirectoryExists("Content"))
            sourceArchive.CreateDirectory("Content");


        foreach (string s in ContentFileList)
        {
            string targetName = String.Format("Content/{0}", s);
            string sourceName = String.Format("Content/{0}", s);
            if(!sourceArchive.FileExists(targetName))
            {
                var outStream = sourceArchive.CreateFile(targetName);
                var contentStream = Application.GetResourceStream(new Uri(sourceName, UriKind.Relative));
                using (var br = new BinaryReader(contentStream.Stream))
                {
                    var length = (int)br.BaseStream.Length;
                    outStream.Write(br.ReadBytes(length), 0, length);
                }
            }   
        }
    }
}

The about page contains only a web browser element that is given the URL to the help files. The entirity of the code that's behind the about page is below.

public partial class AboutPage : PhoneApplicationPage
{
    public AboutPage()
    {
        InitializeComponent();
    }

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        aboutBrowser.Navigate(new Uri("Content/About.html", UriKind.Relative));
    }
}

Where to from Here

There's a number of applications, some related to astronomy and others not that I have in mind for which this functionality will be useful. One example of something not realted to astronomy was an augmented reality application I had in mind for which I wanted the application to shade the models projected on the screen according to the location of the sun. One of the astronomy related applications is that I have access to a room with projectors and screens on all 4 walls. Just for the fun of it I wanted to to get the computers that control the projectors on all 4 walls communicating with each other and displaying a 360 view of the solar system. Getting the sidereal time is a stepping stone for some of these other applications but not the end goal itself. I plan to write on how these other ideas progress as each one of them gets implemented.

Figures and Illustrations

Revision History

  • 2012-09-14 - Initial publication

 

 

 

 

Tags: , ,

Jun 29 2012

Windows Phone 8 Marketplace for 180 Countries

Category: MobileJoel Ivory Johnson @ 07:23

We've come a long way since Microsoft initially launched it's mobile Marketplace. The original Windows Marketplace for Mobile was available to about 30 nations. If you didn't live in one of those nations and you wanted an application that was only distributed through the Marketplace then there was no way to get it. On Windows Phone 7 (unlike Windows Mobile) the Marketplace was the *only* way to load programs, so the implications were even different. At the time of this writing Microsoft has the Marketplace available in 60 countries. But they have  recently announced that the Marketplace for Windows Phone 8 will be available in 180 countries and will make in-app purchasing available (increasing your opportunity for making money). 

Source: Windows Phone Team Blog

Tags: , ,

Apr 17 2012

Compiling Same Code on Windows Phone 7 and Windows 8 Metro

Category: Desktop and Server | MobileJoel Ivory Johnson @ 12:27
Download Code (1.14 MB)

I was thinking about how to write code that will work on both Windows Phone 7 and Windows 8 (Metro). In theory some of the techniques that could be used are well known. But I wanted to try them out. So I decided to try some things out in a piece of throw away code. This gives me some liberty to experiement without worrying about the long term consequences of having not chosen the "best" way. For my throw away application I decided to make an application that would grab some one's Facebook friend list and display it on the screen. So this code will necessarily need to authenticate the user against Facebook. If you've not worked with the Facebook APIs before don't worry about it too much. You should still be able to follow along since this isn't digging deeply into what's available.

I've managed to use some of the techniques to make a Windows Metro version of RestSharp. I'll talk about that in another post.

Register your Application

Before you begin doing any coding you'll want to register your new Facebook application in the Facebook developer portal. This also means that you need to have a facebook account. Because I didn't want to bother my associates with test items in my feed I also created a secondaty facebook account for testing. The portal is available at developer.facebook.com. Clicking on menu item "Apps" at the top of the screen will take you to a page that shows all of your registered applications (if any) and will let you register more. If this is your first time using the portal you'll have no applications. I'll walk you through registering an application. I'll only draw your attention to options as necessary. Options that I don't mention can be left in their default state (which will most likely be blank).

Click on the "Create New App" button. You'll get prompted with a dialog in which you will need to enter the name of the application that you are creating. The dialog will automatically validate the name that you've entered once you've stopped typing and will let you know if a chane needs to be made. Once you have a name that the dialog considers valid click on the "Continue" button.

The next dialog is a Captcha. I really dislike Captchas. While I understand that these dialogs exists to make sure that a bot is not registering the new application the problem that I have with them is that more times than not I'm going to get the entry wrong a few times (ex: is that third character a lowercase 'L', upper case 'L', or an uppercase 'I'?).

Once you pass the Captcha you'll get an opportunity to specify the other details for your application and a chance to change the application's icon and category. It may not be obvious which option to select for "Select how your app integrates with Facebook." None of the available options has a description that you would automatically assocate with a Windows Phone or Metro application. Select "Website". During development it doesn't matter if the website that you've specified exists or not. But you will need to enter an address to a page that would  in theory process the login information. Once you've entered this information select "Save Changes." There are three pieces of information that are on this page that you'll be copying into your application later on: App ID, App Secret, and the Site URL. Keep this page open so that you can copy the information when I refer to it later.

 

 

Now to make your mobile application. At the time of this writing you'll need to use Visual 2010 for Windows Phone applications and Visual Studio 11 for Windows Metro applications. Most of what will be done will be similar enough such that I can talk about the procedure to be followind in parallel and note the occasional differences as they arise.

Creating the New Application Projects

I created both a Silverlight Application for Windows Phone and a Windows 8 Metro project. There are going to be significant blocks of code that will be identical between these two projects. When this occurs I'll only create one version of the file but will have it referenced in both projects. Linked files can easily be recognized by the arrow that shows in the lower-left corner of their icon. If you've never linked files before you can find more information on it here.

How linked files appear in Visual Studio.

Conditionally Compiled Code

There still may be sections in thee files that are specific to Windows Phone or Windows 8. These sections will be conditionally commented out depending on the platform on which the code is being compiled. For code that is specific to Windows 8 Metro applications the code is wrapped in #if NETFX_CORE/#endif blocks. For Windows 7 code I've wrapped the code in #if SILVERLIGHT/#endif blocks. I could have also used #if WINDIWS_PHONE/#endif blocks

If there is a large block of code that is specific to one platform and not the other instead of using the conditional compilation directives one could choose to just include the file containing the code in one file and not in the other. You'll see this done with most of the UI code (the XAML, while similar, is not shared between the two projects).

Creating the Authentication Page

Before I can call any method in Facebook I need to authenticate the user. Facebook uses a form of open authentication. So we will need to use a web browser in the application. Add a new page to your project caled "AuthenticationPage.xaml." The only element that is necessary in this page will be the web browser.  Many of the differences that you will encounter between Metro and Windows Phone 7 are in UI related code. There's a slight difference in the element that needs to be used for representing a browser element.

 <WebView 
     x:Name="AuthenticationWindow" 
     HorizontalAlignment="Stretch" 
     VerticalAlignment="Stretch"
     LoadCompleted="AuthenticationWindow_LoadCompleted" 
/>
 
<phone:WebBrowser  
   Name="AuthenticationWindow" 
   HorizontalAlignment="Stretch"
   VerticalAlignment="Stretch" 
   Navigated="AuthenticationWindow_Navigated" 
/>
Browser element for Windows Metro
 
Browser element for Windows Phone 7

The Application Constants

There are a few elements of data to which we will need. They are all in the entries for the app that you registered with Facebook. I'll need to use these in more than one place. The logic needed for authentication is simple and I left it in the UI code.  I separated the logic that will be shared in the Metro and WP7 versions of this application into a partial class definition. That

static class ApplicationConstants
{
	const string REDIRECT_URL = "https://mysite.com/SomePostAuthenticationPage.html";
	const string APP_ID = "_YOUR_APP_ID_";
	const string APP_SECRET = "_YOUR_APP_SECRET_";
	const string PERMISSIONS = "read_friendlists";
}

You'll need to substitute the values for your own application in these constants (except for the PERMISSIONS constant).

Creating the Authentication Page

An extremely high level overview of what occurs in the code-behind for the authentication page is that it will load the Facebook Open Authentication page and then monitor the path to which the user navigates. If the user has authenticated the browser will be directed to the site that had been registered for the application earlier. When this occurs there will be an authentication code appended to the URL. That code will be parsed out and exchanged for a token. There's not much the UI is really doing. So the codebehind looks simple. 

public partial class AuthenticationPage : PhoneApplicationPage
{
   public AuthenticationPage()
   {
      InitializeComponent();
      ViewModel = (App.Current as J2i.Net.FacebookAuthenticationTest.App).ViewModel;
      ViewModel.AuthenticationAttempted = true;
   }

   private void AuthenticationWindow_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
   {
   }

   private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
   {  
      PrepareForAuthentication();
   }

   private void AuthenticationWindow_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
   {
   }

   private void AuthenticationWindow_Navigating(object sender, NavigatingEventArgs e)
   {
      ProcessWebPageLoad(e.Uri.ToString());
   }
}
Code-behind for the Windows Phone version of the application

Handling Code Differences with Partial Classes

Where there is a slight difference in the code behind for the two pages the rest of the logic is the same. Instead of duplicating it I put the common logic in a partial class definition. The partial class has the same name as the class for the page. So at compile time the code from the code-behind and the partial class will be compiled into a single class. The common code is in AuthenticationPage.xaml.shared.cs.  The first method that the code-behind references from the shared file is PrepareForAuthentication(). This method will build the authentication URL which contains parameters to allow the application to identify itself, what permissions are needed, and a random string that should be passed for security reasone. If you want to see more information on these parameters they can be found in the Facebook authentication documentation.

void PrepareForAuthentication()
{
    _unique = Guid.NewGuid().ToString();

    string targetUrl = String.Format("https://www.facebook.com/dialog/oauth?" +
                                        "client_id={0}" +
                                        "&redirect_uri={1}" +
                                        "&scope={2}" +
                                        "&state={3}",
                                        // for touch/phone friendly version of the authentication page
                                        //"&display=touch",
                                        Uri.EscapeUriString(ApplicationConstants.APP_ID),
                                        Uri.EscapeUriString(ApplicationConstants.REDIRECT_URL),
                                        Uri.EscapeUriString(ApplicationConstants.PERMISSIONS),
                                        Uri.EscapeUriString(_unique));
    AuthenticationWindow.Navigate(new Uri(targetUrl));
}

The ProcessWebPage() method looks at the URL to which the browser is navigating to see whether or not it is our redirect URL. If it's not then nothing happens. If it is then the query parameters are extracted. Theparameters we are most interested in are code and state since these will indicate a successful authentication attempt. The code will be needed to request an access token for making Facebook API calls.

void ProcessWebPageLoad(string targetUriString)
{
   if (targetUriString.IndexOf(ApplicationConstants.REDIRECT_URL) == 0)
   {

      string _code = GetQueryParam(targetUriString, "code");
      string _state = GetQueryParam(targetUriString, "state");
      string _error = GetQueryParam(targetUriString, "error");
      string _errorReason = GetQueryParam(targetUriString, "error_description");
      string _errorDescription = GetQueryParam(targetUriString, "error_reason");

      if (
         (!String.IsNullOrEmpty(_code)) &&
         (!String.IsNullOrEmpty(_state)) &&
         (_unique.Equals(_state))
      )
      {
         ExchangeCodeForAccessToken(_code);
      }
   }
}

Requessting the access token is another web call, but it's not one for which we need the browser. It doesn't return anything user friendly so we can make the call without using the browser. Getting the access token is just a matter of building a URL and grabbing the token from the results.

void ExchangeCodeForAccessToken(string code)
{
    //Building the Request URL
    string targetUrl = String.Format("https://graph.facebook.com/oauth/access_token?" +
                                        "client_id={0}" +
                                        "&redirect_uri={1}" +
                                        "&client_secret={2}" +
                                        "&code={3}",
                                        Uri.EscapeUriString(ApplicationConstants.APP_ID),
                                        Uri.EscapeUriString(ApplicationConstants.REDIRECT_URL),
                                        Uri.EscapeUriString(ApplicationConstants.APP_SECRET),
                                        Uri.EscapeUriString(code));
    var webRequest = HttpWebRequest.CreateHttp(targetUrl);

    //Request the URL
    webRequest.BeginGetResponse((o) =>
    {
        //Stuff the response into a string
        var response = webRequest.EndGetResponse(o);
        var rs = response.GetResponseStream();
        StreamReader sr = new StreamReader(rs);
        var responseString = sr.ReadToEnd();

        //Get the access token and its expiration date
        string accessToken = GetQueryParam(responseString,"access_token");
        DateTime expirationDate = DateTime.MinValue;
        string expiresString = GetQueryParam(responseString, "expires");

        if (!String.IsNullOrEmpty(expiresString))
        {
            int expireTime;
            if (int.TryParse(expiresString, out expireTime))
            {
                expirationDate = DateTime.Now.AddSeconds(expireTime);
            }
        }

        //If we successfully got a token and expiration save them, start grabbing the
        //friend list, and then navigate to the previous page
        if (!String.IsNullOrEmpty(accessToken) && (expirationDate != DateTime.MinValue))
        {
            ViewModel.AccessInfo = new AccessInfo() { ExpirationDate = expirationDate, Token = accessToken };
            ViewModel.SaveAccessToken();
            ViewModel.UpdateFriendList();
            ReturnToPreviousPage();
        }
    }, null);
}

Navigation is a little different on Windows Phone 7 and Windows 8. So I added a ReturnToPreviousPage() method in both the code-behind for the Metro and Windows Phone 7 versions of the application. Each of which contains the appropriate version of the navigation code.

void ReturnToPreviousPage()
{
   ViewModel.DispatchInvoke(() =>
   {
      (Window.Current.Content as Frame).GoBack();

   }
   );
}
 
void ReturnToPreviousPage()
{
   ViewModel.DispatchInvoke(() =>
   {
       NavigationService.GoBack();
   }
   );
}
Windows 8 Metro Version
 
Windows Phone Version

Retreiving the Friend List

The code for retrieving the friend list looks identical on both platforms. Facebook may not return the entire friend list in one call. If there are more friends to be retrieved in the return structure from the call there will be an address in a member in the return structure named Next (nested in Paging) which contains the address to the next page of the results. I'm not parsing the results myself but am instead making use of JSON.Net by James Newton-King. The results are parsed and accumulated into a temporary list. Once I know that I have all the results they are moved to the bindable list that drives the list box on the UI.

public void UpdateFriendList(string targetUrl = null, List previousUserList = null)
{
    if (AccessInfo == null)
        return;
    if (previousUserList == null)
        previousUserList = new List();

    var request = System.Net.HttpWebRequest.CreateHttp(targetUrl ?? String.Format(FRIEND_LIST_REQUEST, AccessInfo.Token));
    request.BeginGetResponse((o) =>
        {
            var response = request.EndGetResponse(o);
            var responseStreamReader = new StreamReader(response.GetResponseStream());
            string friendListText = responseStreamReader.ReadToEnd();
            Newtonsoft.Json.JsonSerializer s = new JsonSerializer();
            Data.FriendListResponse v = Newtonsoft.Json.JsonConvert.DeserializeObject(friendListText);
            if ((v != null) && (v.Data != null))
            {
                foreach (var user in v.Data)
                {
                    previousUserList.Add(user);
                }
            }
            if ((v!=null) && (v.Paging != null) && (v.Paging.Next != null))
                UpdateFriendList(v.Paging.Next, previousUserList);
            else
            {
                previousUserList.Sort((a1, a2) => { return a1.Name.CompareTo(a2.Name); });
                DispatchInvoke(() =>
                    {
                        FriendList.Clear();
                        foreach (var u in previousUserList)
                            FriendList.Add(u);
                    }
                );
            }

        }, null);
}

Dispatching Differences

There is a rule in Windows Platforms that you cannot modify the UI from a seconday thread. On the XAML based platforms you can ensure calls that modify the UI are marshalled to the UI thread by making use of an object called the Dispatcher. The way that you use this object differs on Windows Phone 7 and Windows 8. I abstracted away the differences by making a method called DispatchInvoke(). It compiles different on each platform and will call the appropriate code on each platform.

public void DispatchInvoke(Action a)
{
#if SILVERLIGHT
    if (MainViewModel.Dispatcher == null)
        a();
    else
        Dispatcher.BeginInvoke(a);
#else
    if ((Dispatcher != null) && (!Dispatcher.HasThreadAccess))
    {
        Dispatcher.InvokeAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, (obj, invokedArgs) => { a(); }, this, null);
    }
    else
        a();
#endif
}

Saving Data

For saving simple data types I tend to use a simple component that I made for Windows Phone 7 (it can be found here). On Windows 8 all file IO is asynchronous. So the component had to be modified to work on Metro. I've shown the details of the changes on another page. Since the Windows Phone 7 version uses a blocking call and the Metro version is asynchronous the calls could not be made to look the same. But this difference was compatmentalized to a single method.

public void SaveAccessToken()
{
   if (this.AccessInfo != null)
   {
#if NETFX_CORE
      DataSaver<AccessInfo>.SaveMyDataAsync(this.AccessInfo, "_accessToken.xml");
#endif
#if SILVERLIGHT
     DataSaver<AccessInfo>.SaveMyData(AccessInfo, "_accessToken.xml");
#endif
   }
}

Most of the code for this project is in the MainViewModel class. The entirity of the code for it is below. It shouldn't contain anything that you don't recognize from above. The using directives at the top contain conditional compilation directives since the namespaces in which some of the classes exists differs on Windows Phone 7 and on Windows 8 Metro.

using System;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using Newtonsoft.Json;
using J2i.Net.FacebookAuthenticationTest.Data;
using J2i.Net.FacebookAuthenticationTest.Utility;


#if SILVERLIGHT
using System.Windows.Threading;
#endif

#if NETFX_CORE
using System.Threading.Tasks;
using J2i.Net.FacebookAuthenticationTest.Data;
using Windows.UI.Xaml;

#endif

namespace J2i.Net.FacebookAuthenticationTest.ViewModel
{
    public class MainViewModel: INotifyPropertyChanged 
    {
        const string FRIEND_LIST_REQUEST = "https://graph.facebook.com/me/friends?access_token={0}";


        public MainViewModel()
        {
#if SILVERLIGHT
            var a = DataSaver<AccessInfo>.LoadMyData("_accessToken.xml");
            if(a !=null)
            {
                this.AccessInfo = a;
                this.UpdateFriendList();
            }
#endif

#if NETFX_CORE
            DataSaver<AccessInfo>.LoadDataAsync("_accessToken.xml", (info, exc)=>
            {
                if (info != null)
                {
                    this.AccessInfo = info;
                    this.UpdateFriendList();
                }
            });
#endif
        }

        ObservableCollection<FacebookUser> _friendList;
        public ObservableCollection<FacebookUser> FriendList
        {
            get { return (_friendList) ?? (_friendList = new ObservableCollection<FacebookUser>()); }
            set { _friendList = value; }
        }
 

        string _friendListText = String.Empty;
        public string FriendListText
        {
            get { return _friendListText; }
            set 
            {
                if (value != _friendListText)
                {
                    _friendListText = value;
                    RaisePropertyChanged("FriendListText");
                }
            }
        }


        public void SaveAccessToken()
        {
            if (this.AccessInfo != null)
            {
#if NETFX_CORE
                DataSaver<AccessInfo>.SaveMyDataAsync(this.AccessInfo, "_accessToken.xml");
#endif
#if SILVERLIGHT
               DataSaver<AccessInfo>.SaveMyData(AccessInfo, "_accessToken.xml");
#endif
            }
        }

        AccessInfo _accessInfo;
        public AccessInfo AccessInfo
        {
            get { return (_accessInfo) ?? (_accessInfo = new AccessInfo()); }
            set 
            {
                if (_accessInfo != value)
                {
                    _accessInfo = value;

                }
            }
        }

        bool _authenticationAttempted = false;
        public bool AuthenticationAttempted
        {
            get { return _authenticationAttempted || (!String.IsNullOrEmpty(AccessInfo.Token)); }
            set { _authenticationAttempted = value; }
        }

#if NETFX_CORE
        static Windows.UI.Core.CoreDispatcher _dispatcher;
        public static Windows.UI.Core.CoreDispatcher Dispatcher
        {
            get
            {
                if (_dispatcher != null)
                    return _dispatcher;
                if ((Window.Current==null)||(Window.Current.Content == null))
                    return null;
                return Window.Current.Content.Dispatcher; 
            }
            set { _dispatcher = value; }
        }
#endif

#if SILVERLIGHT
        static Dispatcher _dispatcher;
        public static Dispatcher Dispatcher
        {
            get
            {
                if (_dispatcher!=null)
                    return _dispatcher;
                var app = (App.Current as J2i.Net.FacebookAuthenticationTest.App);
                if (app.RootFrame == null)
                    return null;
                return (app.RootFrame.Dispatcher);
            }
            set
            {
                _dispatcher = value;
            }
        }    
#endif


        public void UpdateFriendList(string targetUrl = null, List<FacebookUser> previousUserList = null)
        {
            if (previousUserList == null)
                previousUserList = new List<FacebookUser>();

            var request = System.Net.HttpWebRequest.CreateHttp(targetUrl ?? String.Format(FRIEND_LIST_REQUEST, AccessInfo.Token));
            request.BeginGetResponse((o) =>
                {
                    var response = request.EndGetResponse(o);
                    var responseStreamReader = new StreamReader(response.GetResponseStream());
                    string friendListText = responseStreamReader.ReadToEnd();
                    Newtonsoft.Json.JsonSerializer s = new JsonSerializer();
                  Data.FriendListResponse v = Newtonsoft.Json.JsonConvert.DeserializeObject<Data.FriendListResponse>(friendListText);
                  if ((v != null) && (v.Data != null))
                  {
                      foreach (var user in v.Data)
                      {
                          previousUserList.Add(user);
                      }
                  }
                  if ((v!=null) && (v.Paging != null) && (v.Paging.Next != null))
                      UpdateFriendList(v.Paging.Next, previousUserList);
                  else
                  {
                      previousUserList.Sort((a1, a2) => { return a1.Name.CompareTo(a2.Name); });
                      DispatchInvoke(() =>
                          {
                              FriendList.Clear();
                              foreach (var u in previousUserList)
                                  FriendList.Add(u);
                          }
                      );
                  }

                }, null);
        }


        void DispatchInvoke(Action a)
        {
#if SILVERLIGHT
            if (MainViewModel.Dispatcher == null)
                a();
            else
                Dispatcher.BeginInvoke(a);
#else
            if ((Dispatcher != null) && (!Dispatcher.HasThreadAccess))
            {
                Dispatcher.InvokeAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, (obj, invokedArgs) => { a(); }, this, null);
            }
            else
                a();
#endif
        }
        protected void RaisePropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                DispatchInvoke(()=>
                    {
                        RaisePropertyChanged(propertyName);
                    });
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Tags: , , ,

Apr 15 2012

Data Serializer updated for Windows Metro

Category: Microsoft | MobileJoel Ivory Johnson @ 12:46

About a year ago I shared a simple utility class for saving serializable data on Windows Phone 7. I just updated the component for Windows 8 Metro. It still retains compatibility with Windows Phone 7 through the use of conditional compiler directives. On Windows Phone 7 I've changed the class so that it is static (so no need to instantiate it). 

For Metro the methos are asynchronous.  For saving an action can be passed that will be called once the save operation is complete. When loading data you'll want to pass an action that will received the loaded data and an exception parameter that will be populated if the data could not be loaded. 

 

As an example of how the code works (and the platform dependent differences in its usage) here is a method from a program I have that is using the code. The program compiles on both Windows Phone 7 and Windows Metro.

        public void LoadAccess()
        {
#if SILVERLIGHT
            AccessInfo = DataSaver<AccessInfo>.LoadMyData("_accessToken.xml");
#endif

#if NETFX_CORE
            DataSaver<AccessInfo>.LoadDataAsync("_accessToken.xml", (info, exc)=>
            {
                if (info != null)
                {
                    this.AccessInfo = info;
                }
            });
#endif
        }

        public void SaveAccessToken()
        {
            if (this.AccessInfo != null)
            {
#if NETFX_CORE
                DataSaver<AccessInfo>.SaveMyDataAsync(this.AccessInfo, "_accessToken.xml");
#endif
#if SILVERLIGHT
               DataSaver<AccessInfo>.SaveMyData(AccessInfo, "_accessToken.xml");
#endif
            }
        }

If you've never seen the #if/#endif directives before it is used to essentially conditionally comment out sections of code based on some condition. In this case the condition is certain compiler constants being defined. Some constants are automatically created for various project types. If you create a windows phone project the WINDOWS_PHONE and SILVERLIGHT constants are defined. For a Windows 8 Metro project the NETFX_CORE constant is defined. When you are viewing the code in the Visual Studio IDE it will gray out any code that is going to be ignored because of the conditional compilation statements. 

Below is the code for the serializer. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;

using System.IO;
#if SILVERLIGHT
using System.IO.IsolatedStorage;
#endif

#if NETFX_CORE
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
#endif

namespace J2i.Net.FacebookAuthenticationTest.Utility
{


    public class DataSaver<MyDataType>
    {

        static List<Type> _knownTypeList = new List<Type>();
        public static List KnownTypeList
        {
            get
            {
                return _knownTypeList;
            }
        }
#if SILVERLIGHT
        private static IsolatedStorageFile _isoFile;
        static IsolatedStorageFile IsoFile
        {
            get
            {
                if (_isoFile == null)
                    _isoFile = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
                return _isoFile;
            }
        }

         public static void SaveMyData(MyDataType sourceData, String targetFileName)
        {
            try
            {
                using (var targetFile = IsoFile.CreateFile(targetFileName))
                {
                    var d = new DataContractSerializer(typeof(MyDataType), KnownTypeList);
                    d.WriteObject(targetFile, sourceData);
                }
            }
            catch (Exception )
            {
                IsoFile.DeleteFile(targetFileName);
            }
        }


        public static MyDataType LoadMyData(string sourceName)
        {
            MyDataType retVal = default(MyDataType);
            if (IsoFile.FileExists(sourceName))
                using (var sourceStream = IsoFile.OpenFile(sourceName, FileMode.Open))
                {
                    var d = new DataContractSerializer(typeof(MyDataType), KnownTypeList);
                    retVal = (MyDataType)d.ReadObject(sourceStream);
                }
            return retVal;
        }
#endif
        public DataSaver()
        {
        }
#if NETFX_CORE
        public static async void SaveMyDataAsync(
            MyDataType sourceData, 
            String targetFileName, 
            Action<MyDataType,String, Exception> OnSaved = null)
        {
            try
            {
                StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
                    targetFileName, CreationCollisionOption.ReplaceExisting
                    );
                IRandomAccessStream raStream = await file.OpenAsync(FileAccessMode.ReadWrite);
                IOutputStream outStream = raStream.GetOutputStreamAt(0);

                DataContractSerializer serializer = new DataContractSerializer(typeof(MyDataType), KnownTypeList);
                serializer.WriteObject(outStream.AsStreamForWrite(), sourceData);
                await outStream.FlushAsync();
                if(OnSaved!=null)
                    OnSaved(sourceData, targetFileName, null);
            }
            catch (Exception exc)
            {
                if (OnSaved != null)
                {
                    OnSaved(sourceData, targetFileName, exc);
                }
            }
        }

        public static async void LoadDataAsync(string fileName, Action<MyDataType, Exception> loadAction)
        {
            if (loadAction == null)
                return;
            try
            {
                StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
                if (file == null) return;
                IInputStream inStream = await file.OpenSequentialReadAsync();

                // Deserialize the Session State. 
                DataContractSerializer serializer = new DataContractSerializer(typeof(MyDataType), KnownTypeList);
                MyDataType data = (MyDataType)serializer.ReadObject(inStream.AsStreamForRead());
                loadAction(data, null);
            }
            catch (Exception e)
            {
                loadAction(default(MyDataType), e);
            }
        }
#endif

    }
}

Tags: ,

Mar 27 2012

New Meetup Presentation Series: Getting Started with Windows Phone

Category: Microsoft | MobileJoel Ivory Johnson @ 06:45

Over the next few weeks the Atlanta Windows Phone Developer Meetup group will be having a series of presentations on getting started with Windows Phone development. Many of the presentations will be done by Glen Gordon. I will be doing a few of the presentations too. These presentations are targeting developers that haven't yet gotten started with Windows Phone. So if you've not done any WP development before no worries, these presentations should be fine for you. If you are interested you can find information on the next and future meetings on the Meetup page here: http://www.meetup.com/Win-Phone-7-Developers-Atlanta/

 

Side note: For the presentations I'm doing the functionality works almost exactly the same for Windows Phone and Windows 8. I'm tempted to make an online presentation for both and put it on the web. But the real world presentation will concentrate only on Windows Phone. 

 

Tags: ,

Feb 7 2012

How Big is this Object on Windows Phone

Category: MobileJoel Ivory Johnson @ 06:44

I wanted to validate some of my understanding on the amound of memory that a .Net instance occupies on Windows Phone and sent a request to Abhinaba Basu of the .Net Compact Framework team for more information. He promptly responded with a nice explanation of how a .Net object is laid out in memory. Instead of reposting his work I'd like to refer you to his blog and the article he wrote explaining this. I'll be making some references to it in the near future. 

image

http://blogs.msdn.com/b/abhinaba/archive/2012/02/02/wp7-clr-managed-object-overhead.aspx

Tags: ,

Jan 22 2012

Adjusting Microsoft Translator WAVE Volume

Category: MobileJoel Ivory Johnson @ 14:30

Video Entry
Download Code (32 Kb)

 

The code in this article was inspired by some questions on Windows Phone 7, but it's generic enough to be used on other .Net based platforms. In the Windows Phone AppHub forums there was a question about altering the volume of the WAVE file that the Microsoft translator service returns. In the StackOverflow forums there was a question about mixing two WAVE files together. I started off working on a solution for the volume question and when I stepped back to examine it I realized I wasn't far away from a solution for the other question. So I have both solutions implemented in the same code. In this first post I'm showing what I needed to do to alter the volume of the WAVE stream that comes from the Microsoft Translation service.

I've kept the code generic enough so that if you want to apply other algorithms to the code you can do so. I've got some ideas on how the memory buffer for the sound data can be better handled that would allow large recordings to be manipulated without keeping the entire recording in memory and allowing the length of the recording to be more easily altered.  But the code as presented demonstrates three things:

  1. Loading a WAVE file from a stream
  2. Alter the WAVE file contents in memory
  3. Save WAVE files files back to a stream

The code for saving a WAVE file is a modified version of the code that I demonstrated some time ago for writing a proper WAVE file for the content that comes from the Microphone buffer.

Prerequisites

I'm making the assumption that you know what a WAVE file and a sample are.I am also assuming that you know how to use the Microsoft Translator web service.

Loading a Wave File

The formats for WAVE files is pretty well documented. There's more than one encoding that can be used in WAVE files, but I'm concentrating on PCM encoded WAVE files and will for now ignore all of the other possible encodings. The document that I used can be found here.  There are a few variants from the document that I found when dealing with real WAVE files and I'll comment on those variants in a moment. In general most of what you'll find in the header are 8, 16, and 32-bit integers and strings. I read the entire header into a byte array and extract the information from that byte array into an appropriate type. To extract a string from the byte array you need to know the starting index for the string and the number of characters it contains. You can then use Encoding.UTF8.GetString to extract the string. If you understand how numbers are encoded (little endian) decoding them is fairly easy. If you want to get a better understanding see the Wikipedia article on the encoding.

Integer Size Extraction Code
8-bit data[i]
16-bit (data[i])|(data[i+1]<<0x08)
32-bit (data[i])|(data[i+1]<<0x08)|(data[i+2]<<0x10)|(data[i+3]<<0x18)

Offset Title Size Type Description
0 ChunkID 4 string(4) literal string "RIFF"
4 ChunkSize 4 int32 Size of the entire file minus eight bytes
8 Format 8 string(4) literal string "WAVE"
12 SubChunkID 4 string(4) literal string "fmt "
16 SubChunk1Size 4 int32 size of the rest of the subchunk
20 AudioFormat 2 int16 Should be 1 for PCM encoding. 
22 Channel Count 2 int16 1 for mono, 2 for stereo,...
24 SampleRate 4 int32  
28 ByteRate 4 int32 (SampleRate)*(Channel Count)*(Bits Per Sample)/8
32 Block Align 2 int16 (Channel Count)*(Bits Per Sample)/8
34 BitsPerSample 2 int16  
  ExtraParamSize 2 int16 possibly not there
  ExtraParams ? ? possibly not there
36+x SubChunk2ID 4 int32 literal string "data"
40+x SubChunk2Size 4 int32  
44+x data SubChunk2Size byte[SubChunk2Size]  
         

The header will always be at least 44 bytes long. So I start off reading the first 44 bytes of the stream. The SubChunk1Size will normally contain the value 16. If it's greater than 16 then the header is greater than 44 bytes and I read the rest. I've allowed for a header size of up to 64 bytes (which is much larger than I have encountered). A header size of larger than 44 bytes will generally mean that there is an extra parameter at the end of SubChunk1. For what I'm doing the contents of the extra parameters don't matter. But I still need to account for the space that they consume to properly read the header.

To my surprise the contents of the fields in the header are not always populated. Some audio editors leave some of the fields zeroed out. My first attempt to read a WAVE file was with a file that came from the open source audio editor Audacity. Among other fields the BitsPerSample field was zeroed. I'm not sure if this is allowed by the format or not. It certainly is not in any of the spec sheets that I've found. But when I encounter this I assume a value of 16.

Regardless of whether a WAVE file contains 8-bit, 16-bit-, or 32-bit samples when read in I store the value in an array of doubles. I chose to do this because double works out better for some of the math operations I have in mind.

public void ReadWaveData(Stream sourceStream, bool normalizeAmplitude = false)
{
    //In general I should only need 44 bytes. I'm allocating extra memory because of a variance I've seen in some WAV files. 
    byte[] header = new byte[60];
    int bytesRead = sourceStream.Read(header, 0, 44);
    if(bytesRead!=44)
        throw new InvalidDataException(String.Format("This can't be a wave file. It is only {0} bytes long!",bytesRead));

    int audioFormat = ChannelCount = (header[20]) | (header[21] << 8);
    if (audioFormat != 1)
        throw new Exception("Only PCM Waves are supported (AudioFormat=1)");

    #region mostless useless code
    string chunkID = Encoding.UTF8.GetString(header, 0, 4);
    if (!chunkID.Equals("RIFF"))
    {
        throw new InvalidDataException(String.Format("Expected a ChunkID of 'RIFF'. Received a chunk ID of {0} instead.", chunkID));
    }
    int chunkSize = (header[4]) | (header[5] << 8) | (header[6] << 16) | (header[7] << 24);
    string format = Encoding.UTF8.GetString(header, 8, 4);
    if (!format.Equals("WAVE"))
    {
        throw new InvalidDataException(String.Format("Expected a format of 'WAVE'. Received a chunk ID of {0} instead.", format));
    }
    string subChunkID = Encoding.UTF8.GetString(header, 12, 4);
    if (!format.Equals("fmt "))
    {
        throw new InvalidDataException(String.Format("Expected a subchunkID of 'fmt '. Received a chunk ID of {0} instead.", subChunkID));
    }
    int subChunkSize = (header[16]) | (header[17] << 8) | (header[18] << 16) | (header[19] << 24);
    #endregion

    if (subChunkSize > 16)
    {
        var bytesNeeded = subChunkSize - 16;
        if(bytesNeeded+44 > header.Length)
            throw new InvalidDataException("The WAV header is larger than expected. ");
        sourceStream.Read(header, 44, subChunkSize - 16);
    }

    ChannelCount = (header[22]) | (header[23] << 8);
    SampleRate = (header[24]) | (header[25] << 8) | (header[26] << 16) | (header[27] << 24);
    #region Useless Code
    int byteRate = (header[28]) | (header[29] << 8) | (header[30] << 16) | (header[31] << 24);
    int blockAlign = (header[32]) | (header[33] << 8);
    #endregion
    BitsPerSample = (header[34]) | (header[35] << 8);

    #region Useless Code
    string subchunk2ID = Encoding.UTF8.GetString(header, 20 + subChunkSize, 4);
    #endregion

    var offset = 24 + subChunkSize;
    int dataLength = (header[offset+0]) | (header[offset+1] << 8) | (header[offset+2] << 16) | (header[offset+3] << 24);

    //I can't find any documentation stating that I should make the following inference, but I've
    //seen wave files that have 0 in the bits per sample field. These wave files were 16-bit, so 
    //if bits per sample isn't specified I will assume 16 bits. 
    if (BitsPerSample == 0)
    {
        BitsPerSample = 16;
    }

    byte[] dataBuffer = new byte[dataLength];

    bytesRead = sourceStream.Read(dataBuffer, 0, dataBuffer.Length);


    Debug.Assert(bytesRead == dataLength);


    if (BitsPerSample == 8)
    {
        byte[] unadjustedSoundData = new byte[dataBuffer.Length / (BitsPerSample / 8)];
        Buffer.BlockCopy(dataBuffer, 0, unadjustedSoundData, 0, dataBuffer.Length);

        SoundData = new double[unadjustedSoundData.Length];
        for (var i = 0; i < (unadjustedSoundData.Length); ++i)
        {
            SoundData[i] = 128d*(double)unadjustedSoundData[i];
        }

    }
    if (BitsPerSample == 16)
    {
        short[] unadjustedSoundData = new short[dataBuffer.Length / (BitsPerSample / 8)];
        Buffer.BlockCopy(dataBuffer, 0, unadjustedSoundData, 0, dataBuffer.Length);


        SoundData = new double[unadjustedSoundData.Length];
        for (var i = 0; i < (unadjustedSoundData.Length); ++i)
        {
            SoundData[i] = (double) unadjustedSoundData[i];
        }
    }
    else if(BitsPerSample==32)
    {
        int[] unadjustedSoundData = new int[dataBuffer.Length / (BitsPerSample / 8)];
        Buffer.BlockCopy(dataBuffer, 0, unadjustedSoundData, 0, dataBuffer.Length);

        SoundData = new double[unadjustedSoundData.Length];
        for (var i = 0; i < (unadjustedSoundData.Length); ++i)
        {
            SoundData[i] = (double)unadjustedSoundData[i];
        }
    }

    Channels = new PcmChannel[ChannelCount];
    for (int i = 0; i < ChannelCount;++i )
    {
        Channels[i]=new PcmChannel(this,i);
    }
        if (normalizeAmplitude )
            NormalizeAmplitude();

}

Mono vs Stereo

In a mono (single channel) file the samples are ordered one after another, no mystery there. For stereo files the data stream will contain the first sample for channel 0, then the first sample for channel 1, then the second sample for channel 0, second sample for channel 1, and so on. Every other sample will be for the left channel or right channel. The sample data is stored in memory in the same way. in an array called SampleData. To work exclusively with one channel or the other there is also a property named Channels (of type PcmChannel) that can be used to access that one channel.

public class PcmChannel
{
    internal PcmChannel(PcmData parent, int channel)
    {
        Channel = channel;
        Parent = parent;
    }
    protected PcmData Parent { get; set;  }
    public int Channel { get; protected set; }
    public int Length
    {
        get { return (int)(Parent.SoundData.Length/Parent.ChannelCount);  }
    }
    public double this[int index]
    {
        get { return Parent.SoundData[index*Parent.ChannelCount + Channel]; }
        set { Parent.SoundData[index*Parent.ChannelCount + Channel] = value; }
    }
}

//The following is a simplified interface definition for how the PcmChannel
//data type is relevant to our PCM data. The actual PcmData class has more 
//more members than what follows.
public class PcmData
{
   public double[] SoundData { get; set; }
   public int ChannelCount { get; set; }
   public PcmChannel[] Channels { get; set; }
}

Where's 24-bit support

Yes, there do exists 24-bit WAVE files. I'm not supporting them (yet) because there's more code required to handle them and most of the scenarios I have in mind are going to use 8 and 16-bit files. Adding support for 32-bit files was only 5 more lines of code. I'll be handing 24-bit files in a forthcoming code.

Altering the Sound Data

Changes made to the values in the SoundData[] array will alter the sound data. There are some constrains on how the data can be modified. Since I'm writing this to a 16-bit WAVE file the maximum and minimum values that can be written out are 32,768 and -32,767. The double data type has a range significantly larger than this. The properties, AdjustmentFactor and AdjustmentOffset are used to alter the sound data when it is being prepared to be written back to a file. They are used to apply a linear transformation to the sound data (remember y=mx+b?). Finding the right values for these is done for you through the NormalizeAmplitude method. Calling this method after you've altered your sound data will result in appropriate values being chose. By default this method will try to normalize the sound data to 99% of maximum amplitude. You can pass an argument to this method between the values of 0 and 1 for some other amplitude.

public void NormalizeAmplitude( double percentMax = 0.99d)
{
    var max = SoundData.Max();
    var min = SoundData.Min();

    double rangeSize = max - min+1 ;
    AdjustmentFactor = ((percentMax * (double)short.MaxValue) - percentMax * (double)short.MinValue) / (double)rangeSize;
    AdjustmentOffset = (percentMax * (double)short.MinValue) - (min * AdjustmentFactor);

    int maxExpected = (int)(max * AdjustmentFactor + AdjustmentOffset);
    int minExpected = (int)(min * AdjustmentFactor + AdjustmentOffset);
}

Saving WAVE Data

To save the WAVE data I'm using a variant of something I used to save the stream that comes from the Microphone. The original form of the code had a bug that makes a difference when working with a stream with multiple channels. The microsphone produces a single channel stream and wasn't impacted by this bug (but it's fixed here). The code for writing the wave produces a header from the parameters it is given, then it writes out the WAVE data. The WAVE data must be converted from the double[] array to a byte[] array containing 16-bit integers in little endian format.

public class PcmData
{
    public void Write(Stream destinationStream)
    {
        byte[] writeData = new byte[SoundData.Length*2];
        short[] conversionData = new short[SoundData.Length];

        //convert the double[] data back to int16[] data
        for(int i=0;i<SoundData.Length;++i)
        {
            double sample = ((SoundData[i]*AdjustmentFactor)+AdjustmentOffset);
            //if the value goes outside of range then clip it
            sample = Math.Min(sample, (double) short.MaxValue);
            sample = Math.Max(sample, short.MinValue);
            conversionData[i] = (short) sample;
        }
        int max = conversionData.Max();
        int min = conversionData.Min();
        //put the int16[] data into a byte[] array
        Buffer.BlockCopy(conversionData, 0, writeData, 0, writeData.Length);

        WaveHeaderWriter.WriteHeader(destinationStream,writeData.Length,ChannelCount,SampleRate);
        destinationStream.Write(writeData,0,writeData.Length);
    }
}

public class WaveHeaderWriter
{
    static byte[] RIFF_HEADER = new byte[] { 0x52, 0x49, 0x46, 0x46 };
    static byte[] FORMAT_WAVE = new byte[] { 0x57, 0x41, 0x56, 0x45 };
    static byte[] FORMAT_TAG = new byte[] { 0x66, 0x6d, 0x74, 0x20 };
    static byte[] AUDIO_FORMAT = new byte[] { 0x01, 0x00 };
    static byte[] SUBCHUNK_ID = new byte[] { 0x64, 0x61, 0x74, 0x61 };
    private const int BYTES_PER_SAMPLE = 2;

    public static void WriteHeader(
            System.IO.Stream targetStream,
            int byteStreamSize,
            int channelCount,
            int sampleRate)
    {

        int byteRate = sampleRate * channelCount * BYTES_PER_SAMPLE;
        int blockAlign =  BYTES_PER_SAMPLE;

        targetStream.Write(RIFF_HEADER, 0, RIFF_HEADER.Length);
        targetStream.Write(PackageInt(byteStreamSize + 36, 4), 0, 4);

        targetStream.Write(FORMAT_WAVE, 0, FORMAT_WAVE.Length);
        targetStream.Write(FORMAT_TAG, 0, FORMAT_TAG.Length);
        targetStream.Write(PackageInt(16, 4), 0, 4);//Subchunk1Size    

        targetStream.Write(AUDIO_FORMAT, 0, AUDIO_FORMAT.Length);//AudioFormat   
        targetStream.Write(PackageInt(channelCount, 2), 0, 2);
        targetStream.Write(PackageInt(sampleRate, 4), 0, 4);
        targetStream.Write(PackageInt(byteRate, 4), 0, 4);
        targetStream.Write(PackageInt(blockAlign, 2), 0, 2);
        targetStream.Write(PackageInt(BYTES_PER_SAMPLE * 8), 0, 2);
        //targetStream.Write(PackageInt(0,2), 0, 2);//Extra param size
        targetStream.Write(SUBCHUNK_ID, 0, SUBCHUNK_ID.Length);
        targetStream.Write(PackageInt(byteStreamSize, 4), 0, 4);
    }

    static byte[] PackageInt(int source, int length = 2)
    {
        if ((length != 2) && (length != 4))
            throw new ArgumentException("length must be either 2 or 4", "length");
        var retVal = new byte[length];
        retVal[0] = (byte)(source & 0xFF);
        retVal[1] = (byte)((source >> 8) & 0xFF);
        if (length == 4)
        {
            retVal[2] = (byte)((source >> 0x10) & 0xFF);
            retVal[3] = (byte)((source >> 0x18) & 0xFF);
        }
        return retVal;
    }
}

Using the Code

Once you've gotten the wave stream only a few lines of code are needed to do the work. For the example program I am downloading a spoken phrase from the Microsoft Translation service, amplifying it, and then writing both the original and amplified versions to a file.

static void Main(string[] args)
{
    PcmData pcm;

    //Download the WAVE stream
    MicrosoftTranslatorService.LanguageServiceClient client = new LanguageServiceClient();            
    string waveUrl = client.Speak(APP_ID, "this is a volume test", "en", "audio/wav","");
    WebClient wc = new WebClient();
    var soundData = wc.DownloadData(waveUrl);

          
    //Load the WAVE stream and let it's amplitude be adjusted to 99% maximum
    using (var ms = new MemoryStream(soundData))
    {
        pcm = new PcmData(ms, true);               
    }

    //Write the amplified stream to a file
    using (Stream s = new FileStream("amplified.wav", FileMode.Create, FileAccess.Write))
    {
        pcm.Write(s);
    }

    //write the original unaltered stream to a file
    using (Stream s = new FileStream("original.wav", FileMode.Create, FileAccess.Write))
    {
        s.Write(soundData,0,soundData.Length);
    }
}

The End Result

The code works as designed, but I found a few scenarios that can make it ineffective. One scenario is that not all phones have the same response frequency for their speakers. Frequencies that comes through loud and clear on one phone may come through sounding quieter on another. The other scenario is that the source files may have a sample that goes to the maximum or minimum reading even though a majority of the other samples may come no where near to the same level of amplitude. When this occurs the spurious sample will limit the amount of amplification that is applied to the file. I opened an original and amplified WAVE file in audacity to see my results and I was pleased to see that the amplified WAVE does actually look louder when I view it's graph in audacity.

Part 2 - Overlaying Wave Files

The other problem that this code can solve is combining wave files together in various ways. I'll be putting that up in the next post. Between now and then I've got a presentation at the Windows Phone Developers Atlanta meeting this week (if you are in the Atlanta area come on out!) and will get back to this code after the presentation.

Tags: , , ,

Jan 17 2012

Announcing Windows Phone Developers Atlanta

Category: Joel Ivory Johnson @ 08:03

I wanted to take a moment to announce a meetup group for Windows Phone 7 developers based out of Atlanta. I'm one of the cofounders and organizers for this group. We've had several meetings but I wanted to wait until we got into a routine before making an announcement. I've been pretty comfortable with how things are going so I'm sharing with you now. 

The information for our meetings is posted on Meetup.com ( http://www.meetup.com/Win-Phone-7-Developers-Atlanta/ ). We meet one Tuesday per month (except for December, for which we've decided to have no meetings because of end-of-the year demands and holidays). Our next meeting will be Tuesday 24 January 2012. This month I'll be presenting on Windows Azure for Windows Phone developers and we may have one other person presenting on Localization. After the meeting I'll share the notes (and possibly a recording of the presentation).  

Tags: , ,

Jan 13 2012

Referencing Pages in Other Assemblies

Category: Joel Ivory Johnson @ 11:07

I''ve been working with Google APIs recently and most of the ones that I've used require OAuth2 authentication so that the user can grant the application access to their data. Rather than copy-and-paste the OAUTH2 implementation to the different projects that I'm using I decided to make a class library that included the authentication code. From a usage standpoint I wanted to to do something similar to the the tasks and choosers where you create an object, call a method, and after the component does its magic you get back the item that the user chose. 

The format for the URI to another assembly that you would use in Silverlight looked like the following

new Uri("{assemblyName};component/{pagePath.xaml}", UriKind.Relative);

Of course here {assemblyName} and {pagePath.xaml} are placeholders for your actual assembly namd and pathway to the page and not literal values here. It took me more time than I care to admit to figure out why this would not work on Windows Phone 7. The correct format to use on Windows Phone 7 is as follows:

new Uri("/{assemblyName};component/{pagePath.xaml}", UriKind.Relative);

See the difference? It's subtle, but the difference is the little forward slash at the begining of the string. Had I paid closer attention to the exception message that was returned I would have realized this. 

The one thing I don't like about this method is when one navigates away from the page any program state that is saved in the page's code-behind is going to be lost. I also plan to provide an alternative method for showing the OAUTH in a user control. Either method has its advantages and disadvantages. I'm alsmost done with the code and will be posting it here later this week. 

Tags:

Nov 15 2011

Using Windows Phone Location Services

Category: MobileJoel Ivory Johnson @ 10:11

Introduction

I was assisting some college students at a Microsoft event on the Georgia Tech campus. Some questions about location services came up. A few days later I was assisting some other college students in the Microsoft Windows Phone Development Forums and some of the same questions came up. So I've decided to do a quick write up on location services to have as a reference the next time the question comes up.

I'll cover what needs to be done to get the user's location and formatting it for the screen.

How Does Windows Phone Acquire my Location

Windows Phone uses three technologies for finding your location; GPS, WiFi based location, and Cell Tower based location. The technology with which most are familiar is GPS. In a nutshell GPS technology relies on satellites in orbit and the phone's ability to receive signals from at least three of these satellites. These satellites are all transmitting the time from synchronized clocks. But because of the satellites being different distances from the phone the time received from the satellites will be slightly different (since the further away the satellite is from the user the more time it takes for the signal to be received). A user's position can be calulated from these time differences.

Cell Tower based location and WiFi based location are similar in how they work. When you use either one of these the phone will try to get the identifiers for near by WiFi access points and cell towers. The identifier is sent to a Microsoft service which keeps records of the general area in which those WiFi access points or cell towers have been seen and returns that information to the device. This information isn't static; cell towers can be taken down and new towers can be built and when people move to new apartments and houses they take their access points with them. To keep their information up to date Microsoft acquires new information from vehicles driving around as a part of the StreetSide Project and also receives data from other phones that are using location services. If a phone is able to get location from GPS and also sees a new access point then that access point is reported as having been seen at that location.

I've got a bad habit of generically referring to any type of location service as GPS. This isn't correct since GPS is one type of location technology that may or may not be used to acquire the user's location. Also GPS is also the name for the location system that makes use of satellites launched by the USA. There are other systems such as GLONASS ( Globalnaya navigatsionnaya sputnikovaya sistema), Galileo, and so on (The Samsung Focus S supports both GPS and GLONASS).

Accuracy or Battery Friendly

Each one of these location technologies differs in the accuracy of the location that it provides. GPS is the most accurate with the margin of error being around 10 meters (32 feet) in favourable conditions. The next most precise location technology is WiFi based location. WiFi access points don't transmit nearly as far as cell towers. When you detect one you usually are literally within stone throwing distance of it. Cell tower based location is the least precise.

Generally speaking the more precise location technology (GPS) also has a more significant impact on battery life. When you need to acquire location information you can specify low or high accuracy mode. To preserve the user's battery life only request high accuracy information if you need it. If you only need a general location and don't expect your user to move around much during a session in your program it's a good idea to turn off location services after you've retrieved their location. Note: If you need the user's speed or elevation you will need to request a high accuracy location.

Once you have the user's location if you do not need to continually retrieve updated location information then turn off your location watcher. Doing so will power down the radios involved in retrieving the user's location and save battery life.

Getting Permission to Retrieve Location

Before retrieving the user's location you need to have permission to do so. There are two levels on which you'll need to retrieve location; on the device level and on the user level. On the user level it's just a matter of asking the user for permission to get their location. If precise location isn't absolutely needed then you may also want to give the user the option to select their location from a list of cities. You will also want to display a privacy notice to the user so that he or she knows how their location information will be used. There are some scenarios where you don't need to do this, but for now assume that this is always required. Retrieving the user location without getting permission and transmitting it over the Internet is a sure fire way to fail certification.

On the device level there are two things that you must do to have permission to use location services. You'll need to ensure that your application has requested the location services capability in the WMAppManifest.xml file. To do this ensure that you have the line <Capability Name="ID_CAP_LOCATION"/> in the <Capabilities/> section. Without this your application will not be able to make use of location services. Also note that the presence of this declarative capability request will also cause your application to be listed as an application that requires location services in the Marketplace.

Even though you've requested the location services capability a user can completely disable location services on their phone. I'll talk about how to deal with this in a moment.

What's in a Location

Location information is returned in the GeoCoordinate class. Let's take a look at the properties that class exposes

Member Type Description
IsUnknown bool Indicates whether or not the location object contains latitude/longitude data
Latitude double User's latitude
Longitude double User's longitude
HorizontalAccuracy double The accuracy of the latitude and longitude in meters
Altitude double User's altitude in meters
VerticalAccuracy double Accuracy in meters
Speed double User's speed in meter's per second

I listed IsUnknown first because if it is true then there's no use in looking at the other fields; they will contain no data of interest. I think that Latitude and Longitude explain themselves. The HorizontalAccuracy property tells you how far off the user's actual coordinates may be in meters while VerticalAccuracy tells how far off the altitude may be in meters. Those living in North America will want to take note that the Altitude and Speed properties user meters and meters per second. The Imperial measurement system is not being used here. If you need to display something in Imperial units remember to do a conversion first.

Getting the Location

Let's break away from discussing concepts and go through some code to retrieve the user's location. To retrieve the user's location you'll need to create a geocoordinate watcher, subscribe to a few events, and call it's Start() method.

private GeoCoordinateWatcher _watcher = null;
public GeoCoordinate MostRecentPosition { get; set; }
	
void StartWatcher()
{
 if(_watcher == null)
  {
    _watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
    _watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
    _watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
    } 
   _watcher.Start();
   if(_watcher.Permission == GeoPositionPermission.Denied)
   {
      //Location services is disable on the phone. Show a message to the user.
   }
}
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
   MostRecentPosition = e.Position.Location;
}
 void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{

}
	

When the phone has more updated location information the PositionChanged event is fired. I'm placing the updated position in a property called MostRecentPosition and all subsequent code will work using the value in that property. Something to keep in mind is that Location Services operates on a thread separate from the UI thread. In many Windows based technologies (including Windows Phone and Silverlight) you cannot interact directly with UI elements from seconday threads. If you wanted to update a UI element it is necessary to use a Dispatcher to execute code back on the UI thread.

void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
   MostRecentPosition = e.Position.Location;
   this.Dispatcher.BeginInvoke(()=>
   {
      MyTextBox.Text = "Received a location!";
   }
}

Location Services Status

The geocoordinate watcher can have various status defined in the GeoPositionStatus enumeration. When the status changes the StatusChanged event is fired (also on a secondary thread).

GeoPositionStatus Enumeration
Value Meaning
Disabled Location services has been disabled
NoData None of the location providers have any location information
Initializing Location services is still initializing (such as looking for GPS Satellite)
Ready Location services has data ready to be provided

Before Mango (Windows Phone 7.1) one would only expect the Disabled status if location services had been disabled before the program began to run; if it was available when the program started up then you would not need to check for it again during the life time of the program. Under Mango location services can become disabled at any time. While your program is running the user can press the Start button and go to settings (during which your program is still alive in the background) and then return to your program (waking it up) and location services will be disabled.

Formatting the Location Data

The members on the GeoCoordinate object that you get back that contain the latitude and longitude are of type double. You can format these fields the same way you would any other set of doubles. For example, lets say that you wanted to display the location with three figures past the decimal point. You could do it with the following.

string formatedLocation = String.Format("Lat/Long: {0:0.000},{1:0.000}",position.Latitude, position.Longitude);

Another way of displaying these numbers is using degrees, minutes, and seconds. As you might guess there is a relationship between these units and the units you might use to describe time on a clock. Every 24 hours the earth rotates 360 degrees (it's actually every 23 hours 56 minutes, but I won't get into the specifics of that). The amount that the earth rotates in one hour can be found by dividing 360 by 24. It is 15 degrees. So when some one mentions one hour of rotation that means the same as 15 degrees. There are 60 minutes in an hour. So how many degrees are in a minute of rotation? That can be found by dividing the 15 degrees of an hour of rotation by 60 which results in 0.25 degrees. To get degrees in a second of rotation divide the 0.25 degrees by 60. Given a rotational measurement you could use the following to perform the same steps to convert a measurement to degrees, minutes, seconds:

public static string DegreesToDegreesMinutesSecondsString(double source)
{
    var d = Math.Floor(source);
    var remainder = (source - d) * 60;
    var m = Math.Floor(remainder);
    remainder = (remainder - m) * 60;
    var s = remainder;

    return String.Format("{0:0}° {1:00}' {2:00}\"", d, m, s);
}

Meeting Certification Requirements

When you are using location services there are a few things you must do to pass certification. Most importantly you must inform the user how you plan to use their location data. If the location data will never leave the device then let the user know this. If you need to call public web services with their location data but don't plan to transmit identifying information then let the user know this.

Your program must also be able to handle scenarios in which location services is disabled gracefully. Depending on the nature of your program you may be able to provide the user with a list of cities or allow the user to enter location information (such as a postal code) to manually set their location. If your program absolutely must have location services information (such as a speedometer application) then let the user know that the program needs location services is needed for operation.

Example Program

The application attached to this posting retrieves location information and displays it on the screen. It also will allow you to send a link to your location via e-mail and will show you how to to convert information retrieved to feet and miles per hour(for those that use the emperial system) and kilometers per hour. The program uses databinding to display information on the screen but I don't make much use of converters. Since the information can also be sent via e-mail I do formatting within code and both bind it to the screen and send it in an e-mail.

Things to Remember

  • Request the Location Services capability in your WMAppManifest.xml
  • Ask the user for permission to get their location
  • Be prepared for the possibility of location services being disabled on the phone
  • Don't transmit the user's location to any service without their permission
  • Only use high accuracy location when needed
  • Turn off location services when it is nolonger needed

Tags: ,

Nov 5 2011

Chevron Labs is Open for Unlocking Your Phone

Category: MobileJoel Ivory Johnson @ 10:20

If you want to do development on your phone and don't want to register for the Marketplace you have an alternative to paying the full 99 USD to be able to deploy to your phone. You can also register with Chevron Labs 10 USD to get deployment abilities. For the 10 USD fee your phone is unlocked and ready to run your own applications. When you are prepared to deploy to the Marketplace you will need to do the full 99 USD registration, but until then this is a nice start.

Find out more here

Tags:

Nov 5 2011

No Video Screenshots?

Category: MobileJoel Ivory Johnson @ 05:27

Presently if you need a screenshot of your application on Windows Phone 7 you will need to use the emulator. Recently I came across a developer that found this would not work for him. He needed a screenshot of his application playing video and the video looks lower quality in the emulator. For this scenario you'll need to simulate the screen playing video. It only took me a few minutes to figure out how to do this. I'll start off showing the finished product:

 

To make this I started off recording a video in the emulator. Since there is no real camera in the emulator you get a video of a black square going around the screen. That was fine for my purposes. 

 

Using the image editor of your choice (I used Paint.Net, which is free!) erase the white area in the top of the image. The rest of the image needs to be divided into three layers.

The top layer is going to be made of the transparent part of the image. Using the Magic Wand tool hold down the CONTROL button and click on the transparent overlay above and below the progress bar. Also select the parts of it that are in the play/rewind/forward buttons. Of the time indicator on your screenshot has any numbers with closed shapes (0,4,6,8, or 9) then you will also need to select the inside of the closed area. Once everything is selected go to the edit menu and select "cut." Create a new layer and paste the transparent part into it. You may need to move it around on the screen to get it back into it's original position. 

Right now the transparent isn't really transparent. If you edit the layer properties you can change the transparency. I set the transparency to 196/255. I also changed the color of the overlay to be slightly lighter. 

The opaque parts of the image will still be on the original layer. Create a third layer and in this layer place the image that you want to represent the video. You may need to reorder your layers but after that you are done, just save the image. 

When I did the above I saved my work. So you can download the Paint.Net file and just change the image. If Paint.Net isn't your image editor of choice I also exported the layers into PNG file. You can download them from Skydrive

 

Tags: