;+
;PROCEDURE:   mvn_ramdir
;PURPOSE:
;  Calculates the spacecraft orbital velocity relative to the body-fixed
;  rotating Mars frame (IAU_MARS).  If you sit on the spacecraft and look
;  in this direction, the flow will be in your face.  The default is to 
;  calculate this direction in the MAVEN_SPACECRAFT frame.
;
;  Careful!  Some people consider the opposite of the spacecraft velocity
;  to be the RAM direction.  Watch for sign errors.
;
;  This vector can be rotated into the APP frame or any of the instrument
;  frames.  See mvn_frame_name for a list.  This is a pure rotation, so
;  don't try to rotate into the MSO frame, since the result would still be 
;  in a frame that rotates with the planet.  Instead, use the MSO keyword.
;
;  The co-rotation velocity in the IAU_MARS frame as a function of altitude
;  (h) and latitude (lat) is:
;
;      V_corot = (240 m/s)*[1 + h/3390]*cos(lat)
;
;  Models (LMD and MTGCM) predict that peak horizontal winds are 190-315 m/s 
;  near the exobase and 155-165 m/s near the homopause.  These are comparable 
;  to the co-rotation velocity.  The spacecraft velocity is ~4200 m/s in this 
;  altitude range, so winds could result in up to a ~4-deg angular offset of 
;  the actual flow from the nominal ram direction.
;
;  You must have SPICE installed for this routine to work.  This routine will
;  check to make sure SPICE has been initialized and that the loaded kernels
;  cover the specified time range.  With reconstructed kernels, this routine
;  has an accuracy of ~0.1 deg.
;
;USAGE:
;  mvn_ramdir, time
;
;INPUTS:
;       time:     If time has two elements, interpret it as a time range and
;                 create an array of evenly spaced times with resolution DT.
;
;                 If time has more than two elements, then the ram direction
;                 is calculated for each time in the array.
;
;                 Otherwise, attempt to get the time range from tplot and
;                 create an array of evenly spaced times with resolution DT.
;
;KEYWORDS:
;       DT:       Time resolution (sec).  Default is 10 sec.
;
;       FRAME:    String or string array for specifying one or more frames
;                 to rotate the ram direction into.  Any frame recognized
;                 by SPICE is allowed.  The default is 'MAVEN_SPACECRAFT'.
;                 Other possibilities are: 'MAVEN_APP', 'MAVEN_STATIC', etc.
;                 Type 'mvn_frame_name(/list)' to see a full list of frames.
;                 Minimum matching name fragments (e.g., 'sta', 'swe') are
;                 allowed -- see mvn_frame_name for details.  This is a pure
;                 rotation!
;
;       POLAR:    If set, convert the direction to polar coordinates and
;                 store as additional tplot variables.
;                    Mag = sqrt(x*x + y*y + z*z) ; units km/s
;                    Phi = atan(y,x)*!radeg      ; units deg [  0, 360]
;                    The = asin(z/Mag)*!radeg    ; units deg [-90, +90]
;
;       PH_180:   If set, the range for Phi is -180 to +180 degrees.
;
;       REVERSE:  Reverse the sense of RAM to be the velocity of the incoming
;                 ions with respect to the spacecraft.  If you sit on the 
;                 spacecraft and look in this direction the flow will be into
;                 the back of your head.
;
;       MSO:      Calculate ram vector in the MSO frame instead of the
;                 rotating IAU_MARS frame.  May be useful at high altitudes.
;
;       ERROR:    Calculate the magnitude of the RAM pointing error (deg) in
;                 one of the APP frames and store as a separate tplot variable.
;                 This only works for the APP, NGIMS and STATIC frames, as well
;                 as the reverse of those frames.  Note that the NGIMS frame is
;                 slightly shifted relative to the APP frame, so the calculated 
;                 error is slightly different.  If ERROR is set but no APP frame
;                 is specified in FRAME, then MAVEN_APP is appended to the FRAME 
;                 list.
;
;                 Using ephemeris predicts refreshed on a regular basis, the
;                 spacecraft can usually point the APP into the RAM direction
;                 with an accuracy of ~0.5 deg.  Occasionally, the error can
;                 be up to ~2 deg.  The reconstructed RAM direction has an 
;                 accuracy of ~0.1 deg.
;
;       PANS:     Named variable to hold the tplot variables created.  For the
;                 default frame, this would be 'V_sc_MAVEN_SPACECRAFT'.
;
;       RESULT:   Named variable to hold the result structure.
;
;       FORCE:    Ignore the SPICE check and forge ahead anyway.
;
;       SUCCESS:  Returns 1 on normal operation, 0 otherwise.
;
; $LastChangedBy: dmitchell $
; $LastChangedDate: 2025-09-08 12:49:13 -0700 (Mon, 08 Sep 2025) $
; $LastChangedRevision: 33605 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/trunk/projects/maven/general/mvn_ramdir.pro $
;
;CREATED BY:    David L. Mitchell
;-
pro mvn_ramdir, trange, dt=dt, pans=pans, frame=frame, mso=mso, polar=polar, result=result, $
                ph_180=ph_180, error=error, force=force, success=success, reverse=reverse

  success = 0
  result = 0
  doerr = keyword_set(error)
  dopol = keyword_set(polar) or doerr
  noguff = keyword_set(force)
  ph_360 = ~keyword_set(ph_180)
  sign = keyword_set(reverse) ? -1. : 1.

  if (size(frame,/type) ne 7) then frame = 'MAVEN_SPACECRAFT'
  frame = mvn_frame_name(frame, success=i)
  gndx = where(i, count)
  if (count eq 0L) then begin
    print,"No valid frames."
    return
  endif else frame = frame[gndx]

