Calculations custom solar system in c# - c#

I am trying to create a custom solar system for a research project, to test different numerical methods and see their difference.
We are making our own solar system instead of trying to simulate earth/sun since that seemed harder.
For now we have a sun M1 and earth M2.
M1 = 3500
M2 = 500
distance from M1 to M2 = 200
We use a different G = 0.2
When we calculate the gravitional force it should be, F = 8.75 or -8.75 (F = GM1M2/r^2)
When we calculate the constant speed the earth should have to orbit around earth we find v = 2 (v = sqrt(G(M1+M2)/r))
To calculate the vec2 of the force we use the follow code
static double GravitationalConstant = 0.2f;//6.674e-11;
static public Vector2f GravitationalForce(SolarObject onObj, SolarObject fromObj)
{
Vector2f result = fromObj.OldPosition - onObj.OldPosition;
float distance = result.Lenght();
Console.WriteLine(distance);
result = new Vector2f(result.X / distance, result.Y / distance); // Normalized, but I've the lenght al ready so
result *= (float)((GravitationalConstant * onObj.Mass * fromObj.Mass) / (distance * distance));
return result;
}
And to update the position/velocity we use this
public void Update(float dt, List<SolarObject> objects)
{
foreach(SolarObject s in objects.Skip(1))
{
Vector2f f = Utility.GravitationalForce(s, objects[0]);
Vector2f a = f / s.Mass;
s.OldPosition = s.NewPosition;
s.NewPosition += dt * s.Velocity
s.Velocity += dt * a;
}
}
The object does fly around the sun, but it's not an orbit at all. The distance between M1/M2 is not constant <-> the force is also not always equal to 8.75f. We know euler has an 'error', but this seems to big, since without even orbiting one circle 25% of the distance is al ready increased. So there has to be a bug somewhere.

Unfortunately, this behavior is intrinsic to Euler integration - you will always overshoot a curved path by some degree. This effect can be suppressed by using a smaller timestep, which works even without doubles:
dt = 0.01:
dt = 0.001:
dt = 0.0001:
dt = 0.00001:
As you can see, the Euler method's accuracy improves with decreasing timestep. The inner planets (smaller orbital radius = greater curvature = more overshoot) start to follow their projected orbits (green) more consistently. The overshoot becomes only just visible for the innermost planet at dt = 0.0001, and not at all for dt = 0.00001.
To improve on the Euler method without having to resort to ridiculously small timesteps, one could use e.g. Runge-Kutta integration (the 4-th order variant is popular).
Also, the orbital velocity should be v = sqrt(G*Msun/r)) rather than v = sqrt(G(M1+M2)/r)), although for the large star limit this should not cause too much of a problem.
(If you would like my test code please let me know - although it is very badly written, and the core functions are identical to yours.)

Related

Unity find objects within range of two angles and with a max length (pie slice)

I've been programming an ability for a Hack n Slash which needs to check Units within a pie slice (or inbetween two angles with max length). But I'm stuck on how to check whether an unit is within the arc.
Scenario (Not enough, rep for an image sorry im new)
I currently use Physics2D.OverlapSphere() to get all of the objects within the maximum range. I then loop through all of the found objects to see whether they are within the two angles I specify. Yet this has janky results, probably because angles don't like negative values and value above 360.
How could I make this work or is there a better way to do this?
I probably need to change the way I check whether the angle is within the bounds.
Thanks in advance guys! I might respond with some delay as I won't be at my laptop for a couple hours.
Here is the code snippet:
public static List<EntityBase> GetEntitiesInArc(Vector2 startPosition, float angle, float angularWidth, float radius)
{
var colliders = Physics2D.OverlapCircleAll(startPosition, radius, 1 << LayerMask.NameToLayer("Entity"));
var targetList = new List<EntityBase>();
var left = angle - angularWidth / 2f;
var right = angle + angularWidth / 2f;
foreach (var possibleTarget in colliders)
{
if (possibleTarget.GetComponent<EntityBase>())
{
var possibleTargetAngle = Vector2.Angle(startPosition, possibleTarget.transform.position);
if (possibleTargetAngle >= left && possibleTargetAngle <= right)
{
targetList.Add(possibleTarget.GetComponent<EntityBase>());
}
}
}
return targetList;
}
Vector2.Angle(startPosition, possibleTarget.transform.position);
This is wrong. Imagine a line from the scene origin (0,0) to startPosition and a line to the transform.position. Vector2.Angle is giving you the angle between those two lines, which is not what you want to measure.
What you actually want is to give GetEntitiesInArc a forward vector then get the vector from the origin to the target position (var directionToTarget = startPosition - possibleTarget.transform.position) and measure Vector2.Angle(forward, directionToTarget).

