<!--

//*************************************************************************
//
// Copyright 1998 by John A Bolton.  All Rights Reserved.
//
// THIS JAVASCRIPT SOURCE CODE IS NOT PUBLIC DOMAIN.
// ALL RIGHTS ARE RESERVED BY THE AUTHOR.
//
//*************************************************************************

var num_torq_points = 0, output = null
var torque_arr = new Array(), rpm_arr = new Array()
var expireDays = 180, blankInputText = "blank"
var memName = new Array("mem1nam", "mem2nam", "mem3nam", "mem4nam")
var memData = new Array("mem1dat", "mem2dat", "mem3dat", "mem4dat")

function check_button(radioGroup)
{
  for (var i=0; i<radioGroup.length; i++)
    if (radioGroup[i].checked) return i
  return 0
}

function round(num, decimalPlaces)
{
  var factor = Math.pow(10,decimalPlaces)
  return Math.round(num*factor)/factor
}

function sround(num, decimalPlaces)
{
  var i, s, x=-1, n=round(num,decimalPlaces)

  s = n.toString()  

  for (i=0; i<s.length; i++)
  {
    if (s.substring(i,i+1) == ".")
    {
      x = i
      break
    }
  }

  if (x == -1)
  {
    s += "."
    x = s.length-1
  }

  for (i=s.length-1; s.length-1-x<decimalPlaces; i++)
    s += "0"

  return s
}

function load_torq(inForm)
{
  // if form input objects read directly in torque(), must use parseInt() and
  // parseFloat() there, and that REALLY slows things down...

  num_torq_points = 0

  for (var i=0; i<inForm.torq.length; i++)
  {
    if (!(isNaN(inForm.torq[i].value) || isNaN(inForm.rpm[i].value)) &&
        inForm.torq[i].value > 0 && inForm.rpm[i].value > 0)
    {
      num_torq_points++
      torque_arr.length++
      torque_arr[i] = parseFloat(inForm.torq[i].value)
      rpm_arr.length++
      rpm_arr[i] = parseInt(inForm.rpm[i].value)
    }
  }
}

