Arduino UNO - GPS - Float on SDcard

Hello everyone!
I am almost a beginner in Arduino / UNO.
A friend helped me a lot in building the code I'm going to post.
Basically it works perfectly, but since I need to put more "coordinates" (latitude and longitude). I will try to be as clear as possible, I apologize in advance, because I do not understand well about C / Arduino programming.

Code working perfectly with floats in the Arduino's own memory (I believe it is Flash memory).

1 - GPS code:

====================================

#include <SoftwareSerial.h>
#include <TinyGPS.h>

// Serial GPS RX e TX
SoftwareSerial ss(5, 6);
TinyGPS gps;

// aprox 100m
float margem = 0.0010000;

float latmax;
float latmin;
float longmax;
float longmin;

float tracklat[]   = {-14.8540023, -14.8537225, -14.8523179, -14.8513175, -14.8509654, -14.8497273, -14.8489098, -14.8490814};
float tracklong[]  = {-40.8368401, -40.8367495, -40.8363290, -40.8360174, -40.8358995, -40.8355745, -40.8355944, -40.8356088};


int tracksize = 8; 
int led = 8;

void setup() {
 Serial.begin(9600);
 ss.begin(9600);
 pinMode(led, OUTPUT);
 digitalWrite(led, LOW);
 
}

void loop() {
 bool newdata = false;
 unsigned long start = millis();

 // Every 5 seconds we print an update
 while (millis() - start < 5000) {
   if (ss.available()) {
     char c = ss.read();
     // Serial.print(c);  // uncomment to see raw GPS data
     if (gps.encode(c)) {
       newdata = true;
      // break;  // uncomment to print new data immediately!
     }
   }
 }
 
 if (newdata) {
   Serial.println("Acquired Data");
   Serial.println("-------------");
   float gpslat;
   float gpslong;
   gps.f_get_position(&gpslat, &gpslong);
   latmax = gpslat + margem;
   latmin = gpslat - margem;
   longmax = gpslong + margem;
   longmin = gpslong - margem;
   Serial.println("-------------");
   Serial.println();
 }
 
 for(int i = 0; i < tracksize; i++){
 delay(10);
   
   while ((tracklat[i] <= latmax) && (tracklat[i] >= latmin) && (tracklong[i] <= longmax) && (tracklong[i] >= longmin)) {
       
       Serial.println("True");
       digitalWrite(led, HIGH);
       delay(10);
       break;
   }
 }
 
 
}

====================================

The code above, as I mentioned, is perfect for what I need!
Here's my big problem, how can I put the "floats" coordinates inside an SDcard so I can have more coordinates to add?
Example (explaining roughly):

float tracklat [] = {latitude.txt};
float tracklong [] = {longitude.txt};

Thanks in advance for any help !!!

Writing ANY kind of data to an open file on the SD card is a matter of using the print() method of the instance of the File class that opened the file.

I don't see what that has to do with arrays.

This part I did not understand about the "array".
Could you explain please?

Your post contained:

float tracklat [] = {latitude.txt};
float tracklong [] = {longitude.txt};

The [] are how an array is defined.

PaulS:
I don't see what that has to do with arrays.

Agreed.

@dboxmonitor, do you want to fill those arrays from an SD file? I think that's what you want, according to your latest response:

What I would like is for those values between "{}" to be within one (or two) ".txt" so that they do not use the Arduino Flash memory, you know?

Other comments:

* Coordinate values like -14.8540023 will get rounded off to ~7 digits, because Arduino floats are single-precision numbers. So you would really be comparing against -14.85400.

* You may be using a location that is not valid yet. This can happen when the GPS does not know the location, perhaps due to poor satellite reception.

* Instead of stopping for 5 seconds to read GPS information, just read it all the time. Update the LED once every 5 seconds, based on the latest GPS information.

* Consider using a different serial port library. SoftwareSerial is library is very inefficient, because it disables interrupts for long periods of time. This can interfere with other parts of your sketch or with other libraries. Here is a description of the alternatives, from best to worst.

