I want to calculate bearing between 2 GPS positions, I foollowed this page recommandations for my algorythm:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(pt1.Latitude) * Math.Sin(pt2.Latitude) - Math.Sin(pt1.Latitude) * Math.Cos(pt2.Latitude) * Math.Cos(pt2.Longitude - pt1.Longitude);
double y = Math.Sin(pt2.Longitude - pt1.Longitude) * Math.Cos(pt2.Latitude);
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2)%(Math.PI * 2);
}
Then I transform my value in degrees using this method
public static double RadiansToDegrees(double angle)
{
return (angle * 180.0) / Math.PI;
}
I have the following test sample:
Point1 (lat, long) = 43.6373638888888888888888888888889, 1.35762222222222222222222222222222
Point2 (lat, long) = 43.6156444444444444444444444444444,1.380225
Expected bearing = 323°
However, I obtain a bearing of 315.5° (5.5062235835910762 rad). If i calculate the expected radian value, i get 5.637413 which leaves no doubt that my problem lies in my bearing method.
I already implemented other computation methods using .Net Math package (including Cos, Sin, Tan and ATan methods) and my unit tests pass with 1e-12 precision. What am I missing?
PS: I also tryied to reimplement the Atan2 method in case there is a lack of precision in it. I obtain the very same result
edit: My Latitude and Longitude are double as per the following interface
public interface IPointGps
{
double Latitude { get; }
double Longitude { get; }
}
Math.Sin() and all similar methods expect argument in radians, but your latitudes and longitudes are in degrees. You have to convert IPointGps to radians before you calculate bearing, or modify Bearing calculation, e.g.:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(DegreesToRadians(pt1.Latitude)) * Math.Sin(DegreesToRadians(pt2.Latitude)) - Math.Sin(DegreesToRadians(pt1.Latitude)) * Math.Cos(DegreesToRadians(pt2.Latitude)) * Math.Cos(DegreesToRadians(pt2.Longitude - pt1.Longitude));
double y = Math.Sin(DegreesToRadians(pt2.Longitude - pt1.Longitude)) * Math.Cos(DegreesToRadians(pt2.Latitude));
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2) % (Math.PI * 2);
}
public static double DegreesToRadians(double angle)
{
return angle * Math.PI / 180.0d;
}
returns bearing 5.637716736134105.
It looks like your latitude and longitude variables are float (single precision). If that is the case, then your are facing a precision error.
Related
I want to calculate bearing between 2 GPS positions, I foollowed this page recommandations for my algorythm:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(pt1.Latitude) * Math.Sin(pt2.Latitude) - Math.Sin(pt1.Latitude) * Math.Cos(pt2.Latitude) * Math.Cos(pt2.Longitude - pt1.Longitude);
double y = Math.Sin(pt2.Longitude - pt1.Longitude) * Math.Cos(pt2.Latitude);
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2)%(Math.PI * 2);
}
Then I transform my value in degrees using this method
public static double RadiansToDegrees(double angle)
{
return (angle * 180.0) / Math.PI;
}
I have the following test sample:
Point1 (lat, long) = 43.6373638888888888888888888888889, 1.35762222222222222222222222222222
Point2 (lat, long) = 43.6156444444444444444444444444444,1.380225
Expected bearing = 323°
However, I obtain a bearing of 315.5° (5.5062235835910762 rad). If i calculate the expected radian value, i get 5.637413 which leaves no doubt that my problem lies in my bearing method.
I already implemented other computation methods using .Net Math package (including Cos, Sin, Tan and ATan methods) and my unit tests pass with 1e-12 precision. What am I missing?
PS: I also tryied to reimplement the Atan2 method in case there is a lack of precision in it. I obtain the very same result
edit: My Latitude and Longitude are double as per the following interface
public interface IPointGps
{
double Latitude { get; }
double Longitude { get; }
}
Math.Sin() and all similar methods expect argument in radians, but your latitudes and longitudes are in degrees. You have to convert IPointGps to radians before you calculate bearing, or modify Bearing calculation, e.g.:
public static double Bearing(IPointGps pt1, IPointGps pt2)
{
double x = Math.Cos(DegreesToRadians(pt1.Latitude)) * Math.Sin(DegreesToRadians(pt2.Latitude)) - Math.Sin(DegreesToRadians(pt1.Latitude)) * Math.Cos(DegreesToRadians(pt2.Latitude)) * Math.Cos(DegreesToRadians(pt2.Longitude - pt1.Longitude));
double y = Math.Sin(DegreesToRadians(pt2.Longitude - pt1.Longitude)) * Math.Cos(DegreesToRadians(pt2.Latitude));
// Math.Atan2 can return negative value, 0 <= output value < 2*PI expected
return (Math.Atan2(y, x) + Math.PI * 2) % (Math.PI * 2);
}
public static double DegreesToRadians(double angle)
{
return angle * Math.PI / 180.0d;
}
returns bearing 5.637716736134105.
It looks like your latitude and longitude variables are float (single precision). If that is the case, then your are facing a precision error.
I would like to know what's the best way to calculate the current speed with GPS.
I've an external GPS receiver which is connected via USB to my car-notebook. It gives me just the following information:
- Longitude
- Latitude
- Altitude
My try is to get two location-infos with timestamps.
Then I am finding the difference in time (timestamp2 - timestamp1) and calculating the speed (distance/time).
Are there any other possibilites oder maybe any libraries available?
To calculate the distance, you will need the Haversine Formula.
You will find many implementations of it around the web, here is one I use in C#:
private static double ArcInMeters(double lat0, double lon0, double lat1, double lon1)
{
double earthRadius = 6372797.560856; // m
return earthRadius * ArcInRadians(lat0, lon0, lat1, lon1);
}
private static double ArcInRadians(double lat0, double lon0, double lat1, double lon1)
{
double latitudeArc = DegToRad(lat0 - lat1);
double longitudeArc = DegToRad(lon0 - lon1);
double latitudeH = Math.Sin(latitudeArc * 0.5);
latitudeH *= latitudeH;
double lontitudeH = Math.Sin(longitudeArc * 0.5);
lontitudeH *= lontitudeH;
double tmp = Math.Cos(DegToRad(lat0)) * Math.Cos(DegToRad(lat1));
return 2.0 * Math.Asin(Math.Sqrt(latitudeH + tmp * lontitudeH));
}
private static double DegToRad(double x)
{
return x * Math.PI / 180;
}
I have a database with a list of latitude and longitude
-DeviceName
-Latitude
-Longitude
Given my current device's latitude and longitude, I want to get all devices within the database list in distance/proximity of X kilometres.
How do I calculate the proximity of my location vs other locations?
I think you want to have a peep at this amazing presentation. it will tell you how to use (and for bonus points explains!) the haversine formula to calcuate distances on the surface of the earth accounting for curviture and how to avoid some common mistakes in your database queries etc. His dataset is pretty much exactly what yours is - item, longitude and latitude.
If you're after the raw code, this should help you:
private static Double rad2deg(Double rad) {
return (rad / Math.PI * 180.0);
}
private static Double deg2rad(Double deg) {
return (deg * Math.PI / 180.0);
}
private const Double kEarthRadiusKms = 6376.5;
private static Double CalculateDistance(Double latitude1, Double longitude1, Double latitude2, Double longitude2) {
double theta = longitude1 - longitude2;
double dist = Math.Sin(deg2rad(latitude1)) * Math.Sin(deg2rad(latitude2)) + Math.Cos(deg2rad(latitude1)) * Math.Cos(deg2rad(latitude2)) * Math.Cos(deg2rad(theta));
dist = Math.Acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
dist = dist * 1.609344;
return (dist);
}
I have two functions that are intended to contain angles between (-180,180] and (-π,π]. The intent is that given any angle from -inf to +inf it will retain the equivalent angle in the intervals specified. For example the angle for 1550° is 110°.
public double WrapBetween180(double angle)
{
return angle - 360d * Math.Round(angle / 360d, MidpointRounding.AwayFromZero);
}
public double WrapBetweenPI(double angle)
{
const double twopi = 2d * Math.PI;
return angle - twopi * Math.Round(angle / twopi, MidpointRounding.AwayFromZero);
}
which yields the following results
WrapBetween180(-180) = -180
WrapBetween180( 180) = 180
WrapBetweenPI(-Math.PI) = Math.PI
WrapBetweenPI( Math.PI) = -Math.PI
none of which is what I want. What I wanted is:
WrapBetween180(-180) = 180
WrapBetween180( 180) = 180
WrapBetweenPI(-Math.PI) = Math.PI
WrapBetweenPI( Math.PI) = Math.PI
I tryied playing around with the rounding methods, but still cannot get the desired results. The problem is pronounced because sometimes the angles I deal with are only approximately close to -π or π and I am getting discontinuities it my results.
Any suggestions on how to best implement angle wrapping functions with non-inclusive low limit and inclusive high limits?
For the angle in degrees, if x is between -180 and 180, then 180 - x is between 0 and 360. What you want is equivalent to asking that 180 - x is between 0 (inclusive), and 360 (exclusive). So, as soon as 180 - x reaches 360, we want to add 360 to the angle. This gives us:
return angle + 360d * Math.Floor((180d - angle) / 360d);
Same thing for the angle in radians:
return angle + twopi * Math.Floor((Math.PI - angle) / twopi);
It does not address the rounding issue, but here is how I would to what you want to do :
private static double ConvertAngle(double angle)
{
bool isNegative = angle < 0;
if (isNegative)
angle *= -1;
angle = angle % 360;
if (isNegative)
angle = -1 * angle + 360;
if (angle > 180)
angle = (angle - 360);
return angle;
}
Note: This way supposes you want "behind" to be 180 degrees, not -180 degrees.
Isn't this a case for a modulo operation?
private double Wrap180(double value)
{
// exact rounding of corner values
if (value == 180) return 180.0;
if (value == -180) return 180.0;
// "shift" by 180 and use module, then shift back.
double wrapped = ((Math.Abs(value) + 180.0) % 360.0) - 180.0;
// handle negative values correctly
if (value < 0) return -wrapped;
return wrapped;
}
It passes this tests
Assert.AreEqual(170.0, wrap(-190.0));
Assert.AreEqual(180.0, wrap(-180.0));
Assert.AreEqual(-170.0, wrap(-170.0));
Assert.AreEqual(0.0, wrap(0.0));
Assert.AreEqual(10.0, wrap(10.0));
Assert.AreEqual(170.0, wrap(170.0));
Assert.AreEqual(180.0, wrap(180.0));
Assert.AreEqual(-170.0, wrap(190.0));
If I'm given the Polar coordinates of a Fourier transform and I want to go back to the Cartesian (Real/Imaginary) coordiates, how would I go about doing that?
I'm able to get the Polar numbers from the Cartesian coordiates with the following code:
private double GetPhase(double real, double imaginary)
{
return Math.Atan2(imaginary, real);
}
private double GetMagnitude(double real, double imaginary)
{
return Math.Sqrt((real * real) + (imaginary * imaginary));
}
But how do I go back?
Isn't that just:
(pseudocode)
x = cos(angle) * magnitude
y = sin(angle) * magnitude
(use negative sin if you're using computer's inverted coordinate system)
?
To add to #BlueMonkMN's answer:
private double GetX (double angle, double magnitude)
{
return Math.Cos(angle) * magnitude;
}
private double GetY (double angle, double magnitude)
{
return Math.Sin(angle) * magnitude;
}