There are some very cheap GPS receivers on the market today, notably GPS originally designed for plugging into dash-cams. These receivers have a 4-way 3.5mm jack connection, and send out 9600 baud serial data in ASCII format.
You can also buy very cheap Arduino Nano boards and cheap OLED 128x64 pixel displays.
Putting this all together you can have a useful tool for amateur radio to display your latitude, longitude, Maidenhead Locator, date and time. Here are few of the code snippets useful for handling the data
HEADER definitions, libraries, defines and variables
#include "Oled_128X64_I2C.h" #include "SoftwareSerial.h" #include "Wire.h" // GPS connections, #define FROMGPS 12 #define TOGPS 13 #define SW 4 // GPS data buffer char gpsbuf[200]; // data extracted from $GPRMC, ACSII char tm[20]; // time HHMMSS char fix[5]; // fix A|V, init void char dt[20]; // date YYMMDD char la[15]; // latitude char ns[2]; // NS char lo[15]; // longitude char ew[2]; // EW // Maidenhead Locator char mh[10] = ""; // Date, Time Lat & lon decimal byte hrs, mns, sec; byte yr, mth, dy; byte dow; double lat, lon; // Serial object RX TX SoftwareSerial gps(FROMGPS, TOGPS);
CODE for setup
void setup() {
// pins
pinMode(FROMGPS, INPUT);
pinMode(TOGPS, OUTPUT);
pinMode(SW, INPUT_PULLUP);
// I2C init
Wire.begin();
// OLED init, I2C addr 0x3C
oled.begin();
// GPS serial init
gps.begin(9600);
strcpy(fix, "V");
dispUpdate();
}The basic loop CODE
void loop() {
getGPS(); // get GPS, extract data
if (strcmp(fix, "A") == 0) { // when GPS Aquired
getDateTime();
getMH();
dispUpdate();
}
}And finally the functions
// get RMC line data
void getGPS() {
do {
getline(gpsbuf);
} while (strncmp(gpsbuf, "$GPRMC", 6) != 0);
// extract strings from $GPRMC fields
xtract(gpsbuf, 1, tm); // time HHMMSS
xtract(gpsbuf, 2, fix); // fix A or V
xtract(gpsbuf, 9, dt); // date YYMMDD
xtract(gpsbuf, 3, la); // latitude
xtract(gpsbuf, 4, ns); // NS
xtract(gpsbuf, 5, lo); // longitude
xtract(gpsbuf, 6, ew); // EW
}
// get a line from the GPS, inc /r/n, add /0
void getline(char *out) {
char c;
int p;
p = 0; // buffer pointer
do {
if (gps.available() > 0) { // data?
c = gps.read(); // read character
out[p++] = c; // put in buffer
}
} while ( c != '\n' ); // stop on /n
out[p] = '\0'; // terminate string
}
// extract field and return string in outbuf
void xtract(char *in, int field, char *out) {
int ip = 0; // input buffer pointer
int op = 0; // output buffer pointer
int f = 0; // field counter
while (f < field) { // find start of field, ip
while (in[ip++] != ',');
f++;
}
while (in[ip] != ',') { // scan to next ','
out[op++] = in[ip++]; // copy in to out
}
out[op] = '\0'; // terminate out string
}
// ================ Date & Time, Dow =================
void getDateTime() {
// get GPS data in bytes, calc dow
hrs = strtob(tm, 0); // HH....
mns = strtob(tm, 2); // ..MM..
sec = strtob(tm, 4); // ....SS
dy = strtob(dt, 0); // DD....
mth = strtob(dt, 2); // ..MM..
yr = strtob(dt, 4); // ....YY
dow = calcDow(yr, mth, dy);
}
// convert ASCII (0-99), starting at bp, to byte
byte strtob(char *in, int bp) {
char out[20];
strncpy(out, in + bp, 2); // copy 2 char
return (byte)atoi(out); // return byte
}
// calc dow
byte calcDow(byte year, byte month, byte day)
{
unsigned long days;
unsigned int febs;
unsigned int months[] =
{
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 // days until 1st of month
};
days = year * 365; // days up to year
febs = year;
if (month > 2) febs++; // number of completed Februaries
// add in the leap days
days += ((febs + 3) / 4);
days -= ((febs + 99) / 100);
days += ((febs + 399) / 400);
days += months[month - 1] + day;
return (byte)(((days + 5) % 7)); // sun = 0
}
// ====================Maidenhead functions================
// calculate maideng=head locator from lat & lon
void getMH() {
// extract lat * lon from GPS data
xtract(gpsbuf, 3, la);
xtract(gpsbuf, 4, ns);
xtract(gpsbuf, 5, lo);
xtract(gpsbuf, 6, ew);
lat = convertPos(la, ns);
lon = convertPos(lo, ew);
calcMH(mh, lat, lon);
}
// convert Lat, Lon strings to decimal +/-NS|EW
double convertPos(char *pos, char *d) {
double pp, mm, ans;
int dd;
pp = atof(pos); // get in decimal ddmm.mmmmmmm
dd = (int)pp / 100; // get degrees part
mm = pp - (100 * dd); // get minutes
ans = dd + (double)mm / 60.0; // calc decimal degrees
if (strcmp(d, "N") == 0 || strcmp(d, "E") == 0) // if positive
return ans;
else
return - ans; // negative
}
// calc MH from lat & lon
void calcMH(char *dst, double fa, double fo) {
int a1, a2, a3;
int o1, o2, o3;
double rd;
// Latitude
rd = fa + 90.0;
a1 = (int)(rd / 10.0);
rd = rd - (double)a1 * 10.0;
a2 = (int)(rd);
rd = rd - (double)a2;
a3 = (int)(24.0 * rd);
// Longitude
rd = fo + 180.0;
o1 = (int)(rd / 20.0);
rd = rd - (double)o1 * 20.0;
o2 = (int)(rd / 2.0);
rd = rd - 2.0 * (double)o2;
o3 = (int)(12.0 * rd);
dst[0] = (char)o1 + 'A';
dst[1] = (char)a1 + 'A';
dst[2] = (char)o2 + '0';
dst[3] = (char)a2 + '0';
dst[4] = (char)o3 + 'A';
dst[5] = (char)a3 + 'A';
dst[6] = '\0';
}
// ============Picture Display ===============
// picture loop
void dispUpdate() {
oled.firstPage();
do {
dispMsg(55, 0, "GPS");
if (strcmp(fix, "V") == 0) {
dispMsgL(10, 25, "NO GPS");
}
else if (strcmp(fix, "A") == 0) {
dispMsg(10, 12, ns);
dispNum(25, 12, lat, 2);
dispMsg(75, 12, ew);
dispNum(90, 12, lon, 2);
dispMsg(45, 25, mh);
dispDate(15, 37, dow, dy, mth, yr);
dispTime(35, 52, hrs, mns, sec);
}
} while ( oled.nextPage() );
}
No comments:
Post a Comment