; Make sure at least one APP frame is included if ERROR is set

  if (doerr) then begin
    i = where((frame eq 'MAVEN_APP') or (frame eq 'MAVEN_NGIMS') or (frame eq 'MAVEN_STATIC'), count)
    if (count eq 0) then begin
      frame = [frame, 'MAVEN_APP']
      print,"  Adding MAVEN_APP frame to calculate RAM error."
    endif
  endif

; The spacecraft CK is always needed.  Check to see if the APP CK is also needed.

  need_app_ck = max(strmatch(frame,'*STATIC*',/fold)) or $
                max(strmatch(frame,'*NGIMS*',/fold)) or $
                max(strmatch(frame,'*IUVS*',/fold)) or $
                max(strmatch(frame,'*APP*',/fold))

; Create the UT array

  npts = n_elements(trange)
  if (npts lt 2) then begin
    tplot_options, get=topt
    trange = topt.trange_full
    if (max(trange) lt time_double('2013-11-18')) then begin
      print,"  Invalid time range or time array."
      return
    endif
    npts = 2L
  endif
  if (npts lt 3) then begin
    tmin = min(time_double(trange), max=tmax)
    dt = keyword_set(dt) ? double(dt[0]) : 10D
    npts = ceil((tmax - tmin)/dt) + 1L
    ut = tmin + dt*dindgen(npts)
  endif else ut = time_double(trange)

; Check the time range against the ephemeris coverage -- bail if there's a problem

  bail = 0
  mk = spice_test('*', verbose=-1)
  indx = where(mk ne '', count)
  if (count eq 0) then begin
    print,"  You must initialize SPICE first."
    bail = 1
  endif else begin
    mvn_spice_stat, summary=sinfo, check=minmax(ut), /silent
    ok = sinfo.spk_check and sinfo.ck_sc_check
    if (need_app_ck) then ok = ok and sinfo.ck_app_check
    if (not ok) then begin
      print,"  Insufficient SPICE coverage for the requested time range."
      print,"    -> Reinitialize SPICE to include your time range."
      bail = 1
    endif
  endelse

  if (noguff) then begin
    if (bail) then print,"    -> Keyword FORCE is set, so trying anyway."
    bail = 0
  endif

  if (bail) then return

; Calculate the state vector

  if keyword_set(mso) then begin
    vframe = 'MAVEN_SSO'
    vlabel = 'MSO'
  endif else begin
    vframe = 'IAU_MARS'
    vlabel = 'GEO'
  endelse
  timestr = time_string(ut,prec=5)
  cspice_str2et, timestr, et
  cspice_spkezr, 'MAVEN', et, vframe, 'NONE', 'MARS', svec, ltime

; Store the spacecraft velocity in the IAU_MARS (or MSO) frame

  store_data,'V_sc',data={x:ut, y:sign*transpose(svec[3:5,*]), v:[0,1,2], vframe:vframe}
  options,'V_sc',spice_frame=vframe,spice_master_frame='MAVEN_SPACECRAFT'

  result = {name : 'MAVEN RAM Velocity', vframe : vframe}