function torque(rrpm)
{
  var lower_rpm=0, upper_rpm=0, lower_torq=0, upper_torq=0
  var diff_rpm=0, diff_torq=0, extrap_torq=0, pct_diff=0, i=0, torq_diff=0
  var done=0, rpm=Math.round(rrpm)

  for (i=0; i<num_torq_points; i++)
  {
    if (rpm <= rpm_arr[i])
      break
  }

  if ((i < num_torq_points) && (rpm == rpm_arr[i]))
    extrap_torq = torque_arr[i]
  else
  {
    if (i < num_torq_points)
    {
      upper_rpm  = rpm_arr[i]
      upper_torq = torque_arr[i]
    }
    else
    {
      diff_rpm   = rpm_arr[i-1] - rpm_arr[i-2]
      diff_torq  = torque_arr[i-1] - torque_arr[i-2]
      upper_rpm  = rpm_arr[i-1]
      upper_torq = torque_arr[i-1]

      while (!done)
      {
        upper_rpm  += diff_rpm
        upper_torq += diff_torq
        if (rpm <= upper_rpm) done=1
      }
    }

    if (i>0)
    {
      lower_rpm  = rpm_arr[i-1]
      lower_torq = torque_arr[i-1]
    }
    else
    {
      lower_rpm  = 0
      lower_torq = 0
    }


    pct_diff    = (rpm-lower_rpm)/(upper_rpm-lower_rpm)
    torq_diff   = pct_diff*(upper_torq-lower_torq)
    extrap_torq = torq_diff + lower_torq
  }

  return extrap_torq
}
/*
function roll_torq(mph, rpm, weight, press)
{
  var c1=0.034667*(press-35.00000)*(press-45.00000) + -0.063333*(press-20.00000)*(press-45.00000) + 0.032000*(press-20.00000)*(press-35.00000)
  var c2=0.0000007704*(press-35.00000)*(press-45.00000) + -0.000000833333*(press-20.00000)*(press-45.00000) + 0.0000003148*(press-20.00000)*(press-35.00000)
  var factor=c1 + c2*Math.pow(mph,2.5)

  return factor*(weight/1000.0)*(mph/375.0)*5252/rpm
}
*/
function compute(win, inForm, outDoc)
{
  var PI=round(Math.PI,7), G=32.1, FPS_TO_MPH=0.6818
  var air_density=0.0228645*parseFloat(inForm.baro.value)/(0.5555*(parseFloat(inForm.temp.value)-32)+273.15)
  var weight=parseInt(inForm.weight.value), max_gees=0
  var rpm=parseInt(inForm.launch.value), mass=weight/G, velocity=0, velocity_l=0, distance=0
  var force_net, force_friction, force_wheels, force_factor, max_mph_time=0, max_mph_dist=0
  var accel, torque_inst, diff_rpm, force_next_gear, rpm_next_gear, rpm_prev_gear, mph=0, max_mph=0.0
  var rpm_factor=parseFloat(inForm.axle.value)*720/(PI*parseFloat(inForm.tire_diam.value))
  var friction_rolling=weight*parseFloat(inForm.cf_roll.value)
  var drag_factor=0.5*parseFloat(inForm.drag_coeff.value)*parseFloat(inForm.cross_sect.value)*air_density
  var time_60ft=null, time_330ft=null, time_660ft=null, time_1000ft=null, time_1320ft=null
  var mph_60ft=null, mph_330ft=null, mph_660ft=null, mph_1000ft=null, mph_1320ft=null
  var wait_60ft=1, wait_330ft=1, wait_660ft=1, wait_1000ft=1, wait_1320ft=1
  var wait_660ft_1st=1, wait_1320ft_1st=1, trap_1st_back=66
  var wait_60mph=1, wait_100mph=1, time_60mph=null, time_100mph=null
  var mph_660ft_1st=1, mph_1320ft_1st=1, trac_ctl=0, clutch_slip_factor = 0.80
  var terminal_velocity=0, need_to_shift, shift_light=0, shifted=0, first_light=1
  var gear_num=0, coasting=0, done=0, time=1, new_gear=0
  var num_gears=0, note = new Array(), num_notes=0
  var i, dt, disp_dt, mode_sel, rollout=parseInt(inForm.rollout.value)/12
  var accel_gees, weight_rear_static=weight*(parseFloat(inForm.weight_rear.value)/100)
  var weight_rear=weight_rear_static, cg_height=parseFloat(inForm.cg_ht.value)
var max_weight=0
var bgc="bgcolor=FFFFC9"

  i  = check_button(inForm.tdelta)
  time_delta = parseInt(inForm.tdelta[i].value)
  dt = time_delta/1000
  i  = check_button(inForm.ddelta)
  disp_delta = parseInt(inForm.ddelta[i].value)
  i  = check_button(inForm.mode)
  mode_sel = inForm.mode[i].value

  load_torq(inForm)

  if (inForm.rwtorq.checked)
    force_factor = inForm.axle.value * 24 / inForm.tire_diam.value
  else
    force_factor = (1-(inForm.torq_loss.value/100)) * inForm.axle.value * 24 / inForm.tire_diam.value

  for (var i=0; i<inForm.gear.length; i++)
  {
    if (!(isNaN(inForm.gear[i].value)) && inForm.gear[i].value > 0.0)
      num_gears++;
    else
      break;
  }
  outDoc.open()
  outDoc.clear()
  outDoc.write("<html><head><title>Acceleration Simulator Results</title></head><body bgcolor=white>")
  outDoc.writeln("<center><strong>Computing...</strong><br><br>")

  if (!inForm.summary.checked)
  {
    outDoc.writeln("<table border=1>")
    outDoc.writeln("<th "+bgc+">ET</th><th "+bgc+">MPH</th><th "+bgc+">Feet</th><th "+bgc+">RPM</th><th "+bgc+">Gear</th><th "+bgc+">G</th><th "+bgc+">Note(s)</th>")
  }

  while (!done)
  {
      force_friction  = friction_rolling+(drag_factor*velocity*velocity)  // roll resist + drag

      if (coasting > 0)
      {
        force_wheels = 0.0
        coasting--
        if (coasting <= 0)
          new_gear = 1
      }
      else
      {
        torque_inst = torque(rpm) * clutch_slip_factor
        force_wheels = torque_inst * inForm.gear[gear_num].value * force_factor
        if (gear_num < num_gears-1)
        {
          rpm_next_gear   = velocity * inForm.gear[gear_num+1].value * rpm_factor
          force_next_gear = torque(rpm_next_gear) * inForm.gear[gear_num+1].value * force_factor
          shift_light     = ((force_next_gear > force_wheels) && first_light)
          if (shift_light)
          {
            first_light = 0
            note[num_notes++] = "<small><font color=blue>*optimum shift pt*</font></small>"
          }
        }
        else
          force_next_gear = 0.0

        need_to_shift = ((force_wheels-force_friction) < 5) || (rpm >= inForm.redline.value)
        if (gear_num < num_gears-1)
          need_to_shift = need_to_shift || (shift_light && inForm.auto[gear_num].checked) || ((!inForm.auto[gear_num].checked) && (rpm >= inForm.shift[gear_num].value))

        if (need_to_shift)
        {
         if (gear_num < num_gears-1)
          {
            shifted       = 1
            first_light   = 1
            shift_light   = 0
            rpm_prev_gear = rpm
            force_wheels  = 0.0
            coasting      = Math.round(inForm.dura[gear_num].value/time_delta)
            if (rpm >= inForm.redline.value) note[num_notes++]="<small><font color=red>*redline*</font></small>"
            gear_num++
          }
          else
          {
            terminal_velocity = 1
            note[num_notes++]="<small><font color=red>*terminal velocity*</font></small>"
          }
        }
        else
        {
          accel_gees  = (dt*(force_wheels-force_friction)/mass)/(dt*G)
          weight_rear = weight_rear_static+(accel_gees*weight*inForm.cg_ht.value/inForm.wheelbase.value)
          if (force_wheels > weight_rear*inForm.cf_static.value)
          {
            force_wheels = weight_rear*inForm.cf_static.value
            if (!trac_ctl) note[num_notes++] = "<small>*traction ctl on*</small>"
            trac_ctl = 1
          }
          else
          {
            if (trac_ctl) note[num_notes++] = "<small>*traction ctl off*</small>"
            trac_ctl = 0
          }
        }
      } // if coasting

      force_net  = force_wheels - force_friction
      accel      = force_net / mass
      velocity_l = velocity
      velocity   = velocity + (accel * dt)
      accel_gees = (velocity-velocity_l)/(dt*G)
      if (accel_gees > max_gees) max_gees = accel_gees
      distance   = distance + (velocity * dt)
      rpm        = velocity * inForm.gear[gear_num].value * rpm_factor
      mph        = velocity * FPS_TO_MPH

      if ((gear_num == 0) && (rpm < inForm.launch.value))
      {
        rpm = parseInt(inForm.launch.value)
        clutch_slip_factor = 0.80
      }
      else
        clutch_slip_factor = 1.00

      if (mph > max_mph)
      {
        max_mph      = mph
        max_mph_time = time
        max_mph_dist = distance
      }

      if (wait_60mph)
      {
        if (mph >= 60.0)
        {
          wait_60mph = 0
          time_60mph = time
          note[num_notes++] = "<small><font color=green>*60 MPH*</font></small>"
        }
      }
      else if (wait_100mph)
      {
        if (mph >= 100.0)
        {
          wait_100mph = 0
          time_100mph = time
          note[num_notes++] = "<small><font color=green>*100 MPH*</font></small>"
        }
      }

      if (wait_60ft)
      {
        if (distance >= 60.0)
        {
          wait_60ft = 0
          time_60ft = time
          mph_60ft  = mph
          note[num_notes++] = "<small><font color=green>*60 ft*</font></small>"
        }
      }
      else if (wait_330ft)
      {
        if (distance >= 330.0)
        {
          wait_330ft = 0
          time_330ft = time
          mph_330ft  = mph
          note[num_notes++] = "<small><font color=green>*330 ft*</font></small>"
        }
      }
      else if (wait_660ft_1st)
      {
        if (distance >= 660.0-trap_1st_back)
        {
          wait_660ft_1st = 0
          mph_660ft_1st  = mph
          note[num_notes++] = "<small><font color=green>*1/8 mi (1)*</font></small>"
        }
      }
      else if (wait_660ft)
      {
        if (distance >= 660.0)
        {
          wait_660ft = 0
          time_660ft = time
          mph_660ft  = mph
          note[num_notes++] = "<small><font color=green>*1/8 mi (2)*</font></small>"
        }
      }
      else if (wait_1000ft)
      {
        if (distance >= 1000.0)
        {
          wait_1000ft = 0
          time_1000ft = time
          mph_1000ft  = mph
          note[num_notes++] = "<small><font color=green>*1000 ft*</font></small>"
        }
      }
      else if (wait_1320ft_1st)
      {
        if (distance >= 1320.0-trap_1st_back)
        {
          wait_1320ft_1st = 0
          mph_1320ft_1st  = mph
          note[num_notes++] = "<small><font color=green>*1/4 mi (1)*</font></small>"
        }
      }
      else if (wait_1320ft)
      {
        if (distance >= 1320.0)
        {
          wait_1320ft = 0
          time_1320ft = time
          mph_1320ft  = mph
          note[num_notes++] = "<small><font color=green>*1/4 mi (2)*</font></small>"
        }
      }

      if (!inForm.summary.checked && (((time*time_delta) % disp_delta == 0) || num_notes>0 || (new_gear && force_wheels > 0)))
      {
        new_gear = 0
        if (clutch_slip_factor != 1.0)
          note[num_notes++] = "<small><font color=brown>*slipping clutch*</font></small>"

        outDoc.writeln("<tr align=right>")

        if (!terminal_velocity)
          outDoc.writeln("<td>"+sround(time*dt,2)+"</td>")
        else
          outDoc.writeln("<td>"+sround((time-1)*dt,2)+"</td>")

        outDoc.writeln("<td>"+sround(mph,2)+"</td>")

        outDoc.writeln("<td>"+sround(distance,1)+"</td>")

        if (!(shifted || force_wheels <= 0.0))
          outDoc.writeln("<td>"+Math.round(rpm)+"</td>")
        else
          outDoc.writeln("<td>"+Math.round(rpm_prev_gear)+"</td>")

        outDoc.write("<td align=center>")
        if (!(shifted || force_wheels <= 0.0))
          outDoc.write(gear_num+1)
        else
          outDoc.write("<i><small><small>shift</small></small></i>")
        outDoc.writeln("</td>")

        outDoc.writeln("<td>"+sround(accel_gees,3)+"</td>")

        outDoc.write("<td align=left>")
        for (var i=0; i<num_notes; i++)
          outDoc.write(" "+note[i])
        outDoc.writeln("</td>")
        num_notes = 0

        outDoc.writeln("</tr>")
      }
      if (shifted) shifted = 0
      if (mode_sel == "TOP") done = terminal_velocity
      else if (mode_sel == "1/4") done = (distance >= 1320.0)
      else done = (distance >= 660.0)
      if (!done && distance>rollout) time++
  }
  if (!inForm.summary.checked)
  {
    outDoc.writeln("<tr align=center><td "+bgc+"><strong>ET</td><td "+bgc+"><strong>MPH</td><td "+bgc+"><strong>Feet</td><td "+bgc+"><strong>RPM</td><td "+bgc+"><strong>Gear</td><td "+bgc+"><strong>G</td><td "+bgc+"><strong>Note(s)</td></tr>")
    outDoc.writeln("</table>")
  }

  outDoc.writeln("<br><strong><big>Summary</big></strong><br>")
  outDoc.writeln("<table>")

  if (time_60mph)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>0-60:</strong></td>")
    outDoc.writeln("<td>"+sround(time_60mph*dt,2)+"</td>")
    outDoc.writeln("</tr>")
  }

  if (time_100mph)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>0-100:</strong></td>")
    outDoc.writeln("<td>"+sround(time_100mph*dt,2)+"</td>")
    outDoc.writeln("</tr>")
  }

  if (time_60ft)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>60 ft:</strong></td>")
    outDoc.writeln("<td>"+sround(time_60ft*dt,2)+"</td>")
    outDoc.writeln("<td>@</td>")
    outDoc.writeln("<td>"+sround(mph_60ft,2)+"</td>")
    outDoc.writeln("</tr>")
  }

  if (time_330ft)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>330 ft:</strong></td>")
    outDoc.writeln("<td>"+sround(time_330ft*dt,2)+"</td>")
    outDoc.writeln("<td>@</td>")
    outDoc.writeln("<td>"+sround(mph_330ft,2)+"</td>")
    outDoc.writeln("</tr>")
  }

  if (time_660ft)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>1/8 mile:</strong></td>")
    outDoc.writeln("<td>"+sround(time_660ft*dt,2)+"</td>")
    outDoc.writeln("<td>@</td>")
    outDoc.writeln("<td>"+sround((mph_660ft+mph_660ft_1st)/2,2)+"</td><td><small>("+sround(mph_660ft,2)+" actual)</small></td>")
    outDoc.writeln("</tr>")
  }

  if (time_1000ft)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>1000 ft:</strong></td>")
    outDoc.writeln("<td>"+sround(time_1000ft*dt,2)+"</td>")
    outDoc.writeln("<td>@</td>")
    outDoc.writeln("<td>"+sround(mph_1000ft,2)+"</td>")
    outDoc.writeln("</tr>")
  }

  if (time_1320ft)
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>1/4 mile:</strong></td>")
    outDoc.writeln("<td>"+sround(time_1320ft*dt,2)+"</td>")
    outDoc.writeln("<td>@</td>")
    outDoc.writeln("<td>"+sround((mph_1320ft+mph_1320ft_1st)/2,2)+"</td><td><small>("+sround(mph_1320ft,2)+" actual)</small></td>")
    outDoc.writeln("</tr>")
  }

  if (mode_sel == "TOP")
  {
    outDoc.writeln("<tr align=right>")
    outDoc.writeln("<td><strong>Top Speed:</strong></td>")
    outDoc.writeln("<td>"+sround(max_mph_time*dt,2)+"</td>")
    outDoc.writeln("<td>@</td>")
    outDoc.writeln("<td>"+sround(max_mph,2)+"</td>")
    outDoc.writeln("<td><small>("+Math.round(max_mph_dist)+" ft)</small></td>")
    outDoc.writeln("</tr>")
  }

  outDoc.writeln("<tr align=right>")
  outDoc.writeln("<td><strong>Max Accel:</strong></td>")
  outDoc.writeln("<td>"+sround(max_gees,3)+"G</td>")
  outDoc.writeln("</tr>")
