Confused by GPS logger example code?

Hello,I am using an adafruit ultimate GPS logger shield with an Arduino Uno. I am running the example shield_sdlog sketch from the downloaded adafruit GPS library. I'm trying to modify the example program to log some other data side-by-side with the GPS NMEA sentences (literally side by side--I want the log file to contain a NMEA sentence, then several other values on the same line separated by semicolons, then another sentence on a new line). I have identified the part of the code that actually does the logging, and now I am trying to modify it, but I find that I don't even understand it well enough to do that.

Here is the full code of the example sketch:

#include <SPI.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include <avr/sleep.h>

// Ladyada's logger modified by Bill Greiman to use the SdFat library
//
// This code shows how to listen to the GPS module in an interrupt
// which allows the program to have more 'freedom' - just parse
// when a new NMEA sentence is available! Then access data when
// desired.
//
// Tested and works great with the Adafruit Ultimate GPS Shield
// using MTK33x9 chipset
//    ------> http://www.adafruit.com/products/
// Pick one up today at the Adafruit electronics shop 
// and help support open source hardware & software! -ada
// Fllybob added 10 sec logging option
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO  true
/* set to true to only log to SD when GPS has a fix, for debugging, keep it false */
#define LOG_FIXONLY false  

// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy

// Set the pins used
#define chipSelect 10
#define ledPin 13

File logfile;

// read a Hex value and return the decimal equivalent
uint8_t parseHex(char c) {
  if (c < '0')
    return 0;
  if (c <= '9')
    return c - '0';
  if (c < 'A')
    return 0;
  if (c <= 'F')
    return (c - 'A')+10;
}

// blink out an error code
void error(uint8_t errno) {
  /*
  if (SD.errorCode()) {
   putstring("SD error: ");
   Serial.print(card.errorCode(), HEX);
   Serial.print(',');
   Serial.println(card.errorData(), HEX);
   }
   */
  while(1) {
    uint8_t i;
    for (i=0; i<errno; i++) {
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
    }
    for (i=errno; i<10; i++) {
      delay(200);
    }
  }
}

void setup() {
  // for Leonardos, if you want to debug SD issues, uncomment this line
  // to see serial output
  //while (!Serial);

  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);
  Serial.println("\r\nUltimate GPSlogger Shield");
  pinMode(ledPin, OUTPUT);

  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);

  // see if the card is present and can be initialized:
  //if (!SD.begin(chipSelect, 11, 12, 13)) {
    if (!SD.begin(chipSelect)) {      // if you're using an UNO, you can use this line instead
    Serial.println("Card init. failed!");
    error(2);
  }
  char filename[15];
  strcpy(filename, "GPSLOG00.TXT");
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = '0' + i/10;
    filename[7] = '0' + i%10;
    // create if does not exist, do not open existing, write, sync after write
    if (! SD.exists(filename)) {
      break;
    }
  }

  logfile = SD.open(filename, FILE_WRITE);
  if( ! logfile ) {
    Serial.print("Couldnt create "); 
    Serial.println(filename);
    error(3);
  }
  Serial.print("Writing to "); 
  Serial.println(filename);

  // connect to the GPS at the desired rate
  GPS.begin(9600);

  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // uncomment this line to turn on only the "minimum recommended" data
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  // For logging data, we don't suggest using anything but either RMC only or RMC+GGA
  // to keep the log files at a reasonable size
  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 100 millihertz (once every 10 seconds), 1Hz or 5Hz update rate

  // Turn off updates on antenna status, if the firmware permits it
  GPS.sendCommand(PGCMD_NOANTENNA);

  // the nice thing about this code is you can have a timer0 interrupt go off
  // every 1 millisecond, and read data from the GPS for you. that makes the
  // loop code a heck of a lot easier!
  useInterrupt(true);

  Serial.println("Ready!");
}


// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  #ifdef UDR0
      if (GPSECHO)
        if (c) UDR0 = c;  
      // writing direct to UDR0 is much much faster than Serial.print 
      // but only one character can be written at a time. 
  #endif
}

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } 
  else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}

void loop() {
  if (! usingInterrupt) {
    // read data from the GPS in the 'main loop'
    char c = GPS.read();
    // if you want to debug, this is a good time to do it!
    if (GPSECHO)
      if (c) Serial.print(c);
  }
  
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences! 
    // so be very wary if using OUTPUT_ALLDATA and trying to print out data
    
    // Don't call lastNMEA more than once between parse calls!  Calling lastNMEA 
    // will clear the received flag and can cause very subtle race conditions if
    // new data comes in before parse is called again.
    char *stringptr = GPS.lastNMEA();
    
    if (!GPS.parse(stringptr))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another

    // Sentence parsed! 
    Serial.println("OK");
    if (LOG_FIXONLY && !GPS.fix) {
      Serial.print("No Fix");
      return;
    }

    // Rad. lets log it!
    Serial.println("Log");

    uint8_t stringsize = strlen(stringptr);
    if (stringsize != logfile.write((uint8_t *)stringptr, stringsize))    //write the string to the SD file
        error(4);
    if (strstr(stringptr, "RMC") || strstr(stringptr, "GGA"))   logfile.flush();
    Serial.println();
  }
}


/* End code */

Here is just the part that I am trying to understand/modify:

uint8_t stringsize = strlen(stringptr);
    if (stringsize != logfile.write((uint8_t *)stringptr, stringsize))    //write the string to the SD file
        error(4);
    if (strstr(stringptr, "RMC") || strstr(stringptr, "GGA"))   logfile.flush();
    Serial.println();