Drawing a parabolic arc between two points based on a known projectile angle

I am trying to draw an arc between two points that represents a projectile's path. The angle that the projectile leaves point A at is known, and the X/Y coordinates of both points are known.
I'm trying to figure out the math behind this, and how to draw it up in c#.
Here is my failed attempt, based on some path examples I found
var g = new StreamGeometry();
var xDistance = Math.Abs(pointA.X - pointB.X);
var yDistance = Math.Abs(pointA.Y - pointB.Y);
var angle = 60;
var radiusX = xDistance / angle;
var radiusY = yDistance / angle;
using (var gc = g.Open())
{
gc.BeginFigure(
startPoint: pointA,
isFilled: false,
isClosed: false);
gc.ArcTo(
point: pointB,
size: new Size(radiusX, radiusY),
rotationAngle: 0d,
isLargeArc: false,
sweepDirection: SweepDirection.Clockwise,
isStroked: true,
isSmoothJoin: false);
}
Any help would be greatly appreciated!
Edit #2 (added clarity): For this problem assume that physics play no role (no gravity, velocity, or any outside forces). The projectile is guaranteed to land at point B and move along a parabolic path. The vertex will be halfway between point A and point B on the horizontal axis. The angle that it launches at is the angle up from the ground (horizontal).
So Point A (Ax, Ay) is known.
Point B (Bx, By) is known.
The angle of departure is known.
The X half of the vertex is known (Vx = Abs(Ax - Bx)).
Does this really boil down to needing to figure out the Y coordinate of the vertex?
Following on from the comments, we need a quadratic Bezier curve. This is defined by 3 points, the start, end, and a control point:
It is defined by the following equation:
We therefore need to find P1 using the given conditions (note that the gravity strength is determined implicitly). For a 2D coordinate we need two constraints / boundary conditions. They are given by:
The tangent vector at P0:
We need to match the angle to the horizontal:
The apex of the curve must be directly below the control point P1:
Therefore the vertical coordinate is given by:
[Please let me know if you would like some example code for the above]
Now for how to add a quadratic Bezier; thankfully, once you have done the above, it is not too difficult
The following method creates the parabolic geometry for the simple symmetric case. The angle is measured in degrees counterclockwise from the horizontal.
public Geometry CreateParabola(double x1, double x2, double y, double angle)
{
var startPoint = new Point(x1, y);
var endPoint = new Point(x2, y);
var controlPoint = new Point(
0.5 * (x1 + x2),
y - 0.5 * (x2 - x1) * Math.Tan(angle * Math.PI / 180));
var geometry = new StreamGeometry();
using (var context = geometry.Open())
{
context.BeginFigure(startPoint, false, false);
context.QuadraticBezierTo(controlPoint, endPoint, true, false);
}
return geometry;
}
A body movement subject only to the force of gravity (air resistance is ignored) can be evaluated with the following equations:
DistanceX(t) = dx0 + Vx0·t
DistanceY(t) = dy0 + Vy0·t - g/2·t^2
Where
g : gravity acceleration (9.8 m/s^2)
dx0 : initial position in the X axis
dy0 : initial position in the Y axis
Vy0 : initial X velocity component (muzzle speed)
Vy0 : initial Y velocity component (muzzle speed)
Well that doesn't seem very helpful, but lets investigate further. Your cannon has a muzzle speed V we can consider constant, so Vx0 and Vy0 can be written as:
Vx0 = V·cos(X)
Vy0 = V·sin(X)
Where X is the angle at which you are shooting. Ok, that seems interesting, we finally have an input that is useful to whoever is shooting the cannon: X. Lets go back to our equations and rewrite them:
DistanceX(t) = dx0 + V·cos(X)·t
DistanceY(t) = dy0 + V·sin(X)·t - g/2·t^2
And now, lets think through what we are trying to do. We want to figure out a way to hit a specific point P. Lets give it coordinates: (A, B). And in order to do that, the projectile has to reach that point in both projections at the same time. We'll call that time T. Ok, lets rewrite our equations again:
A = dx0 + V·cos(X)·T
B = dy0 + V·sin(X)·T - g/2·T^2
Lets get ourselves rid of some unnecessary constants here; if our cannon is located at (0, 0) our equations are now:
A = V·cos(X)·T [1]
B = V·sin(X)·T - g/2·T^2 [2]
From [1] we know that: T = A/(V·cos(X)), so we use that in [2]:
B = V·sin(X)·A/(V·cos(X)) - g/2·A^2/(V^2·cos^2(X))
Or
B = A·tan(X) - g/2·A^2/(V^2*cos^2(X))
And now some trigonometry will tell you that 1/cos^2 = 1 + tan^2 so:
B = A·tan(X) - g/2·A^2/V^2·(1+tan^2(X)) [3]
And now you have quadratic equation in tan(X) you can solve.
DISCLAIMER: typing math is kind of hard, I might have an error in there somewhere, but you should get the idea.
UPDATE The previous approach would allow you to solve the angle X that hits a target P given a muzzle speed V. Based on your comments, the angle X is given, so what you need to figure out is the muzzle speed that will make the projectile hit the target with the specified cannon angle. If it makes you more comfortable, don't think of V as muzzle speed, think of it as a form factor of the parabola you are trying to find.
Solve Vin [3]:
B = A·tan(X) - g/2·A^2/V^2·(1+tan^2(X))
This is a trivial quadratic equation, simply isolate V and take the square root. Obviously the negative root has no physical meaning but it will also do, you can take any of the two solutions. If there is no real number solution for V, it would mean that there is simply no possible shot (or parabola) that reaches P(angle X is too big; imagine you shoot straight up, you'll hit yourself, nothing else).
Now simply eliminate t in the parametrized equations:
x = V·cos(X)·t [4]
y = V·sin(X)·t - g/2·t^2 [5]
From [4] you have t = x/(V·cos(X)). Substitute in [5]:
y = tan(X)·x - g·x^2 /(2·V^2*cos^2(X))
And there is your parabola equation. Draw it and see your shot hit the mark.
I've given it a physical interpretation because I find it easier to follow, but you could change all the names I've written here to purely mathematical terms, it doesn't really matter, at the end of the day its all maths and the parabola is the same, any which way you want to think about it.

GPS lap & Segment timer

I've been searching for a while but haven't found exactly what I'm looking for.
I'm working on an app that will go in a race car. It will give the driver the ability to press a button to mark a Start/Finish line. It will also have a button to allow a driver to set segment times.
Keep in mind a track can be an oval which I'm working on first. It could be a road course or it could be an auto cross where the start and finish line aren't the exact same location. They could be with 50 feet of each other or so but the car never crosses where it starts.
I have my gps data coming in and I convert the NMea messages to my classes and I store Lat, Lon, Speed, Course etc. In my research I've ran across this which is interesting. The GPS will be mounted outside the roof for better signal. It generates 10 hits per second. (Garmin Glo)
http://www.drdobbs.com/windows/gps-programming-net/184405690?pgno=1
It's old but it talks about UTM and the Cartesian coordinate system. So using the DecDeg2UTM, I convert Lat & Lon to X & coordinates as well.
I've also been trying to use the Intersect formula I found Here I took the intersect and tried to convert it to C# which I'll post at the end. However, feeding coordinates of an oval track, it doesn't seem to be working. Also, I'm not sure exactly what it's supposed to be doing. But the coordinates it returns when it does somethign like -35.xxx & 98.xxxx which out in an ocean somewhere 1000's of miles from where the track is.
I looking for answers to the following.
I assume I need to take the location recorded when a button is pressed for Start/Finish or Segment and calculate a line perpendicular to the direction the car in able to be able to do some sort of Line Intersection calculation. The Cartesian coordinates seems to calculate the bearing fairly well. But the question here is how do you get the "left and right coordinates". Also, keep in mind, an oval track may be 60 feet wide. But as mentioned an auto cross track may only be 20 ft wide and part of the track may be with 50 ft. Note I'm fine with indicating to set the points, the car needs to be going slow or stopped at the points to get an accurate coordinate. Some tracks they will have to be set while walking the track.
Based on this, should I be trying to use decimal lat lon or would utilizing the Cartesian coordinate system based on UTM be a more accurate method for what I'm trying to do?
Either one is there a .Net library or C based library with source code that has methods for making these calculations?
How can this be accurately handled. (Not that great with Math, links to code samples would help tremendously.)
Next, after I have the lines or whatever is needed for start/finish and segments, as I get GPS sign from the car racing, I need to figure out the most accurate way to tell when a car has crossed those segments. again if I'm lucky I'll get 10 hits per second but it will probably be lower. Then the vehicle speeds could vary significantly depending on the type of track and vehicle. So the GPS hit could be many feet "left or right" of a segment. Also, it could be many feet before or after a segment.
Again, if there is a GIS library out there I can feed coordinates and all this is calculated, that's would work as well as long as it's performant. If not again I'm trying to decide if it's best to break down coordinates to X Y or some geometry formulas for coordinates in decimal format. Mods, I assume there is hard data to support an answer of either way and this isn't responses aren't fully subjective to opinions.
Here is the C# code I came up with from the Script page above. I'm starting to feel UTM and the Cartesian Coordinate system would be better for accuracy and performance. But again I'm open to evidence to the contrary if it exists.
Thanks
P.S. Note GeoCoordinate is from the .Net System.Device.Location assemble. GpsData is just a class I use to convert NMEA messages into Lat, Lon, Course, NumSats, DateTime etc.
The degree Radian methods are extensions as as follows.
public static double DegreeToRadians(this double angle)
{
return Math.PI * angle / 180.0;
}
public static double RadianToDegree(this double angle)
{
return angle * (180.0 / Math.PI);
}
}
public static GeoCoordinate CalculateIntersection(GpsData p1, double brng1, GpsData p2, double brng2)
{
// see http://williams.best.vwh.net/avform.htm#Intersection
// Not sure I need to use Cosine
double _p1LatRadians = p1.Latitude.DegreeToRadians();
double _p1LonToRadians = p1.Longitude.DegreeToRadians();
double _p2LatToRadians = p2.Latitude.DegreeToRadians();
double _p2LonToRadians = p2.Longitude.DegreeToRadians();
double _brng1ToRadians = brng1.DegreeToRadians();
double _brng2ToRadians = brng2.DegreeToRadians();
double _deltaLat = _p2LatToRadians - _p1LatRadians;
double _deltaLon = _p2LonToRadians - _p1LonToRadians;
var _var1 = 2 * Math.Asin(Math.Sqrt(Math.Sin(_deltaLat / 2) * Math.Sin(_deltaLat / 2)
+ Math.Cos(_p1LatRadians) * Math.Cos(_p2LatToRadians) * Math.Sin(_deltaLon / 2) * Math.Sin(_deltaLon / 2)));
if (_var1 == 0) return null;
// initial/final bearings between points
var _finalBrng = Math.Acos((Math.Sin(_p2LatToRadians) - Math.Sin(_p1LatRadians) * Math.Cos(_var1)) / (Math.Sin(_var1) * Math.Cos(_p1LatRadians)));
//if (isNaN(θa)) θa = 0; // protect against rounding
var θb = Math.Acos((Math.Sin(_p1LatRadians) - Math.Sin(_p2LatToRadians) * Math.Cos(_var1)) / (Math.Sin(_var1) * Math.Cos(_p2LatToRadians)));
var θ12 = Math.Sin(_p2LonToRadians - _p1LonToRadians) > 0 ? _finalBrng : 2 * Math.PI - _finalBrng;
var θ21 = Math.Sin(_p2LonToRadians - _p1LonToRadians) > 0 ? 2 * Math.PI - θb : θb;
var α1 = (_brng1ToRadians - θ12 + Math.PI) % (2 * Math.PI) - Math.PI; // angle 2-1-3
var α2 = (θ21 - _brng2ToRadians + Math.PI) % (2 * Math.PI) - Math.PI; // angle 1-2-3
if (Math.Sin(α1) == 0 && Math.Sin(α2) == 0) return null; // infinite intersections
if (Math.Sin(α1) * Math.Sin(α2) < 0) return null; // ambiguous intersection
α1 = Math.Abs(α1);
α2 = Math.Abs(α2);
// ... Ed Williams takes abs of α1/α2, but seems to break calculation?
var α3 = Math.Acos(-Math.Cos(α1) * Math.Cos(α2) + Math.Sin(α1) * Math.Sin(α2) * Math.Cos(_var1));
var δ13 = Math.Atan2(Math.Sin(_var1) * Math.Sin(α1) * Math.Sin(α2), Math.Cos(α2) + Math.Cos(α1) * Math.Cos(α3));
var _finalLatRadians = Math.Asin(Math.Sin(_p1LatRadians) * Math.Cos(δ13) + Math.Cos(_p1LatRadians) * Math.Sin(δ13) * Math.Cos(_brng1ToRadians));
var _lonBearing = Math.Atan2(Math.Sin(_brng1ToRadians) * Math.Sin(δ13) * Math.Cos(_p1LatRadians), Math.Cos(δ13) - Math.Sin(_p1LatRadians) * Math.Sin(_finalLatRadians));
var _finalLon = _p1LonToRadians + _lonBearing;
var _returnLat = _finalLatRadians.RadianToDegree();
var _latToDegree = _finalLon.RadianToDegree();
var _returnLon = ( _latToDegree + 540) % 360 - 180;
return new GeoCoordinate(_returnLat, _returnLon);
//return new LatLon(φ3.toDegrees(), (λ3.toDegrees() + 540) % 360 - 180); // normalise to −180..+180°
}