* Consider using my NeoGPS library. It is smaller, faster, more accurate and more reliable than all other GPS libraries. Here is your sketch, modified to use NeoGPS and NeoSWSerial:

#include <NeoSWSerial.h>
#include <NMEAGPS.h>

// Serial GPS RX e TX
//NeoSWSerial gpsPort( 5, 6 );
#define gpsPort Serial1

const int LED = 8;

NMEAGPS gps;          // Parser
gps_fix fix;          // GPS information structure
int     fixCount = 0; // Number of fixes received (also a seconds count)

const float MARGEM = 0.0010000; // km = 100m

NeoGPS::Location_t track[] =
  {
    { -148540023L, -408368401L },
    { -148537225L, -408367495L },
    { -148523179L, -408363290L },
    { -148513175L, -408360174L },
    { -148509654L, -408358995L },
    { -148497273L, -408355745L },
    { -148489098L, -408355944L },
    { -148490814L, -408356088L }
  };
const int trackSize = sizeof(track) / sizeof(track[0]); // works for any array

bool onTrack = false;


void setup()
{
  Serial.begin(9600);
  gpsPort.begin(9600);

  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
}


void loop()
{
  // Process GPS characters
  if (gps.available( gpsPort )) {

    // Once per second a new fix structure is ready.
    fix = gps.read();
    fixCount++;

    //  Check the track distance once every 5 seconds
    if (fixCount >= 5) {
      fixCount = 0; // reset counter

      Serial.println( F("Acquired Data") ); // F macro saves RAM, two prints in one
      Serial.println( F("-------------") );

      if (fix.valid.location) {

        Serial.print  ( fix.latitude(), 6 );
        Serial.print  ( ',' );
        Serial.println( fix.longitude(), 6 );

        onTrack = false;
        for(int i = 0; i < trackSize; i++) {

          float distance = fix.location.DistanceKm( track[ i ] );

          if (distance < MARGEM) {
            onTrack = true;
            break;
          }
        }

      } else {
        //  No location available (yet).
        Serial.println( '?' );

        onTrack = false;
      }

      updateTrackLED( onTrack );
    }
  }

} // loop


void updateTrackLED( bool onOff )
{
  if (onOff) {
    Serial.println("True");
    digitalWrite(LED, HIGH);
  } else {
    Serial.println("False");
    digitalWrite(LED, LOW);
  }
} // updateTrackLED

If you'd like to try it, it's available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries. Even if you don't use it, there are many tips and suggestions on the Installation and Troubleshooting pages.

Notes on the NeoGPS sketch:

* The lat/lon values are scaled by 10000000. This retains the full precision of the value, up to 10 significant digits. This value will not be truncated: -148540023. This will make the distance calculation very accurate.

* It puts the track lat/lon in one structure, not in two separate arrays. Then, these lat/lon structures are in one NeoGPS::Location_t array:

