JNISpice
version 2.0.0

spice.basic
Class SurfaceInterceptWithDSKInfo

java.lang.Object
  extended by spice.basic.SurfaceIntercept
      extended by spice.basic.SurfaceInterceptWithDSKInfo

public class SurfaceInterceptWithDSKInfo
extends SurfaceIntercept

Class SurfaceInterceptWithDSKInfo provides methods that compute ray-DSK surface intercepts and return information on the data source used to define the surface representation at the intercept points.

A SurfaceInterceptWithDSKInfo instance consists of:

This class provides functionality analogous to that of the SPICE routine DSKXSI. In this class, surface intercept computations are performed by construction of SurfaceInterceptWithDSKInfo objects. The principal constructor is SurfaceInterceptWithDSKInfo( boolean, Body, Surface[], Time, ReferenceFrame, Vector3, Vector3 ).

For functionality similar to that of the SPICE routine SINCPT, see the constructor SurfaceIntercept.SurfaceIntercept( String, Body, Time, ReferenceFrame, AberrationCorrection, Body, ReferenceFrame, Vector3 ).

For functionality similar to that of the SPICE routine DSKXV, see the method SurfaceIntercept.create( boolean, Body, Surface[], Time, ReferenceFrame, Vector3[], Vector3[]).

Code examples are provided in the detailed documentation for the constructors and methods.

Files

Appropriate SPICE kernels must be loaded by the calling program before methods of this class are called.

The following data are required:

The following data may be required:

Kernel data are normally loaded once per program run, NOT every time a method of this class is called.

Particulars

This is the lowest-level public interface for computing ray-surface intercepts, where the target surface is modeled using topographic data provided by DSK files, and where DSK segment selection is performed automatically. The highest-level interface for this purpose is the principal constructor of SurfaceIntercept. Ray-surface intercept computations can be performed using data from individual, user-specified segments by calling the methods

   dskx**
of class CSPICE.

In cases where the data source information returned by this routine are not needed, the routine SurfaceIntercept.create( boolean, Body, Surface[], Time, ReferenceFrame, Vector3[], Vector3[]) may be more suitable.

This routine works with multiple DSK files. It places no restrictions on the data types or coordinate systems of the DSK segments used in the computation. DSK segments using different reference frames may be used in a single computation. The only restriction is that any pair of reference frames used directly or indirectly are related by a constant rotation.

This routine enables calling applications to identify the source of the data defining the surface on which an intercept was found. The file, segment, and segment-specific information such as a DSK type 2 plate ID are returned.

This routine can be used for improved efficiency in situations in which multiple ray-surface intercepts are to be performed using a constant ray vertex.

Using DSK data

DSK loading and unloading

DSK files providing data used by this class are loaded by calling KernelDatabase.load(java.lang.String) and can be unloaded by calling KernelDatabase.unload(java.lang.String) or KernelDatabase.clear() See the documentation of KernelDatabase.load for limits on numbers of loaded DSK files.

For run-time efficiency, it's desirable to avoid frequent loading and unloading of DSK files. When there is a reason to use multiple versions of data for a given target body---for example, if topographic data at varying resolutions are to be used---the surface list can be used to select DSK data to be used for a given computation. It is not necessary to unload the data that are not to be used. This recommendation presumes that DSKs containing different versions of surface data for a given body have different surface ID codes.

DSk data priority

A DSK coverage overlap occurs when two segments in loaded DSK files cover part or all of the same domain---for example, a given longitude-latitude rectangle---and when the time intervals of the segments overlap as well.

When DSK data selection is prioritized, in case of a coverage overlap, if the two competing segments are in different DSK files, the segment in the DSK file loaded last takes precedence. If the two segments are in the same file, the segment located closer to the end of the file takes precedence.

When DSK data selection is unprioritized, data from competing segments are combined. For example, if two competing segments both represent a surface as sets of triangular plates, the union of those sets of plates is considered to represent the surface.

