cgeogeofencing

Check if geo location is within radius of other geolocation without using sin/cos/tan


I want to develop a simple geo-fencing algorithm in C, that works without using sin, cos and tan. I am working with a small microcontroller, hence the restriction. I have no space left for <math.h>. The radius will be around 20..100m. I am not expecting super accurate results this way.

My current solution takes two coordinate sets (decimal, .00001 accuracy, but passed as a value x10^5, in order to eliminate the decimal places) and a radius (in m). When multiplying the coordinates with 0.9, they can approximately be used for a Pythagorean equation which checks, if one coordinate lies within the radius of another:

static int32_t
geo_convert_coordinates(int32_t coordinate)
{
     return (cordinate * 10) / 9;
}

bool                                                                             
geo_check(int32_t  lat_fixed,
          int32_t  lon_fixed,
          int32_t  lat_var,
          int32_t  lon_var,
          uint16_t radius)
{
    lat_fixed = geo_convert_distance(lat_fixed);
    lon_fixed = geo_convert_distance(lon_fixed);
    lat_var   = geo_convert_distance(lat_var);
    lon_var   = geo_convert_distance(lon_var);

    if (((lat_var - lat_fixed) * (lat_var - lat_fixed) + (lon_var - lon_fixed) * (lon_var - lon_fixed))
        <= (radius * radius))
    {
        return true;
    }

    return false;
}

This solution works quite well for the equator, but when changing the latitude, this becomes increasingly inaccurate, at 70°N the deviation is around 50%. I could change the factor depending on the latitude, but I am not happy with this solution.

Is there a better way to do this calculation? Any help is very much appreciated. Best regards!

UPDATE

I used the input I got and managed to implement a decent solution. I used only signed ints, no floats.

The haversine formula could be simplified: due to the relevant radii (50-500m), the deltas of the latitude and longitude are very small (<0.02°). This means, that the sine can be simplified to sin(x) = x and also the arcsine to asin(x) = x. This approach is very accurate for angles <10° and even better for the small angles used here. This leaves the cosine, which I implemented according to @meaning-matters 's suggestion. The cosine will take an angle and return the actual result multiplied by 100, in order to be able to use ints. The square root was implemented with an iterative loop (I cannot find the so post anymore). The haversine calculation was done with the inputs multiplied by powers of 10 in order to achieve accuracy and afterwards divided by the necessary power of 10.

For my 8bit system, this caused a memory usage of around 2000-2500 Bytes.


Solution

  • Implement the Havesine function using your own trigonometric functions that use lookup tables and do interpolation.

    Because you don't want very accurate results, small lookup tables, of perhaps twenty points, would be sufficient. And, simple linear interpolation would also be fine.

    In case you don't have much memory space: Bear in mind that to implement sine and cosine, you only need one lookup table for 90 degrees of either function. All values can then be determined by mirroring and offsetting.