
Find below an updated version of my Arduino Scripted GPS Simulator. Here are some of the program’s features:
- Configurable update rates: 1, 5 and 10Hz.
- Outputs NMEA RMC Sentences (can easily be converted for others).
- 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;
}
}
}
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!
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