GPS Emulators

May 24, 2010 at 7:35 PM

I'm trying to define and use the GPS Emulator (GeoFramework.Gps.Emulators).

I didn't quite understand how can I use it, I tried using all three emulators:

  1. NmeaEmulator
  2. TextEmulator
  3. RouteEmulator

I succeeded with the initializing of the emulators, but it seems that the emulator is not generating any data, I get this timeout exception:

The emulator has not generated any data within the read timeout period.

Has anyone succeeded using the GPS Emulators ?

Developer
May 24, 2010 at 8:31 PM
Edited May 24, 2010 at 8:37 PM

The emulators are basically just encapsulated data algorithms.  They don't raise events or provide any other hooks for your code to use.  Instead, they must be consumed by a VirtualDevice object, which wraps the emulator's algorithm and presents it as a Stream of data.  The type of data in the stream depends on which emulator you use, but in most cases, it will be a sequence of NMEA sentences.  You could write code to process this NMEA data yourself, but an easier approach would be to use the NmeaInterpreter class, which is included with GPS.Net.  The NmeaInterpreter parses the NMEA data and then updates the corresponding objects and properties and raises the corresponding events.

Here's an example that shows how to use the NmeaEmulator:

public static void Main() 
{
    // Use an emulated GPS device
    var emulatedDevice = new VirtualDevice(new NmeaEmulator());
	
    // Create a NmeaInterpreter and hook up its PositionChanged event
    nmeaInterpreter = new NmeaInterpreter();
    nmeaInterpreter.PositionChanged += NmeaInterpreter_PositionChanged;

    // Start the Interpreter to begin receiving GPS events
    nmeaInterpreter.Start(emulatedDevice);
}

private void NmeaInterpreter_PositionChanged(object sender, PositionEventArgs e)
{
    Console.WriteLine("The GPS position has changed to " + e.Position);
}

You can also use the GPS Diagnostics utility that's included with GPS.Net to test emulators.  Just replace the code in the startButton_Click event to look like this:

private void startButton_Click(object sender, EventArgs e)
{
    try
    {
        nmeaInterpreter1.Start(new VirtualDevice(new NmeaEmulator()));
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Cannot connect to GPS");
    }
}

 

May 24, 2010 at 8:50 PM

Thanks for your quick reply.

I'm sorry, but I forgot to mention that I've used VirtualDevice in my first post.
I've tried your simply example, just to be sure, but I got the same timeout exception saying: 'The emulator has not generated any data within the read timeout period'.

I also tried to change the startButton_Click event in the GPS Diagnostics utility.
I can see in the status bar: "Connected. Waiting for data..." the position values are all 0 and I'm still getting the same exception. (Just to make things clearer, this exception is well handled inside the Emulator.cs class, But the position values are not being updated.

Please tell me, what behavior should I except when using the NmeaEmulator ? should it provide me with randomized position values ? should it provide me static position value ?

My goal is to 'Reply' a route that I've been previously recorded, so I can test my code with valid GPS values.

 

Developer
May 24, 2010 at 9:03 PM

Have you set the emulator's ReadTimeout property or the static Emulator.DefaultReadTimeout property?  The default value is five seconds, which should be plenty of time to ensure that you don't get timeout errors.  I can't think of anything else off-hand that would be causing timeouts.

The NmeaEmulator just starts at a 0° latitude and 0° logitude, and increases both values sligtly every second.  So, you should start off in the south Atlantic Ocean, just off the coast of Africa and should head in a southwestern direction from there.

If your goal is to emulate a previously-recorded route, then the TextFileEmulator or RouteFileEmulator should meet your needs perfectly.  The difference between the two is that the TextFileEmulator expects a text file that contains raw GPS data (e.g. NMEA sentences), whereas the RouteFileEmulator expects a text file that contains a latitude/longitude pair on each line. 

May 24, 2010 at 9:32 PM

I haven't touched the Timeout properties. I Actually downloaded all the source code from scratch again, and manipulated only the Diagnostics utility start button event, and still the same behavior.

The exception is being raised from the Read(byte[] buffer, int offset, int count) method in the Emulator abstract class when trying to calculate the number of bytes to read: 

// Calculate the number of bytes to read
int bytesToRead = _ReadBuffer.Count < count ? _ReadBuffer.Count : count;

Those this information give you a clue about what happening at my side ? What do you think I can do ? Maybe there are pre-requirements that I'm not following ?

 

 

 

Developer
May 24, 2010 at 9:46 PM

When you downloaded the source for GPS.Net / GeoFramework, did you download from the "Downloads" page or from the "Source Code" page? The reason I ask is that the "Source Code" page has newer versions that include a lot of bug fixes.  I can't think of any fixes that should be affecting this particular issue, but it might be worth a try. The latest code for GPS.Net is Change Set 58665.

The error message that you're getting is thrown in this block of code:

if (_ReadBuffer.Count == 0
    && !_ReadDataAvailableWaitHandle.WaitOne(_ReadTimeout))
{
    throw new TimeoutException("The emulator has not generated any data within the read timeout period.");
}

As you can see, there's not much going on there. The only thing that could be causing the error is if the timeout expires before data becomes available.  That's why I thought it might be the Emulator.ReadTimeout property.  But since it's not that, I'm totally lost as to what could be causing the problem.

May 25, 2010 at 2:57 PM
Edited May 25, 2010 at 3:00 PM

