include include include define LEN_UA 20000 # Maximum user header define LEN_COMMENT 70 # Maximum comment length define PTYPES "|extracted|gaussian|slit|" define EXTRACTED 1 # Extracted format define GAUSS 2 # Gaussian (pexp = 2) define SLIT 3 # Slit (pexp = 10) # T_MKECHELLE -- Make echelle spectra. # Extracted or full two dimensional formats may be created. # The spectrum may consist of a constant continuum, a blackbody continuum, # and emission and absorption lines of varying widths and strengths. # The spectral features may come from a line list or be randomly generated. # A redshift may be applied to the spectrum. The order profiles may # be either a gaussian or a slit with a specified FWHM. Both the # spectral features and the profile are subsampled. The echelle format # includes a blaze function corrected for light losses to other other # other orders and reflected components. If a focal length is specified # the wavelength nonlinearity is included. procedure t_mkechelle() int images # List of echelle spectra to be created int nc # Number of columns (across dispersion) int nl # Number of lines (along dispersion) int norders # Number of orders int profile # Profile type real width # Profile width (pixels) real scattered # Scattered light peak intensity real xc, yc # Central pixel postion real pixsize # Pixel size (mm) int mc[2] # Central order real f[2] # Focal length (mm) real gmm[2] # Grating grooves per mm real blaze[2] # Blaze angle (degrees) real t[2] # Angle from blaze angle real wc[2] # Central wavelength real disp[2] # Central dispersion real z # Redshift real cont # Continuum at central wavelength real temp # Blackbody temperture (Kelvin) int lines # List of files containing lines int nrandom # Number of spectral lines real peak # Peak/continuum real sigma # Sigma of lines (Angstroms) long seed # Random number seed real subsample # Subsampling (nxsub param) real nsigma # Dynamic range of gaussian (dynrange param) int ncnew, nlnew, nonew bool new, flag[2] int i, j, k, k1, k2, m, m1, m2, dc long seed1 real mwc, mw1, mw2, dmw, x, x1, x2, dx, w, p, s, xc1, dx1 real p1, p2, pcen, fwhm, flux, flux1 real a[2], b[2], c[2], tb[2], cb[2], tt[2], ctb[2], t2tb[2], xmin[2], xmax[2] real aplow[2], aphigh[2] double w1, dw pointer sp, image, fname, apnum, comment pointer im, mw, waves, peaks, sigmas, buf, spec, bf1, bf2, asi, data long clgetl(), clktime() int clgeti(), clgwrd(), imtopenp(), imtgetim() int nowhite(), access(), open(), fscan(), nscan() real clgetr(), urand(), asigrl() real ecx2w(), ecxdx(), ecw2x(), ecw2xr, ecdelta() pointer immap(), mw_open(), impl2r(), imgl2r() bool clgetb() errchk open(), ecgrating() begin call smark (sp) call salloc (image, SZ_FNAME, TY_CHAR) call salloc (fname, SZ_FNAME, TY_CHAR) call salloc (apnum, SZ_FNAME, TY_CHAR) call salloc (comment, LEN_COMMENT, TY_CHAR) # Get parameters. if (clgetb ("make")) images = imtopenp ("images") ncnew = clgeti ("ncols") nlnew = clgeti ("nlines") nonew = clgeti ("norders") profile = clgwrd ("profile", Memc[comment], LEN_COMMENT, PTYPES) width = clgetr ("width") scattered = clgetr ("scattered") xc = clgetr ("xc") yc = clgetr ("yc") pixsize = clgetr ("pixsize") f[1] = clgetr ("f") mc[1] = clgeti ("order") gmm[1] = clgetr ("gmm") blaze[1] = clgetr ("blaze") t[1] = clgetr ("theta") wc[1] = clgetr ("wavelength") disp[1] = clgetr ("dispersion") f[2] = clgetr ("cf") mc[2] = clgeti ("corder") gmm[2] = clgetr ("cgmm") blaze[2] = clgetr ("cblaze") t[2] = clgetr ("ctheta") wc[2] = clgetr ("cwavelength") disp[2] = clgetr ("cdispersion") z = clgetr ("rv") if (clgetb ("z")) z = 1 + z else { z = z / 299792.5 z = sqrt ((1 + z) / (1 - z)) } cont = clgetr ("continuum") temp = clgetr ("temperature") lines = imtopenp ("lines") peak = clgetr ("peak") sigma = clgetr ("sigma") seed = clgetl ("seed") if (IS_INDEFL(seed)) seed1 = seed1 + clktime (long(0)) else seed1 = seed subsample = 1. / clgeti ("nxsub") nsigma = sqrt (2. * log (clgetr ("dynrange"))) # Substitute defaults for INDEF center parameters if (IS_INDEF(xc)) xc = (ncnew - 1) / 2. if (IS_INDEF(yc)) yc = (nlnew - 1) / 2. # Derive and check grating parameters. do i = 1, 2 { if (mc[i] == 0) { if (IS_INDEF(wc[i]) || IS_INDEF(disp[i])) call error (1, "Prism wavelength parameters missing") next } if (!IS_INDEF(pixsize)) { if (!IS_INDEF(f[i])) f[i] = f[i] / pixsize if (!IS_INDEF(disp[i])) disp[i] = disp[i] * pixsize } if (i == 1) flag[i] = true else flag[i] = false iferr (call ecgrating (flag[i], f[i], gmm[i], blaze[i], t[i], mc[i], wc[i], disp[i])) { if (i == 1) call eprintf ("Echelle grating: ") else call eprintf ("Crossdisperser grating: ") if (!IS_INDEF(mc[i])&&!IS_INDEF(wc[i])&&!IS_INDEF(disp[i])) { call eprintf ("Using linear dispersion\n") call erract (EA_WARN) flag[i] = true } else { call eprintf ("\n") call erract (EA_ERROR) } } else flag[i] = false } # List grating parameters if desired. if (clgetb ("list")) call eclist (pixsize, f, gmm, blaze, t, mc, wc, disp) # If not making an image return. if (!clgetb ("make")) { call sfree (sp) return } # Loop through images. Line list files may be missing. Memc[fname] = EOS while (imtgetim (images, Memc[image], SZ_FNAME) != EOF) { i = imtgetim (lines, Memc[fname], SZ_FNAME) # Map image and check for existing images. ifnoerr (im = immap (Memc[image], READ_WRITE, LEN_UA)) { call eprintf ("%s: ") call pargstr (Memc[image]) call flush (STDERR) if (!clgetb ("clobber")) { call eprintf ("Warning: Image already exists (%s)\n") call pargstr (Memc[image]) call imunmap (im) next } new = false if (profile == EXTRACTED) { nl = IM_LEN(im,1) norders = IM_LEN(im,2) } else { nc = IM_LEN(im,1) nl = IM_LEN(im,2) } } else { iferr (im = immap (Memc[image], NEW_IMAGE, LEN_UA)) { call erract (EA_WARN) next } new = true nc = ncnew nl = nlnew norders = nonew IM_PIXTYPE(im) = TY_REAL IM_NDIM(im) = 2 if (profile == EXTRACTED) { IM_LEN(im,1) = nl IM_LEN(im,2) = norders } else { IM_LEN(im,1) = nc IM_LEN(im,2) = nl } } # Set frequently used constants. do i = 1, 2 { if (flag[i]) { f[i] = INDEF a[i] = mc[i] * wc[i] b[i] = mc[i] * disp[i] c[i] = PI * mc[i] * disp[i] } else { b[i] = 1e7 / gmm[i] a[i] = b[i] * sin (DEGTORAD(t[i])) c[i] = b[i] * PI * cos (DEGTORAD(t[i])) ctb[i] = cos (DEGTORAD(blaze[i])) t2tb[i] = tan (DEGTORAD(2 * blaze[i])) tt[i] = tan (DEGTORAD(t[i] - blaze[i])) tb[i] = tan (DEGTORAD(2 * blaze[i] - t[i])) cb[i] = cos (DEGTORAD(2 * blaze[i] - t[i])) xmin[i] = -1. / max (tt[i], f[i] / yc) xmax[i] = -1. / min (tt[i], -f[i] / yc) } } # Set orders. m1 = max (1, mc[1] - (norders-1) / 2) m2 = m1 + norders - 1 mwc = mc[1] * wc[1] mw1 = ecx2w (-yc, 1, a, b, f, cb, tb, t2tb, xmin, xmax) mw2 = ecx2w (yc, 1, a, b, f, cb, tb, t2tb, xmin, xmax) dmw = mw2 - mw1 if (mc[2] == 0) { disp[2] = disp[2] / wc[2] dx1 = 1. } else { xc1 = xc - ecw2x (mc[2]*wc[1], 2, a, b, f, tb, ctb, t2tb) dx1 = ecxdx (xc - xc1, 2, f, tb) } # For 2D images adjust orders to exclude those outside image. if (profile != EXTRACTED) { for (; m1mc[1]; m2=m2-1) { w = mw2 / m2 if (mc[2] == 0) x = (w - wc[1]) / (w * disp[2]) + xc else x = ecw2x (mc[2]*w, 2, a, b, f, tb, ctb, t2tb) + xc1 if (x > 0) break } norders = m2 - m1 + 1 } # Setup header parameters for new images. if (new) { call clgstr ("header", Memc[comment], LEN_COMMENT) iferr (call mkh_header (im, Memc[comment], true, false)) call erract (EA_WARN) call clgstr ("title", IM_TITLE(im), SZ_IMTITLE) if (profile == EXTRACTED) { mw = mw_open (NULL, 2) call mw_newsystem (mw, "multispec", 2) call mw_swtype (mw, 1, 1, "multispec", "") call mw_swtype (mw, 2, 1, "multispec", "") if (IS_INDEF(f[1])) { call mw_swattrs (mw, 1, "label", "Wavelength") call mw_swattrs (mw, 1, "units", "Angstroms") } call smw_open(mw, NULL, im) do m = m1, m2 { i = m - m1 + 1 if (IS_INDEF(f[1])) { w1 = mw1 / m dw = b[1] / m dc = 0 } else { w1 = 1. dw = 1. dc = -1 } w = mwc / m if (mc[2] == 0) x = (w - wc[1]) / (w * disp[2]) + xc else x = ecw2x (mc[2]*w, 2, a, b, f, tb, ctb, t2tb) + xc1 aplow[1] = 1 + x - width aphigh[1] = 1 + x + width aplow[2] = INDEFR aphigh[2] = INDEFR call smw_swattrs (mw, i, 1, i, m, dc, w1, dw, nl, 0D0, aplow, aphigh, "") } call smw_saveim (mw, im) call smw_close (mw) } else call imaddi (im, "DISPAXIS", 2) } # Get the line list if given or create random lines. i = nowhite (Memc[fname], Memc[fname], SZ_FNAME) if (access (Memc[fname], 0, 0) == YES) { i = open (Memc[fname], READ_ONLY, TEXT_FILE) nrandom = 0 while (fscan (i) != EOF) { call gargr (w) call gargr (p) call gargr (s) if (nscan() < 1) next if (nscan() < 3) s = sigma if (nscan() < 2) p = peak * urand (seed1) if (nrandom == 0) { j = 50 call malloc (waves, j, TY_REAL) call malloc (peaks, j, TY_REAL) call malloc (sigmas, j, TY_REAL) } else if (nrandom == j) { j = j + 10 call realloc (waves, j, TY_REAL) call realloc (peaks, j, TY_REAL) call realloc (sigmas, j, TY_REAL) } Memr[waves+nrandom] = w * z Memr[peaks+nrandom] = p / z Memr[sigmas+nrandom] = s * z nrandom = nrandom + 1 } call close (i) } else { nrandom = clgeti ("nrandom") call malloc (waves, nrandom, TY_REAL) call malloc (peaks, nrandom, TY_REAL) call malloc (sigmas, nrandom, TY_REAL) j = max (1, mc[1] - (norders-1) / 2) do i = 0, nrandom-1 { w = z * (mw1 + dmw * urand (seed1)) w = w - dmw * nint ((w - mwc) / dmw) m = j + norders * urand (seed1) Memr[waves+i] = w / m Memr[peaks+i] = peak * urand (seed1) / z Memr[sigmas+i] = sigma * z } if (nrandom > 0 && Memc[fname] != EOS) { i = open (Memc[fname], NEW_FILE, TEXT_FILE) do j = 0, nrandom-1 { call fprintf (i, "%g %g %g\n") call pargr (Memr[waves+j] / z) call pargr (Memr[peaks+j] * z) call pargr (Memr[sigmas+j] / z) } call close (i) } } # Find the absolute response of the gratings at the reference # blaze peak. flux = 1. w = wc[1] m = mc[1] do i = m - 1, 1, -1 { x = ecw2x (i*w, 1, a, b, f, tb, ctb, t2tb) if (IS_INDEF(x)) break p = ecdelta (x, w, 1, f, c, tt) flux = flux + (sin (p) / p) ** 2 if (abs (p) > 100.) break } do i = m + 1, ARB { x = ecw2x (i*w, 1, a, b, f, tb, ctb, t2tb) if (IS_INDEF(x)) break p = ecdelta (x, w, 1, f, c, tt) flux = flux + (sin (p) / p) ** 2 if (abs (p) > 100.) break } j = (a[1] + b[1]) / w do i = j, 1, -1 { x = ecw2xr (i*w, 1, a, b, f, tb, ctb, t2tb) if (IS_INDEF(x)) break p = ecdelta (x, w, 1, f, c, tt) flux = flux + (sin (p) / p) ** 2 if (abs (p) > 100.) break } if (mc[2] != 0) { x = ecw2x (mc[2]*w, 2, a, b, f, tb, ctb, t2tb) p = ecdelta (x, w, 2, f, c, tt) if (p != 0.) { p = (sin (p) / p) ** 2 if (p != 0.) flux = flux / p } } flux1 = flux # Make the 1D spectrum. call malloc (buf, norders*nl, TY_REAL) do m = m1, m2 { spec = buf + (m - m1) * nl call aclrr (Memr[spec], nl) # Make the lines. do i = 0, nrandom-1 { w = m * Memr[waves+i] p = Memr[peaks+i] * subsample dx = m * Memr[sigmas+i] x1 = max (-0.499, ecw2x (w-nsigma*dx, 1, a, b, f, tb, ctb, t2tb)+yc) x2 = min (nl-0.501, ecw2x (w+nsigma*dx, 1, a, b, f, tb, ctb, t2tb)+yc) dx = -0.5 / dx ** 2 for (x = x1; x <= x2; x = x + subsample) { s = ecx2w (x-yc, 1, a, b, f, cb, tb, t2tb, xmin, xmax) j = nint (x) Memr[spec+j] = Memr[spec+j] + p * exp (dx*(s-w)**2) } } # Initialize blackbody function. if (temp > 0.) { w = wc[1] * 1.0e-8 x = exp (1.4388 / (w * temp)) p1 = w**5 * (x-1.0) } # Compute blaze peak correction flux = 1. w = mc[1] * wc[1] / m do i = m - 1, 1, -1 { x = ecw2x (i*w, 1, a, b, f, tb, ctb, t2tb) if (IS_INDEF(x)) break p = ecdelta (x, w, 1, f, c, tt) flux = flux + (sin (p) / p) ** 2 if (abs (p) > 100.) break } do i = m + 1, ARB { x = ecw2x (i*w, 1, a, b, f, tb, ctb, t2tb) if (IS_INDEF(x)) break p = ecdelta (x, w, 1, f, c, tt) flux = flux + (sin (p) / p) ** 2 if (abs (p) > 100.) break } j = (a[1] + b[1]) / w do i = j, 1, -1 { x = ecw2xr (i*w, 1, a, b, f, tb, ctb, t2tb) if (IS_INDEF(x)) break p = ecdelta (x, w, 1, f, c, tt) flux = flux + (sin (p) / p) ** 2 if (abs (p) > 100.) break } do i = 0, nl-1 { w = ecx2w (i-yc, 1, a, b, f, cb, tb, t2tb, xmin, xmax) / m # Scale by continuum. p = cont if (temp > 0.) { x2 = w * 1e-8 x1 = exp (1.4388 / (x2 * temp)) p = p * (p1 / x2**5 / (x1-1.0)) } if (p > 0.) Memr[spec+i] = max (0., p * (1. + Memr[spec+i])) # Apply blaze functions and pixel size corrections. x = 1 / flux p = ecdelta (i-yc, w, 1, f, c, tt) if (p != 0.) x = x * (sin (p) / p) ** 2 if (mc[2] != 0) { s = ecw2x (mc[2]*w, 2, a, b, f, tb, ctb, t2tb) p = ecdelta (s, w, 2, f, c, tt) if (p != 0.) x = x * (sin (p) / p) ** 2 } dx = ecxdx (i-yc, 1, f, tb) * mc[1] / m if (mc[2] != 0) dx = dx * ecxdx (s, 2, f, tb) Memr[spec+i] = Memr[spec+i] * flux1 * x * dx / dx1 } } # Write 1D spectrum or create 2D spectrum. if (profile == EXTRACTED) { do i = 1, norders { spec = buf + (i - 1) * nl if (new) call amovr (Memr[spec], Memr[impl2r(im,i)], nl) else call aaddr (Memr[spec], Memr[imgl2r(im,i)], Memr[impl2r(im,i)], nl) } } else { # Make scattered light response. if (scattered > 0.) { call malloc (bf1, nc, TY_REAL) call malloc (bf2, nl, TY_REAL) if (mc[2] != 0) { do i = 0, nc-1 { s = i - xc1 w = ecx2w (s, 2, a, b, f, cb, tb, t2tb, xmin, xmax) / mc[2] p = ecdelta (s, w, 2, f, c, tt) if (p == 0.) Memr[bf1+i] = scattered else Memr[bf1+i] = scattered * (sin (p) / p) ** 2 } } else call amovkr (scattered, Memr[bf1], nc) s = wc[1] - disp[1] * yc do i = 0, nl - 1 { w = ecx2w (i-yc, 1, a, b, f, cb, tb, t2tb, xmin, xmax) / mc[1] p = ecdelta (i-yc, w, 1, f, c, tt) if (p == 0.) Memr[bf2+i] = 1. else Memr[bf2+i] = (sin (p) / p) ** 2 } } else bf1 = NULL # Make the profile templates stored as an interpolation function # with the returned center, fwhm, and flux. switch (profile) { case GAUSS: call mkprof (2., asi, pcen, fwhm, flux) case SLIT: call mkprof (10., asi, pcen, fwhm, flux) } # Now expand the 1D spectra into 2D profiles. dx = fwhm / width do i = 0, nl-1 { data = impl2r (im, i+1) if (new) call aclrr (Memr[data], nc) else call amovr (Memr[imgl2r(im,i+1)], Memr[data], nc) if (bf1 != NULL) do j = 0, nc-1 Memr[data+j] = Memr[data+j] + Memr[bf1+j] * Memr[bf2+i] w = ecx2w (i-yc, 1, a, b, f, cb, tb, t2tb, xmin, xmax) do j = 0, norders-1 { x = w / (m1 + j) p = Memr[buf+j*nl+i] / flux if (mc[2] == 0) x = (x - wc[1]) / (x * disp[2]) + xc else x = ecw2x (mc[2]*x, 2, a, b, f, tb, ctb, t2tb) + xc1 p1 = max (-0.49, x - pcen / dx) p2 = min (nc - 0.51, x + pcen / dx) if (p1 >= p2) next k1 = p1 + 0.5 k2 = p2 + 0.5 x1 = (p1 - x) * dx + pcen + 1 x2 = (min (p2, k1 + 0.5) - x) * dx + pcen + 1 x1 = max (1., x1) x2 = max (1., x2) m = data + k1 Memr[m] = Memr[m] + p * asigrl (asi, x1, x2) do k = k1+1, k2-1 { x1 = x2 x2 = x1 + dx m = m + 1 Memr[m] = Memr[m] + p * asigrl (asi, x1, x2) } x1 = x2 x2 = (p2 - x) * dx + pcen + 1 m = m + 1 Memr[m] = Memr[m] + p * asigrl (asi, x1, x2) } } call asifree (asi) } call mfree (buf, TY_REAL) if (bf1 != NULL) { call mfree (bf1, TY_REAL) call mfree (bf2, TY_REAL) } call mfree (waves, TY_REAL) call mfree (peaks, TY_REAL) call mfree (sigmas, TY_REAL) # Add comment history of task parameters. if (clgetb ("comments")) { call strcpy ("# ", Memc[comment], LEN_COMMENT) call cnvtime (clktime (0), Memc[comment+2], LEN_COMMENT-2) call mkh_comment (im, Memc[comment]) call mkh_comment (im, "begin mkechelle") call mkh_comment1 (im, "profile", 's') if (profile != EXTRACTED) { call mkh_comment1 (im, "width", 'r') call mkh_comment1 (im, "scattered", 'r') } call mkh_comment1 (im, "norders", 'i') call sprintf (Memc[comment], LEN_COMMENT, "%9txc%24t%g") call pargr (1+xc) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tyc%24t%g") call pargr (1+yc) call mkh_comment (im, Memc[comment]) call mkh_comment1 (im, "pixsize", 'r') call sprintf (Memc[comment], LEN_COMMENT, "%9tf%24t%g") if (IS_INDEF(pixsize) || IS_INDEF(f[1])) call pargr (f[1]) else call pargr (f[1] * pixsize) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tgmm%24t%g") call pargr (gmm[1]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tblaze%24t%g") call pargr (blaze[1]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9ttheta%24t%g") call pargr (t[1]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9torder%24t%d") call pargi (mc[1]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9twavelength%24t%g") call pargr (wc[1]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tdispersion%24t%g") if (IS_INDEF(pixsize) || IS_INDEF(disp[1])) call pargr (disp[1]) else call pargr (disp[1] / pixsize) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tcf%24t%g") if (IS_INDEF(pixsize) || IS_INDEF(f[2])) call pargr (f[2]) else call pargr (f[2] * pixsize) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tcgmm%24t%g") call pargr (gmm[2]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tcblaze%24t%g") call pargr (blaze[2]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tctheta%24t%g") call pargr (t[2]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tcorder%24t%d") call pargi (mc[2]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tcwavelength%24t%g") call pargr (wc[2]) call mkh_comment (im, Memc[comment]) call sprintf (Memc[comment], LEN_COMMENT, "%9tcdispersion%24t%g") if (IS_INDEF(pixsize) || IS_INDEF(disp[2])) call pargr (disp[2]) else call pargr (disp[2] / pixsize) call mkh_comment (im, Memc[comment]) call mkh_comment1 (im, "rv", 'r') call mkh_comment1 (im, "z", 'b') call mkh_comment1 (im, "continuum", 'r') call mkh_comment1 (im, "temperature", 'r') if (nrandom > 0) { if (Memc[fname] != EOS) call mkh_comment1 (im, "lines", 's') call sprintf (Memc[comment], LEN_COMMENT, "%9tnlines%24t%d") call pargi (nrandom) call mkh_comment (im, Memc[comment]) call mkh_comment1 (im, "peak", 'r') call mkh_comment1 (im, "sigma", 'r') call mkh_comment1 (im, "seed", 'i') } } call imunmap (im) } call imtclose (images) call imtclose (lines) call sfree (sp) end # Definitions of INDEF parameter flags. define F 1B define G 2B define B 4B define T 10B define M 20B define W 40B define D 100B # Combinations define FG 3B define FB 5B define FT 11B define FM 21B define FW 41B define GB 6B define GT 12B define GW 42B define GD 102B define BT 14B define BM 24B define BW 44B define BD 104B define TM 30B define TW 50B define TD 110B define MW 60B define MD 120B define WD 140B # ECGRATING -- Derive and check grating parameters. procedure ecgrating (e, f, g, b, t, m, w, d) bool e real f, g, b, t, w, d, x int m int i, flags define err_ 10 begin if (!IS_INDEF(f)) { if (f <= 0.) f = INDEF } if (!IS_INDEF(g)) { if (g <= 0.) g = INDEF else g = g / 1e7 } if (!IS_INDEF(b)) { b = DEGTORAD (b) if (b == 0. && t == 0.) t = INDEF } if (!IS_INDEF(t)) { t = DEGTORAD (t) if (t > PI && !IS_INDEF(b)) t = t - TWOPI + b } if (!IS_INDEFI(m) && m <= 0) m = INDEFI if (!IS_INDEF(w) && w <= 0.) w = INDEF if (!IS_INDEF(d) && d <= 0.) d = INDEF flags = 0 if (IS_INDEF(f)) flags = flags + F if (IS_INDEF(g)) flags = flags + G if (IS_INDEF(b)) flags = flags + B if (IS_INDEF(t)) flags = flags + T if (IS_INDEFI(m)) flags = flags + M if (IS_INDEF(w)) flags = flags + W if (IS_INDEF(d)) flags = flags + D switch (flags) { case 0, F, G, B, T, M, W, D: switch (flags) { case F: f = cos (2 * b - t) / (g * m * d) case G: g = (sin (t) + sin (2 * b - t)) / (m * w) if (g == 0.) g = INDEF case B: if (t > PI) { x = g * m * w / (2 * cos (t)) if (abs (x) > 1.) goto err_ b = asin (x) t = t - TWOPI + b } else { x = g * m * w - sin (t) if (abs (x) > 1.) goto err_ b = (t + asin (x)) / 2 } case T: x = g * m * w / (2 * sin(b)) if (abs (x) > 1.) goto err_ if (e) t = b + acos (x) else t = b - acos (x) case M: m = max (1, nint ((sin(t) + sin(2*b-t)) / (g * w))) } if (!IS_INDEF(g)) { w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) } case FG: x = (sin (t) + sin (2 * b - t)) / (m * w) if (x == 0.) goto err_ g = x f = cos (2 * b - t) / (g * m * d) case FB: if (t > PI) { x = g * m * w / (2 * cos (t)) if (abs (x) > 1.) goto err_ b = asin (x) t = t - TWOPI + b } else { x = g * m * w - sin (t) if (abs (x) > 1.) goto err_ b = (t + asin (x)) / 2 } f = cos (2 * b - t) / (g * m * d) case FT: x = g * m * w / (2 * sin (b)) if (abs (x) > 1.) goto err_ if (e) t = b + acos (x) else t = b - acos (x) f = cos (2 * b - t) / (g * m * d) case FM: m = nint ((sin (t) + sin (2 * b - t)) / (g * w)) f = cos (2 * b - t) / (g * m * d) w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) case FW: w = (sin (t) + sin (2 * b - t)) / (g * m) f = cos (2 * b - t) / (g * m * d) case GB: x = f * d / w if (t > PI) { b = atan (1 / (2 * x - tan (t))) t = t - TWOPI + b } else { x = (tan (t) - x) / (1 + 2 * x * tan (t)) b = atan (x + sqrt (1 + x * x)) } g = (sin (t) + sin (2 * b - t)) / (m * w) case GT: t = b + atan (2 * f * d / w - 1 / tan (b)) g = (sin (t) + sin (2 * b - t)) / (m * w) case GW: g = cos (2 * b - t) / (f * m * d) if (g == 0.) g = INDEF else w = (sin (t) + sin (2 * b - t)) / (g * m) case GD: x = (sin (t) + sin (2 * b - t)) / (m * w) if (x == 0.) goto err_ g = x d = cos (2 * b - t) / (f * g * m) case BT: x = f * g * m * d if (abs (x) > 1.) goto err_ x = acos (x) x = g * m * w - sin (x) if (abs (x) > 1.) goto err_ t = asin (x) b = (acos (f * g * m * d) + t) / 2 case BM: x = f * d / w if (t > PI) { b = atan (1 / (2 * x - tan (t))) t = t - TWOPI + b } else { x = (tan (t) - x) / (1 + 2 * x * tan (t)) b = atan (x + sqrt (1 + x * x)) } m = max (1, nint ((sin(t) + sin(2*b-t)) / (g * w))) b = (t + asin (g * m * w - sin (t))) / 2 w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) case BW: b = (t + acos (f * g * m * d)) / 2 w = (sin (t) + sin (2 * b - t)) / (g * m) case BD: if (t > PI) { x = g * m * w / (2 * cos (t)) if (abs (x) > 1.) goto err_ b = asin (x) t = t - TWOPI + b } else { x = g * m * w - sin (t) if (abs (x) > 1.) goto err_ b = (t + asin (x)) / 2 } d = cos (2 * b - t) / (f * g * m) case TM: x = f * d / w x = b + 2 * atan (x - 1 / (2 * tan (b))) i = max (1, nint ((sin(x) + sin(2*b-x)) / (g * w))) x = g * i * w / (2 * sin (b)) if (abs (x) > 1.) goto err_ if (e) t = b + acos (x) else t = b - acos (x) m = i w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) case TW: x = f * g * m * d if (abs (x) > 1.) goto err_ t = 2 * b - acos (x) w = (sin (t) + sin (2 * b - t)) / (g * m) case TD: x = g * m * w / (2 * sin (b)) if (abs (x) > 1.) goto err_ if (e) t = b + acos (x) else t = b - acos (x) d = cos (2 * b - t) / (f * g * m) case MW: m = max (1, nint (cos (2 * b - t) / (f * g * d))) w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) case MD: m = max (1, nint ((sin(t) + sin(2*b-t)) / (g * w))) w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) case WD: w = (sin (t) + sin (2 * b - t)) / (g * m) d = cos (2 * b - t) / (f * g * m) } if (!IS_INDEF(g)) g = g * 1e7 if (!IS_INDEF(b)) b = RADTODEG (b) if (!IS_INDEF(t)) t = RADTODEG (t) if (IS_INDEF(f) || IS_INDEF(g) || IS_INDEF(b) || IS_INDEF(t) || IS_INDEF(m) || IS_INDEF(w) || IS_INDEF(d)) call error (1, "Insufficient information to to resolve grating parameters") return err_ if (!IS_INDEF(g)) g = g * 1e7 if (!IS_INDEF(b)) b = RADTODEG (b) if (!IS_INDEF(t)) t = RADTODEG (t) call error (2, "Impossible combination of grating parameters") end # ECLIST -- List grating parameters. procedure eclist (p, f, g, b, t, m, w, d) real p, f[2], g[2], b[2], t[2], w[2], d[2] int m[2] begin call printf ("Echelle grating parameters:\n") call printf (" Focal length = %g %s\n") if (IS_INDEF(p) || IS_INDEF(f[1])) call pargr (f[1]) else call pargr (f[1] * p) if (IS_INDEF(p)) call pargstr ("pixels") else call pargstr ("mm") call printf (" Grating = %g grooves/mm\n") call pargr (g[1]) call printf (" Blaze angle = %g degrees\n") call pargr (b[1]) call printf (" Incidence angle = %g degrees\n") call pargr (t[1]) call printf (" Reference order = %d\n") call pargi (m[1]) call printf ( " Blaze wavelength of reference order = %g Angstroms\n") call pargr (w[1]) call printf ( " Blaze dispersion of reference order = %g Angstroms/%s\n") if (IS_INDEF(p) || IS_INDEF(d[1])) call pargr (d[1]) else call pargr (d[1] / p) if (IS_INDEF(p)) call pargstr ("pixels") else call pargstr ("mm") if (m[2] == 0.) { call printf ("Crossdisperser prism parameters:\n") call printf (" Reference wavelength = %g Angstroms/pixel\n") call pargr (w[2]) call printf ( " Dispersion at reference wavelength = %g Angstroms/%s\n") if (IS_INDEF(p) || IS_INDEF(d[2])) call pargr (d[2]) else call pargr (d[2] / p) if (IS_INDEF(p)) call pargstr ("pixels") else call pargstr ("mm") } else { call printf ("Crossdisperser grating parameters:\n") call printf (" Focal length = %g %s\n") if (IS_INDEF(p) || IS_INDEF(f[2])) call pargr (f[2]) else call pargr (f[2] * p) if (IS_INDEF(p)) call pargstr ("pixels") else call pargstr ("mm") call printf (" Grating = %g grooves/mm\n") call pargr (g[2]) call printf (" Blaze angle = %g degrees\n") call pargr (b[2]) call printf (" Incidence angle = %g degrees\n") call pargr (t[2]) call printf (" Order = %d\n") call pargi (m[2]) call printf ( " Blaze wavelength = %g Angstroms\n") call pargr (w[2]) call printf ( " Blaze dispersion = %g Angstroms/%s\n") if (IS_INDEF(p) || IS_INDEF(d[2])) call pargr (d[2]) else call pargr (d[2] / p) if (IS_INDEF(p)) call pargstr ("pixels") else call pargstr ("mm") } call flush (STDOUT) end # ECX2W -- Given pixel position return wavelength. real procedure ecx2w (x, i, a, b, f, cb, tb, t2tb, xmin, xmax) real x, a[2], b[2], f[2], cb[2], tb[2], t2tb[2], xmin[2], xmax[2], w int i begin if (IS_INDEF(f[i])) return (a[i] + b[i] * x) w = x / f[1] w = a[i] + b[i] * cb[i] / sqrt (1 + w * w) * (w + tb[i]) return (w) end # ECX2WR -- Given pixel position return wavelength of reflected component. real procedure ecx2wr (x, i, a, b, f, cb, tb, t2tb, xmin, xmax) real x, a[2], b[2], f[2], cb[2], tb[2], t2tb[2], xmin[2], xmax[2], w int i begin if (IS_INDEF(f[i])) return (INDEF) w = x / f[i] if (x <= xmin[i] || x >= xmax[i]) return (INDEF) w = (w - t2tb[i]) / (1 + w * t2tb[i]) w = a[i] + b[i] * cb[i] / sqrt (1 + w * w) * (w + tb[i]) return (w) end # ECXDX -- Given pixel position return pixel size per unit wavelength # normalized to the central pixel. real procedure ecxdx (x, i, f, tb) real x, f[2], tb[2], dx int i begin if (IS_INDEF(f[i])) return (1.) dx = x / f[i] dx = (1 - dx * tb[i]) / sqrt ((1 + dx * dx) ** 3) return (dx) end # ECW2X -- Given wavelength return pixel position. real procedure ecw2x (w, i, a, b, f, tb, ctb, t2tb) real w, a[2], b[2], f[2], tb[2], ctb[2], t2tb[2], x int i begin if (IS_INDEF(f[i])) return ((w - a[i]) / b[i]) x = (w - a[i]) / b[i] if (x >= 1. || x <= -ctb[i]) return (INDEF) x = x / sqrt (1 - x * x) x = f[i] * (x - tb[i]) / (1 + x * tb[i]) return (x) end # ECW2XR -- Given wavelength return pixel position of reflected component. real procedure ecw2xr (w, i, a, b, f, tb, ctb, t2tb) real w, a[2], b[2], f[2], tb[2], ctb[2], t2tb[2], x int i begin if (IS_INDEF(f[i])) return (INDEF) x = (w - a[i]) / b[i] if (x >= 1. || x <= ctb[i]) return (INDEF) x = x / sqrt (1 - x * x) x = (x - tb[i]) / (1 + x * tb[i]) x = f[i] * (x + t2tb[i]) / (1 - x * t2tb[i]) return (x) end # ECDELTA -- Given pixel position and wavelength return blaze function # phase angle. real procedure ecdelta (x, w, i, f, c, tt) real x, w, f[2], c[2], tt[2], d int i begin if (IS_INDEF(f[i])) return (c[i] / w * x) d = x / f[i] d = 1 / sqrt (1 + d * d) d = c[i] / w * (d * x / f[i] + tt[i] * (1 - d)) return (d) end