NeoGPS::Location_t track[] =
  {
    { -148540023L, -408368401L },  // one lat/lon pair

Then it can use the NeoGPS distance function to see if the current location is close enough to a track point:

          float distance = fix.location.DistanceKm( track[ i ] );

          if (distance < MARGEM) {
            onTrack = true;

It would be fairly easy to declare an array to hold those points:

const size_t MAX_POINTS = 20;
NeoGPS::Location_t track[ MAX_POINT ]
int trackSize = 0; // the number read from SD

Then you read them in during setup. Init the SD, open the file, and read the points in a loop. Remember how many were read in the trackSize variable. You should store the points in the file like I showed in this sketch:

-148540023,-408368401
-148537225,-408367495
-148523179,-408363290
-148513175,-408360174
-148509654,-408358995
-148497273,-408355745
-148489098,-408355944
-148490814,-408356088

This is a comma-separated value format (CSV).

Then read each line pair into a track array element:

  track[ i ].lat( latFromFile );
  track[ i ].lon( lonFromFile );

Do you know how to read values from the SD file?

Cheers,
/dev

I needed to change to: "const float MARGIN = 0.1500000;", the margin may be incorrect? In the case "0.1500000" could be being approximately 150 meters?

Yes, duh. 0.150km is 150m.

I know how to open a file and read in the serial for example, but I do not know how to get the output of that file and put it in the values to be read. I have some questions about the code.

Show what you have tried and explain what doesn't work. Now you can try it with these values. Be sure to use variables of the type int32_t (or simply long) for latFromFile and lonFromFile. Then set the track element as shown above.

    for (int i = 0; i < tracksize;){
      i++;

Why isn't the increment operation part of the for loop?

      while((f_buffer <= latmax) && (f_buffer >= latmin)){
        Serial.println("Latitude OK.");
        latontrack = true;
        Serial.print("latitude OK: ");
        Serial.println(f_buffer, 7);
        latline = i;
        i = 1080;
        break;
      }

This is stupid. You REALLY, REALLY need to determine when a while statement is appropriate vs. when an if statement is appropriate.

THIS should be an if statement. There should be NO for loop. Read the file one line at a time until you find the right line OR until you reach the end of the file without having found the correct line. ONE while loop, no for loops, and NO while loop inside the while loop.

Thanks for the comments, but I confess that I did not understand very well (due to the fact that I was not very knowledgeable about C / Arduino programming). I also gave this code and complement, which is exactly the way I was trying.

Then you need to work on simpler projects until you CAN do this project.

Look at the example for reading data from a file on an SD card. Modify it to read an entire record at a time. Modify it to convert the record to a float. Then, you will understand how to apply it to your code.

Jumping off the forward end of the Titanic part way through its maiden voyage is NOT how you learn to swim.

Sadly, your text file:

    -14.8540023,-40.8368401

...does not look like the integer values I showed you:

  -148540023, -408368401

This float number

    -14.8540023

... has 9 significant digits. Arduino float variables only support 7. You have to use the integer forms if you want that much accuracy (i.e., from 9 significant digits). I have attached the integer text file.

If you can give me more help...

The array should be populated in setup. It looks like you know how to init the SD card and open a file. Just read one line at a time from the file, by using the fgets function. It returns 0 when the end-of-file has been reached:

  while (true) {

    // Read one line at a time
    char line[ 60 ];
    int  count = trackFile.fgets( line, sizeof(line) );
    if (count <= 0)
      break; // EOF

Next, you can "parse" a line by using the C string functions strtok and atol:

      char *field = strtok( line, ", " );

The strtok steps through the line array until it finds a comma or a space (the delimiters). It "cuts" the line into one field by inserting a NUL character (a zero byte) at the first delimiter. It returns the field pointer, which now points to a portion of the line array. For your first data point, the field variable will "contain" (actually "point" to) the characters "-148540023".

Then you can use the atol function to convert the ASCII characters into a long integer value. Hence, its name: atol.

      long value  = atol( field );

One element of the track array is a NeoGPS::Location_t. Each of those elements contains a lat member and a lon member. The long integer values are assigned to each of those members.

      track[ trackSize ].lat( value );

This assumes that you have declared the track variables at the top:

      int   trackSize = 0;
const int   TRACK_MAX = 20;
NeoGPS::Location_t track[ TRACK_MAX ];

Do the same thing to get the longitude value from the rest of the line array:

      field = strtok( nullptr, ", " );
      value = atol( field );
      track[ trackSize ].lat( value );

Notice how the second call to strtok passes nullptr as the first argument (instead of line). This tells the strtok function to continue stepping through the original line array, watching for more comma and space delimiters. If you had more fields on each line, you would keep passing nullptr to strtok to get the extra fields.

Now you can increment the trackSize:

    trackSize++;

... and that's the end of the while loop. Keep reading lines until it breaks out (at EOF).

You should also guard against reading more points than the track array can contain:

    if (trackSize < TRACK_MAX) {
      // ok to parse line and store in track[trackSize]

You could break out of the loop here, too. Or you could count how many extra (unparsed) lines are in the file and print a warning:

  //  Loop through the file
  int lineCount = 0;
  while (true) {

    // Read one line at a time
    char line[ 60 ];
    int  count = trackFile.fgets( line, sizeof(line) );
    if (count <= 0)
      break; // EOF

    lineCount++;

    //  Only save up to TRACK_MAX points
    if (trackSize < TRACK_MAX) {

      //  "Parse" the line by using some string functions.
      char *field = strtok( line, ", " );
           ...

      trackSize++; // increment the track counter
    }

  }

  trackFile.close();

  Serial.print( trackSize );
  Serial.println( F(" points loaded.") );
  
  if (lineCount > trackSize) {
    Serial.print( F("Too many points in file: ") );
    Serial.println( lineCount );
  }

Instead of putting all this code in the setup function, I would suggest putting this in your own function:

void void loadTrack()
{
  File trackFile = SD.open( "track.txt", O_READ );

  //  Loop through the file
    ...
}

... and then call it from setup:

const int LED    = 8;
const int CS_PIN = 4;

void setup()
{
  Serial.begin(9600);
  gpsPort.begin(9600);

  pinMode(LED, OUTPUT );
  digitalWrite(LED, LOW);

  pinMode( CS_PIN, OUTPUT );
  if (!SD.begin( CS_PIN )) {
    Serial.println( F("SdCard Not Found ...") ); // F macro saves RAM!
    while(1);
  }
  Serial.println("SdCard found ...");

  loadTrack();

} // setup

Important: this code uses the current version of the built-in SD library: SdFat. I strongly recommend that you use SdFat instead of SD. SdFat has many bug fixes and performance improvements that are not in the SD library. There is complete documentation in the Libraries/SdFat/extras directory. Just open SdFat.html (or try html/index.html). Of course, the examples show many, many different ways to do a variety of tasks. Ask if you have specific questions.

track.txt (184 Bytes)

According to your code, you are not parsing the lines read from the text file:

     //  "Parse" the line by using some string functions.
      char *field = strtok( line, ", " );

 
      trackSize++; // increment the track counter

That finds the first field (latitude), but does nothing with the field. Then it increments the track counter.

Re-read reply #14, starting here:

Then you can use the atol function to convert the ASCII characters into a long integer value. Hence, its name: atol.

      long value  = atol( field );

One element of the track array is a NeoGPS::Location_t. Each of those elements contains a lat member and a lon member. The long integer values are assigned to each of those members.

      track[ trackSize ].lat( value );

You will not be able to load 900 points on an UNO. The UNO only has 2000 bytes of RAM, and each track element uses 8 bytes. I would recommend loading no more than 150 points:

    const int   TRACK_MAX = 150; // Number of lines in file track.txt

And I changed the name of this variable from MAX_POINTS (earlier posts). Delete the old MAX_POINTS variable and only use the new TRACK_MAX variable. Sorry for that confusion.

dboxmonitor:
So the idea of putting in the SDcard, you understand me?

No.

@dboxmonitor, do you want to fill those arrays from an SD file?

You said "Yes!".

Instead of leaving them directly in the Arduino code, consuming resources, it would leave in the SDcard.

The latest sketch loads all of them into RAM from the SD card. They would not be in your code (the sketch).

Do you want to read one location at a time from the SD card, for comparison with the fix location? You would read the file once every 5 seconds. This would only use RAM for one location, not 900. You would not have an array of locations.

Do you want to read one location at a time from the SD card, for comparison with each new fix location?

This is ok, and would allow you to put 900 locations on the SD card.

The sketch for reading the file once every 5 seconds is different, not impossible.

I'm trying to figure out what you want to do.

Basically what I need is what this code does: ... But that could have the approximately "900" coordinates.

Right. 900 coordinates requires 7200 bytes of memory. 900 coordinates cannot be stored in RAM, because the UNO only has 2048 bytes of RAM.

But they could be stored in FLASH (program space, aka PROGMEM), because the UNO has 32K bytes of program space. There is a way to have 900 coordinates in your sketch.

The disadvantage of having the coordinates in your sketch is that you have to upload a new sketch if the coordinates change. With an SD card, you could copy a text file to the card from your PC. You could also save GPS coordinates onto the card while moving.

The advantage of having the coordinates in your sketch is that you would not need an SD card.

I figured reading on the SDcard would not use Arduino memory.

The SD libraries use a fair amount of RAM (25% of the 2K) and some program space (10% or more of the 32K?). But an SD card could hold 900 or even 900,000,000 coordinates.

However...

I wonder if you really need 900 coordinates. Your code simply compares the current location with one track point . You could compare the current location with a track line segment (see this).

If your track points are along one line, you could store the end points, not all the intermediate coordinates. It looks like some of your points are along one line segment.

You would need some extra functions to compare the current location with each line segment. This answer has some code, and I'm working on a version for NeoGPS.

Here is a version of your sketch with the coordinates in FLASH memory, not RAM:

#include <NeoSWSerial.h>
#include <NMEAGPS.h>

// Serial GPS RX e TX
NeoSWSerial gpsPort( 5, 6 );

const int LED = 8;

NMEAGPS gps;          // Parser
gps_fix fix;          // GPS information structure
int     fixCount = 0; // Number of fixes received (also a seconds count)

const float MARGEM = 0.0010000; // km = 100m

struct FLASHloc_t
{
  int32_t lat, lon;
};

const FLASHloc_t track[] PROGMEM =
  {
    { -148540023L, -408368401L },
    { -148537225L, -408367495L },
    { -148523179L, -408363290L },
    { -148513175L, -408360174L },
    { -148509654L, -408358995L },
    { -148497273L, -408355745L },
    { -148489098L, -408355944L },
    { -148490814L, -408356088L }
  };
const int trackSize = sizeof(track) / sizeof(track[0]); // works for any array

bool onTrack = false;


void setup()
{
  Serial.begin(9600);
  gpsPort.begin(9600);

  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
}


void loop()
{
  // Process GPS characters
  if (gps.available( gpsPort )) {

    // Once per second a new fix structure is ready.
    fix = gps.read();
    fixCount++;

    //  Check the track distance once every 5 seconds
    if (fixCount >= 5) {
      fixCount = 0; // reset counter

      Serial.println( F("Acquired Data") ); // F macro saves RAM, two prints in one
      Serial.println( F("-------------") );

      if (fix.valid.location) {

        Serial.print  ( fix.latitude(), 6 );
        Serial.print  ( ',' );
        Serial.println( fix.longitude(), 6 );

        onTrack = false;
        for(int i = 0; i < trackSize; i++) {

          // Copy one track element out of FLASH memory into RAM
          NeoGPS::Location_t ramLocation;
          memcpy_P( &ramLocation, &track[ i ], sizeof( track[ i ] ) );

          float distance = fix.location.DistanceKm( ramLocation );

          if (distance < MARGEM) {
            onTrack = true;
            break;
          }
        }

      } else {
        //  No location available (yet).
        Serial.println( '?' );

        onTrack = false;
      }

      updateTrackLED( onTrack );
    }
  }

} // loop


void updateTrackLED( bool onOff )
{
  if (onOff) {
    Serial.println("True");
    digitalWrite(LED, HIGH);
  } else {
    Serial.println("False");
    digitalWrite(LED, LOW);
  }
} // updateTrackLED

Notice how it copies one lat/lon pair at a time from FLASH into RAM:

         // Copy one track element out of FLASH memory into RAM
          NeoGPS::Location_t ramLocation;
          memcpy_P( &ramLocation, &track[ i ], sizeof( track[ i ] ) );

This allows you to have 900 coordinates in the PROGMEM (aka FLASH) array:

const FLASHloc_t track[] PROGMEM =
  {
    { -148540023L, -408368401L },
    { -148537225L, -408367495L },
    { -148523179L, -408363290L },
    { -148513175L, -408360174L },
    { -148509654L, -408358995L },
    { -148497273L, -408355745L },
    { -148489098L, -408355944L },
    { -148490814L, -408356088L }
    //     .... put more here...
  };

I just do not understand the "track.txt" - Sdcard.

Instead of having an array in FLASH memory, you could have the "array" on an SD card.

To compare the current location with one element of the array in FLASH memory, you copy it from FLASH to RAM:

        onTrack = false;
        for(int i = 0; i < trackSize; i++) {

          // Copy one track element out of FLASH memory into RAM
          NeoGPS::Location_t ramLocation;
          memcpy_P( &ramLocation, &track[ i ], sizeof( track[ i ] ) );

          float distance = fix.location.DistanceKm( ramLocation );

To compare the current location with one element of the "array" on an SD card, you read one line of the track.txt file and parse it into a RAM location variable:

         trackFile.rewind(); // reset to the beginning
        for (;;) {

          // Read one line at a time
          char line[ 60 ];
          int  count = trackFile.fgets( line, sizeof(line) );
          if (count <= 0)
            break; // EOF

          // Parse into one track location
          NeoGPS::Location_t trackPt;
          char *field = strtok( line, ", " );
          long  value = atol( field );
          trackPt.lat( value );

          field = strtok( nullptr, ", " );
          value = atol( field );
          trackPt.lon( value );

          float distance = fix.location.DistanceKm( trackPt );

This will read the entire track.txt file for each new GPS location (once every 5 seconds). Of course, you have to initialize the SD card and open the trackFile in setup.

That's an array declaration. The elements of the array are char. There are 60 elements in the array.

You used to have an array of float for latitudes, and a separate array for longitudes.

The fgets function will fill the line array from the track.txt file. The function returns the number of characters the were read (up to the end of the line).

Arrays of char are also called "C strings", and there are many functions for scanning and manipulating these strings. I showed the use of strtok and atol.

This is different from the String class. In general, you should not use the String class, because it can cause random crashes and weird behaviour.

Does your library usually work on the D1 Mini PRO?

NeoGPS is supported on ESP8266 and ESP32 platforms, among others.

I think it's time to revisit reply #25.

Your current approach is to compare many points with the current point. This takes 10 seconds because there are so many.

It would be much faster to compare a few line segments with the current point. It is more complicated to calculate the distance from a point to a line segment, but it saves comparing hundreds of points (along the segment) to the one test point. Hundreds.

It would also use much less memory. Storing the endpoints of the line segments is much more efficient than storing all the points along each segment.

I've been working on a little example for you, if you don't mind waiting. In the meantime, spend some time reading the links in reply #25.

You can also filter your points array down to the endpoints of each segment.

Here is the Google maps view of the track you provided in reply #36:

I have added red dots for the endpoints of each segment of that line. Your FLASH array for this track should only contain those endpoints. That's only 23 locations, for 22 segments! Your sketch should be able to calculate the distance of your current location from those segments very quickly.

Here is a function to calculate the distance from a location (e.g., current GPS location) to a segment defined by two points (startPt and endPt):

float distanceFromPtToSegment
 ( const NeoGPS::Location_t & pt,
   const NeoGPS::Location_t & startPt,
   const NeoGPS::Location_t & endPt );
{
 float   distance;

      ...  maths ...

 return distance;

} // distanceFromPtToSegment

This can be called from another function that calculates the distance from a location (again, current GPS location) to an array of locations (the segment endpoints):

float distanceFromPtToLine
  ( const NeoGPS::Location_t & pt, 
    const FLASHloc_t         * line, size_t n,  // number of endpoints in array
    float threshold )
{
  if (n == 0)
    return NAN;

  NeoGPS::Location_t startPt( line[0].pgm_read() );  // Initial start point
  float minDistance = pt.DistanceKm( startPt );

  if (n > 1) {
    float   distance = minDistance;
    size_t  i        = 1;

    while (minDistance > threshold) {

      NeoGPS::Location_t endPt( line[i].pgm_read() );   // next end point

      distance  = distanceFromPtToSegment( pt, startPt, endPt );      //  Calculate distance to this one segment

      //  Keep the closest approach
      if (minDistance > distance) {
        minDistance = distance;
      }

      i++;
      if (i >= n)
        break; // no more segments

      // For next iteration, use the current end point as the next start point
      startPt = endPt;
    }
  }

  return minDistance;

} // distanceFromPtToLine

This function can be called from loop every time a new GPS fix is available:

void loop()
{
  // Process GPS characters
  if (gps.available( gpsPort )) {

    // Once per second a new fix structure is ready.
    fix = gps.read();
    fixCount++;

    //  Check the track distance once every 5 seconds
    if (fixCount >= 5) {
      fixCount = 0; // reset counter

      Serial.println( F("Acquired Data") ); // F macro saves RAM
      Serial.println( F("-------------") );

      //  If the GPS knows the position, compare it to the track points
      bool onTrack = false;
      if (fix.valid.location) {

        Serial.print  ( fix.latitude(), 6 ); // for debugging
        Serial.print  ( ',' );
        Serial.println( fix.longitude(), 6 );

        float distance =
          distanceFromPtToLine( fix.location, track, TRACK_SIZE, MARGEKM );

        Serial.println( distance, 6 );

        if (distance < MARGEKM) {
          onTrack = true;
        }

      } else {
        //  No location available (yet).
        Serial.println( '?' );
      }

      updateTrackLED( onTrack );
    }
  }

} // loop

Complete sketch attached.

Cheers,
/dev

trackSegments.ino (5.54 KB)

If I understand your question...

Yes, you can have multiple tracks, and you can use those functions to check each track. You need an array for each track, and a number of points for each track:

const FLASHloc_t track1 [] PROGMEM = {

{coordinates track}
....
};
const size_t TRACK1_SIZE = sizeof(track) / sizeof(track[0]); // works for any array

const FLASHloc_t track2 [] PROGMEM = {

{coordinates track2}
....
};
const size_t TRACK2_SIZE = sizeof(track2) / sizeof(track2[0]); // works for any array

const FLASHloc_t track3 [] PROGMEM = {

{coordinates track3}
....
};
const size_t TRACK3_SIZE = sizeof(track3) / sizeof(track3[0]); // works for any array

  etc.

Then you call the distanceFromPtToLine for each line, from loop:

void loop()
{
  // Process GPS characters
  if (gps.available( gpsPort )) {

    // Once per second a new fix structure is ready.
    fix = gps.read();
    fixCount++;

    //  Check the track distance once every 5 seconds
    if (fixCount >= 5) {
      fixCount = 0; // reset counter

      Serial.println( F("Acquired Data") ); // F macro saves RAM
      Serial.println( F("-------------") );

      //  If the GPS knows the position, compare it to the track points
      bool onTrack = false;
      if (fix.valid.location) {

        Serial.print  ( fix.latitude(), 6 );
        Serial.print  ( ',' );
        Serial.println( fix.longitude(), 6 );

        float distance =
          distanceFromPtToLine( fix.location, track, TRACK_SIZE, MARGEKM );

        if (distance < MARGEKM) {
          onTrack = true;
        } else {
          distance =
            distanceFromPtToLine( fix.location, track2, TRACK2_SIZE, MARGEKM );

          if (distance < MARGEKM) {
            onTrack = true;
          } else {
          }
        }

For a few tracks, it's ok to cut & paste. For more than 3 or 4, you'll want an array of arrays, and an array of track sizes:

const FLASHloc_t *allTracks[] PROGMEM =
{
  track1, track2, track3
};

const size_t allTrackSizes[] PROGMEM =
{
  TRACK1_SIZE,
  TRACK2_SIZE,
  TRACK3_SIZE
};

Then you can iterate over these new arrays, and check the distance against each track:

        for (size_t i=0; i < TRACK_COUNT; i++) {
          // Get the info for one track
          const FLASHloc_t *track     = (const FLASHloc_t *) pgm_read_ptr( &allTracks[i] );
          const size_t      trackSize = pgm_read_word( &allTrackSizes[i] );

          // Calculate the distance to that one track
                float       distance  =
            distanceFromPtToLine( fix.location, track, trackSize, MARGEKM );

          Serial.print( F("Track") );
          Serial.print( i+1 );
          Serial.print( F(" distance = ") );
          Serial.println( distance, 6 );

          if (distance < MARGEKM) {
            onTrack = true;
            break;  // we are close enough to the current track, allTracks[i]
          }
        }

See attached.

Cheers,
/dev

allTracks.ino (6.39 KB)