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. 

CGEM 800

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: ,

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: , ,