Currently only unprioritized data selection is supported. Because prioritized data selection may be the default behavior in a later version of the routine, the presence of the `pri' argument in the principal constructor is required.

Round-off errors and mitigating algorithms

When topographic data are used to represent the surface of a target body, round-off errors can produce some results that may seem surprising.

Note that, since the surface in question might have mountains, valleys, and cliffs, the points of intersection found for nearly identical sets of inputs may be quite far apart from each other: for example, a ray that hits a mountain side in a nearly tangent fashion may, on a different host computer, be found to miss the mountain and hit a valley floor much farther from the observer, or even miss the target altogether.

Round-off errors can affect segment selection: for example, a ray that is expected to intersect the target body's surface near the boundary between two segments might hit either segment, or neither of them; the result may be platform-dependent.

A similar situation exists when a surface is modeled by a set of triangular plates, and the ray is expected to intersect the surface near a plate boundary.

To avoid having the intercept computation fail to find an intersection when one clearly should exist, the computation uses two "greedy" algorithms:

  1. If the ray passes sufficiently close to any of the boundary surfaces of a segment (for example, surfaces of maximum and minimum longitude or latitude), that segment is tested for an intersection of the ray with the surface represented by the segment's data.

    This choice prevents all of the segments from being missed when at least one should be hit, but it could, on rare occasions, cause an intersection to be found in a segment other than the one that would be found if higher precision arithmetic were used.

  2. For type 2 segments, which represent surfaces as sets of triangular plates, each plate is expanded very slightly before a ray-plate intersection test is performed. The default plate expansion factor is
                1 + XFRACT 
             
    where XFRACT can be obtained from DSK.getTolerance(DSKToleranceKey).

    For example, given a value for XFRACT of 1.e-10, the sides of the plate are lengthened by 1/10 of a millimeter per km. The expansion keeps the centroid of the plate fixed.

    Plate expansion prevents all plates from being missed in cases where clearly at least one should be hit.

    As with the greedy segment selection algorithm, plate expansion can occasionally cause an intercept to be found on a different plate than would be found if higher precision arithmetic were used. It also can occasionally cause an intersection to be found when the ray misses the target by a very small distance.

Version 1.0.0 03-JAN-2017 (NJB)


Field Summary
 
Fields inherited from class spice.basic.SurfaceIntercept
ELLIPSOID
 
Constructor Summary
SurfaceInterceptWithDSKInfo()
          No-arguments constructor.
SurfaceInterceptWithDSKInfo(boolean prioritized, Body target, Surface[] surfList, Time t, ReferenceFrame fixref, Vector3 vertex, Vector3 raydir)
          Construct a SurfaceInterceptWithDSKInfo instance representing a ray-DSK surface intercept.
SurfaceInterceptWithDSKInfo(SurfaceInterceptWithDSKInfo surfx)
          Copy constructor.
 
Method Summary
 DLADescriptor getDLADescriptor()
          Get the DLA Descriptor from a SurfaceInterceptWithDSKInfo instance.
 double[] getDoubleComponent()
          Get the double information component from a SurfaceInterceptWithDSKInfo instance.
 DSK getDSK()
          Get the DSK instance from a SurfaceInterceptWithDSKInfo instance.
 DSKDescriptor getDSKDescriptor()
          Get the DSK Descriptor from a SurfaceInterceptWithDSKInfo instance.
 int[] getIntComponent()
          Get the integer information component from a SurfaceInterceptWithDSKInfo instance.
 
Methods inherited from class spice.basic.SurfaceIntercept
create, getIntercept, getSurfaceVector, getTargetEpoch, wasFound
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

SurfaceInterceptWithDSKInfo

public SurfaceInterceptWithDSKInfo()
No-arguments constructor.


SurfaceInterceptWithDSKInfo

public SurfaceInterceptWithDSKInfo(SurfaceInterceptWithDSKInfo surfx)
                            throws SpiceException
Copy constructor. This constructor creates a deep copy.

Throws:
SpiceException

SurfaceInterceptWithDSKInfo

public SurfaceInterceptWithDSKInfo(boolean prioritized,
                                   Body target,
                                   Surface[] surfList,
                                   Time t,
                                   ReferenceFrame fixref,
                                   Vector3 vertex,
                                   Vector3 raydir)
                            throws SpiceException
Construct a SurfaceInterceptWithDSKInfo instance representing a ray-DSK surface intercept.

Code Examples

The numerical results shown for these examples may differ across platforms. The results depend on the SPICE kernels used as input, the compiler and supporting libraries, and the machine specific arithmetic implementation.

  1. Compute surface intercepts of rays emanating from a set of vertices distributed on a longitude-latitude grid. All vertices are outside the target body, and all rays point toward the target's center.

    Check intercepts against expected values. Indicate the number of errors, the number of computations, and the number of intercepts found.

    Use the meta-kernel shown below to load example SPICE kernels.

    KPL/MK
    
    File: SurfaceInterceptWithDSKInfoEx1.tm
    
    This meta-kernel is intended to support operation of SPICE
    example programs. The kernels shown here should not be
    assumed to contain adequate or correct versions of data
    required by SPICE-based user applications.
    
    In order for an application to use this meta-kernel, the
    kernels referenced here must be present in the user's
    current working directory.
    
    The names and contents of the kernels referenced
    by this meta-kernel are as follows:
    
       File name                        Contents
       ---------                        --------
       phobos512.bds                    DSK based on
                                        Gaskell ICQ Q=512
                                        plate model
    \begindata
    
       PATH_SYMBOLS    = 'GEN'
       PATH_VALUES     = '/ftp/pub/naif/generic_kernels/dsk'
    
       KERNELS_TO_LOAD = ( '$GEN/phobos/phobos512.bds' )
    
    \begintext
    
    
    

    Example code begins here.

    //
    // Program SurfaceInterceptWithDSKInfoEx1
    //
    
    //
    // Multi-segment spear program.
    //
    // This program computes surface intercepts of rays emanating from a set of 
    // vertices distributed on a longitude-latitude grid. All 
    // vertices are outside the target body, and all rays point 
    // toward the target's center. 
    //
    // The program checks intercepts against expected values. It reports the 
    // number of errors, the number of computations, and the 
    // number of intercepts found. 
    //
    // This program expects all loaded DSKs
    // to represent the same body and surface.
    //
    //    Syntax: java -Djava.library.path= 
    //                  SurfaceInterceptWithDSKInfoEx1 
    //
    
    import spice.basic.*;
    import static spice.basic.AngularUnits.*;
    
    public class SurfaceInterceptWithDSKInfoEx1
    {
       //
       // Load SPICE shared library.
       //
       static{ System.loadLibrary( "JNISpice" ); }
    
    
       public static void main( String[] args )
    
          throws SpiceException
       {
          //
          // Local constants
          //
          final String                      META   = 
                                            "SurfaceInterceptWithDSKInfoEx1.tm";
    
           //
          // Local constants
          //
          final double                      DTOL   = 1.0e-14;
          final double                      SML    = 1.0e-12;
    
          final int                         MAXN   = 100000;      
          final int                         MAXSRF = 100;
    
    
          //
          // Local variables
          //
          Body                              target;
    
          DLADescriptor                     dladsc;
          DLADescriptor                     dladscInfo;
    
          DSK                               dsk;
    
          DSKDescriptor                     dskdsc;
    
          LatitudinalCoordinates            vtxLatCoords;
    
          ReferenceFrame                    fixref;
    
          String                            DSKName;
          String                            meta;
    
          Surface[]                         srflst;
    
          SurfaceInterceptWithDSKInfo       surfx;
    
          TDBTime                           et;
    
          Vector3[]                         dirArr;
          Vector3[]                         vtxArr;
          Vector3                           xxpt;
    
          boolean[]                         fndArr      = new boolean[1];
          boolean                           plidok;
          boolean                           prioritized = false;
    
          double                            d;
          double                            lat;
          double                            latstp;
          double                            lon;
          double                            lonstp;
          double                            polmrg;
          double                            r;      
          double[]                          timcov;
          double[]                          xptArr;
          double[]                          xxptArr = new double[3];
    
          int                               bodyid;
          int                               dtype;
          int                               framid;
          int                               handle;
          int                               i;
          int                               latix;
          int                               lonix;
          int                               nderr;
          int                               nhits;
          int                               nlat;
          int                               nlon;
          int                               nrays;
          int                               nstep;
          int                               nsurf;
          int                               plid    = 0;
          int[]                             plidArr = new int[1];
          int                               surfid;
          int                               xplid;
    
    
          try
          {
             //
             // Get meta-kernel name.
             //
             if ( args.length != 1 )
             {
                System.out.println( "Command syntax:  "                +
                                    "SurfaceInterceptWithDSKInfoEx1 "  +
                                    ""                      );
                return;
             }
    
             meta = args[0];
    
             //
             // Load kernels.
             //
             KernelDatabase.load( meta );
    
             //
             // Open the first (according to load order) loaded DSK,
             // then find the first segment and extract the body and 
             // surface IDs. 
             //
             DSKName = KernelDatabase.getFileName( 0, "DSK" );
    
             dsk     = DSK.openForRead( DSKName );
    
             dladsc  = dsk.beginForwardSearch();
    
             dskdsc  = dsk.getDSKDescriptor( dladsc );
    
             bodyid  = dskdsc.getCenterID();
             target  = new Body( bodyid );
    
             surfid  = dskdsc.getSurfaceID();
    
             framid  = dskdsc.getFrameID();
             fixref  = new ReferenceFrame( framid );
    
             //
             // Set the DSK data look-up time to the midpoint of 
             // the time coverage of the segment we just looked up.
             //
             timcov = dskdsc.getTimeBounds();
    
             et = new TDBTime(  ( timcov[0] + timcov[1] ) / 2  ); 
    
             //
             // Set the magnitude of the ray vertices. Use a large
             // number to ensure the vertices are outside of
             // any realistic target.
             //
             r = 1.0e10;
    
             //
             // Spear the target with rays pointing toward
             // the origin.  Use a grid of ray vertices
             // located on a sphere enclosing the target.
             //
             // The variable `polmrg' ("pole margin") can
             // be set to a small positive value to reduce
             // the number of intercepts done at the poles.
             // This may speed up the computation for
             // the multi-segment case, since rays parallel
             // to the Z axis will cause all segments converging
             // at the pole of interest to be tested for an
             // intersection.
             //    
    
             polmrg =    0.5 * RPD;
             latstp =    1.0;
             lonstp =    2.0;
             nlat   =    (int)( (180.0 + SML) / latstp )  +  1;
             nlon   =    (int)( (360.0 + SML) / lonstp );
    
             nhits  =    0;
             nderr  =    0;
    
             lon    = -180.0;
             lat    =   90.0;
             lonix  =    0;
             latix  =    0;
             nrays  =    nlat * nlon;
    
             vtxArr = new Vector3[nrays];
             dirArr = new Vector3[nrays];
    
             //
             // Generate rays. 
             //
    
             i = 0;
    
             while ( lonix < nlon )
             {
                lon = ( lonix * lonstp ) + ( 0.5 * DPR );
    
                while ( latix < nlat )
                {
                   if ( lonix == 0 )
                   {
                      lat = 90.0 - latix*latstp;
                   }
                   else
                   {
                      if ( latix == 0 )
                      {
                         lat =  90.0 - polmrg;
                      }
                      else if ( latix == nlat-1 )
                      {
                         lat = -90.0 + polmrg;
                      }
                      else
                      {
                         lat =  90.0 - latix*latstp;
                      }
                   }
    
                   vtxLatCoords = 
    
                      new LatitudinalCoordinates( r, lon*RPD, lat*RPD );
    
                   vtxArr[i] = vtxLatCoords.toRectangular();
    
                   dirArr[i] = vtxArr[i].negate();
    
                   ++ i;   
                   ++ latix;      
                }
    
                ++lonix;
    
                latix  = 0;
             }
    
             //
             // Assign surface ID list. 
             //
             // We assume all DSK files referenced in the meta-kernel have the 
             // same body and surface IDs. We'll create a trivial surface list
             // containing the surface from the first segment of the first 
             // loaded DSK file. 
             //
             // We could check this using the DSK coverage routines,
             // but for brevity, we won't do so here.
             //
             nsurf     = 1;
             srflst    = new Surface[nsurf];
             srflst[0] = new Surface( surfid, target );
    
             System.out.println ( "Computing intercepts..." );
    
             for ( i = 0;  i < nrays;  i++ )
             {
                //
                // Compute the surface intercept.
                //
                surfx = new SurfaceInterceptWithDSKInfo( prioritized,  target, 
                                                         srflst,       et,
                                                         fixref,       vtxArr[i],
                                                         dirArr[i]               );
                //
                // Check results.
                //
                // Compare the intercept that found by using the low-level
                // intercept interface.
                //
    
                if ( surfx.wasFound() )
                {
                   //
                   // Record the fact that a new intercept was found.
                   //
                   ++ nhits;
    
                   //
                   // Extract the DLA descriptor and DSK handle from
                   // the "info" fields of the intercept instance.
                   //
                   dladscInfo = surfx.getDLADescriptor();
    
                   handle     = surfx.getDSK().getHandle();
    
                   CSPICE.dskx02( handle,              dladscInfo.toArray(), 
                                  vtxArr[i].toArray(), dirArr[i].toArray(),
                                  plidArr,             xxptArr,
                                  fndArr                                     );
                   //
                   // Compute the distance between the intercept and the
                   // point having latitude and longitude of the ray's vertex,
                   // and radius equal to the radius of the intercept.
                   //
                   xxpt = new Vector3( xxptArr );
                   d    = surfx.getIntercept().dist( xxpt );
    
                   //
                   // If the intercept is on a surface provided by a type 2
                   // DSK segment, check the intercept plate ID against the
                   // expected ID. This check is valid only if the intercept
                   // does not occur on a plate edge.
                   // 
    
                   plidok = true;
    
                   dtype  = surfx.getDSKDescriptor().getDataType();
    
                   if ( dtype == 2 )
                   {
                      plid   = surfx.getIntComponent()[0];
    
                      plidok = ( plid == plidArr[0] );
                   }
    
    
                   if (  ( d/r  >  DTOL )  ||  ( !plidok )  )
                   {
                      vtxLatCoords = new LatitudinalCoordinates( vtxArr[i] );
    
                      lon    = vtxLatCoords.getLongitude() * DPR;
                      lat    = vtxLatCoords.getLatitude()  * DPR;
    
    
                      xptArr = surfx.getIntercept().toArray();
    
                      System.out.format ( "===========================%n" );
                      System.out.format ( "Vertex lon = %f, lat = %f%n",
                               lon, lat                        );
                      System.out.format ( "Bad intercept%n"               );
                      System.out.format ( "Distance error = %e%n", d      );
    
                      if ( dtype == 2 )
                      {
                         System.out.format ( "Plate ID mismatch: intercept " +
                                             "may be on a plate edge. "      +
                                             "Expected plate ID = %d; "      +
                                             "actual ID = %d%n",
                                             plidArr[0], plid                 );
                      }
    
                      System.out.format ( "surfx   = (%e %e %e)%n",
                               xptArr[0], xptArr[1], xptArr[2] );
                      System.out.format ( "xxptArr = (%e %e %e)%n",
                               xxptArr[0], xxptArr[1], xxptArr[2] );
    
                      ++ nderr;
    
                      return;
                   }
                }
                else
                {
                   //
                   // Missing the target entirely is a fatal error.
                   //
                   // This is true only for this program, not in
                   // general. For example, if the target shape is
                   // a torus, many rays would miss the target.
    
                   vtxLatCoords = new LatitudinalCoordinates( vtxArr[i] );
    
                   lon    = vtxLatCoords.getLongitude() * DPR;
                   lat    = vtxLatCoords.getLatitude()  * DPR;
    
                   System.out.format ( "===========================%n" );
                   System.out.format ( "Vertex lon = %f; lat = %f%n",
                            lon, lat                                   );
                   System.out.format ( "No intercept%n"                );
                   return;
                }
             }
    
             System.out.println( "Done." );
    
             System.out.format( "nrays = %d%n", nrays );
             System.out.format( "nhits = %d%n", nhits );
             System.out.format( "nderr = %d%n", nderr );
    
    
          } // End of try block
    
          catch ( SpiceException exc )
          {
             exc.printStackTrace();
          }
    
       } // End of main method 
    
    }
    
    

    When this program was executed on a PC/Linux/gcc/64-bit/java 1.5 platform, the output was:

    Computing intercepts...
    Done.
    nrays = 32580
    nhits = 32580
    nderr = 0
    

