Float Number not correct

I feel like I am always crying wolf but this platform does throw me for a loop every now and again. I am comparing floats to a threshold and for some reason I am getting an outrageously large number for the difference when the difference should be around .001.

I have four variables:

float gpsPrevLat = 0.0f;
float gpsPrevLon = 0.0f;
float gpsCurrLat = roundf(t.readLatDeg() * decimal)/decimal ;
float gpsCurrLon = roundf(t.readLonDeg() * decimal)/decimal ;

I wanted to cut off the number to the fifth decimal point so thats the reason for the round.

I then compare these numbers every loop

    float latDiff = fabsf(gpsPrevLat - gpsCurrLat);
    float lonDiff = fabsf(gpsPrevLon - gpsCurrLon);
    
    Particle.publish("LaD", String(latDiff), 60, PRIVATE);
    Particle.publish("LoD", String(lonDiff), 60, PRIVATE);
    
    if(latDiff > .001f || lonDiff > .001f){
        Particle.publish("hM","true",60,PRIVATE);
        return true;
    }else{
        Particle.publish("hM","false",60,PRIVATE);
        return false;
    }

I then afterwards reset the previous variables to the current

gpsPrevLat = gpsCurrLat;
gpsPrevLon = gpsCurrLon;

This all makes sense to me. However when I reprint out the gpsPrevLat I get a crazy number

The output you see is the difference in Lon and Lat. The LaD is latitude difference and LoD is longitude difference.

If more info is needed please let me know. I am out of answers

How does your code behave when you pass in constants instead of t.readLatDeg()/t.readLonDeg()?

Checking all the sub-results (e.g. via Serial.print()) in your calculations might provide some insight where the problem starts.

BTW, since floating point variables are inherently unable to hold arbitrary numbers with full precision (even after “rounding” you may end up with more decimals than you expected) I’d just forget the rounding and only limit the way how you display the numebrs
I’d rather do it like this.

float gpsPrevLat = 0.0f;
float gpsPrevLon = 0.0f;
float gpsCurrLat = t.readLatDeg();
float gpsCurrLon = t.readLonDeg();
...
  float latDiff = fabsf(gpsPrevLat - gpsCurrLat);
  float lonDiff = fabsf(gpsPrevLon - gpsCurrLon);
    
  //Particle.publish("LaD/LoD", String::format("%.5f / %.5f", latDiff, lonDiff), PRIVATE);
  // but since I "despise" String I'd rather do
  char data[32];
  snprintf(data, sizeof(data), "%.5f / %.5f", latDiff, lonDiff);
  Particle.publish("LaD/LoD", data, PRIVATE);
3 Likes

So when doing that I see that the issue is that gpsPrevLat is savings a really high number compared to the actual gpsCurrLat number it should be savings. So I know my issue is there but not how to solve it.

Also,
I tried that as well (not rounding) and it doesnt make a difference to my values either way. I am considering switching to doubles instead

Showing the rest of your code old help.
Maybe you got some buffer violation or pointer issues.