Basically I'm trying to remove whatever part of that code puts each sentence on a new line, so that I can add in my data and my own new line statement. However, as I mentioned, I am very confused here. First, understand why these statements seem to contain the actual logging part within an if statement (it was my previous understanding that if statements only decided what sections of other code to run, that they could do things by themselves). Second, I don't understand where the new lines are coming from, because I tried commenting out the Serial.println() statement, and the sentences still appear on different lines (and I'm sure I have the right section because when I comment it out entirely, no logging happens).

So does anyone know what's going in this section? Thanks in advance

    char *stringptr = GPS.lastNMEA();

Print the string that you get a pointer to:

Serial.print("GPS string: <");
Serial.print(stringptr);
Serial.println(">");

If you get

then the cr/lf is being added later.

If you get

<some stuff

Then the string contains the cr/lf, and you'll need to strip it off. Fortunately, that is easy.

Okay, I tried to comment out everything in the loop that prints to the serial monitor, then added in your code, except with delays in between each print statement (because I'm having a separate problem in which If I print stuff out to the serial monitor too quickly, I get a bunch of garbled characters). So right now, my loop code reads:

void loop() {

  
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences! 
    // so be very wary if using OUTPUT_ALLDATA and trying to print out data
    
    // Don't call lastNMEA more than once between parse calls!  Calling lastNMEA 
    // will clear the received flag and can cause very subtle race conditions if
    // new data comes in before parse is called again.
    char *stringptr = GPS.lastNMEA();
    
    if (!GPS.parse(stringptr))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another

    delay(500);
    Serial.print("GPS string: <");
    delay(500);
    Serial.print(stringptr);
    delay(500);
    Serial.println(">");
    delay(500);
  }
}

However, I apparently somehow didn't comment out everything else that prints to the serial monitor, because I'm still getting some NMEA sentences outside of the greater than and less than signs. I get the following output:

Ultimate GPSlogger Shield
Writing to GPSLOG07.TXT
$PMTK001,314,3*36
$GPGGA,190906.323,,,,,0,0,,,M,,M,,*4D
$GPRMReady!
$GPGGA,190908.323,,,,,0,0,,,M,,M,,*43
$GPRMC,190908.323,V,,,,,0.00,0.00,130815,,,N*48
GPS string: <
$GPRMC,190908.323,V,,,,,0.00,0.00,130815,,,N*48
$GPGGA,190909.323,,,,,0,0,,,M,,M,,*42
$GPRMC,190909.323,V,,,,,0.00,0.00,130815,,,N*49
>
$GPGGA,190910.323,,,,,0,0,,,M,,M,,*4A
$GPRMC,190910.323,V,,,,,0.00,0.00,130815,,,N*41
GPS string: <
$GPRMC,190910.323,V,,,,,0.00,0.00,130815,,,N*41
$GPGGA,190911.323,,,,,0,0,,,M,,M,,*4B
$GPRMC,190911.323,V,,,,,0.00,0.00,130815,,,N*40
>

Interestingly, but somewhat annoyingly, I tried running the sketch again just to make sure I got the same serial output format, and I didn't. After some testing, I have found that the sample serial output I pasted in the above comment occurs when I open the serial port immediately after uploading the program (I can re-create it by plugging in the board, re-uploading the program, and then opening the monitor), but I get a slightly different output if I just plug in the board and open up the serial monitor without re-uploading the program.

This is a sample of the second kind of output I get:

Ultimate GPSlogger Shield
Writing to GPSLOG10.TXT
$PMTK001,314,3*36
$PMTK001,220,3*30
$GPGGA,191212.445,,,,,0,0$GPGGA,191213.445,,,,,0,0,,,M,,M,,*44
$GPRMC,191213.445,V,,,,,0.00,0.00,130815,,,N*4F
Ready!
GPS string: <$GPGGA,191214.445,,,,,0,0,,,M,,M,,*43
$GPRMC,191214.445,V,,,,,0.00,0.00,130815,,,N*48

$GPRMC,191214.445,V,,,,,0.00,0.00,130815,,,N*48
>
$GPGGA,191215.445,,,,,0,0,,,M,,M,,*42
$GPRMC,191215.445,V,,,,,0.00,0.00,130815,,,N*49
GPS string: <$GPGGA,191216.445,,,,,0,0,,,M,,M,,*41
$GPRMC,191216.445,V,,,,,0.00,0.00,130815,,,N*4A

$GPRMC,191216.445,V,,,,,0.00,0.00,130815,,,N*4A
>
$GPGGA,191217.445,,,,,0,0,,,M,,M,,*40
$GPRMC,191217.445,V,,,,,0.00,0.00,130815,,,N*4B
GPS string: <$GPGGA,191218.445,,,,,0,0,,,M,,M,,*4F
$GPRMC,191218.445,V,,,,,0.00,0.00,130815,,,N*44

$GPRMC,191218.445,V,,,,,0.00,0.00,130815,,,N*44
>
$GPGGA,191219.445,,,,,0,0,,,M,,M,,*4E
$GPRMC,191219.445,V,,,,,0.00,0.00,130815,,,N*45
GPS string: <$GPGGA,191220.445,,,,,0,0,,,M,,M,,*44
$GPRMC,191220.445,V,,,,,0.00,0.00,130815,,,N*4F

$GPRMC,191220.445,V,,,,,0.00,0.00,130815,,,N*4F
>
$GPGGA,191221.445,,,,,0,0,,,M,,M,,*45
$GPRMC,191221.445,V,,,,,0.00,0.00,130815,,,N*4E
GPS string: <$GPGGA,191222.445,,,,,0,0,,,M,,M,,*46
$GPRMC,191222.445,V,,,,,0.00,0.00,130815,,,N*4D

$GPRMC,191222.445,V,,,,,0.00,0.00,130815,,,N*4D
>
$GPGGA,191223.445,,,,,0,0,,,M,,M,,*47
$GPRMC,191223.445,V,,,,,0.00,0.00,130815,,,N*4C
GPS string: <$GPGGA,191224.445,,,,,0,0,,,M,,M,,*40
$GPRMC,191224.445,V,,,,,0.00,0.00,130815,,,N*4B

So I really have no idea why these 2 are different, or why I'm getting sentences that are contained within the <> signs. The only thing I can definitively conclude is that there is definitely some kind of new line character embedded in the sentence (although it seems to be at an inconsistent location somehow).

There is a newline and carriage return at the end of every GPS sentence following the checksum value.

Okay, so how would I go about removing these? I looked around a little, and found that the string trim function should do exactly what I want, but it gives me an error when I try to use it here, presumably because I tried to use it on stringptr, which is not technically a string. Do I have to convert it to a string to do what I want, or is there a simpler way?

I use an array to hold my GPS sentences. I have that code in this thread,
http://forum.arduino.cc/index.php?topic=341711.0

Thanks, but it seems a little bit too complicated for what I want to do--I really just want to log each sentence to a log file, but in the exact format that I want. I decided to try just converting to a string and then trimming. The code in question now reads:

//convert stringptr character array to string gps_message so we can use the trim function
    String gps_message(stringptr);
    //trim string to remove embedded new line characters
    gps_message.trim();

    delay(500);
    Serial.print("GPS string: <");
    delay(500);
    Serial.print(gps_message);
    delay(500);
    Serial.println(">");
    delay(500);

This successfully removes the new line character at the beginning and the end of the string. However, I guess there are a couple embedded within the middle of the string as well, because this is my new serial monitor output:

GPS string: <$GPGGA,181849.957,,,,,0,0,,,M,,M,,*4E
$GPRMC,181849.957,V,,,,,0.00,0.00,170815,,,N*41
$GPRMC,181848.957,V,,,,,0.00,0.00,170815,,,N*40>
$GPGGA,181850.957,,,,,0,0,,,M,,M,,*46
$GPRMC,181850.957,V,,,,,0.00,0.00,170815,,,N*49
GPS string: <$GPGGA,181851.957,,,,,0,0,,,M,,M,,*47
$GPRMC,181851.957,V,,,,,0.00,0.00,170815,,,N*48
$GPRMC,181850.957,V,,,,,0.00,0.00,170815,,,N*49>
$GPGGA,181852.957,,,,,0,0,,,M,,M,,*44
$GPRMC,181852.957,V,,,,,0.00,0.00,170815,,,N*4B
GPS string: <$GPGGA,181853.957,,,,,0,0,,,M,,M,,*45
$GPRMC,181853.957,V,,,,,0.00,0.00,170815,,,N*4A
$GPRMC,181852.957,V,,,,,0.00,0.00,170815,,,N*4B>

Any suggestions on the easiest way to remove the new line characters from the middle of the string?

The Adafruit library and example sketches are poorly written. They will likely continue to plague you with problems. Start over with something better if you can.

When the Adafruit GPS library read() method detects a newline character (hex 0A) it places a string terminator (hex 00) into the buffer, switches buffers, and then writes the the newline character to the start of the other buffer. So every NMEA string starts with a newline and ends with a return (hex 0D).

You could probably kludge it work for you by writing:

Serial.write(stringptr+1, stringsize-2);
Serial.println();

or

logfile.write(stringptr+1, stringsize-2);
logfile.println();

This will drop the "$" character with the very first string since the first buffer hasn't been fed an inappropriate newline character yet. But after that it will probably work. Click your heels together and say kludge kludge kludge. Also, don't forget to turn off GPSECHO for looking at the output on the screen or you will be totally confused.

Okay, I will try your kludging solution. In the meantime though, I am interested by what you said about using a different starting point. I have definitely encountered many problems using this example sketch as a starting point--not only do I not understand various aspects of the code such as the confusingly written logging statements, but also this program will randomly stop logging when I add seemingly insignificant other bits of code, which is a significant problem when I'm trying to create my own program from this starting point.

However, I'm not exactly sure where else I would start. I used this example because I need something that will read NMEA sentences from the GPS module and log them to a file, exactly as this sketch is supposed to do. And in addition, this sketch is written by the people who created the hardware that I'm using, so I just assumed that no other examples would exist.

Do you have any idea where a better place to start would be? I've pretty much figured out how to do the rest of my program, but as I mentioned, I need to read GPS data from the adafruit GPS module, and then log in to a text file on the SD card.

Thanks!

The hardware isn't anything special. It's basically just a GPS and an SD card slot. Your software needs to be able to receive the serial data from the GPS, optionally parse it and then write it to the SD card. SoftwareSerial is unfortunately not so great for the first part because it disables interrupts so much of the time. I found it intolerable to use for my GPS datalogging project and wrote my own software serial. You can have it if you want. AltSoftSerial is another option although it requires a small hardware change. For writing the data you can use SD or SdFat, they'll both do the job just fine.

The middle part, the parsing, depends on what your application is. If you need to break down the string into its component pieces I think NeoGPS or TinyGPS are preferable over the Adafruit library. They are much better designed because they parse as the data comes in rather than using big buffers. But it doesn't appear you are doing any parsing right now. You just want to know when the end of the string occurs. That's easy.

I wish I could point you to a perfect example to start with. There is a need for that, something akin to what the Adafruit people have provided but that is more robust and extensible. I've written a number of scripts for logging but I'm not sure I have just the right thing to give you.

Here's a very simple logger that uses SD and my gSoftSerial library code (which is attached below). The sketch just writes the GGA and RMC strings along with some extra stuff on each line. I think that's kind of what you're trying to do at the moment. Maybe it will help.

#include <gSoftSerial.h>
#include <SD.h>

#define RX_PIN    8
#define TX_PIN    7
#define SD_CHIP_SELECT 10

#define SYNC_PERIOD    5L*60L*1000L    // 5 minutes between calls to flush()

gSoftSerial ss(RX_PIN, TX_PIN);
File logfile;

long ms;

void setup()
{
  Serial.begin(115200);
  Serial.print(F("\nHello\n"));
  
  ss.begin(9600);
  ss.println(F("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"));     // RMC and GGA
  ss.listen();
  
  Serial.println(F("Type any char to start logging."));
  Serial.println(F("\nEchoing to screen in the meantime:"));
  delay(1000);
  while (!Serial.available()) {
    if (ss.available()) {
      char c = ss.read();
      if (c == '\r') {                  // end of line detected
      Serial.write(", millis = ");      // add something before the CRLF
      Serial.print(millis());
      Serial.println();                 // CRLF
    } else if (c == '\n') {
      // ignore the newline since it's printed after receiving CR
    } else
      Serial.write(c);
    }
  }
  while (Serial.available()) Serial.read();    // flush input buffer

  Serial.print(F("\n\nInitializing SD card..."));
  if (!SD.begin(SD_CHIP_SELECT)) {
    Serial.println(F("Card init. failed!"));
    while (true) {}
  }
  Serial.println(F("done."));
  
  Serial.print(F("Opening file..."));
  char filename[15];
  strcpy(filename, "GPSLOG00.TXT");
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = '0' + i/10;
    filename[7] = '0' + i%10;
    if (!SD.exists(filename)) {
      break;
    }
  }

  logfile = SD.open(filename, FILE_WRITE);
  if(!logfile) {
    Serial.print(F("Couldnt create ")); 
    Serial.println(filename);
    while (true) {}
  }
  Serial.print(F(" Writing to ")); 
  Serial.println(filename);
    
  Serial.println(F("Type any character to close the file."));
  ms = SYNC_PERIOD;
}


