Barry Thomas

Planetary Motion Source Code

This code is from my demo Android app offering a tutorial in coding Keppler's first two laws in Java for Android. The snippet here is intended to be read with the truly excellent work of Jean Meeus in his book, which I strongly recommend to anyone with an interest in astronomy and computing.

Feel free to use this code for any purpose. I don't guarantee it works. The code isn't written for speed or efficiency, just so it works and you can see what is going on. Feel free to improve it.

Keppler

Mr Keppler. What a guy. Also, Jean Meeus. I thank you both. In Meeus's book, chapter 30, he describes the process of "calculating the position of a body (planet, minor planet or periodic comet) on its elliptical orbit around the Sun at a given instant ... from the orbital elements of the body".

My notes here do not teach you all about this topic - you go and do the work yourself, following Mr Meeus. My code here is just one way of calculating the position of the body, according to Meeus's description.

I'm assuming you know how to create an Android app, know Java and have a copy of his book.

This is my method for drawing the planet at the 'next' position in the orbit. Think of the steps like stepping round a circle, one degree at a time, on a circle which has the same period as the planet you are trying to track. Outside of this method I use a global double as the step counter - called dTime, which contains a number of degrees of rotation.

The key parameters passed to the method are, dEccentricty, dScalar (a scaling factor so the orbit all fits on the display), dYear (the duration of the orbit in Earth years) and to orient the orbit so that perihelion is at the right place on the dial, so to speak, dLongPeri - the Longitude of Perihelion.

Read the comments.

  1. public void drawPlanet (double dEccentricity, double dScalar, double dYear, Canvas canvas, Paint paint,   
  2.             String sName, Bitmap bmp, double dLongPeri)  
  3.         {  
  4.         double dE, dr, dv, dSatX, dSatY, dSatXCorrected, dSatYCorrected;  
  5.         float fX, fY;  
  6.         int iSunXOffset = getWidth() / 2;  
  7.         int iSunYOffset = getHeight() / 2;  
  8.           
  9.         // get the value of E from the angle travelled in this 'tick'  
  10.           
  11.         dE = getE (dTime * (1 / dYear), dEccentricity);  
  12.           
  13.         // get r: the length of 'radius' vector  
  14.           
  15.         dr = getRfromE (dE, dEccentricity, dScalar);  
  16.           
  17.         // calculate v - the true anomaly  
  18.           
  19.         dv = 2 * Math.atan (  
  20.                 Math.sqrt((1 + dEccentricity) / (1 - dEccentricity))  
  21.                 *  
  22.                 Math.tan(dE / 2)  
  23.                 );   
  24.           
  25.         // get X and Y coords based on the origin  
  26.           
  27.         dSatX = dr / Math.sin(Math.PI / 2) * Math.sin(dv);  
  28.         dSatY = Math.sin((Math.PI / 2) - dv) * (dSatX / Math.sin(dv));  
  29.           
  30.         // now correct for Longitude of Perihelion for this planet  
  31.           
  32.         dSatXCorrected = dSatX * (float)Math.cos (Math.toRadians(dLongPeri)) -   
  33.             dSatY * (float)Math.sin(Math.toRadians(dLongPeri));  
  34.         dSatYCorrected = dSatX * (float)Math.sin (Math.toRadians(dLongPeri)) +   
  35.             dSatY * (float)Math.cos(Math.toRadians(dLongPeri));  
  36.           
  37.         // offset the origin to nearer the centre of the display  
  38.           
  39.         fX = (float)dSatXCorrected + (float)iSunXOffset;  
  40.         fY = (float)dSatYCorrected + (float)iSunYOffset;  
  41.           
  42.         if (bDrawOrbits)  
  43.             {  
  44.             // draw the path of the orbit travelled  
  45.             paint.setColor(Color.WHITE);  
  46.             paint.setStyle(Paint.Style.STROKE);  
  47.             paint.setAntiAlias(true);  
  48.           
  49.             // get the size of the rect which encloses the elliptical orbit  
  50.           
  51.             dE = getE (0.0, dEccentricity);  
  52.             dr = getRfromE (dE, dEccentricity, dScalar);  
  53.             rectOval.bottom = (float)dr;  
  54.             dE = getE (180.0, dEccentricity);  
  55.             dr = getRfromE (dE, dEccentricity, dScalar);  
  56.             rectOval.top = (float)(0 - dr);  
  57.           
  58.             // calculate minor axis from major axis and eccentricity  
  59.             // http://www.1728.org/ellipse.htm  
  60.   
  61.             double dMajor = rectOval.bottom - rectOval.top;  
  62.             double dMinor = Math.sqrt(1 - (dEccentricity * dEccentricity)) * dMajor;  
  63.           
  64.             rectOval.left = 0 - (float)(dMinor / 2);  
  65.             rectOval.right = (float)(dMinor / 2);  
  66.   
  67.             rectOval.left += (float)iSunXOffset;  
  68.             rectOval.right += (float)iSunXOffset;  
  69.             rectOval.top += (float)iSunYOffset;  
  70.             rectOval.bottom += (float)iSunYOffset;  
  71.          
  72.             // now correct for Longitude of Perihelion for this orbit's path  
  73.   
  74.             canvas.save();  
  75.                 canvas.rotate((float)dLongPeri, (float)iSunXOffset, (float)iSunYOffset);  
  76.                 canvas.drawOval(rectOval, paint);  
  77.             canvas.restore();  
  78.             }  
  79.           
  80.         int iBitmapHeight = bmp.getHeight();  
  81.           
  82.         canvas.drawBitmap(bmp, fX - (iBitmapHeight / 2), fY - (iBitmapHeight / 2), null);  
  83.   
  84.         // draw planet label  
  85.           
  86.         myPaint.setColor(Color.WHITE);  
  87.         paint.setTextSize(30);  
  88.         canvas.drawText(sName, fX+20, fY-20, paint);  
  89.         }  

