Artokun
Artokun

Reputation: 573

Plotting Space Ship Distance over Time

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

Answers (2)

Artokun
Artokun

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

pingul
pingul

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

  1. The ship starts from rest, accelerates with 9.8 m/s^2 towards the goal until the half point is reached.
  2. The ship starts at speed 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:

  1. Calculate the total distance and the acceleration

  2. Determine if you're in case 1 or 2 given the input time in the function.

  3. 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.

  4. 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

Related Questions