void loop()
{
  if (ss.available()) {
    char c = ss.read();
      if (c == '\r') {                  // end of line detected
      logfile.write(", millis = ");     // add something before the CRLF
      logfile.print(millis());
      logfile.println();                // CRLF
    } else if (c == '\n') {
      // ignore the newline since it's printed after receiving '\r'
    } else
      logfile.write(c);
  }
  if (millis() > ms) {
    logfile.flush();
    ms += SYNC_PERIOD;  // 5 minutes;
  }
  
  if (Serial.available()) {
    logfile.close();
    Serial.println(F("\nFile closed."));
    while (1) {}
  }
}

gSoftSerial_2Jul2015.zip (7.26 KB)

Here's a thread with a program that logs a few sentences, and does it very reliably. It does not use any GPS values, like lat/long, so nothing is parsed. It just saves the received bytes to the SD card.

There's lots of good information earlier in that thread... you might want to read all of it just to get familiar with some of the problems and their solutions.

Cheers,
/dev

/dev, I was hoping you'd chime in.

The Adafruit people are great but sometimes their stuff falls a little short. It would be nice if there were a canonical example that could be referred to.

It would be nice if there were a canonical example that could be referred to.

Please elaborate.

I've seen so many people with an Ultimate GPS Logger shield and an Uno (or similar hardware) frustrated because they can't do what seems on the surface to be such a simple task: write GPS and some sensor data to an SD card. I am empathetic because that was my introduction to Arduino about a year ago. So when I see one of these threads I wish I could post a link where one could find a sketch that solves the basic problem, links to the libraries required and a short tutorial; much like what Adafruit provides but with code that is more extensible.

