Search code examples
c#windows-mobilecompact-frameworkwindows-ce

GPS returns incorrect time stamp on Windows Mobile CE 5.2


I have a Motorola MC65 device, that runs CE OS 5.2. I'm tying to obtain a valid time for the device; I was under the impression that since I'm receiving this data from a satellite it would know the correct time for the location...

I'm receiving the right date, but the time is off by 3 hours... I'm Eastern time zone, the device has the right timezone set and the time on the device now is 11:56am (Its off i know), the GPS returns 4:15PM...

Here is my current code to get the time via GPS, I make use of the GPS Intermediate Driver from Microsoft (https://msdn.microsoft.com/en-us/library/bb202128.aspx):

public DateTime GetGPSTime()
    {
        Boolean satsInView = false;
        Gps g = new Gps();
        g.DeviceStateChanged += new DeviceStateChangedEventHandler(g_DeviceStateChanged);
        g.Open();
        Thread.Sleep(4000);  

        if (g.Opened)
        {
            if (deviceState.ServiceState == GpsServiceState.On)
            {
                GpsPosition pos = g.GetPosition(TimeSpan.Zero); //No Delay in time
                if (pos != null)
                {
                    //First check that we have sats visible for good GPS signal.
                    if (pos.SatellitesInViewCountValid)
                    {
                        if (pos.SatellitesInViewCount >= 1)
                            satsInView = true;
                        else
                            satsInView = false;
                    }

                    if (pos.TimeValid && satsInView)
                    {
                        g.Close();
                        g.DeviceStateChanged -= g_DeviceStateChanged;
                        return pos.Time; //Returned time obtained from GPS Obj
                    }
                }
                g.Close();
                g.DeviceStateChanged -= g_DeviceStateChanged;
                return Helper.GetSystemTimeToNow(); //If GPS obj is null, return current system time.
            }
        }

        g.Close();
        g.DeviceStateChanged -= g_DeviceStateChanged;
        return Helper.GetSystemTimeToNow(); //If GPS obj is null, return current system time.
    }

Also, here is the method from the official Microsoft Intermediate GPS code samples. This method gets called by my code above, GpsPosition pos = g.GetPosition(TimeSpan.Zero);

 /// <summary>
        /// Get the position reported by the GPS receiver that is no older than
        /// the maxAge passed in
        /// </summary>
        /// <param name="maxAge">Max age of the gps position data that you want back. 
        /// If there is no data within the required age, null is returned.  
        /// if maxAge == TimeSpan.Zero, then the age of the data is ignored</param>
        /// <returns>GpsPosition class with all the position details</returns>
        public GpsPosition GetPosition(TimeSpan maxAge)
        {
            GpsPosition gpsPosition = null;
            if (Opened)
            {
                // allocate the necessary memory on the native side.  We have a class (GpsPosition) that 
                // has the same memory layout as its native counterpart
                IntPtr ptr = Utils.LocalAlloc(Marshal.SizeOf(typeof(GpsPosition)));

                // fill in the required fields 
                gpsPosition = new GpsPosition();
                gpsPosition.dwVersion = 1;
                gpsPosition.dwSize = Marshal.SizeOf(typeof(GpsPosition));

                // Marshal our data to the native pointer we allocated.
                Marshal.StructureToPtr(gpsPosition, ptr, false);

                // call native method passing in our native buffer
                int result = GPSGetPosition(gpsHandle, ptr, 500000, 0);
                if (result == 0)
                {
                    // native call succeeded, marshal native data to our managed data
                    gpsPosition = (GpsPosition)Marshal.PtrToStructure(ptr, typeof(GpsPosition));

                    if (maxAge != TimeSpan.Zero)
                    {
                        // check to see if the data is recent enough.
                        if (!gpsPosition.TimeValid || DateTime.Now - maxAge > gpsPosition.Time)
                        {
                            gpsPosition = null;
                        }
                    }
                }
                else if (result == 87) // ERROR_INVALID_PARAMETER)
                {
                    // 
                    // TEMPORARY HACK
                    // 
                    //  http://blogs.msdn.com/cenet/archive/2007/12/06/gpsid-problem-workaround-on-recent-wm6-release.aspx
                    // 

                    Utils.LocalFree(ptr);

                    // allocate the necessary memory on the native side.  We have a class (GpsPosition) that 
                    // has the same memory layout as its native counterpart
                    ptr = Utils.LocalAlloc(376);

                    // fill in the required fields 
                    gpsPosition = new GpsPosition();
                    gpsPosition.dwVersion = 1;
                    gpsPosition.dwSize = 376;

                    // Marshal our data to the native pointer we allocated.
                    Marshal.StructureToPtr(gpsPosition, ptr, false);

                    // call native method passing in our native buffer
                    result = GPSGetPosition(gpsHandle, ptr, 500000, 0);

                    if (result == 0)
                    {
                        // native call succeeded, marshal native data to our managed data
                        gpsPosition = (GpsPosition)Marshal.PtrToStructure(ptr, typeof(GpsPosition));

                        if (maxAge != TimeSpan.Zero)
                        {
                            // check to see if the data is recent enough.
                            if (!gpsPosition.TimeValid || DateTime.Now - maxAge > gpsPosition.Time)
                            {
                                gpsPosition = null;
                            }
                        }
                    }
                }

                // free our native memory
                Utils.LocalFree(ptr);
            }

            return gpsPosition;          
        }

Solution

  • The GPS time should be good, if the GPSposition.validField says that the time is valid.

    The GPS time is always UTC. Setting the system time is the only way to go, the systemtime is also always UTC. As there is a quirk with setting system time from inside a DST timespan to an outside DST timespan and vice versa, the system time should always be set two times! See my blog for details...

        private void SetTimeToGPS(DateTime UTCtime)
        {
            if (m_SetTime)
            {
                // Get the local time zone and a base Coordinated Universal 
                // Time (UTC).
                TimeZone localZone = TimeZone.CurrentTimeZone;
                DateTime baseUTC = UTCtime; // new DateTime(2000, 1, 1);
    
                System.Diagnostics.Debug.WriteLine("\nLocal time: {0}\n",
                    localZone.StandardName);
    
                // Calculate the local time and UTC offset.
                DateTime localTime = localZone.ToLocalTime(baseUTC);
                TimeSpan localOffset =
                    localZone.GetUtcOffset(localTime);
    
                System.Diagnostics.Debug.WriteLine(string.Format("{0,-20:yyyy-MM-dd HH:mm}" +
                    "{1,-20:yyyy-MM-dd HH:mm}{2,-12}{3}",
                    baseUTC, localTime, localOffset,
                    localZone.IsDaylightSavingTime(localTime)));
                //adjust the clock
                //localTime += localOffset;
                PInvokeLibrary.SystemTimeLib.SetTime(localTime);
                m_SetTime = false;
            }
        }
    

    the code is of my GpsSample8 demo at https://github.com/hjgode/win-mobile-code/blob/master/gps8/Gps8/GPS_Sample8/GPSForm1.cs

    When testing with times, please note the time you get from GPS (UTC) and what your device says for SystemTime and LocalTime after setting the SystemTime twice to the GPS time.

    BTW: EST is 5 hours behind UTC and not 3. After applying the offset DST has to be applied or not (depends on day of year).