VISIR Pipeline Reference Manual  4.1.0
visir_spc_optmod.c
1 /* $Id: visir_spc_optmod.c,v 1.25 2012-08-03 01:55:22 llundin Exp $
2  *
3  * This file is part of the VISIR Pipeline
4  * Copyright (C) 2002,2003 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: llundin $
23  * $Date: 2012-08-03 01:55:22 $
24  * $Revision: 1.25 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 /*-----------------------------------------------------------------------------
33  Includes
34  -----------------------------------------------------------------------------*/
35 
36 #include "visir_spc_optmod.h"
37 
38 #include <stddef.h>
39 #include <math.h>
40 #include <assert.h>
41 
42 /*----------------------------------------------------------------------------*/
58 /*----------------------------------------------------------------------------*/
59 
60 /*-----------------------------------------------------------------------------
61  Type definition
62  -----------------------------------------------------------------------------*/
63 
64 enum visir_spc_mode_ {
65  VISIR_SPC_M_ERR = 0,
66  VISIR_SPC_M_LSWN,
67  VISIR_SPC_M_LLWN,
68  VISIR_SPC_M_LSWQ,
69  VISIR_SPC_M_LLWQ,
70  VISIR_SPC_M_LRP,
71  VISIR_SPC_M_MSWN,
72  VISIR_SPC_M_MLWN,
73  VISIR_SPC_M_MSWQ,
74  VISIR_SPC_M_MLWQ,
75  VISIR_SPC_M_GHR01,
76  VISIR_SPC_M_GHR02,
77  VISIR_SPC_M_GHR03,
78  VISIR_SPC_M_GHR04,
79  VISIR_SPC_M_GHR05,
80  VISIR_SPC_M_GHR06,
81  VISIR_SPC_M_GHR07,
82  VISIR_SPC_M_GHR08,
83  VISIR_SPC_M_GHR09,
84  VISIR_SPC_M_GHR10,
85  VISIR_SPC_M_GHR11,
86  VISIR_SPC_M_GHR12,
87  VISIR_SPC_M_GHR13,
88  VISIR_SPC_M_GHR14,
89  VISIR_SPC_M_GHR15,
90  VISIR_SPC_M_GHR16,
91  VISIR_SPC_M_GHR17,
92  VISIR_SPC_M_GHR18,
93  VISIR_SPC_M_GHR19,
94  VISIR_SPC_M_GHR20,
95  VISIR_SPC_M_GHR21,
96  VISIR_SPC_M_GHR22,
97  VISIR_SPC_M_GHR23,
98  VISIR_SPC_M_GHR24,
99  VISIR_SPC_M_GHR25,
100  VISIR_SPC_M_GHR26,
101  VISIR_SPC_M_GHR27,
102  VISIR_SPC_M_HR01,
103  VISIR_SPC_M_HR02,
104  VISIR_SPC_M_HR03,
105  VISIR_SPC_M_HR04,
106  VISIR_SPC_M_HR05,
107  VISIR_SPC_M_HR06,
108  VISIR_SPC_M_HR07,
109  VISIR_SPC_M_HR08,
110  VISIR_SPC_M_HR09,
111  VISIR_SPC_M_HR10
112 };
113 
114 typedef enum visir_spc_mode_ visir_spc_mode;
115 
117  /* The Central Wavelength of the settings */
118  double wlen;
119 
120  /* Angles are in radians */
121  double angle_a; /* Angle in 1st term */
122  double angle_b0; /* Angle in 2nd term, center of 1st pixel */
123  double angle_bm; /* Angle in 2nd term, center of spectral range */
124  double angle_b1; /* Angle in 2nd term, center of last pixel */
125 
126  double angle_scan; /* Scan-angle */
127  double sinus_sum; /* sin(a + scan-angle) + sin(b + scan-angle) */
128 
129  /* The unit of d has to be equal to that of the wavelength */
130  double d; /* Echelle groove spacing @30K [m] */
131 
132  /* These settings are only defined in High Resolution Grism mode, i.e. when
133  mode == VISIR_SPC_R_GHR */
134  double gg; /* grism groove spacing @ 30K [m] */
135  double w; /* grism wedge [radians] */
136  double offset; /* Parameter for grism cross-dispersion */
137  double factor; /* Parameter for grism cross-dispersion */
138 
139  double dcolbeam; /* Diameter of collimated beam */
140  double ld; /* The (unit-less) Linear Dispersion factor */
141 
142  /* The spectroscopy resolution corresponding to the settings */
143  visir_spc_resol resolution;
144 
145  /* The spectroscopy mode corresponding to the settings */
146  visir_spc_mode mode;
147 
148  int m; /* Echelle order (1, 2, ..., 18) */
149 
150  /* These settings are only defined in High Resolution, i.e. when
151  mode == VISIR_SPC_R_GHR || mode == VISIR_SPC_R_HR */
152  int side_is_A; /* 1 if side is 'A', 0 if side is 'B' */
153 
154 };
155 
157 
158 /*-----------------------------------------------------------------------------
159  Functions prototypes
160  -----------------------------------------------------------------------------*/
161 
162 static double visir_spc_optmod_krs5(double);
163 static void visir_spc_optmod_scan_angle(visir_optmod_private *);
164 
167 /*-----------------------------------------------------------------------------
168  Function codes
169  -----------------------------------------------------------------------------*/
170 
171 /*----------------------------------------------------------------------------*/
185 /*----------------------------------------------------------------------------*/
186 int visir_spc_optmod_init(visir_spc_resol resol, double wlen,
187  visir_optmod * pins, int is_aqu)
188 {
189 
191 
192  /* The angles are coded in degrees and then converted to radians */
193  const double rad_per_deg = atan(1)/45;
194 
195  visir_spc_mode mode = VISIR_SPC_M_ERR;
196 
197 
198  /* Ensure that the public struct has the proper size */
199  assert( sizeof(visir_optmod_private) == sizeof(visir_optmod) );
200 
201  if (self == NULL) return -1;
202 
203  /* Switch on the resolution
204  Constants here are coded in micron - convert to m */
205  wlen *= 1e6;
206  switch (resol) {
207  case VISIR_SPC_R_LRP:
208  {
209  mode = VISIR_SPC_M_LRP;
210  wlen = VISIR_SPC_LRP_CWLEN;
211  break;
212  }
213 
214  case VISIR_SPC_R_LR:
215  {
216  if ( 7.5 <= wlen && wlen <= 10.2) mode = VISIR_SPC_M_LSWN;
217  else if (10.2 < wlen && wlen <= 14.5) mode = VISIR_SPC_M_LLWN;
218  else if (15.0 <= wlen && wlen <= 20.4) mode = VISIR_SPC_M_LSWQ;
219  else if (20.4 < wlen && wlen <= 28.0) mode = VISIR_SPC_M_LLWQ;
220  break;
221  }
222 
223  case VISIR_SPC_R_MR:
224  {
225  if ( 7.5 <= wlen && wlen <= 10.2) mode = VISIR_SPC_M_MSWN;
226  else if (10.2 < wlen && wlen <= 14.0) mode = VISIR_SPC_M_MLWN;
227  else if (15.0 <= wlen && wlen <= 20.4) mode = VISIR_SPC_M_MSWQ;
228  else if (20.4 < wlen && wlen <= 28.0) mode = VISIR_SPC_M_MLWQ;
229  break;
230  }
231 
232  case VISIR_SPC_R_GHR:
233  {
234  if ( 7.6 <= wlen && wlen <= 7.8 ) mode = VISIR_SPC_M_GHR01;
235  else if ( 7.8 < wlen && wlen <= 8.03) mode = VISIR_SPC_M_GHR02;
236  else if ( 8.03 < wlen && wlen <= 8.26) mode = VISIR_SPC_M_GHR03;
237  else if ( 8.26 < wlen && wlen <= 8.52) mode = VISIR_SPC_M_GHR04;
238  else if ( 8.52 < wlen && wlen <= 8.78) mode = VISIR_SPC_M_GHR05;
239  else if ( 8.78 < wlen && wlen <= 9.07) mode = VISIR_SPC_M_GHR06;
240  else if ( 9.07 < wlen && wlen <= 9.36) mode = VISIR_SPC_M_GHR07;
241  else if ( 9.36 < wlen && wlen <= 9.69) mode = VISIR_SPC_M_GHR08;
242  else if ( 9.69 < wlen && wlen <= 10.03) mode = VISIR_SPC_M_GHR09;
243  else if (10.03 < wlen && wlen <= 10.20) mode = VISIR_SPC_M_GHR10;
244  else if (10.2 < wlen && wlen <= 10.41) mode = VISIR_SPC_M_GHR11;
245  else if (10.41 < wlen && wlen <= 10.80) mode = VISIR_SPC_M_GHR12;
246  else if (10.80 < wlen && wlen <= 11.24) mode = VISIR_SPC_M_GHR13;
247  else if (11.24 < wlen && wlen <= 11.70) mode = VISIR_SPC_M_GHR14;
248  else if (11.70 < wlen && wlen <= 12.21) mode = VISIR_SPC_M_GHR15;
249  else if (12.21 < wlen && wlen <= 12.76) mode = VISIR_SPC_M_GHR16;
250  else if (12.76 < wlen && wlen <= 13.37) mode = VISIR_SPC_M_GHR17;
251  else if (13.37 < wlen && wlen <= 14.04) mode = VISIR_SPC_M_GHR18;
252  else if (14.04 < wlen && wlen <= 14.77) mode = VISIR_SPC_M_GHR19;
253  else if (15.60 < wlen && wlen <= 16.49) mode = VISIR_SPC_M_GHR20;
254  else if (16.49 < wlen && wlen <= 17.55) mode = VISIR_SPC_M_GHR21;
255  else if (17.55 < wlen && wlen <= 18.67) mode = VISIR_SPC_M_GHR22;
256  else if (18.67 < wlen && wlen <= 20.06) mode = VISIR_SPC_M_GHR23;
257  else if (20.06 < wlen && wlen <= 21.49) mode = VISIR_SPC_M_GHR24;
258  else if (21.49 < wlen && wlen <= 23.40) mode = VISIR_SPC_M_GHR25;
259  else if (23.40 < wlen && wlen <= 25.32) mode = VISIR_SPC_M_GHR26;
260  else if (25.32 < wlen && wlen <= 28.08) mode = VISIR_SPC_M_GHR27;
261  break;
262  }
263  case VISIR_SPC_R_HR:
264  {
265  if ( 7.97 <= wlen && wlen <= 8.27) mode = VISIR_SPC_M_HR01;
266  else if ( 8.83 <= wlen && wlen <= 9.05) mode = VISIR_SPC_M_HR02;
267  else if ( 9.52 <= wlen && wlen <= 9.72) mode = VISIR_SPC_M_HR03;
268  /* FIXME: VISIR_SPC_M_HR04 really goes up to 12.21 */
269  else if (11.85 <= wlen && wlen < 12.19) mode = VISIR_SPC_M_HR04;
270  else if (12.19 <= wlen && wlen <= 12.37) mode = VISIR_SPC_M_HR05;
271  else if (12.37 < wlen && wlen <= 12.71) mode = VISIR_SPC_M_HR06;
272  else if (12.71 < wlen && wlen <= 12.91) mode = VISIR_SPC_M_HR07;
273  else if (16.80 <= wlen && wlen <= 17.20) mode = VISIR_SPC_M_HR08;
274  else if (18.32 <= wlen && wlen <= 18.67) mode = VISIR_SPC_M_HR09;
275  else if (18.67 < wlen && wlen <= 19.18) mode = VISIR_SPC_M_HR10;
276  break;
277  }
278  default:;
279  }
280  wlen *= 1e-6;
281 
282  if (mode == VISIR_SPC_M_ERR) return -2;
283 
284  self->resolution = resol;
285  self->mode = mode;
286  self->wlen = wlen;
287  self->m = 0;
288 
289  self->angle_a = 0;
290  self->angle_b0 = 0;
291  self->angle_bm = 0;
292  self->angle_b1 = 0;
293 
294  /* Switch on the resolution */
295  switch (resol) {
296  case VISIR_SPC_R_LRP:
297  {
298  break;
299  }
300  case VISIR_SPC_R_LR:
301  {
302  self->angle_a = 6.708;
303  self->angle_bm = 1.291;
304  self->angle_b0 = 0.586;
305  self->angle_b1 = 2.0;
306  self->dcolbeam = 53000; /* [micron] */
307  self->ld = 10332;
308 
309  /* Switch on the (LR)-mode */
310  switch (mode) {
311  case VISIR_SPC_M_LSWN:
312  {
313  self->d = 129.162;
314  self->m = 2;
315  break;
316  }
317  case VISIR_SPC_M_LLWN:
318  {
319  self->d = 172.308;
320  self->m = 2;
321  break;
322  }
323  case VISIR_SPC_M_LSWQ:
324  {
325  self->d = 129.162;
326  self->m = 1;
327  break;
328  }
329  case VISIR_SPC_M_LLWQ:
330  {
331  self->d = 172.308;
332  self->m = 1;
333  break;
334  }
335  default:;
336  }
337  break;
338  }
339  case VISIR_SPC_R_MR:
340  {
341  self->angle_a = 34.208;
342  self->angle_bm = 28.791;
343  self->angle_b0 = 28.086;
344  self->angle_b1 = 29.500;
345  self->dcolbeam = 53000; /* [micron] */
346  self->ld = 10332;
347 
348  /* Switch on the (MR)-mode */
349  switch (mode) {
350  case VISIR_SPC_M_MSWN:
351  {
352  self->d = 17.1478;
353  self->m = 2;
354  break;
355  }
356  case VISIR_SPC_M_MLWN:
357  {
358  self->d = 22.9560;
359  self->m = 2;
360  break;
361  }
362  case VISIR_SPC_M_MSWQ:
363  {
364  self->d = 17.1478;
365  self->m = 1;
366  break;
367  }
368  case VISIR_SPC_M_MLWQ:
369  {
370  self->d = 22.9560;
371  self->m = 1;
372  break;
373  }
374  default:;
375  }
376  break;
377  }
378  case VISIR_SPC_R_GHR:
379  {
380  /* Switch on the (HR Grism)-mode */
381  self->side_is_A = 0;
382  self->dcolbeam = 125000; /* [micron] */
383  self->ld = 23403;
384 
385  switch (mode) {
386  case VISIR_SPC_M_GHR01:
387  {
388  self->d=77.16526;
389  self->m=18;
390  self->gg=36.8906;
391  self->w=9.8;
392  self->offset=166.9;
393  self->factor=2940;
394  break;
395  }
396 
397  case VISIR_SPC_M_GHR02:
398  {
399  self->side_is_A = 1;
400  self->d=79.93104;
401  self->m=18;
402  self->gg=36.8906;
403  self->w=9.8;
404  self->offset=178;
405  self->factor=2940;
406  break;
407  }
408 
409  case VISIR_SPC_M_GHR03:
410  {
411  self->d=77.16526;
412  self->m=17;
413  self->gg=36.8906;
414  self->w=9.8;
415  self->offset=166.9;
416  self->factor=2940;
417  break;
418  }
419 
420  case VISIR_SPC_M_GHR04:
421  {
422  self->side_is_A = 1;
423  self->d=79.93104;
424  self->m=17;
425  self->gg=36.8906;
426  self->w=9.8;
427  self->offset=178;
428  self->factor=2940;
429  break;
430  }
431 
432  case VISIR_SPC_M_GHR05:
433  {
434  self->d=77.16526;
435  self->m=16;
436  self->gg=36.8906;
437  self->w=9.8;
438  self->offset=166.9;
439  self->factor=2940;
440  break;
441  }
442 
443  case VISIR_SPC_M_GHR06:
444  {
445  self->side_is_A = 1;
446  self->d=79.93104;
447  self->m=16;
448  self->gg=36.8906;
449  self->w=9.8;
450  self->offset=178;
451  self->factor=2940;
452  break;
453  }
454 
455  case VISIR_SPC_M_GHR07:
456  {
457  self->d=77.16526;
458  self->m=15;
459  self->gg=36.8906;
460  self->w=9.8;
461  self->offset=166.9;
462  self->factor=2940;
463  break;
464  }
465 
466  case VISIR_SPC_M_GHR08:
467  {
468  self->side_is_A = 1;
469  self->d=79.93104;
470  self->m=15;
471  self->gg=36.8906;
472  self->w=9.8;
473  self->offset=178;
474  self->factor=2940;
475  break;
476  }
477 
478  case VISIR_SPC_M_GHR09:
479  {
480  self->d=77.16526;
481  self->m=14;
482  self->gg=36.8906;
483  self->w=9.8;
484  self->offset=166.9;
485  self->factor=2940;
486  break;
487  }
488 
489  case VISIR_SPC_M_GHR10:
490  {
491  self->side_is_A = 1;
492  self->d=79.93104;
493  self->m=14;
494  self->gg=36.8906;
495  self->w=9.8;
496  self->offset=178;
497  self->factor=2940;
498  break;
499  }
500 
501  case VISIR_SPC_M_GHR11:
502  {
503  self->side_is_A = 1;
504  self->d=79.93104;
505  self->m=14;
506  self->gg=63.5470;
507  self->w=7.6;
508  self->offset=143.1;
509  self->factor=3004;
510  break;
511  }
512 
513  case VISIR_SPC_M_GHR12:
514  {
515  self->d=77.16526;
516  self->m=13;
517  self->gg=63.5470;
518  self->w=7.6;
519  self->offset=131.6;
520  self->factor=3004;
521  break;
522  }
523 
524  case VISIR_SPC_M_GHR13:
525  {
526  self->side_is_A = 1;
527  self->d=79.93104;
528  self->m=13;
529  self->gg=63.5470;
530  self->w=7.6;
531  self->offset=143.1;
532  self->factor=3004;
533  break;
534  }
535 
536  case VISIR_SPC_M_GHR14:
537  {
538  self->d=77.16526;
539  self->m=12;
540  self->gg=63.5470;
541  self->w=7.6;
542  self->offset=131.6;
543  self->factor=3004;
544  break;
545  }
546 
547  case VISIR_SPC_M_GHR15:
548  {
549  self->side_is_A = 1;
550  self->d=79.93104;
551  self->m=12;
552  self->gg=63.5470;
553  self->w=7.6;
554  self->offset=143.1;
555  self->factor=3004;
556  break;
557  }
558 
559  case VISIR_SPC_M_GHR16:
560  {
561  self->d=77.16526;
562  self->m=11;
563  self->gg=63.5470;
564  self->w=7.6;
565  self->offset=131.6;
566  self->factor=3004;
567  break;
568  }
569 
570  case VISIR_SPC_M_GHR17:
571  {
572  self->side_is_A = 1;
573  self->d=79.93104;
574  self->m=11;
575  self->gg=63.5470;
576  self->w=7.6;
577  self->offset=143.1;
578  self->factor=3004;
579  break;
580  }
581 
582  case VISIR_SPC_M_GHR18:
583  {
584  self->d=77.16526;
585  self->m=10;
586  self->gg=63.5470;
587  self->w=7.6;
588  self->offset=131.6;
589  self->factor=3004;
590  break;
591  }
592 
593  case VISIR_SPC_M_GHR19:
594  {
595  self->side_is_A = 1;
596  self->d=79.93104;
597  self->m=10;
598  self->gg=63.5470;
599  self->w=7.6;
600  self->offset=143.1;
601  self->factor=3004;
602  break;
603  }
604 
605  case VISIR_SPC_M_GHR20:
606  {
607  self->side_is_A = 1;
608  self->d=79.93104;
609  self->m=9;
610  self->gg=217.8772;
611  self->w=4.1;
612  self->offset=120.3;
613  self->factor=2980;
614  break;
615  }
616 
617  case VISIR_SPC_M_GHR21:
618  {
619  self->d=77.16526;
620  self->m=8;
621  self->gg=217.8772;
622  self->w=4.1;
623  self->offset=108.7;
624  self->factor=2980;
625  break;
626  }
627 
628  case VISIR_SPC_M_GHR22:
629  {
630  self->side_is_A = 1;
631  self->d=79.93104;
632  self->m=8;
633  self->gg=217.8772;
634  self->w=4.1;
635  self->offset=120.3;
636  self->factor=2980;
637  break;
638  }
639 
640  case VISIR_SPC_M_GHR23:
641  {
642  self->d=77.16526;
643  self->m=7;
644  self->gg=217.8772;
645  self->w=4.1;
646  self->offset=108.7;
647  self->factor=2980;
648  break;
649  }
650 
651  case VISIR_SPC_M_GHR24:
652  {
653  self->side_is_A = 1;
654  self->d=79.93104;
655  self->m=7;
656  self->gg=217.8772;
657  self->w=4.1;
658  self->offset=120.3;
659  self->factor=2980;
660  break;
661  }
662 
663  case VISIR_SPC_M_GHR25:
664  {
665  self->d=77.16526;
666  self->m=6;
667  self->gg=217.8772;
668  self->w=4.1;
669  self->offset=108.7;
670  self->factor=2980;
671  break;
672  }
673 
674  case VISIR_SPC_M_GHR26:
675  {
676  self->side_is_A = 1;
677  self->d=79.93104;
678  self->m=6;
679  self->gg=217.8772;
680  self->w=4.1;
681  self->offset=120.3;
682  self->factor=2980;
683  break;
684  }
685 
686  case VISIR_SPC_M_GHR27:
687  {
688  self->d=77.16526;
689  self->m=5;
690  self->gg=217.8772;
691  self->w=4.1;
692  self->offset=108.7;
693  self->factor=2980;
694  break;
695  }
696  default:;
697  }
698 
699  if (self->side_is_A) {
700  /* side 'A' */
701  self->angle_a = 62.1299;
702  self->angle_bm = 64.8519;
703  self->angle_b0 = 64.5393;
704  self->angle_b1 = 65.1641;
705 
706  } else {
707  /* side 'B' */
708  self->angle_a = 64.8701;
709  self->angle_bm = 62.1483;
710  self->angle_b0 = 62.4609;
711  self->angle_b1 = 61.8361;
712  }
713  self->w *= rad_per_deg;
714 
715  /* Length constants are coded in micron - convert to m */
716  self->gg *= 1e-6;
717  break;
718  }
719  case VISIR_SPC_R_HR:
720  {
721  /* Switch on the (HR Long Slit)-mode */
722  self->side_is_A = 0;
723  self->dcolbeam = 125000; /* [micron] */
724  self->ld = 23403;
725 
726  switch (mode) {
727  case VISIR_SPC_M_HR01:
728  {
729  self->m=17;
730  break;
731  }
732  case VISIR_SPC_M_HR02:
733  {
734  self->side_is_A = 1;
735  self->m=16;
736  break;
737  }
738  case VISIR_SPC_M_HR03:
739  {
740  self->side_is_A = 1;
741  self->m=15;
742  break;
743  }
744  case VISIR_SPC_M_HR04:
745  {
746  self->side_is_A = 1;
747  self->m=12;
748  break;
749  }
750  case VISIR_SPC_M_HR05:
751  {
752  self->m=11;
753  break;
754  }
755  case VISIR_SPC_M_HR06:
756  {
757  self->m=11;
758  break;
759  }
760  case VISIR_SPC_M_HR07:
761  {
762  self->side_is_A = 1;
763  self->m=11;
764  break;
765  }
766  case VISIR_SPC_M_HR08:
767  {
768  self->m=8;
769  break;
770  }
771  case VISIR_SPC_M_HR09:
772  {
773  self->side_is_A = 1;
774  self->m=8;
775  break;
776  }
777  case VISIR_SPC_M_HR10:
778  {
779  self->m=7;
780  break;
781  }
782  default:;
783  }
784  if (self->side_is_A) {
785  /* side 'A' */
786  self->d=79.93104;
787  self->angle_a = 62.1299;
788  self->angle_bm = 64.8519;
789  self->angle_b0 = 64.5393;
790  self->angle_b1 = 65.1641;
791  } else {
792  /* side 'B' */
793  self->d=77.16526;
794  self->angle_a = 64.8701;
795  self->angle_bm = 62.1483;
796  self->angle_b0 = 62.4609;
797  self->angle_b1 = 61.8361;
798  }
799  break;
800  }
801  default:;
802  }
803 
804  if (is_aqu && (resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR)) {
805  /* adapt VLT-TRE-VIS-14321-5046 measurements to new detector size and
806  * pfov */
807  self->factor *= 0.127 / 0.0757;
808  self->ld /= 0.127 / 0.0757;
809  /* drs: b0 at pixel 1, bm at 128.5, b1 at 256 with pfov 0.127
810  * aqu: bm at 512.5, b1 at 1024 with pfov 0.757
811  * => d = 128.5 / tan(b1 - bm) -> (b1 - bm)_new = atan((512.5 / 1.6777) / d) */
812  if (self->angle_b0 < self->angle_bm) { /* A side */
813  self->angle_b0 = self->angle_bm - (self->angle_bm - self->angle_b0) * 2.377;
814  self->angle_b1 = self->angle_bm + (self->angle_b1 - self->angle_bm) * 2.377;
815  }
816  else { /* B side */
817  self->angle_b0 = self->angle_bm + (self->angle_b0 - self->angle_bm) * 2.377;
818  self->angle_b1 = self->angle_bm - (self->angle_bm - self->angle_b1) * 2.377;
819  }
820  self->offset += 162;
821  }
822 
823  if (resol != VISIR_SPC_R_LRP) {
824  self->angle_a *= rad_per_deg;
825  self->angle_b0 *= rad_per_deg;
826  self->angle_bm *= rad_per_deg;
827  self->angle_b1 *= rad_per_deg;
828 
829  /* Length constants are coded in micron - convert to m */
830  self->d *= 1e-6;
831  self->dcolbeam *= 1e-6;
832 
833  assert( self->m > 0);
834 
835  visir_spc_optmod_scan_angle(self);
836 
837  self->sinus_sum = sin(self->angle_a + self->angle_scan)
838  + sin(self->angle_bm + self->angle_scan);
839  }
840 
841  return 0;
842 
843 }
844 
845 
846 /*----------------------------------------------------------------------------*/
860 /*----------------------------------------------------------------------------*/
861 double visir_spc_optmod_wlen(const visir_optmod * pins, double * pwl0,
862  double * pwl1)
863 {
864 
865  const visir_optmod_private * self = (const visir_optmod_private*)pins;
866 
867  if (self == NULL) return -1;
868 
869  if (self->mode == VISIR_SPC_M_LRP) {
870  if (pwl0) *pwl0 = VISIR_SPC_LRP_WLEN0;
871  if (pwl1) *pwl1 = VISIR_SPC_LRP_WLEN1;
872  return VISIR_SPC_LRP_CWLEN;
873  }
874 
875  /* The wavelength on the center of the 1st pixel */
876  if (pwl0) *pwl0 = self->d/self->m*( sin(self->angle_a + self->angle_scan)
877  + sin(self->angle_b0 + self->angle_scan));
878 
879  /* The wavelength on the center of the last pixel */
880  if (pwl1) *pwl1 = self->d/self->m*( sin(self->angle_a + self->angle_scan)
881  + sin(self->angle_b1 + self->angle_scan));
882 
883  /* The wavelength on the detector-center according to the model */
884  return self->d / self->m * self->sinus_sum;
885 
886 }
887 
888 /*----------------------------------------------------------------------------*/
907 /*----------------------------------------------------------------------------*/
908 double visir_spc_optmod_cross_dispersion(const visir_optmod * pins, double wlen)
909 {
910 
911  double sinbeta;
912  double rf_index;
913  const visir_optmod_private * self = (const visir_optmod_private*)pins;
914 
915  if (self == NULL) return -1;
916 
917  if (self->resolution != VISIR_SPC_R_GHR) return -2;
918  if (wlen <= 0) return -3;
919 
920  assert( self->gg != 0 );
921 
922  rf_index = visir_spc_optmod_krs5(wlen);
923 
924  if (rf_index < 1) return -8;
925 
926  sinbeta = sin(self->w) * rf_index - wlen / self->gg;
927 
928  /* Correct any rounding errors */
929  if (sinbeta < -1) return -9;
930  if (sinbeta > 1) return -10;
931 
932  return self->offset + self->factor * tan(asin(sinbeta) - self->w);
933 
934 }
935 
936 
937 /*----------------------------------------------------------------------------*/
960 /*----------------------------------------------------------------------------*/
961 double visir_spc_optmod_echelle(const visir_optmod * pins, double wlen,
962  int ioffset)
963 {
964 
965  int order;
966  const visir_optmod_private * self = (const visir_optmod_private*)pins;
967 
968  if (self == NULL) return -1;
969  if (self->resolution != VISIR_SPC_R_GHR) return -2;
970  if (wlen <= 0) return -3;
971  if (ioffset < -4) return -4;
972  if (ioffset > 4) return -5;
973 
974  order = ioffset + self->m;
975 
976  /* There are 18 echelle orders */
977  if (order < 1) return -6;
978  if (order > 18) return -7;
979 
980  return wlen * self->m / (double) order;
981 
982 }
983 
984 /*----------------------------------------------------------------------------*/
999 /*----------------------------------------------------------------------------*/
1000 int visir_spc_optmod_side_is_A(const visir_optmod * pins)
1001 {
1002  const visir_optmod_private * self = (const visir_optmod_private*)pins;
1003 
1004  if (self == NULL) return -1;
1005 
1006  if (self->resolution != VISIR_SPC_R_GHR &&
1007  self->resolution != VISIR_SPC_R_HR) return -2;
1008 
1009  return self->side_is_A;
1010 
1011 }
1012 
1013 /*----------------------------------------------------------------------------*/
1028 /*----------------------------------------------------------------------------*/
1029 int visir_spc_optmod_get_echelle_order(const visir_optmod * pins)
1030 {
1031  const visir_optmod_private * self = (const visir_optmod_private*)pins;
1032 
1033  if (self == NULL) return -1;
1034 
1035  if (self->resolution != VISIR_SPC_R_GHR &&
1036  self->resolution != VISIR_SPC_R_HR) return -2;
1037 
1038  return self->m;
1039 }
1040 
1041 
1042 /*----------------------------------------------------------------------------*/
1054 /*----------------------------------------------------------------------------*/
1055 
1056 double visir_spc_optmod_resolution(const visir_optmod * pins)
1057 {
1058  const visir_optmod_private * self = (const visir_optmod_private*)pins;
1059 
1060  if (self == NULL) return -1;
1061 
1062  return self->mode == VISIR_SPC_M_LRP
1063  ? VISIR_SPC_LRP_RESOL
1064  : self->dcolbeam * self->sinus_sum
1065  / (2.0 * self->wlen * cos(self->angle_a + self->angle_scan));
1066 }
1067 
1068 /*----------------------------------------------------------------------------*/
1081 /*----------------------------------------------------------------------------*/
1082 
1083 double visir_spc_optmod_dispersion(const visir_optmod * pins)
1084 {
1085  const visir_optmod_private * self = (const visir_optmod_private*)pins;
1086 
1087  if (self == NULL) return -1;
1088 
1089  return self->mode == VISIR_SPC_M_LRP
1090  ? VISIR_SPC_LRP_RESOL_DISP
1091  : self->ld * self->sinus_sum
1092  / (self->wlen * cos(self->angle_bm + self->angle_scan));
1093 }
1094 
1097 /*----------------------------------------------------------------------------*/
1104 /*----------------------------------------------------------------------------*/
1105 static double visir_spc_optmod_krs5(double wlen) {
1106 
1107  const double a0 = 5.96032159;
1108  const double a1 = -5.36135205e-4;
1109  const double a2 = 1.77047634;
1110  const double a3 = -2.79310980e1;
1111  const double a4 = -1.28684883;
1112  const double a5 = -4.34541795e-2;
1113 
1114  double n2 = 0;
1115 
1116 
1117  assert( wlen > 0 );
1118 
1119  /* The above constants are for a wavelength in microns */
1120  wlen *= 1e6;
1121 
1122  wlen *= wlen;
1123 
1124  n2 = a0 + a1 * wlen + (((a5/wlen + a4)/wlen + a3)/ wlen + a2)/wlen;
1125 
1126  /* The refractive index is greater than 1 */
1127  return n2 > 1 ? sqrt(n2) : -1;
1128 
1129 }
1130 
1131 /*----------------------------------------------------------------------------*/
1138 /*----------------------------------------------------------------------------*/
1139 static void visir_spc_optmod_scan_angle(visir_optmod_private * self)
1140 {
1141 
1142  /* Determine the Scan-angle from Mode and Central Wavelength */
1143 
1144  const double mld = self->m * self->wlen / self->d;
1145  const double sab = sin(self->angle_bm) + sin(self->angle_a);
1146  const double cab = cos(self->angle_bm) + cos(self->angle_a);
1147  const double A = sab * sab + cab * cab;
1148  /*
1149  const double B = -2 * mld * cab;
1150  */
1151  const double C = mld * mld - sab * sab;
1152 
1153  double D = A - mld * mld; /* D = B^2 - 4 * A * C */
1154  double u1, u2;
1155 
1156  /* D can only become negative due to rounding errors */
1157  D = D > 0 ? sqrt(D) : 0;
1158  D *= fabs(sab);
1159 
1160  /* A, cab, mld & D are all positive */
1161  u1 = (cab * mld + D) / A;
1162 
1163  /* u2 may be a lot smaller than both D/A and cab * mld/A, so subtraction
1164  of those two terms may lead to loss of precision. u2 is instead
1165  computed by use of the fact that C = A * u1 * u2 */
1166  u2 = C / A / u1;
1167 
1168  /* u1 is not a physically meaningful solution, it is just an
1169  intermediate result */
1170 
1171  self->angle_scan = asin(u2);
1172 
1173 }