; Calculate the ram direction in frame(s) specified by keyword FRAME
  
  pans = ['']
  
  indx = where(frame ne '', nframes)
  for i=0,(nframes-1) do begin
    fnum = strtrim(string(i,format='(i)'),2)
    to_frame = strupcase(frame[indx[i]])
    spice_vector_rotate_tplot,'V_sc',to_frame,trange=minmax(ut),check='MAVEN_SPACECRAFT'

    labels = ['X','Y','Z']
    fname = strmid(to_frame, strpos(to_frame,'_')+1)
    case strupcase(fname) of
      'MARS'       : fname = 'Mars'
      'SPACECRAFT' : fname = 'PL'
      'APP'        : labels = ['I','J','K']
      else         : ; do nothing
    endcase

    str_element, result, 'frame'+fnum, to_frame, /add

    vname = 'V_sc_' + to_frame
    get_data, vname, data=Vsc, index=k
    if (k gt 0) then begin
      str_element, Vsc, 'vframe', vframe, /add
      str_element, Vsc, 'units', 'km/s', /add
      store_data, vname, data=Vsc
      options,vname,'ytitle',vlabel + ' RAM (' + fname + ')!ckm/s'
      options,vname,'colors',[2,4,6]
      options,vname,'labels',labels
      options,vname,'labflag',1
      options,vname,'constant',0
      pans = [pans, vname]
      str_element, result, 'vector'+fnum, Vsc, /add

      if (dopol) then begin
        xyz_to_polar, Vsc, mag=vmag, theta=the, phi=phi, ph_0_360=ph_360
        str_element, vmag, 'vframe', vframe, /add
        str_element, vmag, 'units', 'km/s', /add
        str_element, the, 'vframe', vframe, /add
        str_element, the, 'units', 'deg', /add
        str_element, phi, 'vframe', vframe, /add
        str_element, phi, 'units', 'deg', /add

        mag_name = 'V_sc_' + fname + '_Mag'
        store_data,mag_name,data=vmag
        options,mag_name,'ytitle',vlabel + ' RAM Vel (' + fname + ')!ckm/s'
        options,mag_name,'ynozero',1
        options,mag_name,'psym',3
        options,mag_name,'colors',6

        the_name = 'V_sc_' + fname + '_The'
        store_data,the_name,data=the
        options,the_name,'ytitle',vlabel + ' RAM The (' + fname + ')!cdeg'
        options,the_name,'ynozero',1
        options,the_name,'psym',3
        options,the_name,'colors',6

        phi_name = 'V_sc_' + fname + '_Phi'
        store_data,phi_name,data=phi
        if (ph_360) then ylim,phi_name,0,360,0 else ylim,phi_name,-180,180,0
        options,phi_name,'ytitle',vlabel + ' RAM Phi (' + fname + ')!cdeg'
        options,phi_name,'yticks',4
        options,phi_name,'yminor',3
        options,phi_name,'ynozero',1
        options,phi_name,'psym',3
        options,phi_name,'colors',6

        pans = [pans, the_name, phi_name]
        str_element, result, 'mag'+fnum, vmag, /add
        str_element, result, 'the'+fnum, the, /add
        str_element, result, 'phi'+fnum, phi, /add

        if (doerr) then begin
          appframe = 1
          case (to_frame) of
            'MAVEN_APP'    : phi0 = (sign gt 0.) ? 0. : 180.*!dtor
            'MAVEN_NGIMS'  : phi0 = (sign gt 0.) ? 0. : 180.*!dtor
            'MAVEN_STATIC' : phi0 = (sign lt 0.) ? 0. : 180.*!dtor
            else : appframe = 0
          endcase
          if (appframe) then begin
            dang = acos(cos(phi.y*!dtor - phi0)*cos(the.y*!dtor))*!radeg
            store_data,'RAM_Error',data={x:phi.x, y:dang}
            ylim,'RAM_Error',0,3,0
            options,'RAM_Error','ytitle','RAM Error!cdeg'
            options,'RAM_Error','psym',3
            options,'RAM_Error','line_colors',5
            options,'RAM_Error','colors',6
            options,'RAM_Error','constant',[0.5,2]
            options,'RAM_Error','const_color',[4,5]
            options,'RAM_Error','const_line',[2,2]
            doerr = 0  ; just calculate the error once
          endif
        endif
      endif
    endif else begin
      print,"  Could not rotate to frame: ",to_frame
    endelse
  endfor

  if (n_elements(pans) lt 2) then begin
    print,"  No valid ram directions."
    return
  endif

  pans = pans[1:*]
  store_data,'V_sc',/delete
  success = 1

  return

end