1 Like
    // This #include statement was automatically added by the Particle IDE.
    #include <AssetTracker.h>

    #include <math.h>

    // Used to keep track of the last time we published data
    long lastPublish = 0;

    // How many minutes between publishes? 10+ recommended for long-time continuous publishing!
    int delayMinutes = 2;

    // Creating an AssetTracker named 't' for us to reference
    AssetTracker t = AssetTracker();

    int isMoving = 0;

    //Device number
    char did[9] = "58965412";

    //variable to hold JSON data
    char data[64];

    float gpsPrevLat;
    float gpsPrevLon;

    int decimal = 100000;


    // setup() and loop() are both required. setup() runs once when the device starts
    // and is used for registering functions and variables and initializing things
    void setup() {
        // Sets up all the necessary AssetTracker bits
        t.begin();

        // Enable the GPS module. Defaults to off to save power.
        // Takes 1.5s or so because of delays.
        t.gpsOn();
        
        gpsPrevLon = 0.0f;
        gpsPrevLat = 0.0f;

        // Opens up a Serial port so you can listen over USB
        Serial.begin(9600);

        Particle.function("gps", gpsPublish);
    }

    // loop() runs continuously
    void loop() {
        
        
        // You'll need to run this every loop to capture the GPS output
        t.updateGPS();

        // if the current time - the last time we published is greater than your set delay...
        if (millis()-lastPublish > delayMinutes*60*1000) {
            // Remember when we published
            lastPublish = millis();
            //Particle.publish("A", pubAccel, 60, PRIVATE);

            // GPS requires a "fix" on the satellites to give good data,
            // so we should only publish data if there's a fix
            if (t.gpsFix()) {
                float gpsCurrLat = roundf(t.readLatDeg() * decimal)/decimal ;
                float gpsCurrLon = roundf(t.readLonDeg() * decimal)/decimal ;
                
                bool IsMoved = hasMoved(gpsPrevLat, gpsPrevLon, gpsCurrLat, gpsCurrLon);
                
                
                gpsPrevLat = gpsCurrLat;
                gpsPrevLon = gpsCurrLon;
                //creates a string in JSON structure
                sprintf(data,"{\"did\": %s, \"gpsLat\": %e, \"gpsLon\": %e, \"isMoving\" : %d}",did,gpsCurrLat,gpsCurrLon,isMoving);
                // Short publish names save data!
                Particle.publish("Data", data, 60, PRIVATE);
                //try webhook and check if it worked
                Particle.publish("update_gps",data, 60, PRIVATE);
                
                
                
                // Short publish names save data!
                //Particle.publish("G", t.readLatLon(), 60, PRIVATE);
                
                // but always report the data over serial for local development
                //Serial.println(t.readLatLon());
            }
        }
    }


    // Actively ask for a GPS reading if you're impatient. Only publishes if there's
    // a GPS fix, otherwise returns '0'
    int gpsPublish(String command) {
        if (t.gpsFix()) {
            Particle.publish("G", t.readLatLon(), 60, PRIVATE);

            // uncomment next line if you want a manual publish to reset delay counter
            // lastPublish = millis();
            return 1;
        } else {
          return 0;
        }
    }

    bool hasMoved(float gpsPrevLat, float gpsPrevLon, float gpsCurrLat, float gpsCurrLon){
        
        
        float latDiff = fabsf(gpsPrevLat - gpsCurrLat);
        float lonDiff = fabsf(gpsPrevLon - gpsCurrLon);
        
        Particle.publish("LaD", String(latDiff), 60, PRIVATE);
        Particle.publish("LoD", String(lonDiff), 60, PRIVATE);
        
        if(latDiff > .001f || lonDiff > .001f){
            Particle.publish("hM","true",60,PRIVATE);
            return true;
        }else{
            Particle.publish("hM","false",60,PRIVATE);
            return false;
        }
    }

The literals alone are already taking 49 (if I counted right :sunglasses:) of 63 characters, so there might be a possible problem.

To prevent buffer overflow rather go with snprintf()

snprintf(data, sizeof(data), "{\"did\": %s, \"gpsLat\": %e, \"gpsLon\": %e, \"isMoving\" : %d}",did,gpsCurrLat,gpsCurrLon,isMoving);

Doing that change made my Webhook no longer work. Also it had no change to the difference in my latitude or longitude

So I ran the code last night and got outrageous numbers. Now when I run the code everything works fine and the distance is correct.

OK? I can't quite understand what the combined essence of these last two posts would be?

What change made the webhook stop?
What change made the difference come out correct?
What's still open now?

BTW, if the snprintf() change killed the webhook but cured the calculation it's a reliable indicator that your char data[64] is too small for your string :wink:

Looks like I got it wrong. the snprintf cured my calculation and it calls my webhook but my API attached to the webhook is not accepting it. Checking the logs to see what that is but may just increase the size of “data”.

Can someone explain why snprintf cured my calculation? I’m more Java than C so the buffer and pointers are a tad difference, as in they exist heavily compared to Java.

The most probable reason is that your string was too long to fit into char[64] but sprintf() doesn’t care about that and will probably have put the overhang into adjacent memory where your floats lived - consequently corrupting the contents of these floats.
And with snprintf() your backend won’t accept the string since now it’s not a properly terminated string anymore (that’s what happens with a string in snprintf() that won’t fit) but will contain “random” bytes (whatever follows in RAM up to the first 0x00 byte).

BTW, if you had assigned the floats after sprintf() and the publish instruction, you would probably not have noticed the issue.

the function

snprintf(myBuffer, sizeof(myBuffer), ...)

prevents inadvertantly writing past the the end of myBuffer which may cause the corruption of some other data

sprintf has no such safety mechanism…

1 Like

Ok so now that my webhook is not being called. So If I change it back to sprintf but increase the buffer size it should work fine?

Nope, just increase the buffer and never go back to sprintf() :wink: unless you want to plant yourself some time bombs :bomb:

