Auto detection NTP code - General Discussion - Netduino Forums
   
Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Auto detection NTP code


  • Please log in to reply
4 replies to this topic

#1 ZakieM

ZakieM

    Advanced Member

  • Members
  • PipPipPip
  • 33 posts
  • LocationIsrael

Posted 24 March 2012 - 11:43 AM

In this code an implementation of NTP client and auto-detection classes for actual
timezone. To avoid too large assemblies, I did not use any XML or JSON parsing
classes. If your application is using XML or JSON parsing, the use of those here
could reduce code size.

The code refreshes local time with NTP server in a configurable number of hours. I noticed that on my device with 10 hours the clock gets drifted in couple of seconds. Use 5-6 hours for refresh. This is the main reason I wrote this code... I did not implement a refresh when DST turns on or off. If anyone adds that, please share.

How it Works:


By quering geobytes.com and askGeo.com I am finding the actual time-zone of the device:
Through geobytes.com I am finding my location in the world
Through askGeo.com I am finding the timezone data of this location


Limitations:


This auto-detection will work if the device is connected to the internet through
standard NAT configuration, but with remote proxies the auto-detection will be
wrong as it will detect the timezone of the proxy itself. For most applications
however it is good enough.

Don't expect a GPS accuracy for your location. It will probably get you the
name of city/town where your ISP is, and not your actual location, still for
timezone purposes this good enough.

Please read http://www.askgeo.com Terms of Use and legal disclaimer


Special NOTE:


In this code an API Key that is registered to the author of this code.
>>>> Please modify it to your registered key <<<<
Search below for <<<< to location of where you need to put your key


You may use the code, modify or include in any project you have, posted in the hope it will be useful.
Please read http://www.askgeo.com Terms of Use and legal disclaimer
Please read http://www.geobytes.com Terms and Conditions

/* =====================================================================================
 * NTP Protocol synchronization code
 * In this code an implementation of NTP client and auto-detection classes for actual
 * timezone. To avoid too large assemblies, I did not use any XML or JSON parsing
 * classes. If your application is using XML or JSON parsing, the use of those here
 * could reduce code size.
 * 
 * How it Works:
 *      By quering geobytes.com and askGeo.com I am finding the actual time-zone of the
 *      device:
 *          Through geobytes.com I am finding my location in the world
 *          Through askGeo.com I am finding the timezone data of this location
 * Limitations:
 *      This auto-detection will work if the device is connected to the internet through
 *      standard NAT configuration, but with remote proxies the auto-detection will be
 *      wrong as it will detect the timezone of the proxy itself. For most applications
 *      however it is good enough.
 *       	
 *      Don't expect a GPS accuracy for your location. It will probably get you the
 *      name of city/town where your ISP is, and not your actual location, still for
 *      timezone purposes this good enough.
 *      
 *      Please read http://www.askgeo.com Terms of Use and legal disclaimer
 *      
 * Special NOTE:
 *      In this code an API Key that is registered to the author of this code.
 *      >>>> Please modify it to your registered key <<<<
 *      Search below for <<<< to location of where you need to put your key
 * =====================================================================================
 * By: Zakie Mashiah
 * You may use the code, modify or include in any project you have.
 * Please read http://www.askgeo.com Terms of Use and legal disclaimer
 * Please read http://www.geobytes.com Terms and Conditions
 * =====================================================================================
 */
using System;
using System.Threading;
using System.IO;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Net;
using Microsoft.SPOT.Time;

using SecretLabs.NETMF;
using SecretLabs.NETMF.Hardware;

namespace NTPThread
{
    public class GeoLoactionByIP
    {
        public string country;
        public string region;
        public string city;
        public string latitude;
        public string longitude;
        public string timezone;
        public int timeZoneMinutesGMT; // How many minutes from GMT
        public string externalIPAddress;

        private string queryString 
        {
            get { return "http://www.geobytes.com/IpLocator.htm?GetLocation&Template=XML.txt"; }
        }

        /// 
        /// Converts the internal string member of timezone to internal member of minutes from GMT
        /// 
        private void _parseTimeZoneString()
        {
            int sign;
            int hour;
            int minute;

            sign = timezone[0] == '+' ? 1 : -1;
            hour = (timezone[1] - '0') * 10 + (timezone[2] - '0');
            minute = (timezone[4] - '0') * 10 + (timezone[5] - '0');

            timeZoneMinutesGMT = sign * ((hour * 60) + minute);
        }

