Instrument Neutral Distributed Interface INDI 2.2.5
pmfit.c
Go to the documentation of this file.
1#include <stdio.h>
2#include <stdlib.h>
3#include <math.h>
4#include <string.h>
5#include <sofa.h>
6
7int Bfun ( int, double, char, double, double, double* );
8int Simeqn (int, double*, double* );
9
10int Pmfit ( double phi, char mount, int n, double* obs, int nt,
11 double* pm, double* sigmas, double* skysig )
12/*
13** - - - - - -
14** P m f i t
15** - - - - - -
16**
17** Fit a canonical model to pointing test observations.
18**
19** Given:
20** phi double latitude (geodetic, radians)
21** mount char mount type: 'E' or 'A'
22** n int number of observations
23** obs double* observations (each 4 doubles)
24** nt int number of terms (see the function Bfun)
25**
26** Returned:
27** pm double[nt] pointing model coefficients (radians)
28** sigmas double[nt] model coefficient standard deviations (radians)
29** skysig double* on-sky standard deviation of the fit (radians)
30**
31** Returned (function value):
32** int status: 0 = OK
33** -1 = illegal n
34** -2 = memory allocation error
35** -3 = unsupported mount
36** -4 = a matrix is singular
37**
38** Notes:
39**
40** 1) The supported pointing model is IH ID CH ME MA TF.
41**
42** 2) Each of the n observations comprises four angles (all radians):
43**
44** observed HA or Az
45** observed Dec or El
46** HA or Az demand
47** Dec or El demand
48**
49** 3) If an error status is returned, the contents of array pm are
50** undefined.
51**
52** Called: Bfun, Simeqn, iauAnpm
53**
54** This revision: 23 September 2023
55**
56** Author P.T.Wallace - see license notice at end.
57*/
58{
59/* Basis function addressing */
60 #define BF(i,j) bf[i*2+j]
61
62/* Internal form of the observations */
63 typedef struct { double r, p; double xy[2]; } SAM;
64
65/* Pointer to the sample array */
66 SAM* y;
67
68/* Pointer to the [nt] basis functions */
69 double* bf;
70
71/* Pointers to the normal equations [nt][nt] matrix and inverse */
72 double* a;
73 double* ai;
74
75/* Pointer to the normal equations [nt] vector */
76 double* v;
77
78/* Maximum number of iterations */
79 const int nitmax = 20;
80
81 int jstat, it, m, i, j, ij;
82 double tst, oblon, oblat, elon, elat, enclon, enclat, w, adj, sumr2,
83 skyvar;
84
85
86/* Validate n. */
87 if ( n < 1 ) return -1;
88
89/* Safe the malloc pointers. */
90 y = NULL;
91 bf = NULL;
92 a = NULL;
93 ai = NULL;
94 v = NULL;
95
96/* Allocate space for the samples. */
97 y = (SAM*) malloc ( n * sizeof(SAM) );
98
99/* Allocate space for the basis function values. */
100 bf = (double*) malloc ( nt * sizeof(double) * 2 );
101
102/* Allocate space for the normal equations matrix... */
103 a = (double*) malloc ( nt * nt * sizeof(double) );
104
105/* ...and its inverse. */
106 ai = (double*) malloc ( nt * nt * sizeof(double) );
107
108/* Allocate space for the normal equations vector. */
109 v = (double*) malloc ( nt * sizeof(double) );
110
111/* Abort if any errors. */
112 if ( ! ( y && bf && a && ai && v ) ) {
113 jstat = -2;
114 goto free;
115 }
116
117/* Preset the status to failure. */
118 jstat = -4;
119
120/* Initialize the pointing model. */
121 for ( i = 0; i < nt; i++ ) pm[i] = 0.0;
122
123/* Initialize convergence test. */
124 tst = 9999.9;
125
126/* Iterate, making pointing-model adjustments each time. */
127 for ( it = 0; it < nitmax; it++ ) {
128
129 /*
130 ** -----------------------------------------
131 ** Transform observations into internal form
132 ** -----------------------------------------
133 */
134
135 for ( m = 0; m < n; m++ ) {
136
137 /* The observed and demanded coordinates. */
138 oblon = obs[4*m];
139 oblat = obs[4*m+1];
140 enclon = obs[4*m+2];
141 enclat = obs[4*m+3];
142
143 /* Apply the current model to correct the encoder demands. */
144 elon = enclon;
145 elat = enclat;
146 if ( Bfun ( nt, phi, mount, elon, elat, bf ) ) goto free;
147 for ( j = 0; j < nt; j++ ) {
148 elon += ( pm[j] * BF(j,0) ) / cos(elat);
149 elat += pm[j] * BF(j,1);
150 }
151
152 /* Create the sample. */
153 y[m].r = oblon;
154 y[m].p = oblat;
155 y[m].xy[0] = iauAnpm(oblon-elon)*cos(oblat);
156 y[m].xy[1] = iauAnpm(oblat-elat);
157
158 /* Next observation. */
159 }
160
161 /* Initialize the normal equations. */
162 for ( i = 0; i < nt; i++ ) {
163 v[i] = 0.0;
164 for ( j = 0; j < nt; j++ ) {
165 ij = i*nt + j;
166 a[ij] = 0.0;
167 }
168 }
169
170 /* For each sample... */
171 for ( m = 0; m < n; m++ ){
172
173 /* Compute the xi,eta basis functions for this sample. */
174 if ( Bfun ( nt, phi, mount, y[m].r, y[m].p, bf ) ) goto free;
175
176 /* Update normal equations, combining xi and eta components. */
177 for ( i = 0; i < nt; i++ ) {
178 v[i] += BF(i,0)*y[m].xy[0] + BF(i,1)*y[m].xy[1];
179 for ( j = 0; j < nt; j++ ) {
180 ij = i*nt + j;
181 a[ij] += BF(i,0)*BF(j,0) + BF(i,1)*BF(j,1);
182 }
183 }
184 /* Next sample. */
185 }
186
187 /* Solve the normal equations. */
188 if ( Simeqn ( nt, a, v ) ) goto free;
189
190 /* Adjust the pointing model, testing for convergence. */
191 w = 0.0;
192 for ( i = 0; i < nt; i++ ) {
193 adj = v[i];
194 pm[i] += adj;
195 w += adj*adj;
196 }
197 if ( w >= tst ) break;
198 tst = w;
199
200 /* Next iteration. */
201 }
202
203/*
204** ------------------------------
205** Sky RMS and coefficient sigmas
206** ------------------------------
207*/
208
209/* Reset sum of distance on sky squared. */
210 sumr2 = 0.0;
211
212/* For each sample... */
213 for ( m = 0; m < n; m++ ){
214
215 /* The observed and demanded coordinates. */
216 oblon = obs[4*m];
217 oblat = obs[4*m+1];
218 enclon = obs[4*m+2];
219 enclat = obs[4*m+3];
220
221 /* Predicted observed place using the fitted model. */
222 elon = enclon;
223 elat = enclat;
224 if ( Bfun ( nt, phi, mount, elon, elat, bf ) ) goto free;
225 for ( j = 0; j < nt; j++ ) {
226 elon += ( pm[j] * BF(j,0) ) / cos(elat);
227 elat += pm[j] * BF(j,1);
228 }
229
230 /* Accumulate residuals in xi and eta. */
231 w = iauAnpm(oblon-elon)*cos(oblat);
232 sumr2 += w*w;
233 w = iauAnpm(oblat-elat);
234 sumr2 += w*w;
235
236 /* Next sample. */
237 }
238
239/* On-sky variance and RMS. */
240 skyvar = sumr2 / (double) (n-nt);
241
242/* Standard deviations of the model coefficients. */
243 for ( i = 0; i < nt; i++ ) sigmas[i] = sqrt ( skyvar * a[i*(nt+1)] );
244
245/* On-sky standard deviation. */
246 *skysig = sqrt(skyvar);
247
248/* Status = success. */
249 jstat = 0;
250
251/* Free the workspace. */
252free:
253 free ( y );
254 free ( bf );
255 free ( a );
256 free ( ai );
257 free ( v );
258
259/* Return the status. */
260 return jstat;
261}
262
263/* ------------------------------------------------------------------ */
264
265#include <math.h>
266
267int Bfun ( int nt, double phi, char mount, double rdem, double pdem,
268 double* bf )
269/*
270** - - - - - -
271** B a s i s
272** - - - - - -
273**
274** The basis functions for the default model.
275**
276** Given:
277** nt int number of terms
278** phi double latitude (geodetic, radians)
279** mount char mount type: 'E' or 'A'
280** rdem double demand HA or Az (radians)
281** pdem double demand Dec or El (radians)
282**
283** Returned:
284** bf double* [nt][2] basis functions
285**
286** Returned (function value):
287** int status: 0 = OK
288** -1 = invalid mount
289**
290** Notes:
291**
292** 1) It is the caller's responsibility to supply a usable observation,
293** for example in the visible sky.
294**
295** 2) The specific six-term model implemented here is a good starting
296** point for most telescopes but can also be regarded as a template
297** for further development.
298**
299** 3) Each basis function comprises the "collimation" and "pitch"
300** components, respectively, of the pointing correction.
301**
302** 4) The relationship between the default six-term model fitted in the
303** present function and the internal seven-term model set out in
304** Wallace (2002) Section 4.1.3 is as follows:
305**
306** generic equat altaz
307**
308** IA -bf[0] bf[0]
309** IB bf[1] bf[1]
310** AW -bf[3] bf[3]
311** AN bf[2] bf[2]
312** CA -bf[4] -bf[4]
313** VD bf[5]*sin(z) bf[5]*sin(z) z = zenith angle
314** NP 0 0
315**
316** Note: polar-axis/axis-tilt terms (AW/AN) are fitted before the
317** collimation term (CA) so that a 4-term fit on 3 sync points is
318** well-conditioned for polar-alignment use cases.
319**
320** 5) The omission of the roll/pitch nonperpendicularity term NP from
321** the default model is because (i) the three terms IA, NP and CA
322** are sufficiently correlated that typical pointing tests find it
323** difficult to disentangle them, and it is often best to fix one of
324** the three at zero and let the other two take up the slack, and
325** (ii) mount manufacturers routinely achieve mechanical
326** perpendicularity better than pointing tests can measure, and so
327** it is better to assume zero for this model coefficient.
328**
329** Reference (see Note 4):
330**
331** Wallace, P.T., 2002, "A rigorous algorithm for telescope
332** pointing", Advanced Telescope and Instrumentation Control
333** Software II, edited by Lewis, Hilton, proceedings of the SPIE,
334** 4848, 125.
335**
336** Called: iauHd2ae, iauAe2hd
337**
338** This revision: 20 September 2023
339**
340** Author P.T.Wallace - see license notice at end.
341*/
342{
343/* Basis function addressing */
344 #define BF(i,j) bf[i*2+j]
345
346/* Number of terms supported by the present function */
347 const double nterms = 6;
348
349 double sp, cp, hdem, ddem, adem, edem,
350 sh, ch, sd, cd, sa, ca, se, ce;
351
352
353/* Verify that the model length is valid. */
354 if ( nt < 1 || nt > (int)nterms ) return -1;
355
356/* Functions of latitude. */
357 sp = sin(phi);
358 cp = cos(phi);
359
360/* Observed coordinates and functions. */
361 switch ( (int) mount ) {
362
363 case 'E':
364
365 /* Equatorial: copy HA,Dec and compute Az,El. */
366 hdem = rdem;
367 ddem = pdem;
368 iauHd2ae ( hdem, ddem, phi, &adem, &edem );
369 break;
370
371 case 'A':
372
373 /* Altazimuth: copy Az,El and compute HA,Dec. */
374 adem = rdem;
375 edem = pdem;
376 iauAe2hd ( adem, edem, phi, &hdem, &ddem );
377 break;
378
379 default:
380 return -1;
381 }
382
383/* Functions that may be needed in basis function formulas. */
384 sh = sin(hdem);
385 ch = cos(hdem);
386 sd = sin(ddem);
387 cd = cos(ddem);
388 sa = sin(adem);
389 ca = cos(adem);
390 se = sin(edem);
391 ce = cos(edem);
392
393/*
394** ---------------------------------------------------
395** The basis functions at the current point in the sky
396** ---------------------------------------------------
397**
398** Each term is an [x,y] correction vector at the given point in the
399** sky, where the x-axis is east-west for equatorials or left-right
400** for altazimuths and the y-axis north-south or up-down respectively.
401**
402** The equatorial terms are from Appendix 1 of Wallace, P.T., 1975,
403** Programming the Control Computer of the Anglo-Australian 3.9 Metre
404** Telescope. In Telescope Automation: proceedings of an International
405** Conference held at MIT, Cambridge, MA USA, April 29 - May 1, 1975,
406** Eds. Maureen K. Huguenin and Thomas B. McCord. Published by MIT,
407** 1975, p.281.
408**
409** The altazimuth terms are from Table 3 in Jeffrey G. Mangum,
410** Jacob W. M. Baars, Albert Greve, Robert Lucas, Ralph C. Snel,
411** Patrick Wallace, Mark Holdaway, 2006, Evaluation of the ALMA
412** Prototype Antennas, Publications of the Astronomical Society of the
413** Pacific, 118, 847, 1260.
414**
415** n.b. The formulas are first-order approximations, not rigorous frame
416** rotations, valid for relatively small values of the pointing-model
417** coefficients.
418*/
419
420 switch ( (int) mount ) {
421
422 case 'E':
423
424 /* Equatorial: IH, ID, ME, MA, CH, TF */
425 /* Polar-axis terms (ME, MA) precede collimation (CH) so that a */
426 /* 4-term fit on 3 points is well-conditioned for polar alignment. */
427
428 if (nt > 0) { BF(0,0) = cd; BF(0,1) = 0.0; } /* IH */
429 if (nt > 1) { BF(1,0) = 0.0; BF(1,1) = 1.0; } /* ID */
430 if (nt > 2) { BF(2,0) = sh*sd; BF(2,1) = ch; } /* ME */
431 if (nt > 3) { BF(3,0) = -ch*sd; BF(3,1) = sh; } /* MA */
432 if (nt > 4) { BF(4,0) = 1.0; BF(4,1) = 0.0; } /* CH */
433 if (nt > 5) { BF(5,0) = cp*sh; BF(5,1) = cp*ch*sd-sp*cd; } /* TF */
434
435 break;
436
437 default:
438
439 /* Altazimuth: IA, IE, AN, AW, CA, TF */
440 /* Axis-tilt terms (AN, AW) precede collimation (CA) for the same */
441 /* reason as the equatorial reordering above. */
442
443 if (nt > 0) { BF(0,0) = -ce; BF(0,1) = 0.0; } /* IA */
444 if (nt > 1) { BF(1,0) = 0.0; BF(1,1) = 1.0; } /* IE */
445 if (nt > 2) { BF(2,0) = -sa*se; BF(2,1) = -ca; } /* AN */
446 if (nt > 3) { BF(3,0) = -ca*se; BF(3,1) = sa; } /* AW */
447 if (nt > 4) { BF(4,0) = -1.0; BF(4,1) = 0.0; } /* CA */
448 if (nt > 5) { BF(5,0) = 0.0; BF(5,1) = -ce; } /* TF */
449
450 }
451
452/* Success. */
453 return 0;
454}
455
456/*--------------------------------------------------------------------*/
457
458#include "stdlib.h"
459#include "math.h"
460
461int Simeqn (int n, double *a, double *y )
462/*
463** - - - - - - -
464** S i m e q n
465** - - - - - - -
466**
467** Solve simultaneous equations Ax = b, returning also the inverse of
468** matrix A.
469**
470** Given:
471** n int number of equations
472** a double* the n x n matrix A
473** y double* the right-hand-side variables b
474**
475** Returned:
476** a double* the inverse of A
477** y double* the solution x
478**
479** Returned (function value):
480** int status: 0 = OK
481** -1 = illegal n
482** -2 = memory allocation error
483** -3 = matrix is singular
484**
485** Notes:
486**
487** 1) If the matrix a is singular, a -3 status is returned and the
488** results are undefined.
489**
490** 2) The algorithm used is Gaussian elimination with partial pivoting;
491** this is fast and respectably accurate.
492**
493** 3) Example call:
494**
495** const int n = ...
496** double a[n][n], y[n];
497** int j;
498** :
499** j = spkDmati ( n, (double*) a, double y[] );
500**
501** n.b. the [n][n] matrix a is passed as a single pointer.
502**
503** Last revision: 3 September 2023
504**
505** Author P.T.Wallace - see license notice at end.
506*/
507{
508 #define TINY 1e-30
509
510 #define SWAP(a,b) ( t=a, a=b, b=t )
511
512 int* iw;
513
514 int js, k, imx, i, j, ki;
515 double t, amx, yk;
516
517/* Pointers to beginnings of rows in n x n matrix A */
518 double *ak, /* row k */
519 *ai, /* row i */
520 *aimx; /* row imx */
521
522
523/* Validate n. */
524 if ( n < 1 ) return -1;
525
526/* Workspace. */
527 iw = (int*) malloc ( n * sizeof(int) );
528 if ( ! iw ) return -2;
529
530/* Preset status to fail. */
531 js = -3;
532
533/* Find the inverse of A and the solution x. */
534 for ( k = 0, ak = a; k < n; k++, ak += n ) {
535 amx = fabs ( ak[k] );
536 imx = k;
537 aimx = ak;
538 if ( k != n ) {
539 for ( i = k+1, ai = ak+n; i < n; i++, ai += n ) {
540 t = fabs ( ai[k] );
541 if ( t > amx ) {
542 amx = t;
543 imx = i;
544 aimx = ai;
545 }
546 }
547 }
548 if ( amx < TINY ) goto free;
549 if ( imx != k ) {
550 for ( j = 0; j < n; j++ ) {
551 SWAP ( ak[j], aimx[j] );
552 }
553 SWAP ( y[k], y[imx] );
554 }
555 iw[k] = imx;
556 ak[k] = 1.0 / ak[k];
557 for ( j = 0; j < n; j++ ) {
558 if ( j != k ) {
559 ak[j] *= ak[k];
560 }
561 }
562 yk = y[k] * ak[k];
563 y[k] = yk;
564 for ( i = 0, ai = a; i < n; i++, ai += n ) {
565 if ( i != k ) {
566 for ( j = 0; j < n; j++ ) {
567 if ( j != k ) {
568 ai[j] -= ai[k] * ak[j];
569 }
570 }
571 y[i] -= ai[k] * yk;
572 }
573 }
574 for ( i = 0, ai = a; i < n; i++, ai += n ) {
575 if ( i != k ) {
576 ai[k] *= - ak[k];
577 }
578 }
579 }
580 for ( k = n; k-- > 0; ) {
581 ki = iw[k];
582 if ( k != ki ) {
583 for ( i = 0, ai = a; i < n; i++, ai += n ) {
584 SWAP ( ai[k], ai[ki] );
585 }
586 }
587 }
588
589/* Success. */
590 js = 0;
591
592/* Discard the workspace. */
593free:
594 free ( iw );
595
596/* Return the status. */
597 return js;
598}
599
600/*----------------------------------------------------------------------
601**
602** Copyright (C) 2025 by P.T.Wallace
603**
604** Permission to use, copy, modify, and/or distribute this software for
605** any purpose with or without fee is hereby granted.
606**
607** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
608** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
609** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
610** AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
611** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
612** OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
613** NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
614** CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
615**
616**--------------------------------------------------------------------*/
int Simeqn(int, double *, double *)
Definition pmfit.c:461
int Pmfit(double phi, char mount, int n, double *obs, int nt, double *pm, double *sigmas, double *skysig)
Definition pmfit.c:10
int Bfun(int, double, char, double, double, double *)
Definition pmfit.c:267
#define BF(i, j)
#define SWAP(a, b)
#define TINY
#define iauAe2hd
Definition sofa.h:56
#define iauHd2ae
Definition sofa.h:55
#define iauAnpm
Definition sofa.h:39