IIINSTRUMENT Pipeline Reference Manual 4.6.1
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
64enum 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
114typedef enum visir_spc_mode_ visir_spc_mode;
115
116struct visir_optmod_private_ {
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
156typedef struct visir_optmod_private_ visir_optmod_private;
157
158/*-----------------------------------------------------------------------------
159 Functions prototypes
160 -----------------------------------------------------------------------------*/
161
162static double visir_spc_optmod_krs5(double);
163static void visir_spc_optmod_scan_angle(visir_optmod_private *);
164
167/*-----------------------------------------------------------------------------
168 Function codes
169 -----------------------------------------------------------------------------*/
170
171/*----------------------------------------------------------------------------*/
185/*----------------------------------------------------------------------------*/
186int visir_spc_optmod_init(visir_spc_resol resol, double wlen,
187 visir_optmod * pins, int is_aqu)
188{
189
190 visir_optmod_private * self = (visir_optmod_private*)pins;
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/*----------------------------------------------------------------------------*/
861double 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/*----------------------------------------------------------------------------*/
908double 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/*----------------------------------------------------------------------------*/
961double 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/*----------------------------------------------------------------------------*/
1000int 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/*----------------------------------------------------------------------------*/
1029int 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
1056double 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
1083double 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/*----------------------------------------------------------------------------*/
1105static 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/*----------------------------------------------------------------------------*/
1139static 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}