In the last post I described how I wanted to build a GPS input to my VFO, and use this to display my Locator, from the Lat & Lon data, and calibrate the time and date of my RTC. There is a little progress I can report. the VFO has a new 3.5mm socket on the back to connect to the GPS. After a mixup, I have solved the problems of getting the right connections to the GPS and to the Arduino. Arduino pin 12 is the data input from the GPS, connected to the GPS TX output, and pin 13 is connected to the GPS RX input.
VFO with 3.5mm socket for GPS input
I have some code working - see below.
VFO with time and date set from GPS
Power up the VFO by USB. Then plug in the GPS. push the button and there is a GPS FIX the RTC will be programmed. The display will show the RTC date time and the Maidenhead Locator. WSPR or JT65 sketches can now be loaded on the VFO and will transmit at the correct time (on UTC minute)
It may be possible to integrate this GPS calibration into the WSPR and JT65 sketches, but there may not be enough memory to do this - I have a vague plan to store the MH in EEPROM so it accessible by WSPR programs.
CODE
// DATE_TIME_MH_SET_GPS
// V2.5 read GPS time, fix & date, calc dow, set RTC, store MH
// display RTC date & time, MH
// GPS VK-163 jack connections
// 1 tip VCC
// 2 TX GPS output
// 3 RX GPS input
// 4 ring GND
#include "Oled_128X64_I2C.h"
#include "SoftwareSerial.h"
#include "Wire.h"
#include "EEPROM.h"
// Arduino pins TX GPS -> RX(12), RX GPS <- TX(13), button
#define RX 12
#define TX 13
#define SW 4
// RTC address
#define RTCADDR 0x68
// 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];
// RTC data, decimal
byte hrs, mns, sec;
byte yr, mth, dy;
byte dow;
double lat, lon;
// Serial object
SoftwareSerial gps(RX, TX);
// ============= setup ==============
void setup() {
// pins
pinMode(RX, INPUT);
pinMode(TX, OUTPUT);
pinMode(SW, INPUT_PULLUP);
// I2C init
Wire.begin();
// OLED init, I2C addr 0x3C
oled.begin();
// GPS serial init
gps.begin(9600);
// init GPS void
strcpy(fix, "V");
}
// ============== loop ===============
void loop() {
dispUpdate();
// if buuton pressed, get GPS data
if (digitalRead(SW) == LOW) {
strcpy(fix, "V"); // set V for display update
dispUpdate();
getGPS(); // get GPS, extract data
if (strcmp(fix, "A") == 0) { // when GPS Aquired
setRTC(); // set RTC date time dow
setMH(); // calculate MH Locator
}
}
getRTC(); // read RTC
}
// ========= GPS funcitons =========
// 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
}
// ================ RTC functions SET =================
// set date and time bytes to RTC BCD
void setRTC() {
// 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);
// program RTC
Wire.beginTransmission(RTCADDR);
Wire.write(0); // next input at sec register
Wire.write(decToBcd(sec)); // set seconds
Wire.write(decToBcd(mns)); // set minutes
Wire.write(decToBcd(hrs)); // set hours
Wire.write(decToBcd(dow)); // set day of week
Wire.write(decToBcd(dy)); // set date (1 to 31)
Wire.write(decToBcd(mth)); // set month (1-12)
Wire.write(decToBcd(yr)); // set year (0 to 99)
Wire.endTransmission();
}
// 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
}
// Convert decimal to BCD
byte decToBcd(byte dec)
{
return ( (dec / 10 * 16) + (dec % 10) );
}
// 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;
// now we have day number such that 0000-01-01(Sat) is day 1
return (byte)(((days + 5) % 7));
}
// ====================Maidenhead functions================
// calculate maideng=head locator from lat & lon
void setMH() {
// 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);
findMH(mh, lat, lon);
saveMH(); // save in EEPROM
}
// 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
}
// find MH from lat & lon
void findMH(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';
}
// save mh in EEPROM bytes 0-6
void saveMH() {
int addr;
byte i;
addr = 0;
i = 0;
while(i < strlen(mh)) // write MMnnMM to EEPROM
EEPROM.write(addr++, mh[i++]);
EEPROM.write(addr, '\0'); // terminate string
}
// ====================RTC functions READ =================
// get time from RTC, convert BCD to decimal
void getRTC() {
// Reset the RTC register pointer
Wire.beginTransmission(RTCADDR);
Wire.write(0);
Wire.endTransmission();
// request 7 bytes from the RTC address
Wire.requestFrom(RTCADDR, 7);
// get the time date
sec = bcdToDec(Wire.read()); // 0 - 59
mns = bcdToDec(Wire.read()); // 0 - 59
hrs = bcdToDec(Wire.read() & 0b111111); // mask 12/24 bit
dow = bcdToDec(Wire.read()); // 0 = Sunday
dy = bcdToDec(Wire.read()); // 1 - 31
mth = bcdToDec(Wire.read()); // 0 = jan
yr = bcdToDec(Wire.read()); // ..yy
}
// Convert BCD to decimal numbers
byte bcdToDec(byte val) {
return ( (val / 16 * 10) + (val % 16) );
}
// ============Picture Display ===============
// picture loop
void dispUpdate() {
oled.firstPage();
do {
dispMsg(55, 0, "GPS");
if (strcmp(fix, "V") == 0) { // no fix V
dispMsgL(30, 15, "GET GPS");
dispMsg(25, 45, "Press Button");
}
else {
dispMsgL(30, 15, mh); // fix A
dispDate(15, 32, dow, dy, mth, yr);
dispTimeL(25, 45, hrs, mns, sec);
}
} while ( oled.nextPage() );
}
No comments:
Post a Comment