#!/usr/bin/perl
#
# Compute the motion of a cart rolling up and down a tilted ramp
#   in the presence of gravity and rolling friction.
#
# This accompanies the impulse/momentum lab exercise.
#
# Usage:   perl impulse_lab.pl   interval
#
# where
#            interval       is an interval of time between the cart
#                               leaving bottom of ramp, and returning
#                               to bottom of ramp (sec)
#
# The program will print out values for 
#           - the initial speed (going up)
#           - the final speed (coming back down)
#           - the distance travelled from bottom to top of motion (cm)
#     
# MWR 8/9/2013

use POSIX;
$debug = 0;
$my_pi = 3.14159;
$DEGTORAD = $my_pi/180.0;

# accel due to gravity, cm/s^2
$g = 980.0;
# tilt of ramp (degrees)
$theta_deg = 2.18;
#$theta_deg = 3.10;
$theta = $theta_deg*$DEGTORAD;
# coefficient of rolling friction
$mu = 0.0061;
#$mu = 0.0078;
# number of bumps we simulate
$n_bump = 4;

# quit iterating when computed duration matches actual duration 
#     within this margin
$epsilon_t = 0.0003;
# add/subtract this much speed to improve initial guess
$v_step = 0.003;


# read input arg (duration)
if ($#ARGV != 0) {
  printf " usage: impulse_lab.pl  duration \n";
  exit(1);
}
$duration = $ARGV[0];
if ($duration <= 0.0) {
  printf " error: invalid duration %s \n", $duration;
  exit(1);
}



# first bump
  # guess an initial speed, based on motion without friction
  $v_guess = 0.5*$duration*$g*sin($theta);

  if ($debug > 0) {
    printf " guess v_guess %8.4f \n", $v_guess;
  }

  $done = 0;
  while ($done == 0) {
  
    # compute the duration it would take, using current guess for initial speed
    ($calc_duration, $calc_dist, $v_bot) = compute_duration($v_guess);
    
    # compare actual to calculated duration
    $delta_t = $duration - $calc_duration;
    if (fabs($delta_t) < $epsilon_t) {
      # good enough.  Quit
      $done = 1;
      next;
    }
    
    if ($delta_t > 0) {
      $v_guess = $v_guess + $v_step;
    }
    else {
      $v_guess = $v_guess - $v_step;
    }
  
  }
  
  # print out initial speed going up, final speed when comes back down,
  #   (speeds in cm/s),  and distance travelled up (cm)
  printf " v_up %8.3f  v_down %8.3f   dist %8.2f \n",
      $v_guess, $v_bot, $calc_dist;
  printf " v_up %8.3f  v_down %8.3f  t %8.3f   dist %8.2f \n",
      $v_guess, $v_bot, $calc_duration, $calc_dist;
  
  


# iterate over $n_bump bumps
for ($i = 0; $i < $n_bump; $i++) {
  
  $new_v = $v_bot;
  ($calc_duration, $calc_dist, $v_bot) = compute_duration($new_v);

  # print out initial speed going up, final speed when comes back down,
  #   (speeds in cm/s),  and distance travelled up (cm)
  printf " v_up %8.3f  v_down %8.3f  t %8.3f   dist %8.2f \n",
      $new_v, $v_bot, $calc_duration, $calc_dist;
  
}

exit 0;



###################################################################
# PROCEDURE: calc_duration
#
# DESCRIPTION: Given an initial speed, compute two times:
#                 - time to roll up ramp
#                 - time to roll down ramp
#
#              Add the two times together to find total time
#              for trip.
#
# RETURNS:
#              (total time for trip, distance up ramp, speed at bottom)
#
sub compute_duration {
 
  my($v_guess);
  my($t_top, $t_bot, $t_total);
  my($a, $dist);

  $v_guess = $_[0];
  if ($debug > 0) {
    printf "  compute_duration: input v = %8.3f \n", $v_guess;
  }

  # Step I: compute motion as cart rolls up the ramp
  #
  # calculate acceleration -- which is opposite to motion
  $a = $g*sin($theta) + $g*cos($theta)*$mu;
  # time to reach apex of motion
  $t_top = $v_guess / $a;
  # distance rolled up along the ramp
  $dist = $v_guess*$t_top - 0.5*$a*($t_top**2);
  if ($debug > 0) {
    printf "  compute_duration: go up t_top = %10.5f  dist %8.2f \n",
            $t_top, $dist;
  }

  
  # Step II: compute motion as cart rolls down the ramp
  #
  # calculate acceleration -- which is in direction of motion now
  $a = $g*sin($theta) - $g*cos($theta)*$mu;
  # time to roll down
  $t_bot = sqrt(  (2*$dist) / $a );
  # speed at bottom of ramp
  $v_bot = $a*$t_bot;
  if ($debug > 0) {
    printf "  compute_duration: down  t_bot = %10.5f  \n", $t_bot;
  }

  $t_tot = $t_top + $t_bot;

  if ($debug > 0) {
    printf " t_top  %10.5f  t_bot %10.5f  t_tot %10.5f   dist %8.2f \n",
           $t_top, $t_bot, $t_tot, $dist;
  }

  return($t_tot, $dist, $v_bot);
}