/*
  outDoc.writeln("<tr align=right>")
  outDoc.writeln("<td><strong>Max Weight Rear:</strong></td>")
  outDoc.writeln("<td>"+sround(max_weight,2)+"</td>")
  outDoc.writeln("</tr>")
*/
  outDoc.writeln("</table>")

  outDoc.writeln("</center></body></html>")
  outDoc.close()
}

function go()
{
  output = window.open("","AccelOut","width=500,height=450,scrollbars,toolbar,resizable,status")
  output.focus()
  output.defaultStatus = "Computing..."
  compute(window,window.document.as,output.document)
  output.defaultStatus = ""
  if (window.document.as.recall.checked)
    saveToCookie(memData[0])
}

function autoRecall()
{
  var n = null

  if (cookieGet(memName[0]) != null)
  {
    document.as.recall.checked = true
    loadFromCookie(memData[0])
  }
  
  for (var i=1; i<4; i++)
  {
    n = cookieGet(memName[i])
    if (n != null)
      document.mem.locname[i].value = n
  }
}

function loadFromCookie(c)
{
  var p=0, p2=0, s = cookieGet(c)

  if (s != null)
  {
//    alert(s)
    for (var i=0; i<document.as.elements.length; i++)
    {
      if (document.as.elements[i].name != "recall")
      {
        if (document.as.elements[i].type == "text" ||
            document.as.elements[i].type == "checkbox" ||
            document.as.elements[i].type == "radio")
        {
          while (s.substring(p2,p2+1) != " " && p2 < s.length)
            p2++

          token = s.substring(p,p2)

          if (document.as.elements[i].type == "text")
          {
            if (token != blankInputText)
              document.as.elements[i].value = /*parseFloat(*/ token /*)*/
            else
              document.as.elements[i].value = ""
          }
          else
          {
           document.as.elements[i].checked = (token == "true")
          }
          p  = p2+1
          p2 = p
        }
      }
    }
  }
//  else
//    alert("No settings found here")
}

