October 2012

Volume 27 Number 10

Touch and Go - Viewing the Night Sky on Your Windows Phone

By Charles Petzold | October 2012

Charles PetzoldIn the opening pages of his classic history, “The Copernican Revolution” (Harvard University Press, 1957), Thomas S. Kuhn evokes a world many centuries ago in which people had an intimate familiarity with the night sky. Not yet subject to the persistent illumination of artificial lighting, these observers experienced firsthand how the dome of stars seems to rotate during the night in a long arc around the North Star, and how the configuration of stars at sunrise and sunset progresses through a yearly cycle of 12 steps of the zodiac.

They also observed how several stars did not follow this same pattern. These oddities seemed to wander through the panorama of fixed stars, sometimes even reversing motion relative to the celestial dome. These exceptions to the general rule are still known by the ancient Greek word for wanderer: πλανήτης, or planet.

The attempt to trace and predict the motion of these planets was the beginning of the most famous upheaval in the history of science, eventually leading to the overthrow of primitive cosmology and the displacement of the Earth from the center of the universe. At the same time that the human race gained a much better understanding of its place in the cosmos, it also began to lose that special relationship with the night sky.

Using AstroPhone

These days most of us have not the time, patience or quietness of mind to stare at the night sky long enough to get a feel for its cyclical patterns or detect the wandering of planets. Instead we rely on star charts or computer-based astronomical tools to help out.

This column describes my own modest contribution to the genre, a little program for Windows Phone with the silly name AstroPhone. The downloadable source code for the application consists of an XNA program called AstroPhone and a library named Petzold.Astronomy that contains much of the data and number crunching.

This program has no UI whatsoever except the effort required to point the back of the phone toward a location in the night sky. The phone’s screen then displays the stars, constellations and planets located there. Figure 1 shows a typical screen, made at 3:45 p.m. local time on July 15, 2012, in the region of New York City, with the phone held in a westerly direction.

A Typical AstroPhone Screen
Figure 1 A Typical AstroPhone Screen

The green line is the horizon labeled with compass points. (Notice the W for west.) The red line is the ecliptic, which is the plane through which all the planets approximately orbit, with tick marks every 15°. You can see Jupiter and Venus due to set within the next 90 minutes or so. Also visible—on the screen if not in the mid-­afternoon sky—is the constellation of Taurus.

To position the stars and planets correctly on the screen, the program needs to combine information from several sources:

  • Data and algorithms to derive the position of the stars and planets (described in the following sections).
  • The current date and time to use in those algorithms.
  • The phone’s GPS so the program knows where the phone is located on the surface of the Earth.
  • The phone’s Motion sensor so the program knows how the phone is oriented in 3D space, and hence what part of the celestial sphere it’s pointed toward.

Due to its heavy reliance on the phone’s sensors, this program will not run on the Windows Phone emulator.

In several previous installments of this column I’ve been discussing some of the concepts of using the Motion sensor and converting that information to various coordinate systems. Those earlier articles are all available on the MSDN Magazine Web site (bit.ly/NoMc8R). Much of my approach to positional astronomy is based on the book by Jean Meeus, “Astronomical Algorithms” (Willmann-Bell, 1998), although I’ve sometimes used somewhat simplified calculations, and I make no guarantees that I’ve eliminated all errors.

Stars and Constellations

If you look up a particular star on Wikipedia, you’ll find its location is given in terms of two numbers—a right ascension and a declination. For example, Betelgeuse has a right ascension of 5 hours, 55 minutes, 10.3053 seconds, and a declination of 7°, 24 minutes, 25.426 seconds. This position is relative to the equatorial coordinate system, in which the Earth’s equator divides the universe into positive angular declinations to the north and negative angular declinations to the south. (Polaris, also known as the North Star, has a declination of very nearly 90°.)

Traditionally, right ascension is measured in hours, with each hour equivalent to 15°. A right ascension of zero corresponds to the same direction as north at midnight local time on the date of the vernal equinox. Because the plane of the Earth’s equator remains roughly constant throughout the year (and throughout the centuries), and because the stars are so far away, they are considered to have the same equatorial coordinates regardless of the position of the Earth. These equatorial coordinates for stars are specified for a particular epoch (for example, the year 2000), and small annual adjustments can be applied to account for the slight motion of the stars.

