1

I have a simulation with a planet orbiting a star. The equation for force on the planet is:

G⋅M1⋅M2⋅(q1−q2)/||q1−q2||3

Where G is the gravitational constant, M1 and M2 are the masses of the star and planet, and q1−q2 is the difference between the position vectors of the two bodies.

I have converted this equation into component form where

Fx = G⋅M1⋅M2⋅(x1−x2)/||q1−q2||3

Fy = G⋅M1⋅M2⋅(y1−y2)/||q1−q2||3

with x1−x2 being the difference between the x component of the vectors, and y1−y2 being the difference between the y components of the vectors.

The ||q1−q2||3 can be rewritten as sqrt((x1−x2)2+(y1−y2)2)3 which can we rewritten as ((x1−x2)2+(y1−y2)2)3/2

This is my code

    xdif = (planets[0].x - stars[0].x);
    ydif = (planets[0].y - stars[0].y);

    planets[0].ax = -10*xdif / pow((pow(xdif, 2) + pow(ydif, 2)), 1.5);
    planets[0].ay = -10*ydif / pow((pow(xdif, 2) + pow(ydif, 2)), 1.5);

For some reason, the planet does not follow an orbit like it should. Does anyone know what I got wrong?

EDIT:: Forgot to mention, all of these variables are doubles, and the language is c++

EDIT 2:: Here is the complete code

    #define WIN32_LEAN_AND_MEAN

    #include <math.h>
    #include <windows.h>
    #include <stdlib.h>
    #include <string>
    #include <iostream>
    #include <fstream>
    #include <chrono>
    using namespace std;
    #define SCREEN_WIDTH  1920
    #define SCREEN_HEIGHT 1080
    #define LGREY RGB(200,200,200)
    #define BLACK RGB(0, 0, 0)
    #define WHITE RGB(255,255,255)

    WPARAM w;
    HINSTANCE hInst;
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    POINT mousePos;
    int t;
    struct star { double x, y; };
    struct Body { double x, y, vx, vy, ax, ay; };
    star stars[2];
    Body planets[1];
    long newtime= std::chrono::system_clock::now().time_since_epoch().count();
    long oldtime=newtime;
    int signx;
    int signy;
    double force = 0;
    double xdif;
    double ydif;

    void drawCircle(int x, int y, int w, int h, COLORREF insidecolor, COLORREF bordercolor, HDC hdc) {

        HGDIOBJ B1 = CreateSolidBrush(insidecolor);
        HPEN P1 = CreatePen(PS_SOLID, 1, bordercolor);
        SelectObject(hdc, B1);
        SelectObject(hdc, P1);
        Arc(hdc, x, y, x + w, y + h, x - 1, y - 1, x + 1, y + 1);
        DeleteObject(B1);
        DeleteObject(P1);

    }

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
        mousePos = { 1200, 600 };
        WNDCLASSEX wc;
        ZeroMemory(&wc, sizeof(WNDCLASSEX));

        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WindowProc;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.lpszClassName = L"WindowClass";
        wc.hbrBackground = CreateSolidBrush(WHITE);

        RegisterClassEx(&wc);

        HWND hWnd = CreateWindowEx(NULL, L"WindowClass", L"Template", WS_OVERLAPPEDWINDOW, 10, 10, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL);

        hInst = hInstance;

        //separated by 10 au
        stars[0].x = 0;
        stars[0].y = 0;
        stars[1].x = 10;
        stars[1].y = 0;
        planets[0].x = -5;
        planets[0].y = 0;
        planets[0].vx = 0;
        planets[0].vy = -1;
        planets[0].ax = 0;
        planets[0].ay = 0;

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);


        MSG msg;
        bool running = TRUE;
        while (running) {
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
                if (msg.message == WM_QUIT) {
                    running = FALSE;
                }
            }
        }
        return msg.wParam;
    }
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
        PAINTSTRUCT ps;
        HDC hdc;

        switch (message)
        {
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);

            planets[0].x += planets[0].vx;
            planets[0].y += planets[0].vy;
            planets[0].vx += planets[0].ax;
            planets[0].vy += planets[0].ay;

            xdif = (planets[0].x - stars[0].x);
            ydif = (planets[0].y - stars[0].y);

            planets[0].ax = -10.0*xdif / pow((pow(xdif, 2) + pow(ydif, 2)), 1.5);
            planets[0].ay = -10.0*ydif / pow((pow(xdif, 2) + pow(ydif, 2)), 1.5);

            for (int i = 0; i < 1; i++) {
                drawCircle(planets[i].x * 50 + 1920 / 2 - 10, planets[i].y + 1080 / 2 - 10, 20, 20, BLACK, BLACK, hdc);
            }
            for (int i = 0; i < 2; i++) {//1920x1080
                drawCircle(stars[i].x*50+1920/2 - 25, stars[i].y+1080/2 - 25, 50, 50, BLACK, BLACK, hdc);
            }
            while (newtime < oldtime + 10000000/60) {//60 frames per second
                newtime = std::chrono::system_clock::now().time_since_epoch().count();
            }
            oldtime = newtime;

            InvalidateRect(hWnd, NULL, TRUE);
            EndPaint(hWnd, &ps);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }

        return 0;
    }

With Visual C++ 2015 it can be compiled as follows (with console subsystem, suitable for testing a malfunctioning application):