function saveToCookie(c)
{
  var s=""

  for (var i=0; i<document.as.elements.length; i++)
  {
    if (document.as.elements[i].name != "recall")
    {
      if (document.as.elements[i].type == "text")
      {
        if (document.as.elements[i].value.length > 0)
          s = s + document.as.elements[i].value + " "
        else
          s = s + blankInputText+" "
      }
      else if (document.as.elements[i].type == "checkbox" ||
               document.as.elements[i].type == "radio")
      {
        if (document.as.elements[i].checked)
          s = s + "true "
        else
          s = s + "false "
      }
    }
  }
  //alert(s)
  cookieSet(c,s,setExpire(expireDays,0))
}

function toggleRecall()
{
  if (document.as.recall.checked)
    cookieSet(memName[0],"lastrun",setExpire(expireDays,0))
  else
    cookieDelete(memName[0])
}

function recallMem(loc)
{
  var i = check_button(loc)
  var n = cookieGet(memName[i])

  if (n != null)
    loadFromCookie(memData[i])
  else
    alert("Nothing stored here")
}

function storeMem(loc)
{
  var i = check_button(loc)

  if (document.mem.locname[i].value.length > 0)
  {
    if (i > 0)
      cookieSet(memName[i],document.mem.locname[i].value,setExpire(expireDays,0))
    saveToCookie(memData[i])
    alert("Settings stored in memory location "+(i+1))
  }
  else
  {
    alert("A location name is required")
    document.mem.locname[i].focus()
  }
}

function clearMem(loc)
{
  var i = check_button(loc)

  if (confirm("Clear memory location "+(i+1)+", \""+document.mem.locname[i].value+"\":  Are you sure?"))
  {
    if (i > 0)
      document.mem.locname[i].value = ""
    cookieDelete(memData[i])
    cookieDelete(memName[i])
  }
}


// -->
