Distance between coordinates

Dear all,

This is my first Arduino project, so my code is quite copy and pasted (apologies in advance if it is difficult to follow). But essentially the program should gather two sets of coordinates from a GPS module and the users phone (using the 1Sheeld) and work out the bearing and distance between them.

My issue is with calculating the distance and the bearing. Initially when I simply put the whole formula before the void setup it just gave an output of 0.000 and 0.000, so I have moved it around. Now there is an error labelled "'CalcDistance' cannot be used as a function"

If anyone has any idea of how to either clean up my code (as it is using a lot of memory) or how to get the distance it would be much appreciated.

Code:

#include <OneSheeld.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2);
Adafruit_GPS GPSM(&mySerial);

#define GPSECHO false
#define CUSTOM_SETTINGS
#define INCLUDE_GPS_SHIELD

//convert degrees to radians
double dtor(double fdegrees)
{
return(fdegrees * PI / 180);
}

//Convert radians to degrees
double rtod(double fradians)
{
return(fradians * 180.0 / PI);
}

boolean usingInterrupt = false;
void useInterrupt(boolean);

float lat_gps;
float long_gps;
float lat_gpsm;
float long_gpsm;
char charlat [10];
char charlon [10];
char readings [80];

void setup()
{

{ OneSheeld.begin();
}
Serial.begin(115200);
GPSM.begin(9600);
GPSM.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPSM.sendCommand(PGCMD_ANTENNA);
useInterrupt(true);
delay(1000);
mySerial.println(PMTK_Q_RELEASE);

long printableDist;
int printableBearing;
float CalcDistance;
float CalcBearing;

printableDist = CalcDistance(0,0,0,0);
printableBearing = CalcBearing(0,0,0,0);
}

SIGNAL(TIMER0_COMPA_vect) {
char c = GPSM.read();

#ifdef UDR0
if (GPSECHO)
if (c) UDR0 = c;

#endif
}