Throws:
SpiceException
Method Detail

getDSK

public DSK getDSK()
           throws PointNotFoundException,
                  SpiceException
Get the DSK instance from a SurfaceInterceptWithDSKInfo instance. This method returns a deep copy.

Throws:
PointNotFoundException
SpiceException

getDLADescriptor

public DLADescriptor getDLADescriptor()
                               throws PointNotFoundException,
                                      SpiceException
Get the DLA Descriptor from a SurfaceInterceptWithDSKInfo instance. This method returns a deep copy.

Throws:
PointNotFoundException
SpiceException

getDSKDescriptor

public DSKDescriptor getDSKDescriptor()
                               throws PointNotFoundException,
                                      SpiceException
Get the DSK Descriptor from a SurfaceInterceptWithDSKInfo instance. This method returns a deep copy.

Throws:
PointNotFoundException
SpiceException

getIntComponent

public int[] getIntComponent()
                      throws PointNotFoundException,
                             SpiceException
Get the integer information component from a SurfaceInterceptWithDSKInfo instance. This method returns a deep copy.

For DSK type 2 segments, element 0 of the returned array contains the ID of the plate on which the intercept was found. This is the only element of the returned array.

Throws:
PointNotFoundException
SpiceException

getDoubleComponent

public double[] getDoubleComponent()
                            throws PointNotFoundException,
                                   SpiceException
Get the double information component from a SurfaceInterceptWithDSKInfo instance. This method returns a deep copy.

For DSK type 2 segments, the returned array contains no meaningful data.

Throws:
PointNotFoundException
SpiceException

JNISpice
version 2.0.0

JNISpice Alpha Test Version 2.0.0 28-JAN-2017 (NJB)