        /// 
        /// Read an attribute in the XML string until the end of the attribute
        /// 
        /// String with the reply XML and starting with the value
        /// The sub-string until end of parameter denotion of XML
        private string _readAttribute(string parser, bool isString = true)
        {
            int start, end;
            string rv = null;
            string de = (isString) ? "\"" : ",";      // Data ends at
            string ds = (isString) ? "\":\"" : "\":"; // Data starts at

            start = parser.IndexOf(ds);
            if (start > 0)
            {
                start += ds.Length;
                end = parser.IndexOf(de, start);
                if (end > 0)
                {
                    rv = parser.Substring(start, end - start);
                }
            }
            return rv;
        }

        /// 
        /// Reads the stream of reply from the geobytes and parses it into the variables
        /// 
        /// the stream of the reply
        private void _readStream(Stream s)
        {
            System.Threading.Thread.Sleep(1000); // Wait a while
            StreamReader sr = new StreamReader(s);
            string parser = sr.ReadToEnd();

            int location = parser.IndexOf("country\":\"");
            if (location > 0)
            {
                parser = parser.Substring(location);
                this.country = _readAttribute(parser);
                
                location = parser.IndexOf("region\":\"");
                if (location > 0)
                {
                    parser = parser.Substring(location);
                    this.region = _readAttribute(parser);
                    
                    location = parser.IndexOf("city\":\"");
                    if (location > 0)
                    {
                        parser = parser.Substring(location);
                        this.city = _readAttribute(parser);

                        location = parser.IndexOf("latitude\":");
                        if (location > 0)
                        {
                            parser = parser.Substring(location);
                            this.latitude = _readAttribute(parser, false);
                            
                            location = parser.IndexOf("longitude\":");
                            if (location > 0)
                            {
                                parser = parser.Substring(location);
                                this.longitude = _readAttribute(parser, false);

                                location = parser.IndexOf("timezone\":\"");
                                if (location > 0)
                                {
                                    parser = parser.Substring(location);
                                    this.timezone = _readAttribute(parser);
                                    location = parser.IndexOf("ipaddress\":");
                                    if (location > 0)
                                    {
                                        parser = parser.Substring(location);
                                        this.externalIPAddress = _readAttribute(parser);

                                        // Now parse the timezone string to minutes
                                        _parseTimeZoneString();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        /// 
        /// Queries the Geobytes service for location of the IP address.
        /// 
        /// 
        public void queryLocation(string ip = null)
        {
            string q = (ip == null) ? queryString : queryString + ip;
            HttpWebRequest request = HttpWebRequest.Create(q) as HttpWebRequest;
            WebResponse resp = null;

            try
            {
                resp = request.GetResponse();
            }
            catch (Exception e)
            {
                Debug.Print("ERROR: Exception in HttpWebRequest.GetResponse() for geobytes.com" + e.ToString());
            }

            if (resp != null)
            {
                Stream respStream = resp.GetResponseStream();
                respStream.ReadTimeout = 5000;
                _readStream(respStream);
                resp.Close();
            }
            request.Dispose();
        }

        public override string ToString()
        {
            return "Country=\t" + country +
                "\n\tRegion=\t" + region +
                "\n\tCity=\t" + city +
                "\n\tLatitude=\t" + latitude +
                "\n\tLongitude=\t" + longitude +
                "\n\tTimezone=\t" + timezone +
                "\n\tMinutesFromGMT=\t" + timeZoneMinutesGMT +
                "\n\tExternalIP=\t" + externalIPAddress;
        }
    }





    public class AskGeoService
    {
        public long currentOffserMS;

        private const string accountID = "";             	// <<<< Put up your account ID, PLEASE!
        private const string apiKey = ""; // <<<< Put here your API key, PLEASE!
        private string queryString(string latitude, string longitude)
        {
            return "http://www.askgeo.com/api/" + accountID + "/" + apiKey + "/timezone.json?points=" + latitude + "%2C" + longitude;
        }


        public void queryLocation(string latitude, string longitude)
        {
            string q = queryString(latitude, longitude);

            HttpWebRequest request = HttpWebRequest.Create(q) as HttpWebRequest;
            WebResponse resp = null;

            try
            {
                resp = request.GetResponse();
            }
            catch (Exception e)
            {
                Debug.Print("ERROR: Exception in HttpWebRequest.GetResponse() for askgeo.com" + e.ToString());
            }

            if (resp != null)
            {
                Stream respStream = resp.GetResponseStream();
                respStream.ReadTimeout = 5000;

                System.Threading.Thread.Sleep(1000); // Wait a while
                StreamReader sr = new StreamReader(respStream);
                string parser = sr.ReadToEnd();
                int location;

                location = parser.IndexOf("currentOffsetMs\":");
                if (location > 0)
                {
                    parser = parser.Substring(location);
                    int start = parser.IndexOf(':') + 1;
                    location = parser.IndexOf(',');
                    string currentOffsetMsString = parser.Substring(start, location - start);

                    currentOffserMS = Convert.ToInt32(currentOffsetMsString);
                }
                resp.Close();
            }
            request.Dispose();
        }
    }





    public class NetworkTimeProtocolThread
    {
        #region Variables
        private static int sleepTime = 60 * 10; // Every 10 hours
        private static string ntpName = "time-a.nist.gov";
        private static int minutesFromGMT;
        #endregion

        

        #region PrivateFunctions
        private static void SleepMinutes()
        {
            int times;

            times = sleepTime == 0 ? 1 : sleepTime;
            for (int i = 0; i < times; i++)
            {
                Debug.Print(DateTime.Now.ToString() + " NTP Thread:: " + i.ToString() + " of " + times.ToString());
                Thread.Sleep(60 * 1000); // Minute
            }
        }

        private static void SetSystemTime(DateTime dt)
        {
            // Simulate timezone and DST of 3 hours (life is good in Tel-Aviv :-)
            TimeSpan timezoneOffset = TimeSpan.FromTicks(minutesFromGMT * TimeSpan.TicksPerMinute);
            dt += timezoneOffset;
            Debug.Print("\tSetting time to:\t" + dt.ToString());
            Utility.SetLocalTime(dt);
            Debug.Print("\tSystem Time is :\t" + DateTime.Now.ToString());
        }
        #endregion

        #region PublicFunctions
        public static void SetRefreshRate(int hoursForRefresh) { sleepTime = hoursForRefresh * 60; }
        public static void SetNTPHost(string host) { ntpName = host; }
        public static int GetTimeZoneOffset() { return minutesFromGMT; }
        public static void SetTimeZoneOffset(int minutes_from_GMT) { minutesFromGMT = minutes_from_GMT; }

        public static DateTime GetNetworkTime()
        {
            DateTime dt = new DateTime(1900, 1, 1, 1, 1, 1);
            IPHostEntry ntpServer = Dns.GetHostEntry(ntpName);
            if (ntpServer == null)
            {
                Debug.Print("ERROR: Can't find DNS entry for " + ntpName);
                return dt;
            }
            if (ntpServer.AddressList == null)
            {
                Debug.Print("ERROR: No addresses found for NTP server " + ntpName);
                return dt;
            }

            IPEndPoint ep = new IPEndPoint(Dns.GetHostEntry(ntpName).AddressList[0], 123);

            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            s.Connect(ep);

            byte[] ntpData = new byte[48]; // RFC 2030
            ntpData[0] = 0x1B;
            for (int i = 1; i < 48; i++)
                ntpData[i] = 0;

            s.Send(ntpData);
            s.Receive(ntpData);

            byte offsetTransmitTime = 40;
            ulong intpart = 0;
            ulong fractpart = 0;
            for (int i = 0; i <= 3; i++)
                intpart = 256 * intpart + ntpData[offsetTransmitTime + i];

            for (int i = 4; i <= 7; i++)
                fractpart = 256 * fractpart + ntpData[offsetTransmitTime + i];

            ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L);

            s.Close();

            TimeSpan timeSpan = TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond);
            DateTime dateTime = new DateTime(1900, 1, 1);
            dateTime += timeSpan;


            TimeSpan offsetAmount = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
            DateTime networkDateTime = (dateTime + offsetAmount);
            Debug.Print("   NetworkDateTime: " + networkDateTime.ToString());

            return networkDateTime;
        }


        #region NTPThreadRunner
        public static void Run()
        {
            Debug.Print("Network Time Protocol Thread starting...\n\tServer\t" + ntpName + "\n\tevery\t" + sleepTime.ToString() + " hours");
            while (true)
            {
                Debug.Print("NTP Thread:: Checking NTP server time. Time before: " + DateTime.Now.ToString());
                DateTime dt = GetNetworkTime();
                SetSystemTime(dt);
                SleepMinutes();
            }
        }
        #endregion
        #endregion
    } // End of NTP Thread class
}


Test Code:
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Collections;

using Microsoft.SPOT;
using Microsoft.SPOT.IO;
using Microsoft.SPOT.Hardware;


using SecretLabs.NETMF;
using SecretLabs.NETMF.Hardware.NetduinoPlus;

using NTPThread;

/// This program demonstrates how to use the .NET Micro Framework HTTP classes 
/// to create a simple HTTP client that retrieves pages from several different 
/// websites, including secure sites.
namespace NTPTestProgram
{

    public static class NTPTestProgram
    {
        #region Variables
        static DateTime startTime;
        #endregion // Variables

        #region Main
        /// 
        /// Retrieves weather current and forecast information from Google Weather and display
        /// it on the LCD with time ticking clock
        /// 
        public static void Main()
        {
            DateTime dt;
            int delay;

            // Initialization
            InitNetwork();
            InitLocation();
            InitRealTimeClock();


            // Loop (forever)
            for (int minute = 0; ; minute += 1000)
            {
                if (minute > (60 * 1000)) 
                    minute = 0;
                dt = DateTime.Now;
                ShowTime();
                TimeSpan ts = DateTime.Now - dt;
                delay = 1000 - ts.Milliseconds;
                if (delay > 0)
                    Thread.Sleep(delay);
                else
                    Debug.Print("Total processing time was: " + ts.Milliseconds.ToString());
            }
        }
        #endregion

        #region InitFunctions
        public static void InitNetwork() { } // Would need to populate if using a Ethernet Shield

        public static void InitRealTimeClock()
        {

            NetworkTimeProtocolThread.SetRefreshRate(5);
            new Thread(NetworkTimeProtocolThread.Run).Start();
            Thread.Sleep(1000); // Wait a second for NTP to push system time

            startTime = DateTime.Now;

            Debug.Print("Starting at: \t[" + startTime.ToString() + "]");
        }

        public static void InitLocation()
        {
            GeoLoactionByIP myLocation = new GeoLoactionByIP();
            AskGeoService askGeo = new AskGeoService();

            myLocation.queryLocation();
            Debug.Print("Location:\n\t" + myLocation.ToString() );

            askGeo.queryLocation(myLocation.latitude, myLocation.longitude);
            
            int minutes = (int)(askGeo.currentOffserMS / 1000 / 60);
            Debug.Print("\tCurrent timezone offset=" +  minutes + " minutes");
            NetworkTimeProtocolThread.SetTimeZoneOffset(minutes);
        }

        #endregion




        #region UtilityFunctions
        private static string DecimalTwoPos(int d)
        {
            if (d < 10)
                return "0" + d.ToString();
            else
                return d.ToString();
        }
        #endregion

        #region DisplayFunctions
        private static void ShowTime()
        {
            DateTime dt = DateTime.Now;
            string s = DecimalTwoPos(dt.Hour) + ":" + DecimalTwoPos(dt.Minute)+ ":" + DecimalTwoPos(dt.Second);
            Debug.Print(s);

        }
        #endregion

    }
}

Attached Files


Zakie Mashiah
Just a curious guy

#2 Giuliano

Giuliano

    Advanced Member

  • Members
  • PipPipPip
  • 361 posts
  • LocationSimi Valley, CA

Posted 08 June 2012 - 12:15 AM

I'll try it out, thanks for sharing!

#3 Valkyrie-MT

Valkyrie-MT

    Advanced Member

  • Members
  • PipPipPip
  • 315 posts
  • LocationIndiana, USA

Posted 08 June 2012 - 05:09 PM

// <<<< Put up your account ID, PLEASE!
// <<<< Put here your API key, PLEASE!


I would HIGHLY recommend removing your account and key from your post.

-Valkyrie-MT

#4 ZakieM

ZakieM

    Advanced Member

  • Members
  • PipPipPip
  • 33 posts
  • LocationIsrael

Posted 08 June 2012 - 07:19 PM

Thanks for noting. Done
Zakie Mashiah
Just a curious guy

#5 Giuliano

Giuliano

    Advanced Member

  • Members
  • PipPipPip
  • 361 posts
  • LocationSimi Valley, CA

Posted 03 July 2012 - 04:55 AM

Does anybody know why UtcNow doesn't change its value after assigning the new Date value from the NTP server? Is this an existing bug?

Posted Image
Also, executing the two lines shown below return OffSetAmount = 0, in this case Utc Now is also 6/01/2011:

Posted Image




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.