Reputation: 573
I am working on some logic for point-to-point spaceship travel across a cartesian map using force, acceleration and mass. The ship will accelerate and burn at 1G towards its destination, flip 180 degrees at the half-way mark, and decelerate at 1G to arrive at a relative stop at its destination.
The problem I am having is determining the (x, y) coordinate using the time traveled while the ship is either under acceleration or deceleration.
Here are the specs on the ship
:
ship = {
mass: 135000, // kg
force: 1324350, // Newtons
p: { x: -1, y: -5 } // (x,y) coordinates
}
dest: {
p: { x: 15, y: 30 } // (x,y) coordinates
}
For the first part of the problem I calculate the time to destination:
var timeToDestination = function(ship, dest) {
// calculate acceleration (F = ma)
var acceleration = ship.force / ship.mass; // ~9.81 (1G)
// calculate distance between 2 points (Math.sqrt((a - x)^2+(b - y)^2))
var totalDistance = Math.sqrt(
Math.pow(dest.p.x - ship.p.x, 2) +
Math.pow(dest.p.y - ship.p.y, 2)
); // 38.48376280978771
// multiply grid system to galactic scale
var actualDistance = totalDistance * 1e9; // 1 = 1Mkm (38,483,763km) Earth -> Venus
// determine the mid-point where ship should flip and burn to decelerate
var midPoint = actualDistance / 2;
// calculate acceleration + deceleration time by solving t for each: (Math.sqrt(2d/a))
return Math.sqrt( 2 * midPoint / acceleration ) * 2; // 125,266s or 34h or 1d 10h
}
The second part is a little trickier, getting the (x, y) coordinate after delta time. This is where I get stuck, but here is what I have so far:
var plotCurrentTimeDistance = function(ship, dest, time) {
// recalculate acceleration (F = ma)
var acc = ship.force / ship.mass; //~9.81m/s^2
// recalculate total distance
var distance = Math.sqrt(
Math.pow(dest.p.x - ship.p.x, 2) +
Math.pow(dest.p.y - ship.p.y, 2)
) * 1e9; // 38,483,762,810m
// get distance traveled (d = (1/2) at^2)
var traveled = (acc * Math.pow(time, 2)) / 2;
// get ratio of distance traveled to actualDistance
var ratio = traveled / distance;
// midpoint formula to test @ 50% time ((x+a)/2,(y+b)/2)
console.log({ x: (ship.p.x+dest.p.x)/2, y: (ship.p.y+dest.p.y)/2})
// get the point using this formula (((1−t)a+tx),((1−t)b+ty))
return {
x: (( 1 - ratio ) * ship.p.x) + (ratio * dest.p.x),
y: (( 1 - ratio ) * ship.p.y) + (ratio * dest.p.y)
};
}
@ 50% time, 62,633s point returns as (~7, ~12.5) which matches the midpoint formula which returns as (~7, ~12.5). However, any other distance/time you input will be wildly wrong. My guess is that acceleration is messing up the calculations but I can't figure out how to change the formula to make it work. Thank you for your time.
Upvotes: 3
Views: 234
Reputation: 573
So thanks to @pingul I was able to get the answer using insights from his suggestions.
var ship = {
mass: 135000,
force: 1324350,
accel: 0,
nav: {
startPos: { x: 0, y: 0 },
endPos: { x: 0, y: 0 },
distanceToDest: 0,
distanceTraveled: 0,
departTime: 0,
timeToDest: 0,
arriveTime: 0,
startDeceleration: 0
}
};
var log = [];
var start = { x: -1, y: -5 };
var end = { x: 15, y: 30 };
var updateLog = function() {
document.getElementById('ship').textContent = JSON.stringify(ship, null, 2);
document.getElementById('pos').textContent = JSON.stringify(log, null, 2);
}
var plotCourse = function(ship, start, end) {
// calculate acceleration (F = ma)
var acceleration = ship.force / ship.mass; // ~9.81 (1G)
// calculate distance between 2 points (Math.sqrt((a - x)^2+(b - y)^2))
var totalDistance = Math.sqrt(
Math.pow(end.x - start.x, 2) +
Math.pow(end.y - start.y, 2)
); // 38.48376280978771
// multiply grid system to galactic scale
var actualDistance = totalDistance * 1e9; // 1 = 1Mkm (38,483,763km) Earth -> Venus
// determine the mid-point where ship should flip and burn to decelerate
var midpoint = actualDistance / 2;
// calculate acceleration + deceleration time by solving t for each: (Math.sqrt(2d/a))
var time = Math.sqrt( 2 * midpoint / acceleration ) * 2; // 125,266s or 34h or 1d 10h
// load data into ship nav
ship.nav = {
startPos: start,
endPos: end,
distanceToDest: actualDistance,
timeToDest: time,
startDeceleration: time / 2
}
ship.accel = acceleration
//log it
updateLog();
};
var goUnderway = function(ship) {
var arrivalEl = document.getElementById('arrivalTime');
// set depart and arrive times
var timeToDest = ship.nav.timeToDest * 1000; // convert to ms
ship.nav['departTime'] = moment().valueOf(); // returns now as unix ms
ship.nav['arriveTime'] = moment(ship.nav.departTime).add(timeToDest).valueOf();
//log it
arrivalEl.textContent = 'Your ship will arrive ' + moment(ship.nav.arriveTime).calendar();
updateLog();
};
var getPosition = function(ship, date) {
var currentTime = date ? moment(date).valueOf() : moment().valueOf() // unix ms
var elapsedTime = (currentTime - ship.nav.departTime) / 1000; // convert to s
var remainingTime = (ship.nav.arriveTime - currentTime) / 1000; // convert to s
var distanceAtMidpoint = 0;
var timeSinceMidpoint = 0;
var pos = { x: 0, y: 0 };
// calculate velocity from elapsed time
if (elapsedTime < ship.nav.startDeceleration) {
// if ship is accelerating use this function
ship.nav.distanceTraveled = 0 + ship.accel * Math.pow(elapsedTime, 2) / 2;
} else if (elapsedTime > ship.nav.startDeceleration) {
// else if ship is decelerating use this function
distanceAtMidpoint = 0 + ship.accel * Math.pow(ship.nav.startDeceleration, 2) / 2; // distance at midpoint
timeSinceMidpoint = elapsedTime - ship.nav.startDeceleration;
ship.nav.distanceTraveled = distanceAtMidpoint + ship.accel * Math.pow(timeSinceMidpoint, 2) / 2;
}
if (remainingTime <= 0) {
ship.force = ship.vel = ship.accel = 0;
pos = ship.nav.endPos;
} else {
// get ratio of distance traveled to actualDistance
var ratio = ship.nav.distanceTraveled / ship.nav.distanceToDest;
// get the point using this formula (((1−t)a+tx),((1−t)b+ty))
pos = {
x: (( 1 - ratio ) * start.x) + (ratio * end.x),
y: (( 1 - ratio ) * start.y) + (ratio * end.y)
};
}
log.push({
timestamp: moment(currentTime),
pos: pos
})
//log it
updateLog();
};
plotCourse(ship, start, end);
goUnderway(ship);
for (var i = 0; i < 35; i++) {
getPosition(ship, moment().add(i, 'hour'));
}
pre {
outline: 1px solid #ccc;
padding: 5px;
margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.1/moment.min.js"></script>
Ship: <span id="arrivalTime"></span>
<pre id="ship"></pre> Last Known Positions:
<pre id="pos"></pre>
Upvotes: 0
Reputation: 3473
First, you say that distance
is the total distance, but it really is the distance left from the ship to the destination. I don't fully understand your plan on how to do the calculations, so I will suggest something different below.
Lets call the start position start
, and it has a x, and y coordinate: start.x
and start.y
. Similarly with end
.
Now, for a simulation like this to work we need velocity as a property on the ship as well, so
ship = {
...
v : {x : 0, y : 0}
}
It will start from rest, and it should reach rest. Ideally it should have acceleration a
for a general movement, but we can skip that right now. We will have two different behaviours
v
at the midpoint, decelerates with -9.8 m/s^2 towards the goal until the speed is 0. Now we should have reached the goal.To get velocity from accelerations we use v = v_start + a*time
, and to get position from velocity we use x = x_start + v*time
. The current position of the ship is then for the two cases
// Case 1
current = start + a*time*time
// the above is due to the fact that
// current = start + v*time
// and the fact that v = v_start + a*time as I wrote previously,
// with v_start.x = v_start.y = 0
//Case 2
current = midpoint + (v_at_midpoint - a*time_since_midpoint)*time_since_midpoint
Note here that start
, current
and a
here are vectors, so they will have a x
and y
(and potentially z
) coordinate each.
The acceleration you get by the following algorithm
a = (start - end)/totalDistance * 9.81 // 9.81 is the desired acceleration -- change it to whatever you want
If you want to understand what the above actually means, it calculates what is called a unit vector, which tells us what direction the force points at.
What you will need to do now is as follows:
Calculate the total distance and the acceleration
Determine if you're in case 1 or 2 given the input time
in the function.
Once you've reached the midpoint, store the velocity and how long it took to get there and use it to determine the motion in case 2.
Stop once you've reached the destination, or you will go back to the start eventually!
Good luck!
P.S I should also note here that this does not take into account special relativity, so if your distances are too far apart you will get non-physical speeds. It gets a lot messier if you want to take this into account, however.
Upvotes: 2