Blame view

src/idl_extern/CMTotal_for_Dustemwrap/qtfind.pro 6.52 KB
517b8f98   Annie Hughes   first commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
;+
; NAME:
;   QTFIND
;
; AUTHOR:
;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
;   craigm@lheamail.gsfc.nasa.gov
;   UPDATED VERSIONs can be found on my WEB PAGE: 
;      http://cow.physics.wisc.edu/~craigm/idl/idl.html
;
; PURPOSE:
;   Find quaternion(s) from direction cosine matrix
;
; MAJOR TOPICS:
;   Geometry
;
; CALLING SEQUENCE:
;   Q = QTFIND(MATRIX)
;
; DESCRIPTION:
;
;   The function QTFIND determines one or more unit quaternions from
;   direction cosine matrices.
;
;   This routine is optimized to avoid singularities which occur when
;   any one of the quaternion components is nearly zero.  Up to four
;   different transformations are attempted to maximize the precision
;   of all four quaternion components.
;
;   QTFIND and QTMAT are functional inverses: use QTFIND to convert a
;   known direction cosine matrix to a new quaternion; use QTMAT to
;   convert a known quaternion to matrix representation.
;
;   NUMERICAL ACCURACY: In a test of 1 billion randomly chosen
;   normalized quaternions at double precision, the maximum numerical
;   error was less than 4.5E-16 in each quaternion component, which is
;   comparable to the numerical accuracy of the double precision
;   representation itself.
;
;  Conventions for storing quaternions vary in the literature and from
;  library to library.  This library uses the convention that the
;  first three components of each quaternion are the 3-vector axis of
;  rotation, and the 4th component is the rotation angle.  Expressed
;  in formulae, a single quaternion is given by:
;
;     Q(0:2) = [VX, VY, VZ]*SIN(PHI/2)
;     Q(3)   =              COS(PHI/2)
;
;  where PHI is the rotation angle, and VAXIS = [VX, VY, VZ] is the
;  rotation eigen axis expressed as a unit vector.  This library
;  accepts quaternions of both signs, but by preference returns
;  quaternions with a positive 4th component.
;
;
; INPUTS:
;
;   MATRIX - array of one or more direction cosine matrices.  For a
;            single matrix, MATRIX should be a 3x3 array.  For N
;            matrices, MATRIX should be a 3x3xN array.  The arrays are
;            assumed to be valid rotation matrices.
;
;
; RETURNS:
;
;   The resulting unit quaternions.  For a single matrix, returns a
;   single quaternion as a 4-vector.  For N matrices, returns N
;   quaternions as a 4xN array.
;
;
; KEYWORD PARAMETERS:
;
;   NONE
;
; EXAMPLE:
;
;   ;; Form a rotation matrix about the Z axis by 32 degrees
;   th1 = 32d*!dpi/180         
;   mat1 = [[cos(th1),-sin(th1),0],[sin(th1),cos(th1),0],[0,0,1]]
;   
;   ;; Form a rotation matrix about the X axis by 116 degrees
;   th2 = 116d*!dpi/180
;   mat2 = [[1,0,0],[0,cos(th2),-sin(th2)],[0,sin(th2),cos(th2)]]
;
;   ;; Find the quaternion that represents MAT1, MAT2 and the
;   composition of the two, MAT2 ## MAT1.
;
;    print, qtfind(mat1), qtfind(mat2), qtfind(mat2 ## mat1)
;       0.0000000       0.0000000      0.27563736      0.96126170
;      0.84804810       0.0000000       0.0000000      0.52991926
;      0.81519615     -0.23375373      0.14606554      0.50939109
;
;
; SEE ALSO
;   QTANG, QTAXIS, QTCOMPOSE, QTERP, QTEXP, QTFIND, QTINV, QTLOG,
;   QTMAT, QTMULT, QTPOW, QTVROT
;
; MODIFICATION HISTORY:
;   Written, July 2001, CM
;   Documented, Dec 2001, CM
;   Re-added check to enforce q(3) GE 0, 15 Mar 2002, CM
;   Usage message, error checking, 15 Mar 2002, CM
;   Fixed bug which could produce all-zero quaternion; add
;     documentation about numerical accuracy, 2014-03-04, CM
;
;  $Id: qtfind.pro,v 1.9 2014/10/20 21:37:08 cmarkwar Exp $
;
;-
; Copyright (C) 2001, 2002, 2014, Craig Markwardt
; This software is provided as is without any warranty whatsoever.
; Permission to use, copy, modify, and distribute modified or
; unmodified copies is granted, provided this copyright and disclaimer
; are included unchanged.
;-

function qtfind, amat

; THIS ROUTINE CONVERTS ROTATION MATRIX AMAT INTO QUATERNION AQT
; IT ASSUMES AMAT IS A VALID ROTATION MATRIX
; THIS IS ADAPTED FROM CHAPTER 12 BY F.L.MARKLEY
;
; MODIFIED 11/22/95 TO AVOID SINGULARITIES (E.G., Q4=0.)
; THE SQUARE OF ONE OF THE QUATERNION ELEMENTS MUST BE >= 0.25
;	SINCE THE 4 SUM TO 1.
; MOD 11/24/95 TO MAKE SURE Q4 >= 0
; MOD 14-DEC-95 TO FIX BUG OF WRONG SIGN OF Q4 IF Q1,Q3,&Q4 < .5

  if n_params() EQ 0 then begin
      info = 1
      USAGE:
      message, 'USAGE:', /info
      message, 'Q = QTFIND(MATRIX)', /info
      message, '   MATRIX must be a 3x3xN array of direction cosines', $
        info=info
      return, 0
  endif

  sz = size(amat)
  if sz(0) LT 2 then begin
      DIM_ERROR:
      
      message, 'ERROR: MATRIX must be a 3x3xN array', /info
      return, 0
  endif
  if sz(1) NE 3 OR sz(2) NE 3 then goto, DIM_ERROR

  nq = n_elements(amat)/9
  ad0 = amat(0,0,*) & ad1 = amat(1,1,*) & ad2 = amat(2,2,*)
  a12 = amat(1,2,*) & a21 = amat(2,1,*)
  a20 = amat(2,0,*) & a02 = amat(0,2,*)
  a01 = amat(0,1,*) & a10 = amat(1,0,*)

  n1 = nq
  q0 = replicate(amat(0)*0+0., nq) & q1 = q0 & q2 = q0 & q3 = q0
  mask = bytarr(nq)

  qd = 1. + ad0 + ad1 + ad2
  wh = where(qd GE 0.99, ct)
  if ct GT 0 then begin
      qx = 0.5*sqrt(qd(wh))
      q3(wh) = qx
      qx = qx * 4
      q0(wh) = (a12-a21)(wh)/qx
      q1(wh) = (a20-a02)(wh)/qx
      q2(wh) = (a01-a10)(wh)/qx
      n1 = n1 - ct
      mask(wh) = 1
  endif 
  if n1 GT 0 then begin
      qd = 1. + ad0 - ad1 - ad2
      wh = where(mask EQ 0 AND qd GE 0.99, ct)
      if ct GT 0 then begin
          qx = 0.5*sqrt(qd(wh))
          q0(wh) = qx
          qx = qx * 4
          q3(wh) = (a12-a21)(wh)/qx
          q2(wh) = (a20+a02)(wh)/qx
          q1(wh) = (a01+a10)(wh)/qx
          n1 = n1 - ct
          mask(wh) = 1
      endif
  endif
  if n1 GT 0 then begin
      qd = 1. - ad0 - ad1 + ad2
      wh = where(mask EQ 0 AND qd GE 0.99, ct)
      if ct GT 0 then begin
          qx = 0.5*sqrt(qd(wh))
          q2(wh) = qx
          qx = qx * 4
          q1(wh) = (a12+a21)(wh)/qx
          q0(wh) = (a20+a02)(wh)/qx
          q3(wh) = (a01-a10)(wh)/qx
          n1 = n1 - ct
          mask(wh) = 1
      endif
  endif
  if n1 GT 0 then begin
      qd = 1. - ad0 + ad1 - ad2
      wh = where(mask EQ 0 AND qd GE 0.99, ct)
      if ct GT 0 then begin
          qx = 0.5*sqrt(qd(wh))
          q1(wh) = qx
          qx = qx * 4
          q2(wh) = (a12+a21)(wh)/qx
          q3(wh) = (a20-a02)(wh)/qx
          q0(wh) = (a01+a10)(wh)/qx
          n1 = n1 - ct
          ;; mask(wh) = 1
      endif
  endif

  wh = where(q3 LT 0, ct)
  if ct GT 0 then begin
      q0(wh) = -q0(wh)
      q1(wh) = -q1(wh)
      q2(wh) = -q2(wh)
      q3(wh) = -q3(wh)
  endif

  return, transpose([[q0],[q1],[q2],[q3]])
end