void useInterrupt(boolean v) {
if (v) {

OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {

TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}

uint32_t timer = millis();

void loop()
{
if (! usingInterrupt) {

char c = GPSM.read();

if (GPSECHO)
if (c) Serial.print(c);
}

if (GPSM.newNMEAreceived()) {
if (!GPSM.parse(GPSM.lastNMEA()))
return;
}

if (timer > millis()) timer = millis();
if (millis() - timer > 2000) {
timer = millis();

lat_gpsm = GPSM.latitudeDegrees;
Serial.print(", ");

long_gpsm = GPSM.longitudeDegrees;

}

{
lat_gps = GPS.getLatitude();
long_gps = GPS.getLongitude();
}

//Calculate distance from lat_gpsm/long_gpsm to lat_gps/long_gps using haversine formula
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns distance in feet
long CalcDistance(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps)
{
double dlon, dlat, a, c;
double dist = 0.0;
dlon = dtor(long_gps - long_gpsm);
dlat = dtor(lat_gps - lat_gpsm);
a = pow(sin(dlat/2),2) + cos(dtor(lat_gpsm)) * cos(dtor(lat_gps)) * pow(sin(dlon/2),2);
c = 2 * atan2(sqrt(a), sqrt(1-a));

dist = 20925656.2 * c; //radius of the earth (6378140 meters) in feet 20925656.2
return( (long) dist + 0.5);
}

//Calculate bearing from lat_gpsm/long_gpsm to lat_gps/long_gps
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns bearing in degrees
int CalcBearing(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps)
{
lat_gpsm = dtor(lat_gpsm);
long_gpsm = dtor(long_gpsm);
lat_gps = dtor(lat_gps);
long_gps = dtor(long_gps);

//determine angle
double bearing = atan2(sin(long_gps-long_gpsm)*cos(lat_gps), (cos(lat_gpsm)*sin(lat_gps))-(sin(lat_gpsm)*cos(lat_gps)*cos(long_gps-long_gpsm)));
//convert to degrees
bearing = rtod(bearing);
//use mod to turn -90 = 270
bearing = fmod((bearing + 360.0), 360);
return (int) bearing + 0.5;
}

Serial.print(printableBearing);
Serial.println(printableDist);

}

  1. read the sticky and use code tags.

  2. you have CalcDistance declared as a variable AND a function. That's why you get that error message.

  3. You should be returning a float or a double from CalcDistance, not a long.

When I don't declare it as a variable it isn't declared in the scope.
Is there a way to get round this?
And why should it be returning a float or a double compared to a long?

Please edit your post to add code tags (see "How to use this forum" if you have questions).

The Arduino does not support the "double" data type. It treats it as "float" (6-7 digits) which means that calculations like the following will be very inaccurate.

dist = 20925656.2 * c;  //radius of the earth (6378140 meters) in feet 20925656.2
#include <OneSheeld.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2);
Adafruit_GPS GPSM(&mySerial);

#define GPSECHO  false
#define CUSTOM_SETTINGS
#define INCLUDE_GPS_SHIELD

//convert degrees to radians
double dtor(double fdegrees)
{
return(fdegrees * PI / 180);
}

//Convert radians to degrees
double rtod(double fradians)
{
return(fradians * 180.0 / PI);
}


boolean usingInterrupt = false;
void useInterrupt(boolean);

float lat_gps;
float long_gps;
float lat_gpsm;
float long_gpsm;
char charlat [10];
char charlon [10];
char readings [80];

void setup()
{
   
  { OneSheeld.begin();
  }
  Serial.begin(115200);
  GPSM.begin(9600);
  GPSM.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  GPSM.sendCommand(PGCMD_ANTENNA);
  useInterrupt(true);
  delay(1000);
  mySerial.println(PMTK_Q_RELEASE);

  long printableDist;
  int printableBearing;
  float CalcDistance;
  float CalcBearing;
 

  printableDist = CalcDistance(0,0,0,0);
  printableBearing = CalcBearing(0,0,0,0);
}


SIGNAL(TIMER0_COMPA_vect) {
  char c = GPSM.read();

#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;

#endif
}

void useInterrupt(boolean v) {
  if (v) {

    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } else {

    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}

uint32_t timer = millis();

void loop()
{
  if (! usingInterrupt) {

    char c = GPSM.read();

    if (GPSECHO)
      if (c) Serial.print(c);
  }


  if (GPSM.newNMEAreceived()) {
    if (!GPSM.parse(GPSM.lastNMEA()))
      return;
  }

  if (timer > millis())  timer = millis();
  if (millis() - timer > 2000) {
    timer = millis();
  
    lat_gpsm = GPSM.latitudeDegrees;
    Serial.print(", ");
    
    long_gpsm = GPSM.longitudeDegrees;

  }

  {
    lat_gps = GPS.getLatitude();
    long_gps = GPS.getLongitude();
  }

//Calculate distance from lat_gpsm/long_gpsm to lat_gps/long_gps using haversine formula
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns distance in feet
long CalcDistance(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps)
{
double dlon, dlat, a, c;
double dist = 0.0;
dlon = dtor(long_gps - long_gpsm);
dlat = dtor(lat_gps - lat_gpsm);
a = pow(sin(dlat/2),2) + cos(dtor(lat_gpsm)) * cos(dtor(lat_gps)) * pow(sin(dlon/2),2);
c = 2 * atan2(sqrt(a), sqrt(1-a));

dist = 20925656.2 * c;  //radius of the earth (6378140 meters) in feet 20925656.2
return( (long) dist + 0.5);
}

//Calculate bearing from lat_gpsm/long_gpsm to lat_gps/long_gps
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns bearing in degrees
int CalcBearing(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps)
{
lat_gpsm = dtor(lat_gpsm);
long_gpsm = dtor(long_gpsm);
lat_gps = dtor(lat_gps);
long_gps = dtor(long_gps);

//determine angle
double bearing = atan2(sin(long_gps-long_gpsm)*cos(lat_gps), (cos(lat_gpsm)*sin(lat_gps))-(sin(lat_gpsm)*cos(lat_gps)*cos(long_gps-long_gpsm)));
//convert to degrees
bearing = rtod(bearing);
//use mod to turn -90 = 270
bearing = fmod((bearing + 360.0), 360);
return (int) bearing + 0.5;
}

Serial.print(printableBearing);
Serial.println(printableDist);

}

a "long" is an integer.

a "float" is a decimal.

For a distance, you almost definitely want to return a double. And, your calculations indicate that is what you want. I didn't know arduino didn't use double, so don't use that, obviously.

I don't know all the in's and out's of how arduino functions handle scope, but you seem to be mixing something up.

Here's why I think you're talking about "scope". . .

You had setup written with CalcDistance() in it. The compiler told you CalcDistance() wasn't defined in that scope. You "solved" this by making up a variable called "CalcDistance" and then you got an error message that CalcDistance() couldn't be a function.

Try putting the FUNCTION "CalcDistance() {...}" before setup().

OR. . .

I'm not sure if this works in arduino, but you also might be able to just write

long CalcDistance(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps);

before setup() and then write the entire function at the end of the program. That should get you around the scope issue.

don't use haversine if you don't really need it; try "equirectangular projection"

This is my first Arduino project, so my code is quite copy and pasted (apologies in advance if it is difficult to follow)

Very copy/pasted, you have nested functions. Make sure braces match properly and don't
define a function inside another, that's not something meaningful in C or C++ (technically
the languages don't support closures).

This looks very suspicious to me:

  if (timer > millis())  timer = millis();

I think it just breaks your code if left running for many days and millis() wrap-around happens.

And

uint32_t timer = millis();

should be

uint32_t timer = 0L;

You can't call millis() that early and expect it to work, and we know it starts from zero.

Global initializers run before lots of stuff setting up the Arduino library, so you shouldn't call
anything which interacts with the hardware - that should be done in setup().

Ive updated the code and there were no errors. However, I tested the program and it continues to spew out 0.0000 and 0.0000.

Does anyone have any idea why? I'll deal with the nitty gritty later.

#include <OneSheeld.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2);
Adafruit_GPS GPSM(&mySerial);

#define GPSECHO  false
#define CUSTOM_SETTINGS
#define INCLUDE_GPS_SHIELD

long CalcDistance(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps);
int CalcBearing(double lat_gpsm, double long_gpsm, double lat_gps, double long_gps);

//convert degrees to radians
double dtor(double fdegrees)
{
return(fdegrees * PI / 180);
}

//Convert radians to degrees
double rtod(double fradians)
{
return(fradians * 180.0 / PI);
}


boolean usingInterrupt = false;
void useInterrupt(boolean);

float lat_gps;
float long_gps;
float lat_gpsm;
float long_gpsm;
char charlat [10];
char charlon [10];
char readings [80];

void setup()
{
   
  { OneSheeld.begin();
  }
  Serial.begin(115200);
  GPSM.begin(9600);
  GPSM.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  GPSM.sendCommand(PGCMD_ANTENNA);
  useInterrupt(true);
  delay(1000);
  mySerial.println(PMTK_Q_RELEASE);

}


SIGNAL(TIMER0_COMPA_vect) {
  char c = GPSM.read();

#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;

#endif
}

void useInterrupt(boolean v) {
  if (v) {

    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  } else {

    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}

uint32_t timer = millis();

void loop()
{
  if (! usingInterrupt) {

    char c = GPSM.read();

    if (GPSECHO)
      if (c) Serial.print(c);
  }


  if (GPSM.newNMEAreceived()) {
    if (!GPSM.parse(GPSM.lastNMEA()))
      return;
  }

  if (timer > millis())  timer = millis();
  if (millis() - timer > 2000) {
    timer = millis();
  
    lat_gpsm = GPSM.latitudeDegrees;
    Serial.print(", ");
    
    long_gpsm = GPSM.longitudeDegrees;

  }

  {
    lat_gps = GPS.getLatitude();
    long_gps = GPS.getLongitude();
  }

//Calculate distance from lat_gpsm/long_gpsm to lat_gps/long_gps using haversine formula
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns distance in feet

{
double dlon, dlat, a, c;
double dist = 0.0;
dlon = dtor(long_gps - long_gpsm);
dlat = dtor(lat_gps - lat_gpsm);
a = pow(sin(dlat/2),2) + cos(dtor(lat_gpsm)) * cos(dtor(lat_gps)) * pow(sin(dlon/2),2);
c = 2 * atan2(sqrt(a), sqrt(1-a));

dist = 20925656.2 * c;  //radius of the earth (6378140 meters) in feet 20925656.2
return( (long) dist + 0.5);
}

//Calculate bearing from lat_gpsm/long_gpsm to lat_gps/long_gps
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns bearing in degrees

{
lat_gpsm = dtor(lat_gpsm);
long_gpsm = dtor(long_gpsm);
lat_gps = dtor(lat_gps);
long_gps = dtor(long_gps);

//determine angle
double bearing = atan2(sin(long_gps-long_gpsm)*cos(lat_gps), (cos(lat_gpsm)*sin(lat_gps))-(sin(lat_gpsm)*cos(lat_gps)*cos(long_gps-long_gpsm)));
//convert to degrees
bearing = rtod(bearing);
//use mod to turn -90 = 270
bearing = fmod((bearing + 360.0), 360);
return (int) bearing + 0.5;
}

Serial.print(CalcDistance(0,0,0,0));
Serial.println(CalcBearing(0,0,0,0));

}

Add some Serial.println()s so you can follow the calculation's progress - that's how to
debug something like that.

Are you getting valid positions?

The coordinates are correct, I am fairly confident there is an issue with the conversion formula

There are many problems with the posted code. These comments suggest that a function entry point should follow, but it is missing.

//Calculate distance from lat_gpsm/long_gpsm to lat_gps/long_gps using haversine formula
//Note lat_gpsm/long_gpsm/lat_gps/long_gps must be in radians
//Returns distance in feet

What do you expect these print statements to produce?

Serial.print(CalcDistance(0,0,0,0));
Serial.println(CalcBearing(0,0,0,0));

The code which obtains the coordinates of the GPS module (GPSM) and the coordinates of the Sheeld is correct as I have tested it. The problem is with the conversion.

The conversion has been manipulated around the program so all of it is there but at different points within the code (i.e. the entry point will be at a different point in the program)

As for the Serialprint (....) the hope is that it prints the output to the calculation (the distance and the bearing)

Your code has way too many issues. You are totally confused about how and where to define and write your functions.

You need to start with the BASICS of function declarations. Get this to work in your environment, and then move on to more complicated things.