The method above calls two further methods which provide values of E (the mean anomaly) and r, the length of the vector at the end of which the planet is found. Check out the equation numbers and line numbers in Meeus's Basic program on page 206:

  1. public double getE (double dTime, double dEccentricity)  
  2.     {  
  3.     // we are passed the degree count in degrees (duh)   
  4.     // and the eccentricity value  
  5.  // the method returns E  
  6.   
  7.     double dM1, dD, dE0, dE = 0// return value E = the mean anomaly  
  8.     double dM; // local value of M in radians  
  9.             
  10.     dM = Math.toRadians (dTime);  
  11.       
  12.     int iSign = 1;  
  13.       
  14.     if (dM > 0) iSign = 1else iSign = -1;  
  15.       
  16.     dM = Math.abs(dM) / (2 * Math.PI); // Meeus, p 206, line 110  
  17.     dM = (dM - (long)dM) * (2 * Math.PI) * iSign; // line 120  
  18.     if (dM < 0)  
  19.         dM = dM + (2 * Math.PI); // line 130  
  20.     iSign = 1;  
  21.     if (dM > Math.PI) iSign = -1// line 150  
  22.     if (dM > Math.PI) dM = 2 * Math.PI - dM; // line 160  
  23.       
  24.     dE0 = Math.PI / 2// line 170  
  25.     dD = Math.PI / 4// line 170  
  26.       
  27.     for (int i = 0; i < 33; i++) // line 180   
  28.         {  
  29.         dM1 = dE0 - dEccentricity * Math.sin(dE0); // line 190  
  30.         dE0 = dE0 + dD * Math.signum((float)(dM - dM1));  
  31.         dD = dD / 2;   
  32.         }  
  33.       
  34.     dE = dE0 * iSign;  
  35.       
  36.     return dE;  
  37.     }  

And once you have E, you need the length of the vector r:

  1. public double getRfromE (double dE, double dEccentricty, double dScalar)  
  2.         {  
  3.         return Math.min(getWidth(), getHeight()) / 2 * dScalar * (1 - (dEccentricty * Math.cos(dE)));  
  4.         }  

If you would like the entire source code of my app, go here.

StatCounter - Free Web Tracker and Counter