Many star catalogs exist, most of them with many more stars than I wanted to display in my program. I decided to use the updated version of a catalog that originated more than 100 years ago: the Bright Star Catalog, 5th revised edition, which I obtained from an FTP site maintained by the Strasbourg Astronomical Data Center (https://cds.unistra.fr/CDS40years).

Even this catalog provided much more information than I needed, and also somewhat less. For the most part I wanted my program to display only the major stars associated with the 88 standard constellations used in modern astronomy. That was easy enough because the stars have designations (first developed by Johann Bayer more than 400 years ago) that indicate which constellation the star belongs to with a three-letter abbreviation and a unique Greek letter.

But I also wanted to enhance each constellation with those familiar line connections between the stars so that Leo would actually look like a stick-figure lion. The lines connecting the stars in the constellations are not standard, and I didn’t know of a star catalog that included them.

So I added them myself. I wrote a little Windows Presentation Foundation (WPF) program that accessed the Bright Star Catalog and displayed the stars of each of the 88 constellations. The program responded to mouse clicks on pairs of stars and recorded the results. Generally I based the connecting lines on diagrams of the constellations on Wikipedia, which are credited to the International Astronomical Union and Sky & Telescope magazine.

The WPF program consolidated the data into a file named Constellations.xml, which you can find in the Data directory of the Petzold.Astronomy library project. Each Constellation tag contains a collection of Star tags. Each star is identified with a number from the Bright Star Catalog. The Star tags in each constellation are then followed by a collection of Connector tags that define lines between pairs of numbered stars.

The Planets

As even primitive stargazers realized, the location of the planets is much more complex than that of the stars. The planets have elliptical orbits around the sun, but irregularities result from mutual gravitational interaction. An algorithm to determine the location of a planet at a particular time is often known as a “theory,” and generally based on a Fourier series.

The planetary theory I used is called VSOP87, Variations Séculaires des Orbites Planétaires, 1987 version, which is maintained by the Bureau des Longitudes in Paris. The files containing the data of a portion of VSOP87 are also included in the Data directory of the Petzold.Astronomy project. The VsopCruncher class performs the calculations for each planet for a particular point in time.

The locations of the planets are usually calculated in ecliptic coordinates, but with the sun in the center—that is, heliocentric rather than geocentric. For each planet, these coordinates consist of:

  • an ecliptic longitude that goes from 0° to 360° as the planet makes a complete orbit around the sun during its particular year
  • an ecliptic latitude that’s close to zero because all the planets orbit the sun in roughly the same plane
  • a radius, which is the distance of the planet from the sun.

These coordinates are great for plotting the movement of planets around the sun. However, if you want to calculate the position of the planet as viewed from Earth, it’s convenient to convert these spherical coordinates into three-dimensional rectangular coordinates, where a planet’s position is indicated by X, Y and Z values with the sun occupying the center origin at (0, 0, 0). By subtracting the rectangular coordinate of any planet from the rectangular coordinate of the Earth, you get a 3D vector from the Earth to that planet, which can then be converted into the right ascension and declination values of a geocentric ecliptic coordinate—the same coordinate system used for the stars.

The moon’s movement is much more complex than that of the stars and planets, and has to be handled as a separate case. I chose not to include the moon in this version of the AstroPhone program. Besides, if I were to include the moon, I’d feel obliged to show the current phase, and that would complicate the job even more.

Chains of Calculations

To perform the necessary calculations in a methodical way, I defined a hierarchy of classes:

CelestialBody
        Constellation
        Star
            SolarSystemBody
                Sun
                Planet
                    Earth

The Star and Constellation classes also play double roles as deserialization targets of the Constellations.xaml file.

As shown in Figure 2, the CelestialBody class is responsible for computing a HorizontalCoordinate from an EquatorialCoordinate based on the geographic location of the phone and the current date and time. The descendant classes calculate that EquatorialCoordinate property in overrides of the OnTimeChanged method.

Figure 2 The Parent CelestialBody Class

public abstract class CelestialBody
{
  // Only used internally
  private GeographicCoordinate Location { set; get; }
  // Set here, used by descendant classes
  protected Time Time { private set; get; }
  // Set by descendant classes, used here
  protected EquatorialCoordinate EquatorialCoordinate { set; private get; }
  // Set here, used external to library
  public HorizontalCoordinate HorizontalCoordinate { private set; get; }
  // Used externally to retain screen location
  public float ScreenX;
  public float ScreenY;
  // Called external to update HorizontalCoordinate
  public void Update(Time time, GeographicCoordinate location)
  {
    bool needsUpdate = false;
    if (!this.Time.Equals(time))
    {
      this.Time = time;
      needsUpdate = true;
      OnTimeChanged();
    }
    if (!this.Location.Equals(location))
    {
      this.Location = location;
      needsUpdate = true;
    }
    if (needsUpdate)
    {
      this.HorizontalCoordinate =
        HorizontalCoordinate.From(this.EquatorialCoordinate,
                                  this.Location, this.Time);
    }
  }
  // Overridden by descendant classes to update EquatorialCoordinate
  protected virtual void OnTimeChanged()
  {
  }
}

The SolarSystemBody class shown in Figure 3 calculates the EquatorialCoordinate property from a HeliocentricLocation vector provided by its descendant classes, and the Planet class, shown in Figure 4, calculates that HeliocentricLocation based on a call to the VsopCruncher.

Figure 3 The SolarSystemBody Class

public class SolarSystemBody : CelestialBody
{
  protected SolarSystemBody()
  {
  }
  protected SolarSystemBody(string name, Color color)
  {
    this.Name = name;
    this.Color = color;
  }
  public string Name { protected set; get; }
  public Color Color { protected set; get; }
  // Set by descendant classes, used here
  protected Vector3 HeliocentricLocation { set; private get; }
  protected override void OnTimeChanged()
  {
    // Calculate geocentric coordinates
    Vector3 bodyLocation = this.HeliocentricLocation -
                           Earth.Instance.HeliocentricLocation;
    EclipticCoordinate geocentricCoordinate =
      new EclipticCoordinate(bodyLocation);
    this.EquatorialCoordinate =
      EquatorialCoordinate.From(geocentricCoordinate, this.Time);
    base.OnTimeChanged();
  }
}

Figure 4 The Planet Class

public class Planet : SolarSystemBody
{
  VsopCruncher vsop;
  public Planet(string strPlanetAbbreviation, 
    string name, Color color) :
    this(strPlanetAbbreviation)
  {
    this.Name = name;
    this.Color = color;
  }
  protected Planet(string strPlanetAbbreviation)
  {
    vsop = new VsopCruncher(strPlanetAbbreviation);
  }
  protected override void OnTimeChanged()
  {
    Angle latitude = Angle.FromRadians(vsop.GetLatitude(this.Time.Tau));
    Angle longitude = Angle.FromRadians(vsop.GetLongitude(this.Time.Tau));
    double radius = vsop.GetRadius(this.Time.Tau);
    this.HeliocentricLocation =
      new EclipticCoordinate(longitude, 
          latitude, radius).RectangularCoordinates;
    base.OnTimeChanged();
  }

The Star class is even simpler because it can calculate an Equatorial­Coordinate with just a small adjustment to its year 2000 position.

The XNA Front End

I began writing the AstroPhone program using Silverlight, which worked great with the first several constellations. But the more constellations I added, the slower it became.

Keep in mind that when using Silverlight for such a program, all the text, lines and dots are TextBlock, Line and Path elements in a Panel of some sort. To get fluid motion as you sweep the phone in an arc, this needs to be just one Panel, and it’s got to have everything in it. Performance degrades even more if you start removing items or making them invisible when they’re not in view.

I spent a lot of time trying to improve the performance of this Silverlight program. I discovered that using a Canvas worked better than a single-cell Grid. I found that when displaying a lot of Line elements, you could improve performance by setting one coordinate of the line to the point (0, 0), adjusting the other coordinate by the same amount and using a TranslateTransform to move it into position.

But with all 88 constellations in place, the video frame rate dropped down to a single frame per second, and there was simply no alternative except to abandon Silverlight. That’s why AstroPhone is an XNA program.

Interestingly, the slowest part of AstroPhone isn’t graphics at all. It’s the deserialization of the Constellations.xml file, which occurs in the OnActivated override of the Game class. The program then builds several collections for updating and rendering the celestial objects. The primary collection used for updating coordinates is called celestialBodies. As the name suggests, this is a collection of instances of classes that derive from CelestialBody, and it contains 832 objects—735 objects of type Star, 88 of type Constellation, one Sun and all eight Planet objects. (Pluto is not included in VSOP87.)

In an XNA program for Windows Phone, both the Update and Draw overrides in the Game class are called at the rate of 30 times per second. Update is responsible for obtaining input from the sensors (GPS and Motion, in this case) and preparing data for the Draw method.

To achieve the smoothest response to movements of the phone, the Update override loops through the entire celestialBodies collection, obtains the HorizontalCoordinate property from each object, and uses the current Motion matrix to convert that to a two-dimensional point on the screen, which it then stores in the ScreenX and ScreenY properties of the CelestialBody object. The Draw method then accesses those ScreenX and ScreenY properties to draw the object on the screen.

But that calculation is solely to account for the movement of the screen. It’s also necessary for each CelestialBody object to periodically update its HorizontalCoordinate property as time passes and both the Earth and other planets move a bit. Still, this update isn’t crucial to the smooth operation of the program. The HorizontalCoordinate property is based on the current date and time and the geographic location of the user, but neither of these items changes quickly enough to affect the position of the stars and planets in the short term.

For this reason, the Update override of the Game class deals with the Update methods of the CelestialBody objects in a leisurely manner. Only one item in the celestialBodies collection is updated for each call of the Game class’s Update override, requiring a cycle of about 28 seconds to loop through the entire collection of 832 objects.

For rendering purposes, other collections are maintained because different types of celestial objects are rendered in different ways. The visibleStars collection contains the 735 Star objects that are rendered on the screen, the constellations collection has the 88 constellations, and the systemBodies collection has the sun and the seven planets, excluding Earth.

Like every other class that derives from CelestialBody, the Constellation class sets an EquatorialCoordinate property, but this is solely for purposes of positioning the name of the constellation. That position is calculated by each Constellation instance by taking an average of the connected stars that make up the constellation.

The connection lines themselves were rather tricky. Each Constellation object has a collection of Connector objects, each of which references a pair of stars in the constellation. But these Connector objects come from the original Constellations.xml file, and they reference the pair of connected stars with ID numbers. To quicken the line drawing, the program spends part of the initialization process supplementing each pair of ID numbers with a StarConnector object, which is a pair of actual Star objects. Thus, the program can draw the connector lines by referencing the ScreenX and ScreenY properties of the actual Star objects.

Figure 5 shows the portion of the Draw method that renders the constellation names, the connecting lines and the stars themselves.

Figure 5 The Draw Method to Render Constellations and Stars

protected override void Draw(GameTime gameTime)
{
  // Dark blue sky
  GraphicsDevice.Clear(new Color(0, 0, 0x20));
  spriteBatch.Begin(SpriteSortMode.Immediate, 
    null, null, null, null, null,
    displayMatrix);
  ...
  // Loop through constellations
  foreach (Constellation constellation in 
    constellations.ConstellationList)
  {
    if (!float.IsNaN(constellation.ScreenX))
    {
      // Display constellation name
      Vector2 textSize = 
        labelFont.MeasureString(constellation.LongName);
      spriteBatch.DrawString(labelFont, constellation.LongName,
        new Vector2(constellation.ScreenX - textSize.X / 2,
        constellation.ScreenY - textSize.Y / 2), Color.Gray);
    }
    // Display constellation connectors
    if (constellation.StarConnectors != null)
    {
      foreach (StarConnector starConnector in 
        constellation.StarConnectors)
      {
        Vector2 point1 = new Vector2((float)starConnector.From.ScreenX,
          (float)starConnector.From.ScreenY);
        Vector2 point2 = new Vector2((float)starConnector.To.ScreenX,
          (float)starConnector.To.ScreenY);
        if (!float.IsNaN(point1.X) && !float.IsNaN(point2.X))
          lineRenderer.Draw(spriteBatch, point1, point2, 
           1, Color.White);
      }
    }
  }
  // Now display the stars themselves
  foreach (Star star in visibleStars)
    if (!float.IsNaN(star.ScreenX))
      starDotRenderer.Draw(spriteBatch,
        new Vector2(star.ScreenX, star.ScreenY), Color.White);
  ...
  spriteBatch.End();
  base.Draw(gameTime);
}

Although programs such as AstroPhone provide a helpful guide to the stars and planets, nothing comes close to the experience of actually looking at them in real life. Perhaps as people gain more knowledge with the help of programs such as this, they will once again develop an intimate familiarity with the night sky.


Charles Petzold is a longtime contributor to MSDN Magazine, and is currently updating his classic book, “Programming Windows” (Microsoft Press, 1998), for Windows 8. His Web site is charlespetzold.com.

Thanks to the following technical expert for reviewing this article: Donn Morse