Okay, sorry for the delay, but I checked out the GPS depth logger that /dev suggested. However, I am having trouble getting it to work. I switched the original chip select line to:

if (!SD.begin(chipSelect)), because I'm using an uno, then compiled, but got an error saying that Serial1 and 2 were not declared. I googled this error, and found out that apparently Arduino Unos don't support multiple serial connections, unlike the mega, which this sketch is apparently written for. So I tried changing all the Serial1 and 2's to just Serial (I commented out the first and third declaration so it wasn't declared multiple times). Then the program compiled successfully (although I'm not sure if what I did will screw up its functionality), but after running it, nothing is logged to the NMEA text file it creates on the SD card.

So is there any way to get this sketch to work on an Uno? Or at least a simplified version of it?

And as for the code posted by JBoy, I tested that out too, and it works, but it is an exactly what I'm looking for because I need to interface with the GPS as well--it looks like this sketch just log stuff to the card.

It is an [isn't?] exactly what I'm looking for because I need to interface with the GPS as well--it looks like this sketch just logs stuff to the card.

Well, now I'm confused. You said,

I just want to log latitude and longitude to a log file in a format that I can control.

To me, that says you don't need to parse the lat/long numbers from the incoming bytes... you just want to write those bytes to file, maybe with some other data. You never compare lat/long to another location, or compute the change in lat/long, etc. Is that right?

Sorry for the confusion. I want to receive latitude and longitude from the GPS module, and then log those values to the SD card in a specific format. I'm not exactly sure what you mean by parse, but you are that I do not want to calculate or compare everything using these numbers (I'll be doing that later using Matlab).

JBoy's sketch seems to just log made up GPS coordinates to an SD card, and if that's the case, that's not what I want to do.

I'm not exactly sure what you mean by parse.

Ok, let's make sure you understand this before we go any further. I like to call it the difference between data and information. "Data" is just the raw bytes or characters. In your case, Serial.read() returns one byte from the GPS device. It's just a character, like 'A' or '2'.

"Information" is what you get when you interpret the raw data. In your case, reading the 6 bytes "170815" means August 17, 2015. That has more significance than '1', '7', '0', '8', '1' and '5'. The sketch had to call read() 6 times to get all those bytes. From the Nano's point of view, it had to wait for those characters to be read: available() returned false many times before those 6 bytes were finally received by the Nano.

If your sketch needs to know the current time, it will have to convert those bytes into a date: the raw data must be "parsed" into the date information (day, month, year). Then your sketch could write your own format of the date, like "2015-AUG-17", to the SD card.

For example, if you were building a clock with alarm features, you would be comparing the current time with the alarm time. You can't do that with a bunch of bytes. You have to parse the bytes into hour/minute/second to compare two times:

struct time_structure
  {
    uint8_t hour;
    uint8_t minute:
    uint8_t second;
  };

time_structure current_time; // filled out by parsing bytes
time_structure alarm_time  ; // set by user buttons?

void loop()
{
  if ((current_time.hour   == alarm_time.hour  ) &&
      (current_time.minute == alarm_time.minute) &&
      (current_time.second == alarm_time.second)) {
    // Beep!
  }

The current_time structure gets filled out by parsing the bytes from read().

That's the same as the difference between "receiving lat/long" (i.e., the bytes) and "parsing lat/long" (i.e., filling out two float variables). Make sense?

Now we can go back to your sketch:

If you are ok with writing the received lat/lon bytes to the SD card, it's just a matter of using Jboy's (lol, I love that diminutive) example or the code I referred to. Both sketches save the bytes received from the GPS and write them, unchanged, to the SD card. You seem to think that

JBoy's sketch just logs made up GPS coordinates to an SD card

That's not the case. Here is the relevant snippet from his sketch:

  if (ss.available()) {
    char c = ss.read();
    if (c == '\r') {                  // end of line detected
      logfile.write(", millis = ");     // add something before the CRLF
      logfile.print(millis());
      logfile.println();                // CRLF
    } else if (c == '\n') {
      // ignore the newline since it's printed after receiving '\r'
    } else
      logfile.write(c);
  }

When a byte is available from ss, it reads it then writes it immediately to the SD logfile. Every byte. Whatever the GPS sends to the Arduino is what it writes to the SD card. (It ignores the received '\n' because the println does both '\r' and '\n'.)

I think this is a little different from what you want to do, because it writes all fields from all the sentences it receives, prefixed with a millis() timestamp. You say you just want lat/lon:

log those values to the SD card in a specific format

...and you want to write "those values" in a specific format. I am still uncertain though:

1) Do you want to write the bytes of the lat/lon fields as they were received (i.e., "DDDmm.mmmm,E"), but in a different order or with extra info, like a timestamp or sequence number?

     14,4315.6789,S,12345.0123,E
     15,4315.6092,S,12344.9998,E

or

2) Do you want to write the parsed lat/lon values as a signed floating-point number (i.e., "±DDD.ddddd")?

     -43.26132,123.75021
     -43.26015,123.75000

If you mean (1), it's a matter of writing a subset of the received bytes. You could start with jboyton's or longjohn119's sketch and whittle it down. You don't need a GPS library.

If you mean (2), you must parse the received bytes into a float before you do a sd.print( float ). (Depending on the library, you could also write them as 32-bit ints to retain significant digits, then scale them in matlab.) You would start with an example from TinyGPS, TinyGPS++, Adafruit_GPS, or NeoGPS and whittle it down. You also mentioned that

I'm going to be pushing the memory capacity for this project

As the author of NeoGPS, I can tell you no other library is as small or fast, especially when you configure it to parse just the fields you need (i.e., lat/lon). Check out the RAM comparisons and speed comparisons. If you want to try it, I would suggest looking at the examples and the DTL configuration. It is more complicated than the other libraries, so please feel free to ask questions.

One other thing... you said

I would make my own sentences by accessing specific values like latitude and longitude. This works, but I am close to running out of memory...

If you are using String, don't. Also, replace all occurrences of

  Serial.print( "string literals like 'Speed:'" );

with

  Serial.print( F("string literals like 'Speed:'") );

Maybe you won't be pushing the limits after that. And be sure to post your code in the [code]...[/code] tags.

Cheers,
/dev