So I increased the buffer size however it is still not calling the webhook

Particle.publish("update_gps",data, 60, PRIVATE);

// This #include statement was automatically added by the Particle IDE.
#include <AssetTracker.h>

#include <math.h>


// How many minutes between publishes? 10+ recommended for long-time continuous publishing!
int delayMinutes = 2;

// Used to keep track of the last time we published data
long lastPublish = 0;

// Creating an AssetTracker named 't' for us to reference
AssetTracker t = AssetTracker();

int isMoving = 0;

//Device number
char did[9] = "58965412";


float gpsPrevLat;
float gpsPrevLon;

int decimal = 100000;


// setup() and loop() are both required. setup() runs once when the device starts
// and is used for registering functions and variables and initializing things
void setup() {
    // Sets up all the necessary AssetTracker bits
    t.begin();

    // Enable the GPS module. Defaults to off to save power.
    // Takes 1.5s or so because of delays.
    t.gpsOn();
    
    gpsPrevLon = 0.0f;
    gpsPrevLat = 0.0f;

    // Opens up a Serial port so you can listen over USB
    Serial.begin(9600);

    Particle.function("gps", gpsPublish);
}

// loop() runs continuously
void loop() {
    
    //variable to hold JSON data
    char data[90];
    
    // You'll need to run this every loop to capture the GPS output
    t.updateGPS();

    // if the current time - the last time we published is greater than your set delay...
    if (millis()-lastPublish > delayMinutes*60*1000) {
        // Remember when we published
        lastPublish = millis();
        //Particle.publish("A", pubAccel, 60, PRIVATE);

        // GPS requires a "fix" on the satellites to give good data,
        // so we should only publish data if there's a fix
        if (t.gpsFix()) {
            float gpsCurrLat = roundf(t.readLatDeg() * decimal)/decimal ;
            float gpsCurrLon = roundf(t.readLonDeg() * decimal)/decimal ;
            
            bool isMoved = hasMoved(gpsPrevLat, gpsPrevLon, gpsCurrLat, gpsCurrLon);
            
            if(isMoved){
                gpsPrevLat = gpsCurrLat;
                gpsPrevLon = gpsCurrLon;
                //creates a string in JSON structure
                snprintf(data,sizeof(data), "{\"did\": %s, \"gpsLat\": %e, \"gpsLon\": %e, \"isMoving\" : %d}",did,gpsCurrLat,gpsCurrLon,isMoving);
                // Short publish names save data!
                Particle.publish("Data", data, 60, PRIVATE);
                //try webhook and check if it worked
                Particle.publish("update_gps",data, 60, PRIVATE);
            }
            // Short publish names save data!
            //Particle.publish("G", t.readLatLon(), 60, PRIVATE);
            
            // but always report the data over serial for local development
            //Serial.println(t.readLatLon());
        }
    }
}


// Actively ask for a GPS reading if you're impatient. Only publishes if there's
// a GPS fix, otherwise returns '0'
int gpsPublish(String command) {
    if (t.gpsFix()) {
        Particle.publish("G", t.readLatLon(), 60, PRIVATE);

        // uncomment next line if you want a manual publish to reset delay counter
        // lastPublish = millis();
        return 1;
    } else {
      return 0;
    }
}

bool hasMoved(float gpsPrevLat, float gpsPrevLon, float gpsCurrLat, float gpsCurrLon){
    
    
    float latDiff = fabsf(gpsPrevLat - gpsCurrLat);
    float lonDiff = fabsf(gpsPrevLon - gpsCurrLon);
    
    Particle.publish("LaD", String(latDiff), 60, PRIVATE);
    Particle.publish("LoD", String(lonDiff), 60, PRIVATE);
    
    if(latDiff > .001f || lonDiff > .001f){
        Particle.publish("hM","true",60,PRIVATE);
        return true;
    }else{
        Particle.publish("hM","false",60,PRIVATE);
        return false;
    }
    
    
}

When i pulled the the data variable in the loop method it begin to make the webhook call.

I am going to go read about buffers. Thanks for the help!

If you can post the contents of data and the full output of the console for the webhook call we might be able to see what’s wrong.

You also got numerous publishes that might actually violate the rate limit of 1/sec with a burst of max. 4 with 4sec cooldown.

On movement you have 3 publishes in hasMoved() followed by 1 "Data" and hence the fifth "update_gps" will be blocked. So this doesn’t relate to your buffers at all.

1 Like