Pugetworks
Seattle Software Developers

blog-archive

Pugetworks Blog Archive!

GPSView

GPSView

Recently, we created GPSView, a free Android application to show a user the location of GPS satellites they can detect. The following post is an explanation into how the math works and provides some Java code examples of how to calculate this.

In regards to GPS, it turns out Android will provide some information that you cannot get on the iPhone. On both platforms you are provided with your current location. But on Android you can also gain access to the satellite data. The data looks a little like this (Azimuth [326.0], Elevation [7.0], PRN [2], SNR [21.0]). Azimuth and elevation are two navigational terms that tell you where an object is relative to your current position. The azimuth is the compass bearing, relative to true (geographic) north, of a point on the horizon directly beneath an observed object. The elevation, also called the altitude, of an observed object is determined by first finding the compass bearing on the horizon relative to true north, and then measuring the angle between that point and the object, from the reference frame of the observer.

The problem

Knowing this, the problem breaks down into 2 steps. First, given the elevation (E) what is the distance (D) to the point directly between the satellite and the reference point? Second, now that you know the distance, bearing, and location, what will the new position be?

Step 1

The following diagram illustrates the problem.

Satellite Diagram

The variables are

  • E = elevation angle
  • R = the earths radius
  • H = the satellite orbit
  • D = the distance we are solving for
  • The angles; Theta, Alpha, and Beta
  1. Solve for Theta
    theta = 90 (degrees) + E
    
  2. Use the law of sines to solve for Alpha

    (Sin(Theta)/(R + H)) = (Sin(Alpha)/R)
    
  3. Use Theta and Alpha now to solve for Beta

    Beta = 180 - Alpha - Theta
    
  4. Convert the angle into radians and multiply by the radius of the earth to get the distance

    D = R * ((Beta * 2 Pi)/360)
    

Step 2

Now that we have a distance and a bearing we can use standard navigational formulas to find a position on the earth. I used Aviation Formulary V1.30 - Ed Williams

     lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
     IF (cos(lat)=0)
        lon=lon1      // endpoint a pole
     ELSE
        lon=mod(lon1-asin(sin(tc)*sin(d)/cos(lat))+pi,2*pi)-pi
     ENDIF

Into Java

Now to actually use this math, the following code snippet was pulled out of the application.

    public static final int LAT_LON_CONVERT = 1000000;
    public static final double EARTH_CIRCUMFRENCE = 40075;
    public static final double EARTH_RADIUS = 6360;
    public static final double GPS_SAT_ORBIT = 20200;
    public static final double SAT_HEIGHT_FROM_CENTER = EARTH_RADIUS + GPS_SAT_ORBIT;

    public static double findDistance(float elevation) {
    	//These equations are based on the law of sins 
    	if(elevation == 90) return 0;

    	double theta = Math.toRadians(elevation) + Math.PI/2; //Add on for the 90 degree tangent
    	double alpha = Math.asin(EARTH_RADIUS * (Math.sin(theta)) / SAT_HEIGHT_FROM_CENTER);
    	double beta = Math.PI - theta - alpha;
    	double distance = beta * EARTH_RADIUS; 

    	return distance;
    }

    public static GeoPoint findPosition(double distance, double lat1, double lon1, double azimuth) {
    	double lat2 = 0;
    	double lon2 = 0;

    	//Switch to radians
    	lat1 = Math.toRadians(lat1);
    	lon1 = Math.toRadians(lon1);
    	azimuth = Math.toRadians(azimuth);
    	distance = (distance/EARTH_CIRCUMFRENCE)* 2* Math.PI;

    	lat2 = Math.asin(Math.sin(lat1)*Math.cos(distance) + Math.cos(lat1) * Math.sin(distance) * Math.cos(azimuth));
    	if(Math.cos(lat1) == 0) {
    		lon2 = lon1; // endpoint a pole
    	} else {
    		lon2 = Math.asin(Math.sin(azimuth)*Math.sin(distance)/Math.cos(lat1));
    		lon2 = lon1 - lon2 + Math.PI;
   			lon2 =(lon2 % (2*Math.PI));   
    	    lon2 = lon2 - Math.PI;
    	}

    	//Convert back to degrees
    	lat2 = Math.toDegrees(lat2);
    	lon2 = Math.toDegrees(lon2);

    	GeoPoint pt = new GeoPoint((int)(lat2 * LAT_LON_CONVERT), (int)(lon2 * LAT_LON_CONVERT));
    	return pt;
    }

Conclusion

Once we have this point its trivial to project that onto a map in Android. The end result is what you are seeing on GPSView.

Enjoy!

References:

For more information please check these sites