Coding Help in calling a function to convert values

I currently have some code which returns GPS co-ordinates in GNSS NEMA format which i have found a function to convert this. The issue i have is my understanding of C is limited and i don’t know how to call this and looking for advice.

I have the following variables correctly populating;

   Gga gga = Gga(_gps);
   if (gga.parse()) { 
       utcTime = gga.utcTime;
       longitude = gga.longitude;
       latitude = gga.latitude;
   }
   Rmc rmc = Rmc(_gps);
   if (rmc.parse()) {
        utcTime = rmc.utcTime;
        longitude = rmc.longitude;
        latitude = rmc.latitude;
   }

What i want to do is modify the longitude and latitude values using the following function but i am not sure how i modify my code above to actually call this?

Any help or guidance would be much appreciated

float conv_coords(float in_coords) {
  //Initialize the location.
  float f = in_coords;
  // Get the first two digits by turning f into an integer, then doing an integer divide by 100;
  // firsttowdigits should be 77 at this point.
  int firsttwodigits = ((int)f)/100; //This assumes that f < 10000.
  float nexttwodigits = f - (float)(firsttwodigits*100);
  float theFinalAnswer = (float)(firsttwodigits + nexttwodigits/60.0);
  return theFinalAnswer;
}

Not sure why you set your three variables twice. The second assignment to each of them will overwrite the previous value - so either Gga or Rmc should be used, not both IMO.

BTW, I already advised in another thread to not write it this way but rather

  Rmc rmc(_gps);

for reasons explained in that thread.

However, not exactly sure why you would want to convert the data that way.
Can you explain what you actually want with that? Then we might be able to advise how to do it “better”.

You need to consider that float cannot store any arbitrary number exactly. So even when you calculate the value correctly, you may not actually get that exact result back when you return it.

Also the .longitude and .latitude values you get from that library are of type String so you’d need to convert that first (better into double rather than float).

1 Like

Hi thanks for the reply, i do plan on fixing those before i finalise things but as it was currently working i was going to come back and change this later.

I think i need to step back as i am missing the north and west indicators.

To work out my values i cam going to need to create a function that will convert them as follows, so for latitude and longitude these will also come along with indicator for N and W which i am currently missing these variables!

The function will take in the variables for each indicator and and latitude value in format of ddmm.mmmmm. Then strip it out as below;

Strip the dd from the message i.e.

ddmm.mmmm

Which becomes

variable1 = dd
variable2 = mm.mmmm
variable3 = variable2 / 60

Rejoin values to make dd.dddd

dd.dddd = variable1.variable3

i.e. Latitude / Longitude for the Siretta office from NMEA GGA message to lat/lon!

Lat: ddmm.mmmm: 5123.1355 (N)
Lat: dd + mm.mmmm: 51 + 23.1355
Lat: dd + dd.dddd: 51 + (23.1355 / 60) 0.38559
Lat: dd.dddd: 51.38559

Lat: 51.38559 (As this is north it is positive, south is negative)

Long: dddmm.mmmm: 00058.4350 (W)
Long: ddd + mm.mmmm: 000 + 58.4350
Long: ddd + dd.dddd: 000 + (58.4350 / 60) 0.9739
Long: ddd.dddd: 000.9739

Long: -000.9739 (As this is west it is negative, east is positive)

This should do what you want

double decDegFromDMMmm(const char* coord) { // coord as DDDMM.mmmmmm
  int    iDeg = atol(coord) / 100;          // full degrees
  double dMin = atof(coord) - 100*iDeg;     // decimal minutes
  return iDeg + dMin / 60.0;
}

and you can call it like this

  double lon = decDegFromDMMmm(rmc.longitude);

You can either use the sign of that value to select N/E or S/W and multiply by -1 for the latter or you take the abs() of the value and use rmc.northSouthIndicator and rmc.eastWestIndicator from the library object.

2 Likes

Thank you so much - i have also just updated my code with all of your other suggestions and all seems to be working as expected - i really appreciate your help you have given me!

1 Like

