Skip to content

Commit

Permalink
one more step refactoring from model_gps to model_breadcrumbs
Browse files Browse the repository at this point in the history
  • Loading branch information
barry-ha committed Apr 2, 2023
1 parent f3feaf6 commit af625e9
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 294 deletions.
44 changes: 22 additions & 22 deletions Griduino.ino
Original file line number Diff line number Diff line change
Expand Up @@ -225,21 +225,6 @@ void floatToCharArray(char* result, int maxlen, double fValue, int decimalPlaces
temp.toCharArray(result, maxlen);
}

//==============================================================
// Breadcrumb Trail model
//==============================================================
#include "model_breadcrumbs.h"
Breadcrumbs trail;

//==============================================================
// Coin Battery Voltage model
//==============================================================
// PCB v7 added a sensor for coin battery voltage
// PCB v4 doesn't measure coin battery
// The hardware differences are handled in lower level code
#include "model_adc.h" // Model of the analog-digital converter
BatteryVoltage gpsBattery;

//==============================================================
//
// GPS Model
Expand Down Expand Up @@ -286,6 +271,21 @@ int fGetDataSource() {

bool waitingForRTC = true; // true=waiting for GPS hardware to give us the first valid date/time

//==============================================================
// Breadcrumb Trail model
//==============================================================
#include "model_breadcrumbs.h"
Breadcrumbs trail;

//==============================================================
// Coin Battery Voltage model
//==============================================================
// PCB v7 added a sensor for coin battery voltage
// PCB v4 doesn't measure coin battery
// The hardware differences are handled in lower level code
#include "model_adc.h" // Model of the analog-digital converter
BatteryVoltage gpsBattery;

//==============================================================
//
// BarometerModel
Expand Down Expand Up @@ -750,19 +750,19 @@ void setup() {
// Only set DAC resolution on devices that have a DAC
analogWriteResolution(12); // 1..32, sets DAC output resolution to 12 bit (4096 levels)
// because Feather M4 maximum output resolution is 12 bit
dacMorse.setup(); // required Morse Code initialization
dacMorse.dump(); // debug
dacMorse.setup(); // required Morse Code initialization
dacMorse.dump(); // debug

dacSpeech.begin(); // required Audio_QSPI initialization
//sayGrid("k7bwh"); // debug test
dacSpeech.begin(); // required Audio_QSPI initialization
//sayGrid("k7bwh"); // debug test
#endif

// ----- init onboard LED
pinMode(RED_LED, OUTPUT); // diagnostics RED LED

// ----- restore GPS driving track breadcrumb trail
model->restore(); // this takes noticeable time (~0.2 sec)
model->restoreGPSBreadcrumbTrail(); //
trail.restoreGPSBreadcrumbTrail(); // this takes noticeable time (~0.2 sec)
model->restore(); //
model->gHaveGPSfix = false; // assume no satellite signal yet
model->gSatellites = 0;

Expand Down Expand Up @@ -865,7 +865,7 @@ void loop() {
Serial.println(msg); // debug

// write this to the GPS breadcrumb trail as indication of "power up" event
//model->remember(); // todo: create new event type "PUP" to save in history buffer
// trail.remember(); // todo: create new event type "PUP" to save in history buffer
}

// every 1 second update the realtime clock
Expand Down
4 changes: 2 additions & 2 deletions cfg_gps.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ class ViewCfgGPS : public View {
void ViewCfgGPS::updateScreen() {
// called on every pass through main()

// ----- fill in replacment string text
// ----- fill in replacement string text
char temp[100];
snprintf(temp, sizeof(temp), "%d of %d", model->getHistoryCount(), trail.numHistory);
snprintf(temp, sizeof(temp), "%d of %d", trail.getHistoryCount(), trail.numHistory);
txtSettings2[TRAILCOUNT].print(temp);

// ----- show selected radio buttons by filling in the circle
Expand Down
257 changes: 248 additions & 9 deletions model_breadcrumbs.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
File: model_breadcrumbs.h
Contains everything about the breadcrumb history trail.
This is a history[] array which periodically does backups to a CSV file.
Software: Barry Hansen, K7BWH, barry@k7bwh.com, Seattle, WA
Hardware: John Vanderbeck, KM7O, Seattle, WA
Expand All @@ -13,19 +14,20 @@
// #include <Adafruit_GPS.h> // "Ultimate GPS" library
// #include "constants.h" // Griduino constants, colors, typedefs
// #include "hardware.h" //
// #include "logger.h" // conditional printing to Serial port
// #include "grid_helper.h" // lat/long conversion routines
// #include "date_helper.h" // date/time conversions
// #include "save_restore.h" // Configuration data in nonvolatile RAM
#include "logger.h" // conditional printing to Serial port
#include "grid_helper.h" // lat/long conversion routines
#include "date_helper.h" // date/time conversions
#include "save_restore.h" // Configuration data in nonvolatile RAM

// ========== extern ===========================================
// extern Adafruit_GPS GPS; // Griduino.ino
// extern Logger logger; // Griduino.ino
// extern Grids grid; // grid_helper.h
// extern Dates date; // date_helper.h
extern Logger logger; // Griduino.ino
extern Grids grid; // grid_helper.h
extern Dates date; // date_helper.h
// extern Model *model; // "model" portion of model-view-controller

// void floatToCharArray(char *result, int maxlen, double fValue, int decimalPlaces); // Griduino.ino
// bool isVisibleDistance(const PointGPS from, const PointGPS to); // view_grid.cpp
void floatToCharArray(char *result, int maxlen, double fValue, int decimalPlaces); // Griduino.ino
bool isVisibleDistance(const PointGPS from, const PointGPS to); // view_grid.cpp

// Size of GPS breadcrumb trail:
// Our goal is to keep track of at least one long day's travel, 500 miles or more.
Expand All @@ -41,12 +43,249 @@ class Breadcrumbs {
int nextHistoryItem = 0; // index of next item to write
Location history[3000]; // remember a list of GPS coordinates and stuff
const int numHistory = sizeof(history) / sizeof(Location);
int saveInterval = 2;

protected:
public:
// Constructor - create and initialize member variables
Breadcrumbs() {}

void remember(Location vLoc) {
// save this GPS location and timestamp in internal array
// so that we can display it as a breadcrumb trail
// optionally write the breadcrumb trail to file

int prevIndex = nextHistoryItem - 1; // find prev location in circular buffer
if (prevIndex < 0) {
prevIndex = numHistory - 1;
}
PointGPS prevLoc = history[prevIndex].loc;
if (isVisibleDistance(vLoc.loc, prevLoc)) {
history[nextHistoryItem] = vLoc;

nextHistoryItem = (++nextHistoryItem % numHistory);

// 2023-04-01 todo:
// . don't reach into the "model" from here and tell it what to do
// . this "breadcrumbs" object should just only manage itself
// now the GPS location is saved in history array, now protect
// the array in non-volatile memory in case of power loss
// if (nextHistoryItem % saveInterval == 0) {
// model->save(); // filename is #define MODEL_FILE[25] above
// }
}
}

// ----- save GPS history[] to non-volatile memory as CSV file -----
const char HISTORY_FILE[25] = CONFIG_FOLDER "/gpshistory.csv"; // CONFIG_FOLDER
const char HISTORY_VERSION[25] = "GPS Breadcrumb Trail v2"; // <-- always change version when changing data format

int saveGPSBreadcrumbTrail() { // returns 1=success, 0=failure
// our breadcrumb trail file is CSV format -- you can open this Arduino file directly in a spreadsheet
// dumpHistoryGPS(); // debug

// delete old file and open new file
SaveRestoreStrings config(HISTORY_FILE, HISTORY_VERSION);
config.open(HISTORY_FILE, "w");

// line 1,2,3,4: filename, data format, version, compiled
char msg[256];
snprintf(msg, sizeof(msg), "File:,%s\nData format:,%s\nGriduino:,%s\nCompiled:,%s",
HISTORY_FILE, HISTORY_VERSION, PROGRAM_VERSION, PROGRAM_COMPILED);
config.writeLine(msg);

// line 5: column headings
config.writeLine("GMT Date, GMT Time, Grid, Latitude, Longitude, Altitude, MPH, Direction, Satellites");

// line 6..x: date-time, grid6, latitude, longitude
int count = 0;
for (uint ii = 0; ii < numHistory; ii++) {
if (!history[ii].isEmpty()) {
count++;

char sDate[12]; // sizeof("2022-11-10") = 10
date.dateToString(sDate, sizeof(sDate), history[ii].timestamp);

char sTime[12]; // sizeof("12:34:56") = 8
date.timeToString(sTime, sizeof(sTime), history[ii].timestamp);

char sGrid6[7];
grid.calcLocator(sGrid6, history[ii].loc.lat, history[ii].loc.lng, 6);

char sLat[12], sLng[12];
floatToCharArray(sLat, sizeof(sLat), history[ii].loc.lat, 5);
floatToCharArray(sLng, sizeof(sLng), history[ii].loc.lng, 5);

char sAlt[12], sSpeed[12], sAngle[12], sSats[6];
floatToCharArray(sAlt, sizeof(sAlt), history[ii].altitude, 1);
floatToCharArray(sSpeed, sizeof(sSpeed), history[ii].speed, 1);
floatToCharArray(sAngle, sizeof(sAngle), history[ii].direction, 1);
int numSatellites = history[ii].numSatellites;

snprintf(msg, sizeof(msg), "%s,%s,%s,%s,%s,%s,%s,%s,%d", sDate, sTime, sGrid6, sLat, sLng, sAlt, sSpeed, sAngle, numSatellites);
config.writeLine(msg);
}
}
logger.info(". Wrote %d entries to GPS log", count);

// close file
config.close();

return 1; // success
}

int restoreGPSBreadcrumbTrail() { // returns 1=success, 0=failure
clearHistory(); // clear breadcrumb memory

// open file
SaveRestoreStrings config(HISTORY_FILE, HISTORY_VERSION);
if (!config.open(HISTORY_FILE, "r")) {
logger.error("SaveRestoreStrings::open() failed to open ", HISTORY_FILE);

// most likely error is 'file not found' so create a new one for next time
saveGPSBreadcrumbTrail();
return 0;
}

// read file line-by-line, ignoring lines we don't understand
// for maximum compatibility across versions, there's no "version check"
// example of CSV line: "2022/06/16,15:44:01,48.09667,-122.85268"
int csv_line_number = 0;
int items_restored = 0;
char csv_line[256], original_line[256];
const char delimiter[] = ",/:";
int count;
bool done = false;
while (count = config.readLine(csv_line, sizeof(csv_line)) && !done) {
// save line for possible console messages because 'strtok' will modify buffer
strncpy(original_line, csv_line, sizeof(original_line));

// process line according to # bytes read
char msg[256];
if (count == 0) {
logger.info(". EOF");
done = true;
break;
} else if (count < 0) {
int err = config.getError();
logger.error(". File error %d", err); // 1=write, 2=read
done = true;
break;
} else {
// snprintf(msg, sizeof(msg), ". CSV string[%2d] = \"%s\"",
// csv_line_number, csv_line); // debug
// logger.info(msg); // debug
int iYear4 = atoi(strtok(csv_line, delimiter)); // YYYY: calendar year
uint8_t iYear2 = CalendarYrToTm(iYear4); // YY: offset from 1970
uint8_t iMonth = atoi(strtok(NULL, delimiter));
uint8_t iDay = atoi(strtok(NULL, delimiter));
uint8_t iHour = atoi(strtok(NULL, delimiter));
uint8_t iMinute = atoi(strtok(NULL, delimiter));
uint8_t iSecond = atoi(strtok(NULL, delimiter));
// must match same order in saveGPSBreadcrumbTrail()
// "GMT Date, GMT Time, Grid, Latitude, Longitude, Altitude, MPH, Direction, Satellites"
double fLatitude = atof(strtok(NULL, delimiter));
double fLongitude = atof(strtok(NULL, delimiter));
double fAltitude = atof(strtok(NULL, delimiter));
double fSpeed = atof(strtok(NULL, delimiter));
double fDirection = atof(strtok(NULL, delimiter));
int fSatellites = atoi(strtok(NULL, delimiter));

// save this return value into history[]
// https://cplusplus.com/reference/cstring/
if (iYear2 > 0 && fLatitude != 0.0 && fLongitude != 0.0) {
// echo info for debug
// char msg[256];
// snprintf(msg, sizeof(msg), ". Internal = %d-%d-%d, %02d:%02d:%02d",
// iYear4, iMonth, iDay, iHour, iMinute, iSecond);
// logger.info(msg);
// Serial.print(fLatitude, 5); // todo: replace with 'printLocation(index,Location item)'
// Serial.print(", ");
// Serial.println(fLongitude, 5);

// save values in the history[] array
TimeElements tm{iSecond, iMinute, iHour, 0, iDay, iMonth, iYear2};
history[nextHistoryItem].timestamp = makeTime(tm); // convert time elements into time_t
history[nextHistoryItem].loc.lat = fLatitude;
history[nextHistoryItem].loc.lng = fLongitude;
history[nextHistoryItem].altitude = fAltitude;
history[nextHistoryItem].numSatellites = fSatellites;
history[nextHistoryItem].speed = fSpeed;
history[nextHistoryItem].direction = fDirection;
history[nextHistoryItem].numSatellites = fSatellites;

// adjust loop variables
nextHistoryItem++;
items_restored++;
} else {
snprintf(msg, sizeof(msg), ". CSV string[%2d] = \"%s\" - ignored",
csv_line_number, original_line); // debug
logger.warning(msg); // debug
}
}
csv_line[0] = 0;
csv_line_number++;
if (nextHistoryItem >= numHistory) {
done = true;
}
}
logger.info(". Restored %d breadcrumbs from %d lines in CSV file", items_restored, csv_line_number);

// This "restore" design always fills history[] from 0..N.
// Make sure the /next/ GPS point logged goes into the next open slot
// and doesn't overwrite any historical data.
int indexOldest = 0; // default to start
TimeElements future{59, 59, 23, 0, 1, 1, 255}; // maximum date = year(1970 + 255) = 2,225
time_t oldest = makeTime(future);

int indexNewest = 0; // default to start
TimeElements past{0, 0, 0, 0, 0, 0, 0}; // minimum date = Jan 1, 1970
time_t newest = makeTime(past);

// find the oldest item (unused slots contain zero and are automatically the oldest)
for (int ii = 0; ii < numHistory; ii++) {
time_t tm = history[ii].timestamp;
if (tm < oldest) {
// keep track of oldest GPS bread crumb
indexOldest = ii;
oldest = tm;
}
if (tm > newest) {
// keep track of most recent GPS bread crumb, out of curiosity
indexNewest = ii;
newest = tm;
}
}
// here's the real meat of the potato
nextHistoryItem = indexOldest;

// report statistics for a visible sanity check to aid debug
char sOldest[24], sNewest[24];
date.datetimeToString(sOldest, sizeof(sOldest), oldest);
date.datetimeToString(sNewest, sizeof(sNewest), newest);

char msg1[256], msg2[256];
snprintf(msg1, sizeof(msg1), ". Oldest date = history[%d] = %s", indexOldest, sOldest);
snprintf(msg2, sizeof(msg2), ". Newest date = history[%d] = %s", indexNewest, sNewest);
logger.info(msg1);
logger.info(msg2);

// close file
config.close();
return 1; // success
}

const int getHistoryCount() {
// how many history slots currently have valid position data
int count = 0;
for (uint ii = 0; ii < numHistory; ii++) {
if (!history[ii].isEmpty()) {
count++;
}
}
return count;
}

void clearHistory() {
// wipe clean the array of lat/long that we remember
for (uint ii = 0; ii < numHistory; ii++) {
Expand Down

0 comments on commit af625e9

Please sign in to comment.