Why doesn't this simple physics freefall code work

I have some code I created to simulate a freefall of an object in a vacuum. When timestep is set to 1000ms it works perfectly with no problems. when I set timestep lower than 1000ms it deviates wildly from actual values falling far faster than normal. It takes around 14 seconds to fall 1000 meters at 1000ms which is the correct value. At 100ms timestep it only takes 5 seconds. at 10ms it only takes 2.2 seconds.
Can someone please tell me what I am doing wrong? I thought I set the timestep calculation to be able to handle smaller steps.
Thank you
Body testbody = new Body();
testbody.pos = new Vector(0, 1000);
testbody.velocity = new Vector(0, 0);
Bodytrack(testbody);
static void Bodytrack(Body body)
{
watch.Start();
int timestep = 1000;
while (body.pos.Y > 0)
{
body.pos = body.pos + (body.velocity * (timestep / 1000.0));
if (body.pos.Y <= 0) { break; }
Thread.Sleep(timestep);
CalculateAcceleration(body);
Console.Clear();
Console.WriteLine(body.velocity.Y);
Console.WriteLine(body.pos.Y);
Console.WriteLine(watch.Elapsed.TotalSeconds);
}
watch.Stop();
}
public static void CalculateAcceleration(Body body)
{
body.acceleration = new Vector(0, -9.80665);
body.velocity = Vector.Add(body.acceleration, body.velocity);
}
This line is your problem:
body.acceleration = new Vector(0, -9.80665);
Every time through the loop, you set the acceleration to -9.8 (which is the acceleration for 1000ms) instead of the acceleration for the amount of time that has passed.
You need to take into account the amount of time that as passed. If 500ms has passed, then acceleration should only be -9.80665/2.
You need to change the method to this:
public static void CalculateAcceleration(Body body, int timestep)
{
body.acceleration = new Vector(0, -9.80665 * (timestep/1000));
body.velocity = Vector.Add(body.acceleration, body.velocity);
}
If I remember my math correctly (and manage to write it down using markdown)...
The idea is that the horizontal position is not affected by anything, gravity only affects the vertical position. So we can easily calculate where the object is horizontally (assuming no collisions).
X(t) = Vx * t.
This means that when you throw something horizontally at 10 metres per second, it will have traveled 100 metres after 10 seconds.
The formula för the horizontal positioning is a bit more advanced. It contains two parts, the first part is the movement without gravity, and the second part acts as a countering force by the gravity.
Y(t) = Vy * t - (g * t^2)/2 where g is gravity (usually 9.82).
Vy * t is the constant motion upwards if gravity wouldn't have pulled it down.
(g * t^2)/2 is the increasing pull of gravity.
A object that's dropped will have no initial force in any direction, thus { Vx = 0, Vy = 0 } and you can easily get the current position of it, at any time, using pos(t) = -(g * t^2)/2. (We also know that it will fall straight down so you do not have to calculate any horizontal positioning.) You already know the speed of it by speed(t) = g * t.
You can not trust time-slices as your code does. For one, Thread.Sleep isn't exact. It's better if you calculate time elapsed since last update, and use that in your calculations.