cl orbit.cpp -D UNICODE gdi32.lib user32.lib kernel32.lib /link /subsystem:console /entry:WinMainCRTStartup
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
Display name
  • 721
  • 2
  • 11
  • 29
  • Your code doesn't seem to match the gravitational equation. Why do you have an exponent of `1.5` when the equation has `3`, and what happened to `M1` and `M2`? Also, watch out for your operations which might be getting cast to `int`, e.g. `-10*xdif`. – Tim Biegeleisen Jun 30 '16 at 23:29
  • Sorry, I forgot to explain that, I'll edit it in a second. Essentially, ||x1-x2||^3 can be rewritten as sqrt((x1-x2)^2+(y1-y2)^2)^3 which can we rewritten as ((x1-x2)^2+(y1-y2)^2)^(3/2) – Display name Jun 30 '16 at 23:33
  • Ok, so is there any way to rewrite ||q1-q2||^3 to work with euclidean space? – Display name Jun 30 '16 at 23:36
  • 1
    Also, I replaced the constants with 10 for simplicity – Display name Jun 30 '16 at 23:45
  • Please provide a *complete* (but minimal) example that readers can copy and paste to reproduce the problem. For example, you might have a loop that steps through one orbit period and asserts that the planet ends up in roughly the initial position. – Cheers and hth. - Alf Jun 30 '16 at 23:54
  • Ok, the simulation is part of a GUI, so I'll upload it with that included if that's OK. – Display name Jul 01 '16 at 00:01
  • The error might be in your handling of the constants, so you should show us that instead of using 10. What exactly are you doing with them? – 1201ProgramAlarm Jul 01 '16 at 00:11
  • What exactly are the planets doing? It's also possible that because you're doing Euler integration, you're experiencing numerical instability. – meepzh Jul 01 '16 at 00:14
  • If I used the actual constants, then it would require a year for the simulation to complete... – Display name Jul 01 '16 at 00:14
  • I don't plant to run it, I just want to see what you're doing. For instance, if you forget to divide out the mass of the planet everything will go all wonky. – 1201ProgramAlarm Jul 01 '16 at 00:15
  • @meepzh I thought that might be possible, but in that case, I should get different results if I made the simulation slower (the integration would be more precise) and I don't – Display name Jul 01 '16 at 00:16
  • @1201ProgramAlarm I understand that, but because the G*M1*M2 is a constant, it should still create an orbit even with changes to the constant value – Display name Jul 01 '16 at 00:18
  • Got it, that makes sense. So could you tell us what the planets are doing right now? That could be helpful for debugging. It might be good to hunker down and do a frame or two of calculation manually, though it looks like your math is right. Edit: I ask that because it's possible your initial values for the planet aren't good. – meepzh Jul 01 '16 at 00:23
  • 1
    @meepzh They are accelerating and slowing down at irregular intervals. I found the problem with my formula, and now the program works. I'll be answering the question in a sec. Thanks everyone who helped – Display name Jul 01 '16 at 00:25
  • It turns out to be the same as the force on an average-looking, or even plain ugly body. – j_random_hacker Jul 01 '16 at 01:01
  • Apparently there are two effects at play here: exceeding escape velocity, and too course-grained simulation. – Cheers and hth. - Alf Jul 01 '16 at 01:17
  • Don't call `InvalidateRect` in `WM_PAINT` handle. – Barmak Shemirani Jul 01 '16 at 01:34

2 Answers2

1

I had a problem with the formula I was using, I believe the correct way to separate the vectors is using the equation C*(q1-q2)/||q1-q2||^2

This is the code:

    xdif = (planets[0].x - stars[0].x);
    ydif = (planets[0].y - stars[0].y);

    planets[0].ax = -.01*xdif / pow(hypot(xdif, ydif), 2);
    planets[0].ay = -.01*ydif / pow(hypot(xdif, ydif), 2);

This seems to work for now, there is some instability which I think I'll be able to deal with. Thanks a lot!

Display name
  • 721
  • 2
  • 11
  • 29
  • Assuming `hypot` is Euclidian distance (the hypotenuse of the coordinate triangle), then this gives acceleration proportional to distance, which is unlike our universe. – Cheers and hth. - Alf Jul 01 '16 at 00:40
  • It isn't proportional to distance, it is proportional to the inverse square of distance. https://en.wikipedia.org/wiki/Inverse-square_law – Display name Jul 01 '16 at 00:44
  • Just to note: using pow to square something calculated as a square root is a little silly: you'd be better off calculating the squared distance directly (ie `hyp=xdif*xdif+ydif*ydif`, and use hyp for both denominators.) – Nick Matteo Jul 01 '16 at 01:04
  • @sam: Proportional to inverse square of distance is our universe. proportional to inverse distance is the code above, if `hypot` is Euclidian distance. Sorry about the word "inverse" somehow slipping away there. – Cheers and hth. - Alf Jul 01 '16 at 01:16
  • You are using the Euler integration scheme. It is known to be very inaccurate and unstable given large time steps. Moreover, it is not symplectic, which means it does not properly solve the Hamilton equations. There is a [symplectic form of the Euler method](https://en.wikipedia.org/wiki/Semi-implicit_Euler_method), but it is just as unstable. Look into the [Leapfrog method](https://en.wikipedia.org/wiki/Leapfrog_integration) or better the [velocity Verlet method](https://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet). – Hristo Iliev Jul 05 '16 at 13:34
0

Not an answer, but this won't fit in a comment.

You can use <chrono> in a much safer, more accurate, more efficient, and more readable manner if you make the following simple adjustments:

auto newtime = std::chrono::system_clock::now();
auto oldtime = newtime;
// ...
using frames = std::chrono::duration<
                                std::chrono::system_clock::rep, std::ratio<1, 60>>;
while (newtime < oldtime + frames{1}) {//60 frames per second
    newtime = std::chrono::system_clock::now();
}
oldtime = newtime;
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577