/* $Procedure CKE03 ( C-kernel, evaluate pointing record, data type 3 ) */ /* Subroutine */ int cke03_(logical *needav, doublereal *record, doublereal * cmat, doublereal *av, doublereal *clkout) { /* System generated locals */ doublereal d__1; /* Local variables */ doublereal frac, axis[3]; extern /* Subroutine */ int vequ_(doublereal *, doublereal *), mtxm_( doublereal *, doublereal *, doublereal *), mxmt_(doublereal *, doublereal *, doublereal *); doublereal cmat1[9] /* was [3][3] */, cmat2[9] /* was [3][3] */, t, angle, delta[9] /* was [3][3] */; extern /* Subroutine */ int chkin_(char *, ftnlen), moved_(doublereal *, integer *, doublereal *), vlcom_(doublereal *, doublereal *, doublereal *, doublereal *, doublereal *); doublereal q1[4], q2[4], t1, t2; extern logical failed_(void); extern /* Subroutine */ int raxisa_(doublereal *, doublereal *, doublereal *), axisar_(doublereal *, doublereal *, doublereal *), chkout_(char *, ftnlen); doublereal av1[3], av2[3]; extern logical return_(void); extern /* Subroutine */ int q2m_(doublereal *, doublereal *); doublereal rot[9] /* was [3][3] */; /* $ Abstract */ /* Evaluate a pointing record returned by CKR03 from a CK type 3 */ /* segment. Return the C-matrix and angular velocity vector associated */ /* with the time CLKOUT. */ /* $ Disclaimer */ /* THIS SOFTWARE AND ANY RELATED MATERIALS WERE CREATED BY THE */ /* CALIFORNIA INSTITUTE OF TECHNOLOGY (CALTECH) UNDER A U.S. */ /* GOVERNMENT CONTRACT WITH THE NATIONAL AERONAUTICS AND SPACE */ /* ADMINISTRATION (NASA). THE SOFTWARE IS TECHNOLOGY AND SOFTWARE */ /* PUBLICLY AVAILABLE UNDER U.S. EXPORT LAWS AND IS PROVIDED "AS-IS" */ /* TO THE RECIPIENT WITHOUT WARRANTY OF ANY KIND, INCLUDING ANY */ /* WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR FITNESS FOR A */ /* PARTICULAR USE OR PURPOSE (AS SET FORTH IN UNITED STATES UCC */ /* SECTIONS 2312-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE */ /* SOFTWARE AND RELATED MATERIALS, HOWEVER USED. */ /* IN NO EVENT SHALL CALTECH, ITS JET PROPULSION LABORATORY, OR NASA */ /* BE LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING, BUT NOT */ /* LIMITED TO, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ /* INCLUDING ECONOMIC DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, */ /* REGARDLESS OF WHETHER CALTECH, JPL, OR NASA BE ADVISED, HAVE */ /* REASON TO KNOW, OR, IN FACT, SHALL KNOW OF THE POSSIBILITY. */ /* RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF */ /* THE SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY */ /* CALTECH AND NASA FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE */ /* ACTIONS OF RECIPIENT IN THE USE OF THE SOFTWARE. */ /* $ Required_Reading */ /* CK */ /* ROTATION */ /* $ Keywords */ /* POINTING */ /* $ Declarations */ /* $ Brief_I/O */ /* Variable I/O Description */ /* -------- --- -------------------------------------------------- */ /* NEEDAV I True if angular velocity is requested. */ /* RECORD I Data type 3 pointing record. */ /* CMAT O C-matrix. */ /* AV O Angular velocity vector. */ /* CLKOUT O SCLK associated with C-matrix. */ /* $ Detailed_Input */ /* NEEDAV is true if angular velocity is requested. */ /* RECORD is a set of double precision numbers returned by CKR03 */ /* that contain sufficient information from a type 3 CK */ /* segment to evaluate the C-matrix and the angular */ /* velocity vector at a particular time. Depending on */ /* the contents of RECORD, this routine will either */ /* interpolate between two pointing instances that */ /* bracket a request time, or it will simply return the */ /* pointing given by a single pointing instance. */ /* When pointing at the request time can be determined */ /* by linearly interpolating between the two pointing */ /* instances that bracket that time, the bracketing */ /* pointing instances are returned in RECORD as follows: */ /* RECORD( 1 ) = Left bracketing SCLK time. */ /* RECORD( 2 ) = lq0 \ */ /* RECORD( 3 ) = lq1 \ Left bracketing */ /* RECORD( 4 ) = lq2 / quaternion. */ /* RECORD( 5 ) = lq3 / */ /* RECORD( 6 ) = lav1 \ Left bracketing */ /* RECORD( 7 ) = lav2 | angular velocity */ /* RECORD( 8 ) = lav3 / ( optional ) */ /* RECORD( 9 ) = Right bracketing SCLK time. */ /* RECORD( 10 ) = rq0 \ */ /* RECORD( 11 ) = rq1 \ Right bracketing */ /* RECORD( 12 ) = rq2 / quaternion. */ /* RECORD( 13 ) = rq3 / */ /* RECORD( 14 ) = rav1 \ Right bracketing */ /* RECORD( 15 ) = rav2 | angular velocity */ /* RECORD( 16 ) = rav3 / ( optional ) */ /* RECORD( 17 ) = pointing request time */ /* The quantities lq0 - lq3 and rq0 - rq3 are the */ /* components of the quaternions that represent the */ /* C-matrices associated with the times that bracket */ /* the requested time. */ /* The quantities lav1, lav2, lav3 and rav1, rav2, rav3 */ /* are the components of the angular velocity vectors at */ /* the respective bracketing times. The components of the */ /* angular velocity vectors are specified relative to the */ /* inertial reference frame of the segment. */ /* When the routine is to simply return the pointing */ /* given by a particular pointing instance, then the */ /* values of that pointing instance are returned in both */ /* parts of RECORD ( i.e. RECORD(1-9) and RECORD(10-16) ). */ /* $ Detailed_Output */ /* CMAT is a rotation matrix that transforms the components */ /* of a vector expressed in the inertial frame given in */ /* the segment to components expressed in the instrument */ /* fixed frame at the returned time. */ /* Thus, if a vector v has components x, y, z in the */ /* inertial frame, then v has components x', y', z' in the */ /* instrument fixed frame where: */ /* [ x' ] [ ] [ x ] */ /* | y' | = | CMAT | | y | */ /* [ z' ] [ ] [ z ] */ /* If the x', y', z' components are known, use the */ /* transpose of the C-matrix to determine x, y, z as */ /* follows. */ /* [ x ] [ ]T [ x' ] */ /* | y | = | CMAT | | y' | */ /* [ z ] [ ] [ z' ] */ /* (Transpose of CMAT) */ /* AV is the angular velocity vector of the instrument fixed */ /* frame defined by CMAT. The angular velocity is */ /* returned only if NEEDAV is true. */ /* The direction of the angular velocity vector gives */ /* the right-handed axis about which the instrument fixed */ /* reference frame is rotating. The magnitude of AV is */ /* the magnitude of the instantaneous velocity of the */ /* rotation, in radians per second. */ /* The angular velocity vector is returned in component */ /* form */ /* AV = [ AV1 , AV2 , AV3 ] */ /* which is in terms of the inertial coordinate frame */ /* specified in the segment descriptor. */ /* CLKOUT is the encoded SCLK associated with the returned */ /* C-matrix and angular velocity vector. */ /* $ Parameters */ /* None. */ /* $ Exceptions */ /* 1) No explicit checking is done to determine whether RECORD is */ /* valid. However, routines in the call tree of this routine */ /* may signal errors if inputs are invalid or otherwise */ /* in appropriate. */ /* $ Files */ /* None. */ /* $ Particulars */ /* If the array RECORD contains pointing instances that bracket the */ /* request time then CKE03 will linearly interpolate between those */ /* two values to obtain pointing at the request time. If the */ /* pointing instances in RECORD are for the same time, then this */ /* routine will simply unpack the record and convert the quaternion */ /* to a C-matrix. */ /* The linear interpolation performed by this routine is defined */ /* as follows: */ /* 1) Let t be the time for which pointing is requested and */ /* let CMAT1 and CMAT2 be C-matrices associated with times */ /* t1 and t2 where: */ /* t1 < t2, and t1 <= t, and t <= t2. */ /* 2) Assume that the spacecraft frame rotates about a fixed */ /* axis at a constant angular rate from time t1 to time t2. */ /* The angle and rotation axis can be obtained from the */ /* rotation matrix ROT12 where: */ /* T T */ /* CMAT2 = ROT12 * CMAT1 */ /* or */ /* T */ /* ROT12 = CMAT2 * CMAT1 */ /* ROT12 ==> ( ANGLE, AXIS ) */ /* 3) To obtain pointing at time t, rotate the spacecraft frame */ /* about the vector AXIS from its orientation at time t1 by the */ /* angle THETA where: */ /* ( t - t1 ) */ /* THETA = ANGLE * ----------- */ /* ( t2 - t1 ) */ /* 4) Thus if ROT1t is the matrix that rotates vectors by the */ /* angle THETA about the vector AXIS, then the output C-matrix */ /* is given by: */ /* T T */ /* CMAT = ROT1t * CMAT1 */ /* T */ /* CMAT = CMAT1 * ROT1t */ /* 5) The angular velocity is treated independently of the */ /* C-matrix. If it is requested, then the AV at time t is */ /* the weighted average of the angular velocity vectors at */ /* the times t1 and t2: */ /* ( t - t1 ) */ /* W = ----------- */ /* ( t2 - t1 ) */ /* AV = ( 1 - W ) * AV1 + W * AV2 */ /* $ Examples */ /* The CKRnn routines are usually used in tandem with the CKEnn */ /* routines, which evaluate the record returned by CKRnn to give */ /* the pointing information and output time. */ /* The following code fragment searches through all of the segments */ /* in a file applicable to the Mars Observer spacecraft bus that */ /* are of data type 3, for a particular spacecraft clock time. */ /* It then evaluates the pointing for that epoch and prints the */ /* result. */ /* CHARACTER*(20) SCLKCH */ /* CHARACTER*(20) SCTIME */ /* CHARACTER*(40) IDENT */ /* INTEGER I */ /* INTEGER SC */ /* INTEGER INST */ /* INTEGER HANDLE */ /* INTEGER DTYPE */ /* INTEGER ICD ( 6 ) */ /* DOUBLE PRECISION SCLKDP */ /* DOUBLE PRECISION TOL */ /* DOUBLE PRECISION CLKOUT */ /* DOUBLE PRECISION DESCR ( 5 ) */ /* DOUBLE PRECISION DCD ( 2 ) */ /* DOUBLE PRECISION RECORD ( 17 ) */ /* DOUBLE PRECISION CMAT ( 3, 3 ) */ /* DOUBLE PRECISION AV ( 3 ) */ /* LOGICAL NEEDAV */ /* LOGICAL FND */ /* LOGICAL SFND */ /* SC = -94 */ /* INST = -94000 */ /* DTYPE = 3 */ /* NEEDAV = .FALSE. */ /* C */ /* C Load the MO SCLK kernel and the C-kernel. */ /* C */ /* CALL FURNSH ( 'MO_SCLK.TSC' ) */ /* CALL DAFOPR ( 'MO_CK.BC', HANDLE ) */ /* C */ /* C Get the spacecraft clock time. Then encode it for use */ /* C in the C-kernel. */ /* C */ /* WRITE (*,*) 'Enter spacecraft clock time string:' */ /* READ (*,FMT='(A)') SCLKCH */ /* CALL SCENCD ( SC, SCLKCH, SCLKDP ) */ /* C */ /* C Use a tolerance of 2 seconds ( half of the nominal */ /* C separation between MO pointing instances ). */ /* C */ /* CALL SCTIKS ( SC, '0000000002:000', TOL ) */ /* C */ /* C Search from the beginning of the CK file through all */ /* C of the segments. */ /* C */ /* CALL DAFBFS ( HANDLE ) */ /* CALL DAFFNA ( SFND ) */ /* FND = .FALSE. */ /* DO WHILE ( ( SFND ) .AND. ( .NOT. FND ) ) */ /* C */ /* C Get the segment identifier and descriptor. */ /* C */ /* CALL DAFGN ( IDENT ) */ /* CALL DAFGS ( DESCR ) */ /* C */ /* C Unpack the segment descriptor into its integer and */ /* C double precision components. */ /* C */ /* CALL DAFUS ( DESCR, 2, 6, DCD, ICD ) */ /* C */ /* C Determine if this segment should be processed. */ /* C */ /* IF ( ( INST .EQ. ICD( 1 ) ) .AND. */ /* . ( SCLKDP + TOL .GE. DCD( 1 ) ) .AND. */ /* . ( SCLKDP - TOL .LE. DCD( 2 ) ) .AND. */ /* . ( DTYPE .EQ. ICD( 3 ) ) ) THEN */ /* CALL CKR03 ( HANDLE, DESCR, SCLKDP, TOL, NEEDAV, */ /* . RECORD, FND ) */ /* IF ( FND ) THEN */ /* CALL CKE03 (NEEDAV,RECORD,CMAT,AV,CLKOUT) */ /* CALL SCDECD ( SC, CLKOUT, SCTIME ) */ /* WRITE (*,*) */ /* WRITE (*,*) 'Segment identifier: ', IDENT */ /* WRITE (*,*) */ /* WRITE (*,*) 'Pointing returned for time: ', */ /* . SCTIME */ /* WRITE (*,*) */ /* WRITE (*,*) 'C-matrix:' */ /* WRITE (*,*) */ /* WRITE (*,*) ( CMAT(1,I), I = 1, 3 ) */ /* WRITE (*,*) ( CMAT(2,I), I = 1, 3 ) */ /* WRITE (*,*) ( CMAT(3,I), I = 1, 3 ) */ /* WRITE (*,*) */ /* END IF */ /* END IF */ /* CALL DAFFNA ( SFND ) */ /* END DO */ /* $ Restrictions */ /* 1) No explicit checking is done on the input RECORD. */ /* $ Literature_References */ /* None. */ /* $ Author_and_Institution */ /* J.M. Lynch (JPL) */ /* F.S. Turner (JPL) */ /* $ Version */ /* - SPICELIB Version 2.0.1, 22-AUG-2006 (EDW) */ /* Replaced references to LDPOOL with references */ /* to FURNSH. */ /* - SPICELIB Version 2.0.0, 13-JUN-2002 (FST) */ /* This routine now participates in error handling properly. */ /* - SPICELIB Version 1.0.0, 25-NOV-1992 (JML) */ /* -& */ /* $ Index_Entries */ /* evaluate ck type_3 pointing data record */ /* -& */ /* $ Revisions */ /* - SPICELIB Version 2.0.0, 13-JUN-2002 (FST) */ /* Calls to CHKIN and CHKOUT in the standard SPICE error */ /* handling style were added. Versions prior to 2.0.0 */ /* were error free, however changes to RAXISA from error */ /* free to error signaling forced this update. */ /* Additionally, FAILED is now checked after the call to */ /* RAXISA. This prevents garbage from being placed into */ /* the output arguments. */ /* -& */ /* SPICELIB functions */ /* Local variables */ /* Standard SPICE error handling. */ if (return_()) { return 0; } else { chkin_("CKE03", (ftnlen)5); } /* Unpack the record, for easier reading. */ t = record[16]; t1 = record[0]; t2 = record[8]; moved_(&record[1], &c__4, q1); moved_(&record[5], &c__3, av1); moved_(&record[9], &c__4, q2); moved_(&record[13], &c__3, av2); /* If T1 and T2 are the same then no interpolation or extrapolation */ /* is performed. Simply convert the quaternion to a C-matrix and */ /* return. */ if (t1 == t2) { q2m_(q1, cmat); *clkout = t1; if (*needav) { vequ_(av1, av); } chkout_("CKE03", (ftnlen)5); return 0; } /* Interpolate between the two pointing instances to obtain pointing */ /* at the request time. */ /* Calculate what fraction of the interval the request time */ /* represents. */ frac = (t - t1) / (t2 - t1); /* Convert the left and right quaternions to C-matrices. */ q2m_(q1, cmat1); q2m_(q2, cmat2); /* Find the matrix that rotates the spacecraft instrument frame from */ /* the orientation specified by CMAT1 to that specified by CMAT2. */ /* Then find the axis and angle of that rotation matrix. */ /* T T */ /* CMAT2 = ROT * CMAT1 */ /* T */ /* ROT = CMAT2 * CMAT1 */ mtxm_(cmat2, cmat1, rot); raxisa_(rot, axis, &angle); if (failed_()) { chkout_("CKE03", (ftnlen)5); return 0; } /* Calculate the matrix that rotates vectors about the vector AXIS */ /* by the angle ANGLE * FRAC. */ d__1 = angle * frac; axisar_(axis, &d__1, delta); /* The interpolated pointing at the request time is given by CMAT */ /* where: */ /* T T */ /* CMAT = DELTA * CMAT1 */ /* and */ /* T */ /* CMAT = CMAT1 * DELTA */ mxmt_(cmat1, delta, cmat); /* Set CLKOUT equal to the time that pointing is being returned. */ *clkout = t; /* If angular velocity is requested then take a weighted average */ /* of the angular velocities at the left and right endpoints. */ if (*needav) { d__1 = 1. - frac; vlcom_(&d__1, av1, &frac, av2, av); } chkout_("CKE03", (ftnlen)5); return 0; } /* cke03_ */
/* $Procedure M2EUL ( Matrix to Euler angles ) */ /* Subroutine */ int m2eul_(doublereal *r__, integer *axis3, integer *axis2, integer *axis1, doublereal *angle3, doublereal *angle2, doublereal * angle1) { /* Initialized data */ static integer next[3] = { 2,3,1 }; /* System generated locals */ integer i__1, i__2; /* Builtin functions */ integer s_rnge(char *, integer, char *, integer); double acos(doublereal), atan2(doublereal, doublereal), asin(doublereal); /* Local variables */ doublereal sign; extern /* Subroutine */ int vhat_(doublereal *, doublereal *), mtxm_( doublereal *, doublereal *, doublereal *); integer c__, i__; logical degen; extern /* Subroutine */ int chkin_(char *, ftnlen); extern logical isrot_(doublereal *, doublereal *, doublereal *); doublereal change[9] /* was [3][3] */; extern /* Subroutine */ int cleard_(integer *, doublereal *), sigerr_( char *, ftnlen), chkout_(char *, ftnlen); doublereal tmpmat[9] /* was [3][3] */; extern /* Subroutine */ int setmsg_(char *, ftnlen), errint_(char *, integer *, ftnlen); extern logical return_(void); doublereal tmprot[9] /* was [3][3] */; extern /* Subroutine */ int mxm_(doublereal *, doublereal *, doublereal *) ; /* $ Abstract */ /* Factor a rotation matrix as a product of three rotations about */ /* specified coordinate axes. */ /* $ Disclaimer */ /* THIS SOFTWARE AND ANY RELATED MATERIALS WERE CREATED BY THE */ /* CALIFORNIA INSTITUTE OF TECHNOLOGY (CALTECH) UNDER A U.S. */ /* GOVERNMENT CONTRACT WITH THE NATIONAL AERONAUTICS AND SPACE */ /* ADMINISTRATION (NASA). THE SOFTWARE IS TECHNOLOGY AND SOFTWARE */ /* PUBLICLY AVAILABLE UNDER U.S. EXPORT LAWS AND IS PROVIDED "AS-IS" */ /* TO THE RECIPIENT WITHOUT WARRANTY OF ANY KIND, INCLUDING ANY */ /* WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR FITNESS FOR A */ /* PARTICULAR USE OR PURPOSE (AS SET FORTH IN UNITED STATES UCC */ /* SECTIONS 2312-2313) OR FOR ANY PURPOSE WHATSOEVER, FOR THE */ /* SOFTWARE AND RELATED MATERIALS, HOWEVER USED. */ /* IN NO EVENT SHALL CALTECH, ITS JET PROPULSION LABORATORY, OR NASA */ /* BE LIABLE FOR ANY DAMAGES AND/OR COSTS, INCLUDING, BUT NOT */ /* LIMITED TO, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ /* INCLUDING ECONOMIC DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, */ /* REGARDLESS OF WHETHER CALTECH, JPL, OR NASA BE ADVISED, HAVE */ /* REASON TO KNOW, OR, IN FACT, SHALL KNOW OF THE POSSIBILITY. */ /* RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF */ /* THE SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY */ /* CALTECH AND NASA FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE */ /* ACTIONS OF RECIPIENT IN THE USE OF THE SOFTWARE. */ /* $ Required_Reading */ /* ROTATION */ /* $ Keywords */ /* ANGLE */ /* MATRIX */ /* ROTATION */ /* TRANSFORMATION */ /* $ Declarations */ /* $ Brief_I/O */ /* Variable I/O Description */ /* -------- --- -------------------------------------------------- */ /* R I A rotation matrix to be factored. */ /* AXIS3, */ /* AXIS2, */ /* AXIS1 I Numbers of third, second, and first rotation axes. */ /* ANGLE3, */ /* ANGLE2, */ /* ANGLE1 O Third, second, and first Euler angles, in radians. */ /* $ Detailed_Input */ /* R is a 3x3 rotation matrix that is to be factored as */ /* a product of three rotations about a specified */ /* coordinate axes. The angles of these rotations are */ /* called `Euler angles'. */ /* AXIS3, */ /* AXIS2, */ /* AXIS1 are the indices of the rotation axes of the */ /* `factor' rotations, whose product is R. R is */ /* factored as */ /* R = [ ANGLE3 ] [ ANGLE2 ] [ ANGLE1 ] . */ /* AXIS3 AXIS2 AXIS1 */ /* The axis numbers must belong to the set {1, 2, 3}. */ /* The second axis number MUST differ from the first */ /* and third axis numbers. */ /* See the $ Particulars section below for details */ /* concerning this notation. */ /* $ Detailed_Output */ /* ANGLE3, */ /* ANGLE2, */ /* ANGLE1 are the Euler angles corresponding to the matrix */ /* R and the axes specified by AXIS3, AXIS2, and */ /* AXIS1. These angles satisfy the equality */ /* R = [ ANGLE3 ] [ ANGLE2 ] [ ANGLE1 ] */ /* AXIS3 AXIS2 AXIS1 */ /* See the $ Particulars section below for details */ /* concerning this notation. */ /* The range of ANGLE3 and ANGLE1 is (-pi, pi]. */ /* The range of ANGLE2 depends on the exact set of */ /* axes used for the factorization. For */ /* factorizations in which the first and third axes */ /* are the same, */ /* R = [r] [s] [t] , */ /* a b a */ /* the range of ANGLE2 is [0, pi]. */ /* For factorizations in which the first and third */ /* axes are different, */ /* R = [r] [s] [t] , */ /* a b c */ /* the range of ANGLE2 is [-pi/2, pi/2]. */ /* For rotations such that ANGLE3 and ANGLE1 are not */ /* uniquely determined, ANGLE3 will always be set to */ /* zero; ANGLE1 is then uniquely determined. */ /* $ Parameters */ /* None. */ /* $ Exceptions */ /* 1) If any of AXIS3, AXIS2, or AXIS1 do not have values in */ /* { 1, 2, 3 }, */ /* then the error SPICE(BADAXISNUMBERS) is signaled. */ /* 2) An arbitrary rotation matrix cannot be expressed using */ /* a sequence of Euler angles unless the second rotation axis */ /* differs from the other two. If AXIS2 is equal to AXIS3 or */ /* AXIS1, then then error SPICE(BADAXISNUMBERS) is signaled. */ /* 3) If the input matrix R is not a rotation matrix, the error */ /* SPICE(NOTAROTATION) is signaled. */ /* 4) If ANGLE3 and ANGLE1 are not uniquely determined, ANGLE3 */ /* is set to zero, and ANGLE1 is determined. */ /* $ Files */ /* None. */ /* $ Particulars */ /* A word about notation: the symbol */ /* [ x ] */ /* i */ /* indicates a coordinate system rotation of x radians about the */ /* ith coordinate axis. To be specific, the symbol */ /* [ x ] */ /* 1 */ /* indicates a coordinate system rotation of x radians about the */ /* first, or x-, axis; the corresponding matrix is */ /* +- -+ */ /* | 1 0 0 | */ /* | | */ /* | 0 cos(x) sin(x) |. */ /* | | */ /* | 0 -sin(x) cos(x) | */ /* +- -+ */ /* Remember, this is a COORDINATE SYSTEM rotation by x radians; this */ /* matrix, when applied to a vector, rotates the vector by -x */ /* radians, not x radians. Applying the matrix to a vector yields */ /* the vector's representation relative to the rotated coordinate */ /* system. */ /* The analogous rotation about the second, or y-, axis is */ /* represented by */ /* [ x ] */ /* 2 */ /* which symbolizes the matrix */ /* +- -+ */ /* | cos(x) 0 -sin(x) | */ /* | | */ /* | 0 1 0 |, */ /* | | */ /* | sin(x) 0 cos(x) | */ /* +- -+ */ /* and the analogous rotation about the third, or z-, axis is */ /* represented by */ /* [ x ] */ /* 3 */ /* which symbolizes the matrix */ /* +- -+ */ /* | cos(x) sin(x) 0 | */ /* | | */ /* | -sin(x) cos(x) 0 |. */ /* | | */ /* | 0 0 1 | */ /* +- -+ */ /* The input matrix is assumed to be the product of three */ /* rotation matrices, each one of the form */ /* +- -+ */ /* | 1 0 0 | */ /* | | */ /* | 0 cos(r) sin(r) | (rotation of r radians about the */ /* | | x-axis), */ /* | 0 -sin(r) cos(r) | */ /* +- -+ */ /* +- -+ */ /* | cos(s) 0 -sin(s) | */ /* | | */ /* | 0 1 0 | (rotation of s radians about the */ /* | | y-axis), */ /* | sin(s) 0 cos(s) | */ /* +- -+ */ /* or */ /* +- -+ */ /* | cos(t) sin(t) 0 | */ /* | | */ /* | -sin(t) cos(t) 0 | (rotation of t radians about the */ /* | | z-axis), */ /* | 0 0 1 | */ /* +- -+ */ /* where the second rotation axis is not equal to the first or */ /* third. Any rotation matrix can be factored as a sequence of */ /* three such rotations, provided that this last criterion is met. */ /* This routine is related to the SPICELIB routine EUL2M, which */ /* produces a rotation matrix, given a sequence of Euler angles. */ /* This routine is a `right inverse' of EUL2M: the sequence of */ /* calls */ /* CALL M2EUL ( R, AXIS3, AXIS2, AXIS1, */ /* . ANGLE3, ANGLE2, ANGLE1 ) */ /* CALL EUL2M ( ANGLE3, ANGLE2, ANGLE1, */ /* . AXIS3, AXIS2, AXIS1, R ) */ /* preserves R, except for round-off error. */ /* On the other hand, the sequence of calls */ /* CALL EUL2M ( ANGLE3, ANGLE2, ANGLE1, */ /* . AXIS3, AXIS2, AXIS1, R ) */ /* CALL M2EUL ( R, AXIS3, AXIS2, AXIS1, */ /* . ANGLE3, ANGLE2, ANGLE1 ) */ /* preserve ANGLE3, ANGLE2, and ANGLE1 only if these angles start */ /* out in the ranges that M2EUL's outputs are restricted to. */ /* $ Examples */ /* 1) Conversion of instrument pointing from a matrix representation */ /* to Euler angles: */ /* Suppose we want to find camera pointing in alpha, delta, and */ /* kappa, given the inertial-to-camera coordinate transformation */ /* +- -+ */ /* | 0.49127379678135830 0.50872620321864170 0.70699908539882417 | */ /* | | */ /* | -0.50872620321864193 -0.49127379678135802 0.70699908539882428 | */ /* | | */ /* | 0.70699908539882406 -0.70699908539882439 0.01745240643728360 | */ /* +- -+ */ /* We want to find angles alpha, delta, kappa such that */ /* TICAM = [ kappa ] [ pi/2 - delta ] [ pi/2 + alpha ] . */ /* 3 1 3 */ /* We can use the following small program to do this computation: */ /* PROGRAM EX1 */ /* IMPLICIT NONE */ /* DOUBLE PRECISION DPR */ /* DOUBLE PRECISION HALFPI */ /* DOUBLE PRECISION TWOPI */ /* DOUBLE PRECISION ALPHA */ /* DOUBLE PRECISION ANG1 */ /* DOUBLE PRECISION ANG2 */ /* DOUBLE PRECISION DELTA */ /* DOUBLE PRECISION KAPPA */ /* DOUBLE PRECISION TICAM ( 3, 3 ) */ /* DATA TICAM / 0.49127379678135830D0, */ /* . -0.50872620321864193D0, */ /* . 0.70699908539882406D0, */ /* . 0.50872620321864170D0, */ /* . -0.49127379678135802D0, */ /* . -0.70699908539882439D0, */ /* . 0.70699908539882417D0, */ /* . 0.70699908539882428D0, */ /* . 0.01745240643728360D0 / */ /* CALL M2EUL ( TICAM, 3, 1, 3, KAPPA, ANG2, ANG1 ) */ /* DELTA = HALFPI() - ANG2 */ /* ALPHA = ANG1 - HALFPI() */ /* IF ( KAPPA .LT. 0.D0 ) THEN */ /* KAPPA = KAPPA + TWOPI() */ /* END IF */ /* IF ( ALPHA .LT. 0.D0 ) THEN */ /* ALPHA = ALPHA + TWOPI() */ /* END IF */ /* WRITE (*,'(1X,A,F24.14)') 'Alpha (deg): ', DPR() * ALPHA */ /* WRITE (*,'(1X,A,F24.14)') 'Delta (deg): ', DPR() * DELTA */ /* WRITE (*,'(1X,A,F24.14)') 'Kappa (deg): ', DPR() * KAPPA */ /* END */ /* The program's output should be something like */ /* Alpha (deg): 315.00000000000000 */ /* Delta (deg): 1.00000000000000 */ /* Kappa (deg): 45.00000000000000 */ /* possibly formatted a little differently, or degraded slightly */ /* by round-off. */ /* 2) Conversion of instrument pointing angles from a non-J2000, */ /* not necessarily inertial frame to J2000-relative RA, Dec, */ /* and Twist. */ /* Suppose that we have pointing for some instrument expressed as */ /* [ gamma ] [ beta ] [ alpha ] */ /* 3 2 3 */ /* with respect to some coordinate system S. For example, S */ /* could be a spacecraft-fixed system. */ /* We will suppose that the transformation from J2000 */ /* coordinates to system S coordinates is given by the rotation */ /* matrix J2S. */ /* The rows of J2S are the unit basis vectors of system S, given */ /* in J2000 coordinates. */ /* We want to express the pointing with respect to the J2000 */ /* system as the sequence of rotations */ /* [ kappa ] [ pi/2 - delta ] [ pi/2 + alpha ] . */ /* 3 1 3 */ /* First, we use subroutine EUL2M to form the transformation */ /* from system S to instrument coordinates S2INST. */ /* CALL EUL2M ( GAMMA, BETA, ALPHA, 3, 2, 3, S2INST ) */ /* Next, we form the transformation from J2000 to instrument */ /* coordinates J2INST. */ /* CALL MXM ( S2INST, J2S, J2INST ) */ /* Finally, we express J2INST using the desired Euler angles, as */ /* in the first example: */ /* CALL M2EUL ( J2INST, 3, 1, 3, TWIST, ANG2, ANG3 ) */ /* RA = ANG3 - HALFPI() */ /* DEC = HALFPI() - ANG2 */ /* If we wish to make sure that RA, DEC, and TWIST are in */ /* the ranges [0, 2pi), [-pi/2, pi/2], and [0, 2pi) */ /* respectively, we may add the code */ /* IF ( RA .LT. 0.D0 ) THEN */ /* RA = RA + TWOPI() */ /* END IF */ /* IF ( TWIST .LT. 0.D0 ) THEN */ /* TWIST = TWIST + TWOPI() */ /* END IF */ /* Note that DEC is already in the correct range, since ANG2 */ /* is in the range [0, pi] when the first and third input axes */ /* are equal. */ /* Now RA, DEC, and TWIST express the instrument pointing */ /* as RA, Dec, and Twist, relative to the J2000 system. */ /* A warning note: more than one definition of RA, Dec, and */ /* Twist is extant. Before using this example in an application, */ /* check that the definition given here is consistent with that */ /* used in your application. */ /* $ Restrictions */ /* None. */ /* $ Literature_References */ /* None. */ /* $ Author_and_Institution */ /* N.J. Bachman (JPL) */ /* $ Version */ /* - SPICELIB Version 1.2.1, 21-DEC-2006 (NJB) */ /* Error corrected in header example: input matrix */ /* previously did not match shown outputs. Offending */ /* example now includes complete program. */ /* - SPICELIB Version 1.2.0, 15-OCT-2005 (NJB) */ /* Updated to remove non-standard use of duplicate arguments */ /* in MXM and MTXM calls. A short error message cited in */ /* the Exceptions section of the header failed to match */ /* the actual short message used; this has been corrected. */ /* - SPICELIB Version 1.1.2, 13-OCT-2004 (NJB) */ /* Fixed header typo. */ /* - SPICELIB Version 1.1.1, 10-MAR-1992 (WLT) */ /* Comment section for permuted index source lines was added */ /* following the header. */ /* - SPICELIB Version 1.1.0, 02-NOV-1990 (NJB) */ /* Header upgraded to describe notation in more detail. Argument */ /* names were changed to describe the use of the arguments more */ /* accurately. No change in functionality was made; the operation */ /* of the routine is identical to that of the previous version. */ /* - SPICELIB Version 1.0.0, 03-SEP-1990 (NJB) */ /* -& */ /* $ Index_Entries */ /* matrix to euler angles */ /* -& */ /* $ Revisions */ /* - SPICELIB Version 1.2.0, 26-AUG-2005 (NJB) */ /* Updated to remove non-standard use of duplicate arguments */ /* in MXM and MTXM calls. A short error message cited in */ /* the Exceptions section of the header failed to match */ /* the actual short message used; this has been corrected. */ /* - SPICELIB Version 1.1.0, 02-NOV-1990 (NJB) */ /* Argument names were changed to describe the use of the */ /* arguments more accurately. The axis and angle numbers */ /* now decrease, rather than increase, from left to right. */ /* The current names reflect the order of operator application */ /* when the Euler angle rotations are applied to a vector: the */ /* rightmost matrix */ /* [ ANGLE1 ] */ /* AXIS1 */ /* is applied to the vector first, followed by */ /* [ ANGLE2 ] */ /* AXIS2 */ /* and then */ /* [ ANGLE3 ] */ /* AXIS3 */ /* Previously, the names reflected the order in which the Euler */ /* angle matrices appear on the page, from left to right. This */ /* naming convention was found to be a bit obtuse by a various */ /* users. */ /* No change in functionality was made; the operation of the */ /* routine is identical to that of the previous version. */ /* Also, the header was upgraded to describe the notation in more */ /* detail. The symbol */ /* [ x ] */ /* i */ /* is explained at mind-numbing length. An example was added */ /* that shows a specific set of inputs and the resulting output */ /* matrix. */ /* The angle sequence notation was changed to be consistent with */ /* Rotations required reading. */ /* 1-2-3 and a-b-c */ /* have been changed to */ /* 3-2-1 and c-b-a. */ /* Also, one `)' was changed to a `}'. */ /* The phrase `first and third' was changed to `first or third' */ /* in the $ Particulars section, where the criterion for the */ /* existence of an Euler angle factorization is stated. */ /* -& */ /* SPICELIB functions */ /* Local parameters */ /* NTOL and DETOL are used to determine whether R is a rotation */ /* matrix. */ /* NTOL is the tolerance for the norms of the columns of R. */ /* DTOL is the tolerance for the determinant of a matrix whose */ /* columns are the unitized columns of R. */ /* Local variables */ /* Saved variables */ /* Initial values */ /* Standard SPICE error handling. */ if (return_()) { return 0; } else { chkin_("M2EUL", (ftnlen)5); } /* The first order of business is to screen out the goofy cases. */ /* Make sure the axis numbers are all right: They must belong to */ /* the set {1, 2, 3}... */ if (*axis3 < 1 || *axis3 > 3 || (*axis2 < 1 || *axis2 > 3) || (*axis1 < 1 || *axis1 > 3)) { setmsg_("Axis numbers are #, #, #. ", (ftnlen)28); errint_("#", axis3, (ftnlen)1); errint_("#", axis2, (ftnlen)1); errint_("#", axis1, (ftnlen)1); sigerr_("SPICE(BADAXISNUMBERS)", (ftnlen)21); chkout_("M2EUL", (ftnlen)5); return 0; /* ...and the second axis number must differ from its neighbors. */ } else if (*axis3 == *axis2 || *axis1 == *axis2) { setmsg_("Middle axis matches neighbor: # # #.", (ftnlen)36); errint_("#", axis3, (ftnlen)1); errint_("#", axis2, (ftnlen)1); errint_("#", axis1, (ftnlen)1); sigerr_("SPICE(BADAXISNUMBERS)", (ftnlen)21); chkout_("M2EUL", (ftnlen)5); return 0; /* R must be a rotation matrix, or we may as well forget it. */ } else if (! isrot_(r__, &c_b15, &c_b15)) { setmsg_("Input matrix is not a rotation.", (ftnlen)31); sigerr_("SPICE(NOTAROTATION)", (ftnlen)19); chkout_("M2EUL", (ftnlen)5); return 0; } /* AXIS3, AXIS2, AXIS1 and R have passed their tests at this */ /* point. We take the liberty of working with TMPROT, a version */ /* of R that has unitized columns. */ for (i__ = 1; i__ <= 3; ++i__) { vhat_(&r__[(i__1 = i__ * 3 - 3) < 9 && 0 <= i__1 ? i__1 : s_rnge( "r", i__1, "m2eul_", (ftnlen)667)], &tmprot[(i__2 = i__ * 3 - 3) < 9 && 0 <= i__2 ? i__2 : s_rnge("tmprot", i__2, "m2eul_", (ftnlen)667)]); } /* We now proceed to recover the promised Euler angles from */ /* TMPROT. */ /* The ideas behind our method are explained in excruciating */ /* detail in the ROTATION required reading, so we'll be terse. */ /* Nonetheless, a word of explanation is in order. */ /* The sequence of rotation axes used for the factorization */ /* belongs to one of two categories: a-b-a or c-b-a. We */ /* wish to handle each of these cases in one shot, rather than */ /* using different formulas for each sequence to recover the */ /* Euler angles. */ /* What we're going to do is use the Euler angle formula for the */ /* 3-1-3 factorization for all of the a-b-a sequences, and the */ /* formula for the 3-2-1 factorization for all of the c-b-a */ /* sequences. */ /* How can we get away with this? The Euler angle formulas for */ /* each factorization are different! */ /* Our trick is to apply a change-of-basis transformation to the */ /* input matrix R. For the a-b-a factorizations, we choose a new */ /* basis such that a rotation of ANGLE3 radians about the basis */ /* vector indexed by AXIS3 becomes a rotation of ANGLE3 radians */ /* about the third coordinate axis, and such that a rotation of */ /* ANGLE2 radians about the basis vector indexed by AXIS2 becomes */ /* a rotation of ANGLE2 radians about the first coordinate axis. */ /* So R can be factored as a 3-1-3 rotation relative to the new */ /* basis, and the Euler angles we obtain are the exact ones we */ /* require. */ /* The c-b-a factorizations can be handled in an analogous */ /* fashion. We transform R to a basis where the original axis */ /* sequence becomes a 3-2-1 sequence. In some cases, the angles */ /* we obtain will be the negatives of the angles we require. This */ /* will happen if and only if the ordered basis (here the e's are */ /* the standard basis vectors) */ /* { e e e } */ /* AXIS3 AXIS2 AXIS1 */ /* is not right-handed. An easy test for this condition is that */ /* AXIS2 is not the successor of AXIS3, where the ordering is */ /* cyclic. */ if (*axis3 == *axis1) { /* The axis order is a-b-a. We're going to find a matrix CHANGE */ /* such that */ /* T */ /* CHANGE R CHANGE */ /* gives us R in the a useful basis, that is, a basis in which */ /* our original a-b-a rotation is a 3-1-3 rotation, but where the */ /* rotation angles are unchanged. To achieve this pleasant */ /* simplification, we set column 3 of CHANGE to to e(AXIS3), */ /* column 1 of CHANGE to e(AXIS2), and column 2 of CHANGE to */ /* (+/-) e(C), */ /* (C is the remaining index) depending on whether */ /* AXIS3-AXIS2-C is a right-handed sequence of axes: if it */ /* is, the sign is positive. (Here e(1), e(2), e(3) are the */ /* standard basis vectors.) */ /* Determine the sign of our third basis vector, so that we can */ /* ensure that our new basis is right-handed. The variable NEXT */ /* is just a little mapping that takes 1 to 2, 2 to 3, and 3 to */ /* 1. */ if (*axis2 == next[(i__1 = *axis3 - 1) < 3 && 0 <= i__1 ? i__1 : s_rnge("next", i__1, "m2eul_", (ftnlen)746)]) { sign = 1.; } else { sign = -1.; } /* Since the axis indices sum to 6, */ c__ = 6 - *axis3 - *axis2; /* Set up the entries of CHANGE: */ cleard_(&c__9, change); change[(i__1 = *axis3 + 5) < 9 && 0 <= i__1 ? i__1 : s_rnge("change", i__1, "m2eul_", (ftnlen)762)] = 1.; change[(i__1 = *axis2 - 1) < 9 && 0 <= i__1 ? i__1 : s_rnge("change", i__1, "m2eul_", (ftnlen)763)] = 1.; change[(i__1 = c__ + 2) < 9 && 0 <= i__1 ? i__1 : s_rnge("change", i__1, "m2eul_", (ftnlen)764)] = sign * 1.; /* Transform TMPROT. */ mxm_(tmprot, change, tmpmat); mtxm_(change, tmpmat, tmprot); /* Now we're ready to find the Euler angles, using a */ /* 3-1-3 factorization. In general, the matrix product */ /* [ a1 ] [ a2 ] [ a3 ] */ /* 3 1 3 */ /* has the form */ /* +- -+ */ /* | cos(a1)cos(a3) cos(a1)sin(a3) sin(a1)sin(a2) | */ /* | -sin(a1)cos(a2)sin(a3) +sin(a1)cos(a2)cos(a3) | */ /* | | */ /* | -sin(a1)cos(a3) -sin(a1)sin(a3) cos(a1)sin(a2) | */ /* | -cos(a1)cos(a2)sin(a3) +cos(a1)cos(a2)cos(a3) | */ /* | | */ /* | sin(a2)sin(a3) -sin(a2)cos(a3) cos(a2) | */ /* +- -+ */ /* but if a2 is 0 or pi, the product matrix reduces to */ /* +- -+ */ /* | cos(a1)cos(a3) cos(a1)sin(a3) 0 | */ /* | -sin(a1)cos(a2)sin(a3) +sin(a1)cos(a2)cos(a3) | */ /* | | */ /* | -sin(a1)cos(a3) -sin(a1)sin(a3) 0 | */ /* | -cos(a1)cos(a2)sin(a3) +cos(a1)cos(a2)cos(a3) | */ /* | | */ /* | 0 0 cos(a2) | */ /* +- -+ */ /* In this case, a1 and a3 are not uniquely determined. If we */ /* arbitrarily set a1 to zero, we arrive at the matrix */ /* +- -+ */ /* | cos(a3) sin(a3) 0 | */ /* | -cos(a2)sin(a3) cos(a2)cos(a3) 0 | */ /* | 0 0 cos(a2) | */ /* +- -+ */ /* We take care of this case first. We test three conditions */ /* that are mathematically equivalent, but may not be satisfied */ /* simultaneously because of round-off: */ degen = tmprot[6] == 0. && tmprot[7] == 0. || tmprot[2] == 0. && tmprot[5] == 0. || abs(tmprot[8]) == 1.; /* In the following block of code, we make use of the fact that */ /* SIN ( ANGLE2 ) > 0 */ /* - */ /* in choosing the signs of the ATAN2 arguments correctly. Note */ /* that ATAN2(x,y) = -ATAN2(-x,-y). */ if (degen) { *angle3 = 0.; *angle2 = acos(tmprot[8]); *angle1 = atan2(tmprot[3], tmprot[0]); } else { /* The normal case. */ *angle3 = atan2(tmprot[6], tmprot[7]); *angle2 = acos(tmprot[8]); *angle1 = atan2(tmprot[2], -tmprot[5]); } } else { /* The axis order is c-b-a. We're going to find a matrix CHANGE */ /* such that */ /* T */ /* CHANGE R CHANGE */ /* gives us R in the a useful basis, that is, a basis in which */ /* our original c-b-a rotation is a 3-2-1 rotation, but where the */ /* rotation angles are unchanged, or at worst negated. To */ /* achieve this pleasant simplification, we set column 1 of */ /* CHANGE to to e(AXIS3), column 2 of CHANGE to e(AXIS2), and */ /* column 3 of CHANGE to */ /* (+/-) e(AXIS1), */ /* depending on whether AXIS3-AXIS2-AXIS1 is a right-handed */ /* sequence of axes: if it is, the sign is positive. (Here */ /* e(1), e(2), e(3) are the standard basis vectors.) */ /* We must be cautious here, because if AXIS3-AXIS2-AXIS1 is a */ /* right-handed sequence of axes, all of the rotation angles will */ /* be the same in our new basis, but if it's a left-handed */ /* sequence, the third angle will be negated. Let's get this */ /* straightened out right now. The variable NEXT is just a */ /* little mapping that takes 1 to 2, 2 to 3, and 3 to 1. */ if (*axis2 == next[(i__1 = *axis3 - 1) < 3 && 0 <= i__1 ? i__1 : s_rnge("next", i__1, "m2eul_", (ftnlen)883)]) { sign = 1.; } else { sign = -1.; } /* Set up the entries of CHANGE: */ cleard_(&c__9, change); change[(i__1 = *axis3 - 1) < 9 && 0 <= i__1 ? i__1 : s_rnge("change", i__1, "m2eul_", (ftnlen)894)] = 1.; change[(i__1 = *axis2 + 2) < 9 && 0 <= i__1 ? i__1 : s_rnge("change", i__1, "m2eul_", (ftnlen)895)] = 1.; change[(i__1 = *axis1 + 5) < 9 && 0 <= i__1 ? i__1 : s_rnge("change", i__1, "m2eul_", (ftnlen)896)] = sign * 1.; /* Transform TMPROT. */ mxm_(tmprot, change, tmpmat); mtxm_(change, tmpmat, tmprot); /* Now we're ready to find the Euler angles, using a */ /* 3-2-1 factorization. In general, the matrix product */ /* [ a1 ] [ a2 ] [ a3 ] */ /* 1 2 3 */ /* has the form */ /* +- -+ */ /* | cos(a2)cos(a3) cos(a2)sin(a3) -sin(a2) | */ /* | | */ /* | -cos(a1)sin(a3) cos(a1)cos(a3) sin(a1)cos(a2) | */ /* | +sin(a1)sin(a2)cos(a3) +sin(a1)sin(a2)sin(a3) | */ /* | | */ /* | sin(a1)sin(a3) -sin(a1)cos(a3) cos(a1)cos(a2) | */ /* | +cos(a1)sin(a2)cos(a3) +cos(a1)sin(a2)sin(a3) | */ /* +- -+ */ /* but if a2 is -pi/2 or pi/2, the product matrix reduces to */ /* +- -+ */ /* | 0 0 -sin(a2) | */ /* | | */ /* | -cos(a1)sin(a3) cos(a1)cos(a3) 0 | */ /* | +sin(a1)sin(a2)cos(a3) +sin(a1)sin(a2)sin(a3) | */ /* | | */ /* | sin(a1)sin(a3) -sin(a1)cos(a3) 0 | */ /* | +cos(a1)sin(a2)cos(a3) +cos(a1)sin(a2)sin(a3) | */ /* +- -+ */ /* In this case, a1 and a3 are not uniquely determined. If we */ /* arbitrarily set a1 to zero, we arrive at the matrix */ /* +- -+ */ /* | 0 0 -sin(a2) | */ /* | -sin(a3) cos(a3) 0 |, */ /* | sin(a2)cos(a3) sin(a2)sin(a3) 0 | */ /* +- -+ */ /* We take care of this case first. We test three conditions */ /* that are mathematically equivalent, but may not be satisfied */ /* simultaneously because of round-off: */ degen = tmprot[0] == 0. && tmprot[3] == 0. || tmprot[7] == 0. && tmprot[8] == 0. || abs(tmprot[6]) == 1.; /* In the following block of code, we make use of the fact that */ /* COS ( ANGLE2 ) > 0 */ /* - */ /* in choosing the signs of the ATAN2 arguments correctly. Note */ /* that ATAN2(x,y) = -ATAN2(-x,-y). */ if (degen) { *angle3 = 0.; *angle2 = asin(-tmprot[6]); *angle1 = sign * atan2(-tmprot[1], tmprot[4]); } else { /* The normal case. */ *angle3 = atan2(tmprot[7], tmprot[8]); *angle2 = asin(-tmprot[6]); *angle1 = sign * atan2(tmprot[3], tmprot[0]); } } chkout_("M2EUL", (ftnlen)5); return 0; } /* m2eul_ */
/* Subroutine */ int deri1_(doublereal *c__, integer *norbs, doublereal * coord, integer *number, doublereal *work, doublereal *grad, doublereal *f, integer *minear, doublereal *fd, doublereal *wmat, doublereal *hmat, doublereal *fmat) { /* Initialized data */ static integer icalcn = 0; /* System generated locals */ integer c_dim1, c_offset, work_dim1, work_offset, i__1, i__2, i__3, i__4, i__5, i__6, i__7, i__8; /* Builtin functions */ integer i_indx(char *, char *, ftnlen, ftnlen), s_wsle(cilist *), do_lio( integer *, integer *, char *, ftnlen), e_wsle(void), s_wsfe( cilist *), do_fio(integer *, char *, ftnlen), e_wsfe(void); /* Local variables */ static integer i__, j, k, l, n1, n2, ll; static doublereal gse; extern doublereal dot_(doublereal *, doublereal *, integer *); extern /* Subroutine */ int mxm_(doublereal *, integer *, doublereal *, integer *, doublereal *, integer *); static integer nend, nati, lcut, loop, natx; static doublereal step; static integer iprt; extern /* Subroutine */ int mtxm_(doublereal *, integer *, doublereal *, integer *, doublereal *, integer *), mecid_(doublereal *, doublereal *, doublereal *, doublereal *), mecih_(doublereal *, doublereal *, integer *, integer *); static logical debug; extern /* Subroutine */ int timer_(char *, ftnlen); static integer ninit; extern /* Subroutine */ int scopy_(integer *, doublereal *, integer *, doublereal *, integer *), dfock2_(doublereal *, doublereal *, doublereal *, doublereal *, integer *, integer *, integer *, integer *, integer *), dijkl1_(doublereal *, integer *, integer *, doublereal *, doublereal *, doublereal *, doublereal *); static doublereal enucl2; extern /* Subroutine */ int dhcore_(doublereal *, doublereal *, doublereal *, doublereal *, integer *, integer *, doublereal *); extern doublereal helect_(integer *, doublereal *, doublereal *, doublereal *); static integer linear; extern /* Subroutine */ int supdot_(doublereal *, doublereal *, doublereal *, integer *, integer *); /* Fortran I/O blocks */ static cilist io___11 = { 0, 6, 0, 0, 0 }; static cilist io___13 = { 0, 6, 0, "(5F12.6)", 0 }; static cilist io___22 = { 0, 6, 0, 0, 0 }; static cilist io___23 = { 0, 0, 0, "(' * * * GRADIENT COMPONENT NUMBER'," "I4)", 0 }; static cilist io___24 = { 0, 0, 0, "(' NON-RELAXED C.I-ACTIVE FOCK EIGEN" "VALUES ', 'DERIVATIVES (E.V.)')", 0 }; static cilist io___25 = { 0, 0, 0, "(8F10.4)", 0 }; static cilist io___26 = { 0, 0, 0, "(' NON-RELAXED 2-ELECTRONS DERIVATIV" "ES (E.V.)'/ ' I J K L d<I(1)J(1)|K(2)L(2)>')", 0 }; static cilist io___28 = { 0, 0, 0, "(4I5,F20.10)", 0 }; static cilist io___29 = { 0, 0, 0, "(' NON-RELAXED GRADIENT COMPONENT',F" "10.4, ' KCAL/MOLE')", 0 }; /* COMDECK SIZES */ /* *********************************************************************** */ /* THIS FILE CONTAINS ALL THE ARRAY SIZES FOR USE IN MOPAC. */ /* THERE ARE ONLY 5 PARAMETERS THAT THE PROGRAMMER NEED SET: */ /* MAXHEV = MAXIMUM NUMBER OF HEAVY ATOMS (HEAVY: NON-HYDROGEN ATOMS) */ /* MAXLIT = MAXIMUM NUMBER OF HYDROGEN ATOMS. */ /* MAXTIM = DEFAULT TIME FOR A JOB. (SECONDS) */ /* MAXDMP = DEFAULT TIME FOR AUTOMATIC RESTART FILE GENERATION (SECS) */ /* ISYBYL = 1 IF MOPAC IS TO BE USED IN THE SYBYL PACKAGE, =0 OTHERWISE */ /* SEE ALSO NMECI, NPULAY AND MESP AT THE END OF THIS FILE */ /* *********************************************************************** */ /* THE FOLLOWING CODE DOES NOT NEED TO BE ALTERED BY THE PROGRAMMER */ /* *********************************************************************** */ /* ALL OTHER PARAMETERS ARE DERIVED FUNCTIONS OF THESE TWO PARAMETERS */ /* NAME DEFINITION */ /* NUMATM MAXIMUM NUMBER OF ATOMS ALLOWED. */ /* MAXORB MAXIMUM NUMBER OF ORBITALS ALLOWED. */ /* MAXPAR MAXIMUM NUMBER OF PARAMETERS FOR OPTIMISATION. */ /* N2ELEC MAXIMUM NUMBER OF TWO ELECTRON INTEGRALS ALLOWED. */ /* MPACK AREA OF LOWER HALF TRIANGLE OF DENSITY MATRIX. */ /* MORB2 SQUARE OF THE MAXIMUM NUMBER OF ORBITALS ALLOWED. */ /* MAXHES AREA OF HESSIAN MATRIX */ /* MAXALL LARGER THAN MAXORB OR MAXPAR. */ /* *********************************************************************** */ /* *********************************************************************** */ /* DECK MOPAC */ /* ******************************************************************** */ /* DERI1 COMPUTE THE NON-RELAXED DERIVATIVE OF THE NON-VARIATIONALLY */ /* OPTIMIZED WAVEFUNCTION ENERGY WITH RESPECT TO ONE CARTESIAN */ /* COORDINATE AT A TIME */ /* AND */ /* COMPUTE THE NON-RELAXED FOCK MATRIX DERIVATIVE IN M.O BASIS AS */ /* REQUIRED IN THE RELAXATION SECTION (ROUTINE 'DERI2'). */ /* INPUT */ /* C(NORBS,NORBS) : M.O. COEFFICIENTS. */ /* COORD : CARTESIAN COORDINATES ARRAY. */ /* NUMBER : LOCATION OF THE REQUIRED VARIABLE IN COORD. */ /* WORK : WORK ARRAY OF SIZE N*N. */ /* WMAT : WORK ARRAYS FOR d<PQ|RS> (2-CENTERS A.O) */ /* OUTPUT */ /* C,COORD,NUMBER : NOT MODIFIED. */ /* GRAD : DERIVATIVE OF THE HEAT OF FORMATION WITH RESPECT TO */ /* COORD(NUMBER), WITHOUT RELAXATION CORRECTION. */ /* F(MINEAR) : NON-RELAXED FOCK MATRIX DERIVATIVE WITH RESPECT TO */ /* COORD(NUMBER), EXPRESSED IN M.O BASIS, SCALED AND */ /* PACKED, OFF-DIAGONAL BLOCKS ONLY. */ /* FD : IDEM BUT UNSCALED, DIAGONAL BLOCKS, C.I-ACTIVE ONLY. */ /* *********************************************************************** */ /* Parameter adjustments */ work_dim1 = *norbs; work_offset = 1 + work_dim1 * 1; work -= work_offset; c_dim1 = *norbs; c_offset = 1 + c_dim1 * 1; c__ -= c_offset; --coord; --f; --fd; --wmat; --hmat; --fmat; /* Function Body */ if (icalcn != numcal_1.numcal) { debug = i_indx(keywrd_1.keywrd, "DERI1", (ftnlen)241, (ftnlen)5) != 0; iprt = 6; linear = *norbs * (*norbs + 1) / 2; icalcn = numcal_1.numcal; } if (debug) { timer_("BEFORE DERI1", (ftnlen)12); } step = .001; /* 2 POINTS FINITE DIFFERENCE TO GET THE INTEGRAL DERIVATIVES */ /* ---------------------------------------------------------- */ /* STORED IN HMAT AND WMAT, WITHOUT DIVIDING BY THE STEP SIZE. */ nati = (*number - 1) / 3 + 1; natx = *number - (nati - 1) * 3; dhcore_(&coord[1], &hmat[1], &wmat[1], &enucl2, &nati, &natx, &step); /* HMAT HOLDS THE ONE-ELECTRON DERIVATIVES OF ATOM NATI FOR DIRECTION */ /* NATX W.R.T. ALL OTHER ATOMS */ /* WMAT HOLDS THE TWO-ELECTRON DERIVATIVES OF ATOM NATI FOR DIRECTION */ /* NATX W.R.T. ALL OTHER ATOMS */ step = .5 / step; /* NON-RELAXED FOCK MATRIX DERIVATIVE IN A.O BASIS. */ /* ------------------------------------------------ */ /* STORED IN FMAT, DIVIDED BY STEP. */ scopy_(&linear, &hmat[1], &c__1, &fmat[1], &c__1); dfock2_(&fmat[1], densty_1.p, densty_1.pa, &wmat[1], &molkst_1.numat, molkst_1.nfirst, molkst_1.nmidle, molkst_1.nlast, &nati); /* FMAT HOLDS THE ONE PLUS TWO - ELECTRON DERIVATIVES OF ATOM NATI FOR */ /* DIRECTION NATX W.R.T. ALL OTHER ATOMS */ /* DERIVATIVE OF THE SCF-ONLY ENERGY (I.E BEFORE C.I CORRECTION) */ *grad = (helect_(norbs, densty_1.p, &hmat[1], &fmat[1]) + enucl2) * step; /* TAKE STEP INTO ACCOUNT IN FMAT */ i__1 = linear; for (i__ = 1; i__ <= i__1; ++i__) { /* L10: */ fmat[i__] *= step; } /* RIGHT-HAND SIDE SUPER-VECTOR F = C' FMAT C USED IN RELAXATION */ /* ----------------------------------------------------------- */ /* STORED IN NON-STANDARD PACKED FORM IN F(MINEAR) AND FD. */ /* THE SUPERVECTOR IS THE NON-RELAXED FOCK MATRIX DERIVATIVE IN */ /* M.O BASIS: F(IJ)= ( (C' * FOCK * C)(I,J) ) WITH I.GT.J . */ /* F IS SCALED AND PACKED IN SUPERVECTOR FORM WITH */ /* THE CONSECUTIVE FOLLOWING OFF-DIAGONAL BLOCKS: */ /* 1) OPEN-CLOSED I.E. F(IJ)=F(I,J) WITH I OPEN & J CLOSED */ /* AND I RUNNING FASTER THAN J, */ /* 2) VIRTUAL-CLOSED SAME RULE OF ORDERING, */ /* 3) VIRTUAL-OPEN SAME RULE OF ORDERING. */ /* FD IS PACKED OVER THE C.I-ACTIVE M.O WITH */ /* THE CONSECUTIVE DIAGONAL BLOCKS: */ /* 1) CLOSED-CLOSED IN CANONICAL ORDER, WITHOUT THE */ /* DIAGONAL ELEMENTS, */ /* 2) OPEN-OPEN SAME RULE OF ORDERING, */ /* 3) VIRTUAL-VIRTUAL SAME RULE OF ORDERING. */ /* PART 1 : WORK(N,N) = FMAT(N,N) * C(N,N) */ i__1 = *norbs; for (i__ = 1; i__ <= i__1; ++i__) { /* L20: */ supdot_(&work[i__ * work_dim1 + 1], &fmat[1], &c__[i__ * c_dim1 + 1], norbs, &c__1); } /* PART 2 : F(IJ) = (C' * WORK)(I,J) ... OFF-DIAGONAL BLOCKS. */ l = 1; if (cibits_1.nbo[1] != 0 && cibits_1.nbo[0] != 0) { /* OPEN-CLOSED */ mtxm_(&c__[(cibits_1.nbo[0] + 1) * c_dim1 + 1], &cibits_1.nbo[1], & work[work_offset], norbs, &f[l], cibits_1.nbo); l += cibits_1.nbo[1] * cibits_1.nbo[0]; } if (cibits_1.nbo[2] != 0 && cibits_1.nbo[0] != 0) { /* VIRTUAL-CLOSED */ mtxm_(&c__[(molkst_1.nopen + 1) * c_dim1 + 1], &cibits_1.nbo[2], & work[work_offset], norbs, &f[l], cibits_1.nbo); l += cibits_1.nbo[2] * cibits_1.nbo[0]; } if (cibits_1.nbo[2] != 0 && cibits_1.nbo[1] != 0) { /* VIRTUAL-OPEN */ mtxm_(&c__[(molkst_1.nopen + 1) * c_dim1 + 1], &cibits_1.nbo[2], & work[(cibits_1.nbo[0] + 1) * work_dim1 + 1], norbs, &f[l], & cibits_1.nbo[1]); } /* SCALE F ACCORDING TO THE DIAGONAL METRIC TENSOR 'SCALAR '. */ i__1 = *minear; for (i__ = 1; i__ <= i__1; ++i__) { /* L30: */ f[i__] *= fokmat_1.scalar[i__ - 1]; } if (debug) { s_wsle(&io___11); do_lio(&c__9, &c__1, " F IN DERI1", (ftnlen)11); e_wsle(); j = min(20,*minear); s_wsfe(&io___13); i__1 = j; for (i__ = 1; i__ <= i__1; ++i__) { do_fio(&c__1, (char *)&f[i__], (ftnlen)sizeof(doublereal)); } e_wsfe(); } /* PART 3 : SUPER-VECTOR FD, C.I-ACTIVE DIAGONAL BLOCKS, UNSCALED. */ l = 1; nend = 0; for (loop = 1; loop <= 3; ++loop) { ninit = nend + 1; nend += cibits_1.nbo[loop - 1]; /* Computing MAX */ i__1 = ninit, i__2 = cibits_1.nelec + 1; n1 = max(i__1,i__2); /* Computing MIN */ i__1 = nend, i__2 = cibits_1.nelec + cibits_1.nmos; n2 = min(i__1,i__2); if (n2 < n1) { goto L50; } i__1 = n2; for (i__ = n1; i__ <= i__1; ++i__) { if (i__ > ninit) { i__2 = i__ - ninit; mxm_(&c__[i__ * c_dim1 + 1], &c__1, &work[ninit * work_dim1 + 1], norbs, &fd[l], &i__2); l = l + i__ - ninit; } /* L40: */ } L50: ; } /* NON-RELAXED C.I CORRECTION TO THE ENERGY DERIVATIVE. */ /* ---------------------------------------------------- */ /* C.I-ACTIVE FOCK EIGENVALUES DERIVATIVES, STORED IN FD(CONTINUED). */ lcut = l; i__1 = cibits_1.nelec + cibits_1.nmos; for (i__ = cibits_1.nelec + 1; i__ <= i__1; ++i__) { fd[l] = dot_(&c__[i__ * c_dim1 + 1], &work[i__ * work_dim1 + 1], norbs); /* L60: */ ++l; } /* C.I-ACTIVE 2-ELECTRONS INTEGRALS DERIVATIVES. STORED IN XY. */ /* FMAT IS USED HERE AS SCRATCH SPACE */ dijkl1_(&c__[(cibits_1.nelec + 1) * c_dim1 + 1], norbs, &nati, &wmat[1], & fmat[1], &hmat[1], &fmat[1]); i__1 = cibits_1.nmos; for (i__ = 1; i__ <= i__1; ++i__) { i__2 = cibits_1.nmos; for (j = 1; j <= i__2; ++j) { i__3 = cibits_1.nmos; for (k = 1; k <= i__3; ++k) { i__4 = cibits_1.nmos; for (l = 1; l <= i__4; ++l) { /* L70: */ xyijkl_1.xy[i__ + (j + (k + (l << 3) << 3) << 3) - 585] *= step; } } } } /* BUILD THE C.I MATRIX DERIVATIVE, STORED IN WMAT. */ mecid_(&fd[lcut - cibits_1.nelec], &gse, vector_1.eigb, &work[work_offset] ); if (debug) { s_wsle(&io___22); do_lio(&c__9, &c__1, " GSE:", (ftnlen)5); do_lio(&c__5, &c__1, (char *)&gse, (ftnlen)sizeof(doublereal)); e_wsle(); /* # WRITE(6,*)' EIGB:',(EIGB(I),I=1,10) */ /* # WRITE(6,*)' WORK:',(WORK(I,1),I=1,10) */ } mecih_(&work[work_offset], &wmat[1], &cibits_1.nmos, &cibits_1.lab); /* NON-RELAXED C.I CONTRIBUTION TO THE ENERGY DERIVATIVE. */ supdot_(&work[work_offset], &wmat[1], civect_1.conf, &cibits_1.lab, &c__1) ; *grad = (*grad + dot_(civect_1.conf, &work[work_offset], &cibits_1.lab)) * 23.061; if (debug) { io___23.ciunit = iprt; s_wsfe(&io___23); do_fio(&c__1, (char *)&(*number), (ftnlen)sizeof(integer)); e_wsfe(); io___24.ciunit = iprt; s_wsfe(&io___24); e_wsfe(); io___25.ciunit = iprt; s_wsfe(&io___25); i__4 = cibits_1.nmos; for (i__ = 1; i__ <= i__4; ++i__) { do_fio(&c__1, (char *)&fd[lcut - 1 + i__], (ftnlen)sizeof( doublereal)); } e_wsfe(); io___26.ciunit = iprt; s_wsfe(&io___26); e_wsfe(); i__4 = cibits_1.nmos; for (i__ = 1; i__ <= i__4; ++i__) { i__3 = i__; for (j = 1; j <= i__3; ++j) { i__2 = i__; for (k = 1; k <= i__2; ++k) { ll = k; if (k == i__) { ll = j; } i__1 = ll; for (l = 1; l <= i__1; ++l) { /* L80: */ io___28.ciunit = iprt; s_wsfe(&io___28); i__5 = cibits_1.nelec + i__; do_fio(&c__1, (char *)&i__5, (ftnlen)sizeof(integer)); i__6 = cibits_1.nelec + j; do_fio(&c__1, (char *)&i__6, (ftnlen)sizeof(integer)); i__7 = cibits_1.nelec + k; do_fio(&c__1, (char *)&i__7, (ftnlen)sizeof(integer)); i__8 = cibits_1.nelec + l; do_fio(&c__1, (char *)&i__8, (ftnlen)sizeof(integer)); do_fio(&c__1, (char *)&xyijkl_1.xy[i__ + (j + (k + (l << 3) << 3) << 3) - 585], (ftnlen)sizeof( doublereal)); e_wsfe(); } } } } io___29.ciunit = iprt; s_wsfe(&io___29); do_fio(&c__1, (char *)&(*grad), (ftnlen)sizeof(doublereal)); e_wsfe(); timer_("AFTER DERI1", (ftnlen)11); } return 0; } /* deri1_ */