OK i have tried this just for longitude at the moment and ignoring the positive/negative part for now. I’ve added in a new ‘test’ variable just to store the value that is being passed to new function. The value for longitude passed is 5553.8194.

What i am finding is that the value 0 is now being passed…

Could this be due to the positioning of the function? I have added this part in after i initialised my variables at the beginning.

Hard to tell with a mere verbal description - why not post the code?

// This #include statement was automatically added by the Particle IDE.
#include <MPU6050.h>
#include "Particle-GPS.h"

//GPS Setup
// ***
// *** Create a Gps instance. The RX an TX pins are connected to
// *** the TX and RX pins on the electron (Serial1).
// ***
Gps _gps(&Serial1);

// ***
// *** Create a timer that fires every 1 ms to capture
// *** incoming serial port data from the GPS.
// ***
Timer _timer(1, onSerialData);

// MPU variables:
MPU6050 accelgyro;
int16_t ax, ay, az;
int16_t gx, gy, gz;

//GPS Variables
String utcTime ="";
String latitude;
String northSouthIndicator ="";
String longitude ="";
String test ="";
String eastWestIndicator ="";

bool ledState = false;
void toggleLed() {
    ledState = !ledState;
    digitalWrite(ledPin, ledState);
}

int analogvalue; //declaring the integer variable
String data = "";  //Used to send value to the cloud

double decDegFromDMMmm(const char* coord) { //coord as DDDMM.mmmmmm
    int iDeg = atol(coord) /100;
    int dMin = atof(coord) - 100 * iDeg;
    return iDeg + dMin / 60.0;
}

void setup() {
    delay(2000);
    Wire.begin();
    Serial.begin();
    _gps.begin(9600);
    _timer.start();
    
    
    //Declare a Particle.variable() for access values in the cloud
    Particle.variable("analogvalue", &analogvalue, INT);
    // The following line will wait until you connect to the Spark.io using serial and hit enter. This gives
    // you enough time to start capturing the data when you are ready instead of just spewing data to the UART.
    //
    // So, open a serial connection using something like:
    // screen /dev/tty.usbmodem1411 9600
    //while(!Serial.available()) SPARK_WLAN_Loop();
    
    //Serial.println("Initializing I2C devices...");
    accelgyro.initialize();

    // Cerify the connection:
    //Serial.println("Testing device connections...");
    //Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
    
}

void loop() {
    // *** Get the Antenna Status ($PGTOP).
   
   Gga gga(_gps);
   if (gga.parse())
   { 
       utcTime = gga.utcTime;
       test = gga.latitude;
       //double latitude = decDegFromDMMmm(gga.latitude);
       latitude = gga.latitude;
       northSouthIndicator = gga.northSouthIndicator;
       longitude = gga.longitude;
       eastWestIndicator = gga.eastWestIndicator;
       
       
   }
   Rmc rmc(_gps);
   if (rmc.parse())
   {
        utcTime = rmc.utcTime;
        test = rmc.latitude;
        //double latitude = decDegFromDMMmm(rmc.latitude);
        latitude = rmc.latitude;
        northSouthIndicator = rmc.northSouthIndicator;
        longitude = rmc.longitude;
        eastWestIndicator = rmc.eastWestIndicator;
        
   }
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    //Serial.print("a/g:\t");
    analogvalue = analogRead(ax);
    data = analogvalue;
    
    if (analogvalue > 5000)
    {
            digitalWrite(ledPin, HIGH);
            delay(1000);
    }
    
    // create JSON ojects set ups
    char buf[622];
    memset(buf, 0, sizeof(buf));
    JSONBufferWriter writer(buf, sizeof(buf)-1);
    
    writer.beginObject();
    addToJSON(writer);
    writer.endObject();
    
    //Particle.publish("JSON", buf, PRIVATE);
    Particle.publish("Potholes", (buf), PRIVATE);
    
    delay(10000);
    

    
}

