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

前端 未结 12 552
离开以前
离开以前 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)));
    }
    
    0 讨论(0)
  • 2021-01-30 18:23

    This is a great tutorial on N-body problems in general.

    http://www.artcompsci.org/#msa

    It's written using Ruby but pretty easy to map into other languages etc. It covers some of the common integration approaches; Forward-Euler, Leapfrog and Hermite.

    0 讨论(0)
  • 2021-01-30 18:23

    You might want to take a look at Celestia, a free space simulator. I believe that you can use it to create fictitious solar systems and it is open source.

    0 讨论(0)
  • 2021-01-30 18:24

    Fundamentals of Astrodynamics by Bate, Muller, and White is still required reading at my alma mater for undergrad Aerospace engineers. This tends to cover the orbital mechanics of bodies in Earth orbit...but that is likely the level of physics and math you will need to start your understanding.

    +1 for @Stefano Borini's suggestion for "everything that Jean Meeus has written."

    0 讨论(0)
  • 2021-01-30 18:26

    Check out nMod, a n-body modeling toolkit written in C++ and using OpenGL. It has a pretty well populated solar system model that comes with it and it should be easy to modify. Also, he has a pretty good wiki about n-body simulation in general. The same guy who created this is also making a new program called Moody, but it doesn't appear to be as far along.

    In addition, if you are going to do n-body simulations with more than just a few objects, you should really look at the fast multipole method (also called the fast multipole algorithm). It can the reduce number of computations from O(N^2) to O(N) to really speed up your simulation. It is also one of the top ten most successful algorithms of the 20th century, according to the author of this article.

    0 讨论(0)
  • 2021-01-30 18:27

    It looks like it is very hard and requires strong knowledge of physics but in fact it is very easy, you need to know only 2 formulas and basic understanding of vectors:

    Attractional force (or gravitational force) between planet1 and planet2 with mass m1 and m2 and distance between them d: Fg = G*m1*m2/d^2; Fg = m*a. G is a constant, find it by substituting random values so that acceleration "a" will not be too small and not too big approximately "0.01" or "0.1".

    If you have total vector force which is acting on a current planet at that instant of time, you can find instant acceleration a=(total Force)/(mass of current planet). And if you have current acceleration and current velocity and current position, you can find new velocity and new position

    If you want to look it real you can use following supereasy algorythm (pseudocode):

    int n; // # of planets
    Vector2D planetPosition[n]; 
    Vector2D planetVelocity[n]; // initially set by (0, 0)
    double planetMass[n];
    
    while (true){
        for (int i = 0; i < n; i++){
            Vector2D totalForce = (0, 0); // acting on planet i
            for (int j = 0; j < n; j++){
                if (j == i)
                    continue; // force between some planet and itself is 0
                Fg = G * planetMass[i] * planetMass[j] / distance(i, j) ^ 2;
            // Fg is a scalar value representing magnitude of force acting
            // between planet[i] and planet[j]
            // vectorFg is a vector form of force Fg
            // (planetPosition[j] - planetPosition[i]) is a vector value
            // (planetPosition[j]-planetPosition[i])/(planetPosition[j]-plantetPosition[i]).magnitude() is a
            // unit vector with direction from planet[i] to planet[j]
                vectorFg = Fg * (planetPosition[j] - planetPosition[i]) / 
                      (planetPosition[j] - planetPosition[i]).magnitude();
                totalForce += vectorFg;
            }
            Vector2D acceleration = totalForce / planetMass[i];
            planetVelocity[i] += acceleration;
        }
    
        // it is important to separate two for's, if you want to know why ask in the comments
        for (int i = 0; i < n; i++)
            planetPosition[i] += planetVelocity[i];
    
        sleep 17 ms;
        draw planets;
    }
    
    0 讨论(0)
提交回复
热议问题