Yes, I downloaded the links from the Source Code page and I also tried the GPS Diagnostics utility on another machine, I'm sad to say that I've got the same results.

But, I managed to find a workaround, that allows the NmeaEmulator to work.

Here are my steps:

  1. When the emulator abstract class starts the EmulatorThreadProc() thread it goes to the method: OnEmulation().
  2. The OnEmulation() method is overridden by the NmeaEmulator class.
  3. The problem starts in here (OnEmulation method):
    1. It seems that while in this method, no condition needs is met ($GPGGA, $GPRMC and etc.) this caused the NmeaEmulator not to generate any data.
    2. To workaround this problem, I simply 'Stepped Into' using the debugger, and ordered the compiler to 'Step In' into the first  $GPGGA condition (Although this condition isn't met):
      // $GPGGA
      if (!_GpggaInterval.Equals(TimeSpan.Zero)
      	// Has enough time elapsed to send the sentence?
      	&& UtcDateTime.Subtract(_GpggaLastSent) > _GpggaInterval)
      {
      	// Get the tracked satellite count
      	int trackedCount = 0;
      	foreach (Satellite item in Satellites)
      		if (item.SignalToNoiseRatio.Value > 0)
      			trackedCount++;
      
      	// Yes
      	_GpggaLastSent = UtcDateTime;
      	
      	// Queue the sentence to the read buffer
      	WriteSentenceToClient(new GpggaSentence(UtcDateTime.TimeOfDay, CurrentPosition, _FixQuality, trackedCount,
      		_HorizontalDOP, Altitude, Distance.Empty, TimeSpan.Zero, -1));
      }
      
    3. The result is that the NmeaEmulator start running correctly and generating data every 1 second.
  4. I'm waiting to test this workaround with the RouteEmulator.

Can you tell me why the emulator did not entered one of the above conditions ($GPGGA, $GPRMC and etc.) ?

Should I provide the emulator with more data ?

I'm trying to figure out why this condition:

 

UtcDateTime.Subtract(_GpggaLastSent) > _GpggaInterval

 

never met.

Thanks you very much for spending the time to help me out.

 

 

Developer
May 25, 2010 at 4:31 PM

Okay, I think I might have figured it out.  What time zone are you in?  Or, more appropriately, what time zone do you have your computers set to?  I'm guessing that if you change your computer's time zone to CST, it will work fine.  The reason is that the OnEmulation method is comparing UTC date/times to local date/times (which is not correct).  The problem is at the top of the NmeaEmulator class:

private DateTime _GpggaLastSent = DateTime.Now;
private DateTime _GpgsaLastSent = DateTime.Now;
private DateTime _GpgllLastSent = DateTime.Now;
private DateTime _GpgsvLastSent = DateTime.Now;
private DateTime _GprmcLastSent = DateTime.Now;

That code should be changed to use UTC date/times, like this:

private DateTime _GpggaLastSent = DateTime.UtcNow;
private DateTime _GpgsaLastSent = DateTime.UtcNow;
private DateTime _GpgllLastSent = DateTime.UtcNow;
private DateTime _GpgsvLastSent = DateTime.UtcNow;
private DateTime _GprmcLastSent = DateTime.UtcNow;

Try making that change and let me know if it solves the problem for you. 

 

 

 

May 25, 2010 at 4:57 PM

FIXED

Well, after this fix everything seems to work flawlessly, my time zone is currently set to GMT+2, changing these lines fixed the problem.

Thank you very much for your time and work.

Developer
May 25, 2010 at 5:06 PM

Awesome! I'm glad to hear that fixed it for you.  I've checked the fixed code into CodePlex.

May 27, 2010 at 9:51 AM
Edited May 27, 2010 at 9:53 AM

Hi all,

the TimeoutException also occurs if one uses the TextFileEmulator. But in this case it seems the NmeaReader does not recognise the end of a line properly.

In my opinion the reason is the OnEmulation() method in the TextFileEmulator class. After

// Read a line from the file
            string line = _Reader.ReadLine()

the variable line does not contain any trailing  control characters. I don't know if it is an elegant solution, but appending an line feed  fixes the problem.

 

            string line = _Reader.ReadLine() + "\n";

 

Furthermore i would like to propose adding the line

ReadDataAvailableWaitHandle.Set();

after

ReadBuffer.AddRange(ASCIIEncoding.ASCII.GetBytes(line));

to signal the Parsing Thread.

 

So "my" OnEmulation() method looks like this:

 

protected override void OnEmulation()
        {
            // Are we at the end of the file?
            if (_Reader == null || _Reader.EndOfStream)
            {
                // Yes.  Re-open it from the beginning
                FileStream stream = new FileStream(_FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                _Reader = new StreamReader(stream);
            }

            // Read a line from the file
            string line = _Reader.ReadLine() + "\n";
            
            // Don't write to the buffer if it's full
            if (ReadBuffer.Count + line.Length > ReadBuffer.Capacity)
                return;

            // Write the string
            ReadBuffer.AddRange(ASCIIEncoding.ASCII.GetBytes(line));
            ReadDataAvailableWaitHandle.Set();

            // Sleep
#if PocketPC
            Thread.Sleep((int)_ReadInterval.TotalMilliseconds);
#else
            Thread.Sleep(_ReadInterval);
#endif
        }

 

Regards,

azel