Arduino Scripted GPS Simulator v2.0

clapper board

Find below an updated version of my Arduino Scripted GPS Simulator. Here are some of the program’s features:

  1. Configurable update rates: 1, 5 and 10Hz.
  2. Outputs NMEA RMC Sentences (can easily be converted for others).
  3. Uses Arduino DataFlash Library to run a “scripted” course and speed.

See this post for an example of the “scripted data” stored inside the dataflash eeprom, and this post for an example C# program to transfer a text file of “scripted data” from a PC to the Adruino/dataflash.

Code:

/*
  Arduino GPS Scripted Simulator
  Released into the public domain
  James M. Eli
  3/12/2012
  
  Note: No checksum calculation for the NMEA sentences (checksum output is XX). 
      If you need to do this, it’s very simple as it’s just a Hex representation of the XOR of 
      all characters in the sentence between (not including) the $ and the * character.
      //XOR the received data
      checksum ^= gprmc[i];
 
  Sentence format:
    $GPRMC,184331.200,V,45.59462,N,122.692900,W,68.39,63.60,80212,,,A*xx
  Specific GPS data is extracted from dataflash:
                  2     45.594620  122.692900   068.39063.60
    245.594620122.692900068.39063.60 (32 chars)
*/
#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <DataFlash.h>

//define update rate
#define UPDATE_RATE_5HZ

//10Hz update rate definitions
#ifdef UPDATE_RATE_10HZ
#define TCNT_BASE  0x9E58
#define UPDATE_RATE_PER_SECOND 10
#endif
//5Hz update rate definitions
#ifdef UPDATE_RATE_5HZ
#define TCNT_BASE  0x3CB0
#define UPDATE_RATE_PER_SECOND 5
#endif
//1Hz update rate definitions
#ifdef UPDATE_RATE_1HZ
#define TCNT_BASE  0x0BDC
#define UPDATE_RATE_PER_SECOND 1
#endif

//dataflash page size (bytes)
#define PAGE_SIZE 512
#define BYTES_PER_LINE 32
#define MAX_LINES 32
 
char sec[2];
char lat[10];
char lng[11];
char hdg[7], *phdg;
char spd[7], *pspd;
volatile uint8_t t4 = 10;
volatile uint8_t t3 = 30;
volatile uint8_t t2 = 0;
volatile uint8_t t1 = 0;
volatile boolean update_flag = 0;
uint32_t lines;
char gprmc[96];

void TimerInit() {
  cli();
  //allow oscillator to stabilize
  _delay_ms(1000);
  //disable the timer 1 and 2 interrupts
  TIMSK1 &= ~((1<<OCIE1A) | (1<<OCIE1B) | (1<<TOIE1));
  TCCR1A = 0;
  TCCR1C = 0;
#ifdef UPDATE_RATE_1HZ
  //select prescaler: 16MHz/256/62500=1Hz overflow
  TCCR1B = (1<<CS12);
#else
  //select prescaler: 16MHz/64/50000=5Hz overflow or 16MHz/64/25000=10Hz overflow
  TCCR1B = (1<<CS11) | (1<<CS10);
#endif
  TIFR1 = ((1<<OCF1A) | (1<<OCF1B) | (1<<TOV1));
  //65536 – 15536 = 50000 counts
  TCNT1 = TCNT_BASE;
  TIMSK1 |= (1<<TOIE1);
}
 
void IncrementTime() {
  t1++;
  if (t1 > (UPDATE_RATE_PER_SECOND - 1)) {
    t1 = 0;
    t2++;
  }	
  if (t2 > 59) {
    t2 = 0;
    t3++;
  }	
  if (t3 > 59) {
    t3 = 0;
    t4++;
  }
  if (t4 > 23)
    t4 = 0;
}	
 
//timer overflow interrupt triggered every 5Hz
ISR(TIMER1_OVF_vect) {
  TCNT1 = TCNT_BASE;
  update_flag = true;
}

//
void ReadDataFlash(void) {
  uint8_t i;
  
  //time
  sec[0] = DataFlash.ReadByte();
  //latitude
  for (i=0; i<9; i++)
    lat[i] = DataFlash.ReadByte();
  //longitude
  for (i=0; i<10; i++)
    lng[i] = DataFlash.ReadByte();
  //heading
  for (i=0; i<6; i++)
    hdg[i] = DataFlash.ReadByte();
  //speed
  for (i=0; i<6; i++)
    spd[i] = DataFlash.ReadByte();
  //lines of data read
  if (++lines >= MAX_LINES) {
    DataFlash.StartRead(1);
    lines = 0;
  }
  //remove leading zeros
  pspd = spd;
  phdg = hdg;
  while (*pspd && *pspd == '0') 
    pspd++;
  while (*phdg && *phdg == '0') 
    phdg++;
}

void setup() {
  cli();
  update_flag = false;
  sec[1] = '\0';
  lat[10] = '\0';
  lng[11] = '\0';
  hdg[7] = '\0';
  spd[7] = '\0';
  lines = 0;
  //set pins here
  //
  Serial.begin(19200);
  TimerInit();
  DataFlash.Init();
  DataFlash.StartRead(1);
  sei();
}

void loop() {
  ReadDataFlash();
  while (1) {
    if (update_flag) {
      IncrementTime();
      //$GPRMC,184331.200,A,4856.3930,N,12247.4841,W,0.07,0.00,080112,,,A*71
      sprintf(gprmc,"$GPRMC,%2.2u%2.2u%2.2u.%1s00,A,%9s,N,%10s,W,%s,%s,080112,,,S*XX", t4, t3, t2, sec, lat, lng, pspd, phdg);
      Serial.println(gprmc);
      ReadDataFlash();
      update_flag = false;
    }
  }
}

Advertisements

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

2 Responses to Arduino Scripted GPS Simulator v2.0

  1. Jordan says:

    I am trying to learn how this GPS simulator works. Do you have a version with further explanation or comments on most lines? Thanks and neat blog!

    • Jim Eli says:

      Jordan,

      It’s a simple program and I apologize for the sparse commenting. It takes pre-formatted data stored in external memory, formats it into a GPS NEMA $GPRMC sentence and outputs (serial) at the proper GPS update rate. The data needs to be “pre-stored” onto some sort of external memory chip (in this case, I used flash memory). I posted information on the memory and the C# program used to load the memory chip elsewhere on my blog.

      With a little work, you could use the program to take some saved GPS data, format the data properly, load it onto a memory chip, and then use an arduino to read back the data, acting like the original GPS. All of the information is scattered about my blog.

      Jim

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s