How to simulate a harmonic oscillator driven by a given signal (not driven by sine wave)

I've got a table of values telling me how the signal level changes over time and I want to simulate a harmonic oscillator driven by this signal. It does not matter if the simulation is not 100% accurate.
I know the frequency of the oscillator.
I found lots of formulas but they all use a sine wave as driver.
I guess you want to perform some time-discrete simulation. The well-known formulae require analytic input (see Green's function). If you have a table of forces at some point in time, the typical analytical formulae won't help you too much.
The idea is this: For each point in time t0, the oscillator has some given acceleration, velocity, etc. Now a force acts on it -according to the table you were given- which will change it's acceleration (F = m * a). For the next time step t1, we assume the acceleration stays at that constant, so we can apply simple Newtonian equations (v = a * dt) with dt = (t1-t0) for this time frame. Iterate until the desired range in time is simulated.
The most important parameter of this simulation is dt, that is, how fine-grained the calculation is. For example, you might want to have 10 steps per second, but that completely depends on your input parameters. What we're doing here, in essence, is an Eulerian integration of the equations.
This, of course, isn't all there is - such simulations can be quite complicated, esp. in not-so-well behaved cases where extreme accelerations, etc. In those cases you need to perform numerical sanity checks within a frame, because something 'extreme' happens in a single frame. Also some numerical integration might become necessary, e.g. the Runge-Kutta algorithm. I guess that leads to far at this point, however.
EDIT: Just after I posted this, somebody posted a comment to the original question pointing to the "Verlet Algorithm", which is basically an implementation of what I described above.
http://en.wikipedia.org/wiki/Simple_harmonic_motion
http://en.wikipedia.org/wiki/Hooke's_Law
http://en.wikipedia.org/wiki/Euler_method
Ok, i finally figured it out and wrote a gui app to test it until it worked. But my pc is not very happy with doing it 1000*44100 times per second, even without gui^^
Whatever: here is my test code (wich worked quite well):
double lastTime;
const double deltaT = 1 / 44100.0;//length of a frame in seconds
double rFreq;
private void InitPendulum()
{
double freq = 2;//frequency in herz
rFreq = FToRSpeed(freq);
damp = Math.Pow(0.8, freq * deltaT);
}
private static double FToRSpeed(double p)
{
p *= 2;
p = Math.PI * p;
return p * p;
}
double damp;
double bHeight;
double bSpeed;
double lastchange;
private void timer1_Tick(object sender, EventArgs e)
{
double now=sw.ElapsedTicks/(double)Stopwatch.Frequency;
while (lastTime+deltaT <= now)
{
bHeight += bSpeed * deltaT;
double prevSpeed=bSpeed;
bSpeed += (mouseY - bHeight) * (rFreq*deltaT);
bSpeed *= damp;
if ((bSpeed > 0) != (prevSpeed > 0))
{
Console.WriteLine(lastTime - lastchange);
lastchange = lastTime;
}
lastTime += deltaT;
}
Invalidate();//No, i am not using gdi^^
}

Categories

Resources