// function to add to JSON object
void addToJSON(JSONBufferWriter &writer){
    writer.name("ax").value(String(ax));
    writer.name("ay").value(String(ay));
    writer.name("az").value(String(az));
    writer.name("gx").value(String(gx));
    writer.name("gy").value(String(gy));
    writer.name("gz").value(String(gz));
    writer.name("utcTime").value(utcTime);
    writer.name("latitude").value(latitude);
    writer.name("test").value(test);
    writer.name("northSouthIndicator").value(northSouthIndicator);
    writer.name("longitude").value(longitude);
    writer.name("eastWestIndicator").value(eastWestIndicator);
}

void onSerialData() {
  _gps.onSerialData();
}

Note i had to comment the new lines out just so it doesn’t break elsewhere but i would have this uncommented with the line under it removed;
latitude = gga.latitude;

You have two variables latitude one globally defined as String latitude and the other local double latitude.
When you assigne to the one you are not also assigning to the other.

And you are still setting the variables twice when only the last of the two will ever prevail.

BTW, it’s usually better to keep the binary/numeric representation of your values instead of storing them as String. The JSON writer can do the conversion on the fly just fine, without you wrapping the values in String(...) :wink:

1 Like

Thank you, when i remove the String latitude from the top initialisation and only leave in within the loop;

double latitude = decDegFromDMMmm(gga.latitude);

When I attempt to compile it complains to say latitude was no declared in this scope.

So i went back and at the top i declared as opposed to string latitude = “”; with instead;

double latitude;

Again this is bringing over 0 within the JSON file for latitude…

I have tried to explain it verbally but obviously couldn’t get it across properly.

So give this a try
https://go.particle.io/shared_apps/5fca9a24e6f0b000092d081c

I haven’t got the hardware to test it, but it should work (better than what you had there :wink: )

1 Like

Thank you! I have tried the updated code, unfortunately for some reason this brings all the accelarometer readings over ax, ay… as zeros. However, for the GPS latidude and longitude i am getting over the first whole number. So my results are
latitude = 55, longitude = 3. What i am missing is the decimal values after the whole number.

Someone in other thread had pointed to using TinyGPS++ which i also tried as i think looking at the filmware it would give what i need, however, this isn’t supported on the particle device.

Not sure what you mean. I am using that library with the Particle AssetTracker.
However, for ublox based GPS devices I’d give AssetTrackerRK a go.

Hmm, that may be pointing at an issue with the JSON writer encoding numeric values.
I can have a try even without the hardware - will see what I find.

Hi i am not sure what i changed from your version but i put my old version back on and only changed the double latitude and its coming over correct now as 55.8833! So you code does work.

Thank you so much for your help on all of this it has been much appreciated, i’ve certainly learned a few things!

1 Like

Can i ask one last question, i know this can probably be done another way but for my own understanding.

I want to multiply the value for latitude by -1 only if the northSouthIndicator = S

My understanding is the indicator is currently a string either “N” or “S”.

My logic tells me i could put in an if statement within the loop after the variables are set and state;

if (northSouthIndicator) = "S"
   { 
       latitude * -1;
  }

However, this gives me errors;

Conversion from ‘String’ to ‘bool’ is ambiguous. I’m thinking bool means as in true or false.
expected primary-expression before ‘=’ token

There are multiple issues with that code:

  • the condition for an if statement has to be enclosed in parentheses entirely
  • the equality check is done via == (the single = is the assignment operator)
  • you cannot merely act on the variable like (latitude * -1) but you need an assignment to overwrite the current value (e.g. latitude = latitude * -1)

What you are trying there would rather be written like that

  if (northSouthIndicator == "S") 
    latitude *= -1;

However, in a more generalized way I’d not base this on another variable, but rather on the value itself like this

  if (latitude < 0) 
    latitude *= -1; // shorthand for latitude = latitude * -1

or

  latitude = abs(latitude); // take the absolute value

BTW, can we mark this thread as solved?

2 Likes

Thank you again for your help, that all makes sense. Your help as been very much appreciated.

1 Like

Can you try this one
https://go.particle.io/shared_apps/5fccb63d483a480017a30e63

The latitude and longitude should no show up with decimal places.