Search code examples
javascriptmathphysicsgame-physics

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.


Solution

  • 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>