What are some algorithms that will allow me to simulate planetary physics?

前端 未结 12 675
离开以前
离开以前 2021-01-30 17:37

I\'m interested in doing a \"Solar System\" simulator that will allow me to simulate the rotational and gravitational forces of planets and stars.

I\'d like to be able t

12条回答
  •  独厮守ぢ
    2021-01-30 18:22

    Algorithms to simulate planetary physics.

    Here is an implementation of the Keppler parts, in my Android app. The main parts are on my web site for you can download the whole source: http://www.barrythomas.co.uk/keppler.html

    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.

    drawPlanet:

    public void drawPlanet (double dEccentricity, double dScalar, double dYear, Canvas canvas, Paint paint, 
                String sName, Bitmap bmp, double dLongPeri)
    {
            double dE, dr, dv, dSatX, dSatY, dSatXCorrected, dSatYCorrected;
            float fX, fY;
            int iSunXOffset = getWidth() / 2;
            int iSunYOffset = getHeight() / 2;
    
            // get the value of E from the angle travelled in this 'tick'
    
            dE = getE (dTime * (1 / dYear), dEccentricity);
    
            // get r: the length of 'radius' vector
    
            dr = getRfromE (dE, dEccentricity, dScalar);
    
            // calculate v - the true anomaly
    
            dv = 2 * Math.atan (
                    Math.sqrt((1 + dEccentricity) / (1 - dEccentricity))
                    *
                    Math.tan(dE / 2)
                    ); 
    
            // get X and Y coords based on the origin
    
            dSatX = dr / Math.sin(Math.PI / 2) * Math.sin(dv);
            dSatY = Math.sin((Math.PI / 2) - dv) * (dSatX / Math.sin(dv));
    
            // now correct for Longitude of Perihelion for this planet
    
            dSatXCorrected = dSatX * (float)Math.cos (Math.toRadians(dLongPeri)) - 
                dSatY * (float)Math.sin(Math.toRadians(dLongPeri));
            dSatYCorrected = dSatX * (float)Math.sin (Math.toRadians(dLongPeri)) + 
                dSatY * (float)Math.cos(Math.toRadians(dLongPeri));
    
            // offset the origin to nearer the centre of the display
    
            fX = (float)dSatXCorrected + (float)iSunXOffset;
            fY = (float)dSatYCorrected + (float)iSunYOffset;
    
            if (bDrawOrbits)
                {
                // draw the path of the orbit travelled
                paint.setColor(Color.WHITE);
                paint.setStyle(Paint.Style.STROKE);
                paint.setAntiAlias(true);
    
                // get the size of the rect which encloses the elliptical orbit
    
                dE = getE (0.0, dEccentricity);
                dr = getRfromE (dE, dEccentricity, dScalar);
                rectOval.bottom = (float)dr;
                dE = getE (180.0, dEccentricity);
                dr = getRfromE (dE, dEccentricity, dScalar);
                rectOval.top = (float)(0 - dr);
    
                // calculate minor axis from major axis and eccentricity
                // http://www.1728.org/ellipse.htm
    
                double dMajor = rectOval.bottom - rectOval.top;
                double dMinor = Math.sqrt(1 - (dEccentricity * dEccentricity)) * dMajor;
    
                rectOval.left = 0 - (float)(dMinor / 2);
                rectOval.right = (float)(dMinor / 2);
    
                rectOval.left += (float)iSunXOffset;
                rectOval.right += (float)iSunXOffset;
                rectOval.top += (float)iSunYOffset;
                rectOval.bottom += (float)iSunYOffset;
    
                // now correct for Longitude of Perihelion for this orbit's path
    
                canvas.save();
                    canvas.rotate((float)dLongPeri, (float)iSunXOffset, (float)iSunYOffset);
                    canvas.drawOval(rectOval, paint);
                canvas.restore();
                }
    
            int iBitmapHeight = bmp.getHeight();
    
            canvas.drawBitmap(bmp, fX - (iBitmapHeight / 2), fY - (iBitmapHeight / 2), null);
    
            // draw planet label
    
            myPaint.setColor(Color.WHITE);
            paint.setTextSize(30);
            canvas.drawText(sName, fX+20, fY-20, paint);
    }
    

    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.

    getE:

    public double getE (double dTime, double dEccentricity)
        {
        // we are passed the degree count in degrees (duh) 
        // and the eccentricity value
        // the method returns E
    
        double dM1, dD, dE0, dE = 0; // return value E = the mean anomaly
        double dM; // local value of M in radians
    
        dM = Math.toRadians (dTime);
    
        int iSign = 1;
    
        if (dM > 0) iSign = 1; else iSign = -1;
    
        dM = Math.abs(dM) / (2 * Math.PI); // Meeus, p 206, line 110
        dM = (dM - (long)dM) * (2 * Math.PI) * iSign; // line 120
        if (dM < 0)
            dM = dM + (2 * Math.PI); // line 130
        iSign = 1;
        if (dM > Math.PI) iSign = -1; // line 150
        if (dM > Math.PI) dM = 2 * Math.PI - dM; // line 160
    
        dE0 = Math.PI / 2; // line 170
        dD = Math.PI / 4; // line 170
    
        for (int i = 0; i < 33; i++) // line 180 
            {
            dM1 = dE0 - dEccentricity * Math.sin(dE0); // line 190
            dE0 = dE0 + dD * Math.signum((float)(dM - dM1));
            dD = dD / 2; 
            }
    
        dE = dE0 * iSign;
    
        return dE;
        }
    

    getRfromE:

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

提交回复
热议问题