aboutsummaryrefslogtreecommitdiff
path: root/noao/artdata
diff options
context:
space:
mode:
authorJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
committerJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
commit40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch)
tree4464880c571602d54f6ae114729bf62a89518057 /noao/artdata
downloadiraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'noao/artdata')
-rw-r--r--noao/artdata/Notes11
-rw-r--r--noao/artdata/Revisions537
-rw-r--r--noao/artdata/artdata.cl18
-rw-r--r--noao/artdata/artdata.hd19
-rw-r--r--noao/artdata/artdata.men10
-rw-r--r--noao/artdata/artdata.par14
-rw-r--r--noao/artdata/doc/gallist.hlp488
-rw-r--r--noao/artdata/doc/mk1dspec.hlp355
-rw-r--r--noao/artdata/doc/mk2dspec.hlp207
-rw-r--r--noao/artdata/doc/mkechelle.hlp585
-rw-r--r--noao/artdata/doc/mkexamples.hlp167
-rw-r--r--noao/artdata/doc/mkheader.hlp84
-rw-r--r--noao/artdata/doc/mknoise.hlp245
-rw-r--r--noao/artdata/doc/mkobjects.hlp636
-rw-r--r--noao/artdata/doc/mkpattern.hlp180
-rw-r--r--noao/artdata/doc/starlist.hlp355
-rw-r--r--noao/artdata/doc/version1.149
-rw-r--r--noao/artdata/gallist.par52
-rw-r--r--noao/artdata/lists/gallist.key63
-rw-r--r--noao/artdata/lists/mkpkg18
-rw-r--r--noao/artdata/lists/starlist.h136
-rw-r--r--noao/artdata/lists/starlist.key47
-rw-r--r--noao/artdata/lists/stcolon.x835
-rw-r--r--noao/artdata/lists/stdbio.x350
-rw-r--r--noao/artdata/lists/stlum.x342
-rw-r--r--noao/artdata/lists/stmix.x144
-rw-r--r--noao/artdata/lists/stplot.x1102
-rw-r--r--noao/artdata/lists/stshow.x142
-rw-r--r--noao/artdata/lists/stspatial.x264
-rw-r--r--noao/artdata/lists/t_gallist.x453
-rw-r--r--noao/artdata/lists/t_starlist.x303
-rw-r--r--noao/artdata/mk1dspec.par31
-rw-r--r--noao/artdata/mk2dspec.par10
-rw-r--r--noao/artdata/mkechelle.par48
-rw-r--r--noao/artdata/mkexamples/archdr.dat19
-rw-r--r--noao/artdata/mkexamples/ecarc.cl20
-rw-r--r--noao/artdata/mkexamples/ecarc2d.cl20
-rw-r--r--noao/artdata/mkexamples/ecarcdc.cl21
-rw-r--r--noao/artdata/mkexamples/echelle.cl22
-rw-r--r--noao/artdata/mkexamples/ecobj2d.cl29
-rw-r--r--noao/artdata/mkexamples/ecthorium.dat655
-rw-r--r--noao/artdata/mkexamples/galcluster.cl39
-rw-r--r--noao/artdata/mkexamples/galfield.cl30
-rw-r--r--noao/artdata/mkexamples/globular.cl23
-rw-r--r--noao/artdata/mkexamples/henear.cl20
-rw-r--r--noao/artdata/mkexamples/henear1.dat56
-rw-r--r--noao/artdata/mkexamples/henear1d.cl4
-rw-r--r--noao/artdata/mkexamples/henear2.dat56
-rw-r--r--noao/artdata/mkexamples/heneardc.cl18
-rw-r--r--noao/artdata/mkexamples/longslit.cl77
-rw-r--r--noao/artdata/mkexamples/lsarc.cl5
-rw-r--r--noao/artdata/mkexamples/lsgal.cl5
-rw-r--r--noao/artdata/mkexamples/lsobj.cl5
-rw-r--r--noao/artdata/mkexamples/mkexamples.cl68
-rw-r--r--noao/artdata/mkexamples/mkexamples.men10
-rw-r--r--noao/artdata/mkexamples/multifiber.cl31
-rw-r--r--noao/artdata/mkexamples/objhdr.dat19
-rw-r--r--noao/artdata/mkexamples/onedspec.men10
-rw-r--r--noao/artdata/mkexamples/spectrum.cl19
-rw-r--r--noao/artdata/mkexamples/starfield.cl22
-rw-r--r--noao/artdata/mkexamples/threedspec.men3
-rw-r--r--noao/artdata/mkexamples/twodspec.men10
-rw-r--r--noao/artdata/mkheader.par4
-rw-r--r--noao/artdata/mkheader.x229
-rw-r--r--noao/artdata/mknoise.par25
-rw-r--r--noao/artdata/mkobjects.par30
-rw-r--r--noao/artdata/mkpattern.par20
-rw-r--r--noao/artdata/mkpkg40
-rw-r--r--noao/artdata/mktemplates.com9
-rw-r--r--noao/artdata/mktemplates.x1390
-rw-r--r--noao/artdata/mktemplates.xBAK1326
-rw-r--r--noao/artdata/mktemplates.xNEW1399
-rw-r--r--noao/artdata/numrecipes.x121
-rw-r--r--noao/artdata/starlist.par43
-rw-r--r--noao/artdata/stdheader.dat10
-rw-r--r--noao/artdata/t_mk1dspec.x443
-rw-r--r--noao/artdata/t_mk2dspec.x297
-rw-r--r--noao/artdata/t_mkechelle.x1248
-rw-r--r--noao/artdata/t_mkheader.x52
-rw-r--r--noao/artdata/t_mknoise.x577
-rw-r--r--noao/artdata/t_mkobjects.x552
-rw-r--r--noao/artdata/t_mkpattern.x318
-rw-r--r--noao/artdata/voigt.x71
-rw-r--r--noao/artdata/x_artdata.x9
84 files changed, 17829 insertions, 0 deletions
diff --git a/noao/artdata/Notes b/noao/artdata/Notes
new file mode 100644
index 00000000..3f8d90c2
--- /dev/null
+++ b/noao/artdata/Notes
@@ -0,0 +1,11 @@
+Ideas for additional tasks:
+
+o Given a list of (x,y) or (x,y,z) make/add a 1D or 2D image
+ o Fit using curfit/gsurfit?
+ o Some other way of fitting?
+
+o Script tasks to make certain classes of data.
+
+o Demo
+
+o Do stuff for making various PLIO masks
diff --git a/noao/artdata/Revisions b/noao/artdata/Revisions
new file mode 100644
index 00000000..beaa9e4c
--- /dev/null
+++ b/noao/artdata/Revisions
@@ -0,0 +1,537 @@
+.help revisions Jan90 noao.artdata
+.nf
+
+mktemplates.x
+doc/mkobjects.hlp
+ Added Sersic model profiles. This was done using only the model
+ name to avoid any additional parameters. (2/24/11, Valdes)
+
+=======
+V2.15.2
+=======
+
+=======
+V2.12.2
+=======
+
+mkheader.x
+ The routine was not putting an EOS for lines longer than IDB_RECLEN
+ resulting in cards longer than the FITS standard.
+ (1/29/04, Valdes)
+
+mktemplates.x
+ Pointer procedure mkt_object() could return w/out value (12/29/03, MJF)
+
+mkheader.x
+ Added checks and warning messages for overflowing the user header
+ area. (11/10/03, Valdes)
+
+t_mknoise.x
+t_mkobjects.x
+t_mk1dspec.x
+t_mkechelle.x
+lists/t_starlist.x
+lists/t_gallist.x
+ The behavior of the random number seed used when specified as INDEF
+ was changed to avoid the 1 second grandularity. See buglog 528.
+ (8/7/03, Valdes)
+
+t_mknoise.x
+ The string for the header file name was increased in length from
+ LEN_COMMENT to SZ_FNAME. (6/27/03, Valdes)
+
+=======
+V2.12.1
+=======
+=====
+V2.12
+=====
+
+t_mkobjects.x
+doc/mkobjects.hlp
+ The star templates are now created only once when there is a list
+ of input images. There was also a bug in matching the objects
+ list to the input list which didn't work and also did not agree with
+ the help. Now there must be a matching list. (12/14/01, Valdes)
+
+stdheader.dat
+ The DATE-OBS had a syntax error, a ':' instead of 'T' separating the
+ date and time. (2/11/00, Valdes)
+
+=========
+V2.11.3p1
+=========
+
+stdheader.dat
+mkexamples/archdr.dat
+mkexamples/objhdr.dat
+ Changed DATE-OBS to new FITS format. (5/19/99, Valdes)
+
+
+doc/mkobjects.hlp
+doc/mkpattern.hlp
+doc/mkechelle.hlp
+ Fixed minor formating problems. (4/22/99, Valdes)
+
+doc/mkechelle.hlp
+ Modified to indicate the input list is not exactly the same as MK1DSPEC
+ since that task was modified to support different profile types.
+ (3/16/98, Valdes)
+
+=====
+V2.11
+=====
+
+t_mk1dspec.x
+t_mknoise.x
+t_mkobjects.x
+lists/t_starlist.x
+lists/t_gallist.x
+doc/mk1dspec.hlp
+doc/mknoise.hlp
+doc/mkobjects.hlp
+doc/starlist.hlp
+doc/gallist.hlp
+ If the random number seed is specified as INDEF then the task will
+ use the clock time (seconds since 1980) as the seed. This allows
+ users to get different random numbers for each execution.
+ (9/23/97, Valdes)
+
+t_mk1dspec.x
+ If the dispersion was negative the task would fail to make the lines.
+ (6/2/97, Valdes)
+
+doc/mk1dspec.hlp
+ Fixed minor typo. (6/2/97, Valdes)
+
+doc/mkpattern.hlp
+ Updated for changes. (4/22/97, Valdes)
+
+artdata.par
+doc/mknoise.hlp
+doc/mkobjects.hlp
+ Changed the default for "ranbuf" to zero. (1/29/97, Valdes)
+
+lists/stdbio.x
+ The parameter information in the output now begins with #.
+ (12/30/96, Valdes)
+
+t_mknoise.x
+t_mk1dspec.x
+t_mk2dspec.x
+t_mkechelle.x
+t_mkobjects.x
+t_mkpattern.x
+ Changed logic to work around the fact that IMACCESS does not always
+ work. This became a problem with the FITS kernel. (11/11/96, Valdes)
+
+t_mkpattern.x
+mkpattern.par
+doc/mkpattern.hlp
+ Added "ushort" and "complex" data types. (11/8/96, Valdes)
+
+numrecipes.x
+ Modified the Poisson deviate routine to return zero for input
+ values less than or equal to zero. (10/1/96, Valdes)
+
+lists/stdbio.x
+ Modified the list formatting code to print the x and y centers of
+ the objects to 3 decimal places of precision. (8/27/96, Davis)
+
+mkpattern.par
+ An ndim of zero is now allowed. (8/15/96, Valdes)
+
+mkobjects.par
+doc/mkobjects.hlp
+ Modified the prompt for "background" and the help page to indicate
+ that the value is in data numbers. (6/27/96, Valdes)
+
+t_mkechelle.x
+ The DC-FLAG was not being set correctly for raw extracted spectra.
+ (4/26/96, Valdes)
+
+doc/mk1dspec.hlp
+ Minor clarification of how the peak parameter is used if a line
+ list is given but there is not peak value. (10/12/95, Valdes)
+
+gallist.par
+ Changed the "ar" parameter to a default value of 0.3 corresponding
+ to the range E0 to E7. (10/10/95, Valdes)
+
+voigt.x +
+mk1dspec.par
+t_mk1dspec.x
+mkpkg
+mkexamples/henear.cl
+mkexamples/heneardc.cl
+mkexamples/longslit.cl
+mkexamples/multifiber.cl
+mkexamples/spectrum.cl
+doc/mk1dspec.hlp
+ 1. MK1DSPEC now includes gaussian, lorentzian, and voigt line profiles.
+ There were parameter changes and changes to the input/output
+ line lists.
+ 2. The example scripts calling MK1DSPEC were modified for the changes
+ to task though they still use only gaussian lines. The spectra
+ will not be exactly the same because the order of the random
+ numbers has changed.
+ (7/28/95, Valdes)
+
+=======
+V2.10.4
+=======
+
+artdata$t_mk1dspec.x
+ Fixed two type mismatches in min/max calls. (12/31/94, Valdes)
+
+artdata$lists/stmix.x
+ The calculation of absorption in spiral models was not working and
+ so no absorption effect was being added as described in the help.
+ This has been fixed. (10/29/94, Valdes)
+
+xtools$numrecipes.x
+ The POIDEV routine can still have a problem in that the tan function
+ can return a very large number triggering either an overflow in
+ the evaluation of em or in the int truncation of em as addressed
+ below. A test is now made on the value of the tan function.
+ (9/14/94, Valdes)
+
+artdata$mknoise.par
+artdata$doc/mknoise.hlp
+ The help was inconsistent with the task in that any background specified
+ is added to both new and existing images. The discussion of subtracting
+ the background in existing images was wrong. The "background" parameter
+ was moved in the par file from the NEW IMAGE section.
+ (7/1/94, Valdes)
+
+artdata$mktemplates.x
+ The structure parameter MKT_SCALE was incorrectly defined as an
+ integer resulting in the output scales of the "expdisk" and "devauc"
+ objects to be incorrect (see bug log 226). (9/17/93, Valdes)
+
+artdata$t_mkpattern.x
+ Fixed bug in indexing. This bug was most noticible with the checkerboard
+ and a larger size which showed the first square was too small by 1.
+ (9/2/93, Valdes)
+
+artdata$mkexamples/longslit.cl
+artdata$mkexamples/lsgal.cl +
+artdata$twodspec.men
+ Added an example,lsgal, of an extended longslit galaxy spectrum.
+ (8/21/93, Valdes)
+
+artdata$numrecipes.x
+ The POIDEV routine from Numerical Recipes can try to coerce a large
+ floating point number to an integer which can cause an exception.
+ If the value is 100 or greater a Gaussian deviate is now returned.
+ (8/11/93, Valdes)
+
+=============
+V2.10.3. beta
+=============
+
+artdata$t_mkobjects.x
+artdata$t_mknoise.x
+ The min/max data values are computed and set in the output image.
+ (4/16/93, Valdes)
+
+artdata$mkexamples
+ 1. The MKEXAMPLES task now provides multiple menus.
+ 2. The spectrum examples have been organized by type.
+ 3. New spectrum examples have been added.
+ 4. The echelle and onedspec examples use the oseed to set the number
+ of orders/apertures.
+ (3/17/93, Valdes)
+
+artdata$t_mkechelle.x
+artdata$t_mk1dspec.x
+artdata$mk1dspec.par
+artdata$doc/t_mk1dspec.hlp
+mkpkg
+ 1. Modified to use SMW WCS routines.
+ 2. The mk1dspec.format parameter was eliminated.
+ (3/17/93, Valdes)
+
+artdata$mkheader.x
+ Added a double type to mkh_comment1. (3/17/93, Valdes)
+
+artdata$mkobjects.x
+artdata$doc/mkobjects.hlp
+ Changed the behavior of xoffset and yoffset to be the offset after
+ distance scaling to allow having a fixed origin. (3/16/93, Valdes)
+
+artdata$lists/stplot.x
+ When there is only one object or when all objects have the same value
+ then the symbol size scaling with amapr gave a floating operand error.
+ A check has been added for this. (3/16/93, Valdes)
+
+artdata$t_mkechelle.x
+ The method of generating random lines was incorrect. This showed up
+ when trying to make the same set of random lines with a different
+ velocity. (3/1/93, Valdes)
+
+ardata$t_mkobjects.x
+ardata$t_mknoise.x
+ardata$t_mkechelle.x
+ardata$t_mk2dspec.x
+ardata$t_mk1dspec.x
+ardata$mkheader.x
+ Replaced use of '\t' in the comment with spaces. (11/12/92, Valdes)
+
+artdata$mktemplates.x
+ In mkt_binprof there was no check that the central subpixels were
+ within the profile. For very small objects this could lead to
+ out of bounds references in the prof array. A check on this was
+ added. (10/30/92, Valdes)
+
+artdata$t_mk2dspec.x
+artdata$t_mkechelle.x
+ Add a limit check to prevent integration of a pixel outside the profile
+ interpolation function. (8/14/92, Valdes)
+
+artdata$t_mkechelle.x
+ Changed a "** 1.5" to a combination of square root and an integer
+ third power. (8/10/92, Valdes)
+
+=======
+V2.10.2
+=======
+=======
+=======
+V2.10.1
+=======
+=======
+V2.10.0
+=======
+
+artdata$t_mkechelle.x
+ "min (tt" --> "min (tt[i]" and "max (tt" --> "max (tt[i]"
+ (6/23/92, Valdes)
+
+=====
+V2.10
+=====
+
+artdata$mkexamples/*
+artdata$doc/mkexamples.hlp
+ Added parameter to control whether comments are generated in the
+ examples. (1/31/92, Valdes)
+
+artdata$mktemplate.x
+ Made substantial changes to how the user and analytic profiles are
+ handled. Primarily, the prefered form for both user supplied and
+ analytic profiles is as intensity profiles rather than cumulative
+ flux profiles. Cumulative profiles may still be input and they are
+ identified by having a zero initial element. (12/13/91, Valdes)
+
+artdata$mktemplates.x
+ Very large gaussians showed an dip in the center due to problems with
+ mkt_fixprof. A better algorithm was instituted. (12/11/91, Valdes)
+
+artdata$lists/stmix.x
+ 1. The random component of the size distribution for the Schecter function
+ was incorrect. It was giving a 100% random factor and was reducing
+ the size by 40%. The fix is 0.2 + 0.4 * urand() ==> 0.8 + 0.4 * urand().
+ 2. The roundness distribution for ellipical galaxies was done incorrectly.
+ (11/26/91, Valdes)
+
+artdata$lists/stdbio.x
+ Instead of putting out beta in the log for the schecter function changed
+ to put out mstar. (11/26/91, Valdes)
+
+artdata$t_mk1dspec.x
+artdata$t_mkechelle.x
+ Fixed minor bugs found by SPPLINT. (11/21/91, Valdes)
+
+artdata$mktemplates2.x
+artdata$lists/stlumold.x
+ Delete old versions. (11/21/91, Valdes)
+
+artdata$doc/t_mkpattern.x
+ Fixed an error in mkpattern:
+ max (1, (line-1)) --> max (1, line)
+ (10/15/91, Valdes)
+
+artdata$doc/mk1dspec.hlp
+ Need to indicate that the blackbody flux is per unit wavelength.
+ (10/9/91, Valdes)
+
+artdata$t_mkpattern.x
+ For 1D images the pattern made corresponded to line 0 instead of line 1.
+ (7/26/91, Valdes)
+
+artdata$mktemplates.x
+ The PSF position angle was not being converted to radians from degrees
+ with the result that the input was interpreted as radians. The
+ conversion with DEGTORAD was added. (6/10/91, Valdes)
+
+artdata$t_mkechelle.x
+artdata$mkechelle.par
+artdata$doc/mkechelle.hlp
+artdata$t_mk1dspec.x
+artdata$x_artdata.x
+artdata$artdata.cl
+artdata$artdata.men
+artdata$artdata.hd
+artdata$mkpkg
+artdata$mkexamples/ecarc2d.cl
+artdata$mkexamples/ecobj2d.cl
+artdata$mkexamples/ecarc1d.cl
+artdata$mkexamples/ecarcdc.cl
+artdata$mkexamples/ecthorium.dat
+artdata$mkexamples/mkexamples.men
+ 1. Added new artificial echelle task.
+ 2. Added echelle examples.
+ 3. Fixed minor bug in MK1DSPEC concerning header format of wend parameter.
+ (3/20/91, Valdes)
+
+artdata$
+ mkheader.x +
+ mkpkg
+ t_mkobjects.x, mkobjects.par, doc/mkobjects.hlp
+ t_mkpattern.x, mkpattern.par, doc/mkpattern.hlp
+ t_mknoise.x, mknoise.par, doc/mknoise.hlp
+ t_mk2dspec.x, mk2dspec.par, doc/mk2dspec.hlp
+ t_mk1dspec.x, mk1dspec.par, doc/mk1dspec.hlp
+ t_mkheader.x, mkheader.par, doc/mkheader.hlp
+ artdata.men
+ stdheader.dat
+ 1. Separated out header stuff into separate file.
+ 2. Changed format of header keyword file to be FITS-like including
+ understanding the output of IMHEADER. The default file
+ stdheader.dat was changed appropriately.
+ 3. Added capability to copy an image header when creating a new
+ image.
+ 4. MKHEADER can append or replace a header from an image or
+ a header keyword file. The clobber parameter was changed
+ to append. The verbose parameter now lists only the image
+ and source of header changes rather than individual keywords.
+ (1/16/91, Valdes)
+
+artdata$mktemplates.x
+ Fixed bug preventing template memory stored in the symbol table
+ from being freed. (12/4/90, Valdes)
+
+artdata$mktemplates.x
+ Changed seeing sampling from 40% to 80% for image template psfs.
+ (11/13/90, Valdes)
+
+artdata$t_mk1dspec.x
+artdata$t_mk2dspec.x
+artdata$t_mkobjects.x
+artdata$t_mknoise.x
+artdata$mk1dspec.par
+artdata$mk2dspec.par
+artdata$mkobjects.par
+artdata$mknoise.par
+artdata$doc/mk1dspec.hlp
+artdata$doc/mk2dspec.hlp
+artdata$doc/mkobjects.hlp
+artdata$doc/mknoise.hlp
+artdata$mkexamples/
+ 1. MK1DSPEC now can create multispec/echelle format images.
+ 2. Added switch to turn off comments.
+ (11/7/90, Valdes)
+
+artdata$t_mkobjects.x
+ 1. Added object list file name to the header comments.
+ 2. Fixed bug which caused objects which went off the edge and were
+ exactly at a half pixel boundry (i.e. 32.5) to end up off by one
+ pixel.
+ (10/23/90, Valdes)
+
+artdata$mktemplates.x
+ Replaced use of i as the function value returned by immap and open
+ with im and fd respectively. For unknown reasons the HPUX compiler
+ left the value of i at 0 after the function call. (10/10/90, Valdes)
+
+artdata$artdata.cl
+artdata$artdata.hd
+artdata$artdata.men
+artdata$doc/mkexamples.hlp +
+artdata$mkexamples/ +
+artdata$mkexamples/mkexamples.cl +
+artdata$mkexamples/mkexamples.men +
+artdata$mkexamples/globular.cl +
+artdata$mkexamples/multifiber.cl +
+artdata$mkexamples/longslit.cl +
+artdata$mkexamples/galcluster.cl +
+artdata$mkexamples/galfield.cl +
+artdata$mkexamples/starfield.cl +
+ 1. A new task, MKEXAMPLES, has been added to make some standard examples
+ for demonstrations and task scripts in this and other packages.
+ 2. The task is driven by simple script files located in the logical
+ directory mkexamples$.
+
+artdata$artdata.cl
+artdata$artdata.hd
+artdata$artdata.men
+artdata$artdata.par
+artdata$x_artdata.x
+artdata$mkheader.par +
+artdata$mk1dspec.par
+artdata$mk2dspec.par
+artdata$mknoise.par
+artdata$mkobjects.par
+artdata$stdheader.dat +
+artdata$mkpkg
+artdata$t_mkheader.x +
+artdata$t_mk1dspec.x
+artdata$t_mk2dspec.x
+artdata$t_mknoise.x
+artdata$t_mkobjects.x
+artdata$doc/mkheader.hlp +
+artdata$doc/mkobjects.hlp
+artdata$doc/mknoise.hlp
+artdata$doc/mk1dspec.hlp
+artdata$doc/mk2dspec.hlp
+ 1. A new task, MKHEADER, has been added to add or modify image headers
+ using a header keyword data file.
+ 2. The tasks MKOBJECTS, MKNOISE, MK1DSPEC, and MK2DSPEC have a new
+ parameter, header, which allows specifying a header keyword data
+ file. A standard header data file is used as the default.
+ 3. The tasks MKOBJECTS, MKNOISE, MK1DSPEC, and MK2DSPEC add some
+ some header parameters such as gain, rdnoise, and exptime as
+ well as extensively comment task and data file parameters.
+ 4. The package version number was incremented.
+
+artdata$mktemplates.x
+ 1. The logic for discriminating between an image template and profile
+ file doesn't work because IMACCESS does not check if the file is
+ an image. Modified to use an error check on IMMAP.
+ 2. The ACCESS call to test for access to a profile file had a missing
+ third argument causing a segmentation error on at least Sun3.
+ 3. For the star profile input an initialization of nxm was missing
+ and a malloc used nxm instead of j resulting in an alloc of 0 length.
+ Basically the profile file input was not working. (8/9/90, Valdes)
+
+artdata$mktemplates.x
+ The gaussian radius was being treated as a full width resulting in
+ stars which are half that expected. (7/19/90, Valdes)
+
+artdata$t_mkobjects
+ A rounding error for objects which go off the
+ lower edges was causing the objects to appear one pixel offset to larger
+ numbers. Replaced rounding done by adding 0.5 by nint. (7/2/90, Valdes)
+
+artdata$t_mk1dspec.x
+artdata$mk1dspec.par
+artdata$doc/mk1dspec.hlp
+ Added redshifting capability. (5/18/90, Valdes)
+
+====
+V2.9
+====
+
+artdata$t_mkpattern.x
+ The COORDINATES pattern was off by 1. (3/12/90, Valdes)
+
+artdata$
+ Davis, Feb 19, 1990
+ 1. The STARLIST and GALLIST tasks for making artificial star and
+ galaxies fields were added to the artdata package.
+
+artdata$* +
+ First version of the artificial data package installed. (2/1/90, Valdes)
+
+.endhelp
diff --git a/noao/artdata/artdata.cl b/noao/artdata/artdata.cl
new file mode 100644
index 00000000..4aa3c59b
--- /dev/null
+++ b/noao/artdata/artdata.cl
@@ -0,0 +1,18 @@
+#{ ARTDATA - Artificial data package
+
+package artdata
+
+task gallist,
+ mk1dspec,
+ mk2dspec,
+ mkechelle,
+ mkheader,
+ mknoise,
+ mkobjects,
+ mkpattern,
+ starlist = "artdata$x_artdata.e"
+
+set mkexamples = "artdata$mkexamples/"
+task mkexamples = "mkexamples$mkexamples.cl"
+
+clbye()
diff --git a/noao/artdata/artdata.hd b/noao/artdata/artdata.hd
new file mode 100644
index 00000000..3ecf368b
--- /dev/null
+++ b/noao/artdata/artdata.hd
@@ -0,0 +1,19 @@
+# Help directory for the ARTDATA package.
+
+$defdir = "noao$artdata/"
+$doc = "noao$artdata/doc/"
+$lists = "noao$artdata/lists/"
+$mkexamples = "noao$artdata/mkexamples/"
+
+gallist hlp=doc$gallist.hlp, src=lists$t_gallist.x
+mk1dspec hlp=doc$mk1dspec.hlp, src=t_mk1dspec.x
+mk2dspec hlp=doc$mk2dspec.hlp, src=t_mk2dspec.x
+mkechelle hlp=doc$mkechelle.hlp, src=t_mkechelle.x
+mkexamples hlp=doc$mkexamples.hlp, src=mkexamples$mkexamples.cl
+mkheader hlp=doc$mkheader.hlp, src=t_mkheader.x
+mknoise hlp=doc$mknoise.hlp, src=t_mknoise.x
+mkobjects hlp=doc$mkobjects.hlp, src=t_mkobjects.x
+mkpattern hlp=doc$mkpattern.hlp, src=t_mkpattern.x
+starlist hlp=doc$starlist.hlp, src=lists$t_starlist.x
+
+revisions sys=Revisions
diff --git a/noao/artdata/artdata.men b/noao/artdata/artdata.men
new file mode 100644
index 00000000..5d4459f4
--- /dev/null
+++ b/noao/artdata/artdata.men
@@ -0,0 +1,10 @@
+ gallist - Make an artificial galaxies list
+ mk1dspec - Make/add artificial 1D spectra
+ mk2dspec - Make/add artificial 2D spectra using 1D spectra templates
+ mkechelle - Make artificial 1D and 2D echelle spectra
+ mkexamples - Make artificial data examples
+ mkheader - Append/replace header parameters
+ mknoise - Make/add noise and cosmic rays to 1D/2D images
+ mkobjects - Make/add artificial stars and galaxies to 2D images
+ mkpattern - Make/add patterns to images
+ starlist - Make an artificial star list
diff --git a/noao/artdata/artdata.par b/noao/artdata/artdata.par
new file mode 100644
index 00000000..1cae6c77
--- /dev/null
+++ b/noao/artdata/artdata.par
@@ -0,0 +1,14 @@
+# ARTDATA - Artificial data package
+
+nxc,i,h,5,1,,Number of PSF centers per pixel in X
+nyc,i,h,5,1,,Number of PSF centers per pixel in Y
+nxsub,i,h,10,1,,Number of pixel subsamples in X
+nysub,i,h,10,1,,Number of pixel subsamples in Y
+nxgsub,i,h,5,1,,Number of galaxy pixel subsamples in X
+nygsub,i,h,5,1,,Number of galaxy pixel subsamples in Y
+dynrange,r,h,100000.,2.,,Profile intensity dynamic range
+psfrange,r,h,10.,2.,,PSF convolution dynamic range
+ranbuf,i,h,0,0,,"Random number buffer size
+"
+version,s,h,"V1.1: August 1990"
+mode,s,h,ql
diff --git a/noao/artdata/doc/gallist.hlp b/noao/artdata/doc/gallist.hlp
new file mode 100644
index 00000000..e1f87623
--- /dev/null
+++ b/noao/artdata/doc/gallist.hlp
@@ -0,0 +1,488 @@
+.help gallist Feb90 noao.artdata
+.ih
+TASK
+gallist -- make an artificial galaxies list
+.ih
+USAGE
+gallist gallist ngals
+.ih
+PARAMETERS
+.ls gallist
+The name of the output text file for the x and y coordinates,
+magnitudes, profile types, half-flux radii, axial ratios, and position
+angles of the artificial galaxies. Output will be appended to this
+file if it exists.
+.le
+.ls ngals = 100
+The number of galaxies in the output galaxies list.
+.le
+.ls interactive = no
+Examine plots and change the parameters of the spatial, luminosity, and
+morphology distributions interactively.
+.le
+
+ SPATIAL DISTRIBUTION
+.ls spatial = "uniform"
+Type of spatial distribution for the galaxies. The types are:
+.ls uniform
+The galaxies are uniformly distributed between \fIxmin\fR, \fIxmax\fR,
+\fIymin\fR, and \fIymax\fR.
+.le
+.ls hubble
+The galaxies are distributed around the center of symmetry \fIxcenter\fR and
+\fIycenter\fR according to a Hubble density law of core radius
+\fIcore_radius\fR and background density \fIbase\fR.
+.le
+.ls file
+The radial density function is contained in the text file \fIsfile\fR.
+.le
+.le
+.ls xmin = 1., xmax = 512., ymin = 1., ymax = 512.
+The range of the output coordinates in pixels.
+.le
+.ls xcenter = INDEF, ycenter = INDEF
+The coordinate of the center of symmetry for the "hubble"
+and "file" radial density functions. The default is the
+midpoint of the coordinate limits.
+.le
+.ls core_radius = 50
+The core radius of the Hubble density distribution in pixels.
+.le
+.ls base = 0.0
+The background density relative to the central density of the Hubble
+density distribution.
+.le
+.ls sseed = 2
+The initial value supplied to the random number generator used to
+generate the output x and y coordinates.
+If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+ MAGNITUDE DISTRIBUTION
+.ls luminosity = "powlaw"
+Type of luminosity distribution for the galaxies. The types are:
+.ls uniform
+The galaxies are uniformly distributed between \fIminmag\fR and
+\fImaxmag\fR.
+.le
+.ls powlaw
+The galaxies are distributed according to a power law with coefficient
+\fIpower\fR.
+.le
+.ls schecter
+The galaxies are distributed according to a Schecter luminosity
+function with characteristic magnitude \fImstar\fR and power law exponent
+\fIalpha\fR between \fIminmag\fR and \fImaxmag\fR.
+.le
+.ls file
+The luminosity function is contained in the text file \fIlfile\fR.
+.le
+.le
+.ls minmag = -7., maxmag = 0.
+The range of output relative magnitudes.
+.le
+.ls mzero = 15.
+Magnitude zero point for Schecter luminosity function.
+.le
+.ls power = 0.6
+Coefficient for the power law magnitude distribution The default value
+of 0.6 is the Euclidean value.
+.le
+.ls alpha = -1.24
+The power law exponent of the Schecter luminosity function.
+The default value is that determined by Schecter from nearby galaxies.
+.le
+.ls mstar = -21.41
+The characteristic magnitude of the Schecter luminosity function.
+.le
+.ls lseed = 2
+The initial value supplied to the random number generator used to
+generate the output magnitudes.
+If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+ MORPHOLOGY DISTRIBUTION
+.ls egalmix = 0.4
+The fraction of the galaxies that are "ellipticals" represented
+by a de Vaucouleurs surface brightness law as opposed to "spirals"
+represented by an exponential disk surface brightness law.
+.le
+.ls ar = 0.3
+Minimum elliptical galaxy axial ratio (major/minor ratio).
+.le
+.ls eradius = 20.0
+The maximum elliptical galaxy half-flux semi-major scale radius. This is
+the radius of an elliptical galaxy with magnitude \fIminmag\fR
+before a random factor is added. Spiral galaxies and fainter galaxies
+are scaled from this value.
+.le
+.ls sradius = 1.0
+Ratio between half-flux scale radii of spiral and elliptical models at the
+same magnitude. For example an elliptical galaxy with magnitude
+\fIminmag\fR will have radius \fIeradius\fR while a spiral galaxy
+of the same magnitude with have radius \fIsradius\fR * \fIeradius\fR.
+.le
+.ls absorption = 1.2
+Absorption correction for edge on spirals in magnitudes.
+.le
+.ls z = 0.05
+Minimum redshift for power law distributed galaxies. This is the
+redshift assigned galaxies of magnitude \fIminmag\fR. The redshifts
+are assumed proportional to the square root of the apparent luminosity;
+i.e the luminosity distance proportional to redshift. The redshift is used
+for computing the mean apparent sizes of the galaxies
+according to (1+z)**2 / z.
+.le
+
+ USER FUNCTIONS
+.ls sfile = ""
+The name of the input text file containing the sampled spatial radial
+density
+function, one sample point per line, with the radius and relative probability
+in columns one and two respectively. The sample points need not be
+uniformly spaced or normalized.
+.le
+.ls nssample = 100
+The number of points at which the spatial density function is
+sampled. If the spatial density function is analytic or approximated
+analytically (the "hubble" option) the function is sampled
+directly. If the function is read from a file (the "file" option) an
+initial smoothing step is performed before sampling.
+.le
+.ls sorder = 10
+The order of the spline fits used to evaluate the integrated spatial
+density function.
+.le
+.ls lfile = ""
+The name of the input text file containing the sampled luminosity
+function, one sample point per line, with the magnitude and relative
+probability in columns one and two respectively. The sample points need
+not be uniformly spaced or normalized.
+.le
+.ls nlsample = 100
+The number of points at which the luminosity function is
+sampled. If the luminosity function is analytic or approximated
+analytically (the "uniform", "powlaw" and "schecter" options) the
+function is sampled directly. If it is read from a file
+(the "file" option) an initial smoothing step is performed before sampling.
+.le
+.ls lorder = 10
+The order of the spline fits used to evaluate the integrated
+luminosity function.
+.le
+
+ INTERACTIVE PARAMETERS
+.ls rbinsize = 10.
+The bin size in pixels of the plotted histogram of the radial density
+distribution.
+.le
+.ls mbinsize = 0.5
+The bin size in magnitudes of the plotted histogram of the luminosity function.
+.le
+.ls dbinsize = 0.5
+The bin size in pixels of the plotted histogram of the half-power semi-major
+axis distribution.
+.le
+.ls ebinsize = 0.1
+The bin size of the plotted histogram of the axial ratio distribution.
+.le
+.ls pbinsize = 20.
+The bin size in degrees of the plotted histogram of the position angle
+distribution.
+.le
+.ls graphics = stdgraph
+The default graphics device.
+.le
+.ls cursor = ""
+The graphics cursor.
+.le
+.ih
+DESCRIPTION
+\fBGallist\fR generates a list of x and y coordinates, magnitudes,
+morphological types, half-power radii, axial ratios, and position
+angles for a sample of \fIngals\fR galaxies based on a user selected
+spatial density function \fIspatial\fR and luminosity function
+\fIluminosity\fR and writes (appends) the results to the text file
+\fIgallist\fR. If the \fIinteractive\fR parameter is "yes" the user can
+interactively examine plots of the spatial density function, the
+radial density function, the luminosity function, radii, axial ratios,
+and position angle distributions and alter the parameters of the task
+until a satisfactory artificial field is generated.
+
+The spatial density function generates x and y values around a center
+of symmetry defined by \fIxcenter\fR and \fIycenter\fR within the x and
+y limits \fIxmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR according to
+the spatial density function specified by \fIspatial\fR. The three
+supported spatial density functions are listed below where R is the
+radial distance in pixels, P is the relative spatial density, C is a
+constant, and f is the best fitting cubic spline function to the spatial
+density function R(user), P(user) supplied by the user in the text file
+\fIsfile\fR.
+
+.nf
+ uniform: P = C
+ hubble: P = 1.0 / (1 + R / core_radius) ** 2 + base
+ file: P = f (R(user), P(user))
+.fi
+
+The Hubble and user spatial density functions are sampled at
+\fInssample\fR equally spaced points, and integrated to give the
+spatial density probability function at each sampled point. The
+integrated probability function is normalized and approximated by a
+cubic spline of order \fIsorder\fR. The x and y coordinates are
+computed by randomly sampling the integrated probability function until
+\fIngals\fR galaxies which satisfy the x and y coordinate limits
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR are generated.
+
+The luminosity function generates relative magnitude values between
+\fIminmag\fR and \fImaxmag\fR (before absorption effects are added)
+according to the luminosity function specified by \fIluminosity\fR.
+The four supported luminosity functions are listed below where M is the
+magnitude, P is the relative luminosity function, C is a constant and f
+is the best fitting cubic spline function to the luminosity function
+M(user), P(user) supplied by the user in the text file \fIlfile\fR.
+
+.nf
+ uniform: P = C
+ powlaw: P = C * 10. ** (power * M)
+ schecter: P = C * 10. ** (alpha * dM) * exp (-10. ** dM)
+ file: P = f (M(user), P(user))
+
+ where dM = 0.4 * (mstar - M + mzero)
+.fi
+
+The uniform distribution is not very physical but may be useful for
+testing. The power law distribution is that expected for a homogeneous
+and isotropic distribution of galaxies. The default value of 0.6 is
+that which can be calculated simply from Euclidean geometry. Observations
+of faint galaxies generally show a smaller value. The Schecter
+function provides a good approximation to a galaxy cluster when
+used in conjunction with the Hubble spatial distribution (though there
+is no mass segregation applied). The "best fit" values for the
+parameters \fImstar\fR and \fIalpha\fR are taken from the paper by
+Schecter (Ap.J 203, 297, 1976). The \fImzero\fR parameter is used
+to convert to absolute magnitudes. Note that it is equivalent to
+set \fImzero\fR to zero and adjust the characteristic magnitude
+to the same relative magnitude scale or to use absolute magnitudes
+directly.
+
+The Schecter and user file distributions are sampled at \fInlsample\fR
+equally spaced points, and integrated to give the luminosity
+probability function at each sampled point. The probability function is
+normalized and approximated by a cubic spline of order \fIlorder\fR.
+The magnitudes are computed by randomly sampling the integrated
+probability function until \fIngals\fR objects which satisfy the
+magnitude limits \fIminmag\fR and \fImaxmag\fR are generated.
+
+The artificial galaxies have one of two morphological types,
+"ellipticals" with a de Vaucouleurs surface brightness law and
+"spirals" with an exponential surface brightness law. The fraction
+of elliptical galaxies is set by the parameter \fIegalmix\fR. The
+position angles of the major axis are distributed uniformly between 0.0
+and 360.0 degrees. The axial ratio (major to minor) of the elliptical
+models is allowed to range uniformly between 1 and \fIar\fR
+(that is E0 - E7).
+
+The spiral models have inclinations, i, ranging uniformly between 0 and
+90 degrees. The axial ratio is then given by
+
+ a/b = sqrt (sin(i)**2 * .99 + .01)
+
+which is taken from Holmberg in Galaxies and the Universe (which
+references the work of Hubble). Note the axial ratio is limited to
+0.1 by this formula. An internal absorption correction is then
+made based on the inclination using the relation
+
+ dM = A * (min (10, cosecant (i)) - 1) / 9
+
+where is the absorption of an edge on galaxy relative to face on and
+the cosecant is limited to 10. Note that this correction changes
+allows galaxies with magnitudes less than \fImaxmag\fR and alters
+the luminosity function somewhat. Or in other words, the luminosity
+function is based on absorption corrected magnitudes.
+
+The sizes of the galaxy images are scaled from the maximum half-flux
+radius of an elliptical galaxy given by the parameter \fIeradius\fR.
+This is the radius given to an elliptical galaxy of magnitude
+\fIminmag\fR (prior to adding a random factor described below). The
+ratio between the half-flux radii of the exponential disk and de
+Vaucouleurs models at a given total magnitude is set by the parameter
+\fIsradius\fR (note this is a fraction of \fIeradius\fR and not an
+actual radius). This allows adjusting the relative surface brightness
+of elliptical and spiral models.
+
+The distribution of sizes is based on the apparent
+magnitude of the galaxies. For the power law magnitude distribution
+the cosmological redshift factor for angular diameters is used. The
+redshift/magnitude relation is assumed to be such that the redshift is
+proportional to the luminosity distance (the square root of the
+apparent luminosity). Thus,
+
+
+.nf
+ Z = z * 10. ** (0.2 * (M - minmag))
+ Zfactor = ((1+Z)**2 / Z) / ((1+z)**2 / z)
+ ellipticals: r = eradisus * Zfactor
+ spirals: r = sradius * eradius * Zfactor
+.fi
+
+where z is the reference redshift at the minimum magnitude, and Z is the
+redshift at magnitude M. For very small z the size varies as the
+luminosity distance but at larger z the images appear more extended with
+lower surface brightness. For very deep simulations a pure luminosity
+distance relation gives faint galaxies which are too small and compact
+compared to actual observations.
+
+For the other magnitude distributions, the Schecter cluster function
+in particular where all galaxies are at the same distance, the scale radius
+obeys the following relation.
+
+.nf
+ ellipticals: r = eradius * 10. ** ((minmag - M) / 6)
+ spirals: r = sradius * eradius * 10. ** ((minmag - M) / 6)
+.fi
+
+This relation gives the size decreasing slightly less rapidly than that
+giving a constant surface brightness. This relation is taken from
+Holmberg (Galaxies and the Universe).
+
+A uniform random factor of 50% is added to the sizes computed for
+the power law magnitude distribution and 20% for the other distributions.
+
+The interactive spatial plot shows the positions of the galaxies, the
+galaxy type (circles are de Vaucouleurs profiles and other types are
+diamonds), and rough size.
+.ih
+CURSORS
+The following interactive keystroke commands are available from within the
+GALLIST task.
+
+.nf
+ Gallist Keystroke Commands
+
+? Print options
+f Fit one or more of following
+ Spatial density function (SDF)
+ Luminosity function (LF)
+ Distribution of morphological type
+ Diameter distribution
+ Roundness distribution
+ Position angle distribution
+x Plot the x-y spatial density function
+r Plot the histogram of the radial density function
+m Plot the histogram of the luminosity function
+d Plot the histogram of the diameter values
+e Plot the histogram of the roundness values
+p Plot the histogram of the position angle values
+: Colon escape commands (see below)
+q Exit program
+.fi
+
+The following parameters can be shown or set from within the GALLIST task.
+
+.nf
+ Gallist Colon Commands
+
+:show Show gallist parameters
+:ngal [value] Number of galaxies
+
+:spatial [string] Spatial density function (SDF) (uniform|hubble|file)
+:xmin [value] Minimum X value
+:xmax [value] Maximum X value
+:ymin [value] Minimum Y value
+:ymax [value] Maximum Y value
+:xcenter [value] X center for SDF
+:ycenter [value] Y center for SDF
+:core [value] Core radius for Hubble density function
+:base [value] Background density for Hubble density function
+
+:luminosity [string] Luminosity function (LF)
+ (uniform|powlaw|schecter|file)
+:minmag [value] Minimum magnitude
+:maxmag [value] Maximum magnitude
+:mzero [value] Magnitude zero-point of schecter LF
+:power [value] Power law coefficient for powlaw LF
+:alpha [value] Schecter parameter
+:mstar [value] Characteristic mag for Schecter LF
+
+:egalmix [value] Elliptical/Spiral galaxy ratio
+:ar [value] Minimum elliptical galaxy axial ratio
+:eradius [value] Maximum elliptical half flux radius
+:sradius [value] Spiral/elliptical radius at same magnitude
+:z [value] Minimum redshift
+:absorption [value] Absorption correction for spirals
+
+:lfile [string] Name of the LF file
+:sfile [string] Name of the SDF file
+:nlsample [value] Number of LF sample points
+:lorder [value] Order of spline approximation to the integrated LF
+:nssample [value] Number of SDF sample points
+:sorder [value] Order of spline approximation to the integrated SDF
+
+:rbinsize [value] Resolution of radial SDF histogram in pixels
+:mbinsize [value] Resolution of magnitude histogram in magnitudes
+:dbinsize [value] Resolution of diameter histogram in pixels
+:ebinsize [value] Resolution of roundness histogram in pixels
+:pbinsize [value] Resolution of position angle histogram in degrees
+.fi
+.ih
+EXAMPLES
+1. Create a galaxy cluster with a power law distribution of field galaxies
+and stars as background/foreground.
+
+.nf
+ ar> gallist galaxies.dat 100 spatial=hubble lum=schecter egal=.8
+ ar> gallist galaxies.dat 500
+ ar> starlist galaxies.dat 100
+ ar> mkobjects galaxies obj=galaxies.dat gain=3 rdnoise=10 poisson+
+.fi
+
+Note that the objects are appended to the same file. Actually making
+the image with \fBmkobjects\fR takes about 5 minutes (2.5 min cpu) on a
+SPARCstation 1.
+
+2. Examine the distributions for a uniform spatial distribution
+and power law magnitude distribution using 1000 galaxies without
+creating a data file.
+
+.nf
+ ar> gallist dev$null 1000 inter+
+ ... an x-y plot will appear on the screen
+ ... type r to examine the radial density function
+ ... type m to examine the luminosity function
+ ... type d to examine the half-flux radii distribution
+ ... type e to examine the axial ratio distribution
+ ... type = to make a copy of any of the plots
+ ... type q to quit
+.fi
+.ih
+REVISIONS
+.ls GALLIST V2.11+
+The random number seeds can be set from the clock time by using the value
+"INDEF" to yield different random numbers for each execution.
+.le
+.ls GALLIST V2.11
+The default value for the minimum elliptical galaxy axial ratio was
+change to 0.3 to cover the range E0-E7 uniformly.
+.le
+.ih
+BUGS
+This is a first version and is not intended to produce a full model
+of galaxy fields. Some of the relations used are empirical and
+simple minded with the aim being to produce reasonably realistic images.
+
+The spline approximation to the spatial density and luminosity
+probability functions can cause wiggles in the output spatial density
+and luminosity functions. Users can examine the results interactively
+and experiment with the spline order and number of sample points if
+they are not satisfied with the results of GALLIST. The default setup
+of 10 sample points per spline piece is generally satisfactory for the
+spatial density and luminosity functions supplied here.
+.ih
+SEE ALSO
+starlist mkobjects
+.endhelp
diff --git a/noao/artdata/doc/mk1dspec.hlp b/noao/artdata/doc/mk1dspec.hlp
new file mode 100644
index 00000000..b96684a8
--- /dev/null
+++ b/noao/artdata/doc/mk1dspec.hlp
@@ -0,0 +1,355 @@
+.help mk1dspec Jul95 noao.artdata
+.ih
+NAME
+mk1dspec -- Make/add artificial 1D spectra
+.ih
+USAGE
+mk1dspec input
+.ih
+PARAMETERS
+.ls input
+Spectra to create or modify.
+.le
+.ls output = ""
+Output spectra when modifying input spectra. If no output spectra are
+given then existing spectra in the input list are modified directly.
+If an output list is given then it must match in number the input list.
+.le
+.ls ap = 1
+Image line to be created or modified in images of dimension greater than 1.
+.le
+.ls rv = 0.
+Radial velocity (km/s) or redshift, as selected by the parameter \fIz\fR,
+applied to line positions and continuum. Velocities are converted to
+redshift using the relativistic relation 1+z = sqrt ((1+rv/c)/(1-rv/c)).
+Note the shift is not a shift in the dispersion parameters but in the
+underlying artificial spectrum.
+.le
+.ls z = no
+Is the velocity parameter a radial velocity or a redshift?
+.le
+
+WHEN CREATING NEW SPECTRA
+.ls title = ""
+Image title to be given to the spectra. Maximum of 79 characters.
+.le
+.ls ncols = 512
+Number of columns.
+.le
+.ls naps = 1
+Number of lines or apertures.
+.le
+.ls header = "artdata$stdheader.dat"
+Image or header keyword data file. If an image is given then the image header
+is copied. If a file is given then the FITS format cards are copied.
+This only applies to new images. The data file consists of lines
+in FITS format with leading whitespace ignored. A FITS card must begin
+with an uppercase/numeric keyword. Lines not beginning with a FITS
+keyword such as comments or lower case are ignored. The user keyword
+output of \fBimheader\fR is an acceptable data file. See \fBmkheader\fR
+for further information.
+.le
+.ls wstart = 4000., wend = 8000.
+Starting and ending wavelengths in Angstroms. The dispersion is
+determined by these values and the number of columns.
+.le
+
+CONTINUUM PARAMETERS
+.ls continuum = 1000., slope = 0.
+Continuum of the starting wavelength at rest and the slope of the continuum.
+.le
+.ls temperature = 5700.
+Blackbody continuum temperature in Kelvin. A value of 0 is used if
+no blackbody continuum is desired. The intensity level is set by
+scaling to the continuum level of the starting wavelength at rest.
+.le
+.ls fnu = no
+Compute the continuum as flux per unit frequency (F-nu) if yes or flux per
+unit wavelength (F-lambda) if no.
+.le
+
+
+LINE PARAMETERS
+.ls lines = ""
+List of spectral line files. Spectral line files contain lines of rest
+wavelength, peak, profile type, and widths (see the DESCRIPTION
+section). The latter parameters may be missing or INDEF in which case they
+default to the task \fIpeak\fR, \fIprofile\fR, \fIgfwhm\fR, and \fIlfwhm\fR
+parameters (note that the \fIpeak\fR parameter is not a constant but the
+random number scaling). If no file or a new (nonexistent) file is
+specified then a number of random lines given by the parameter \fInlines\fR
+is generated. If a new file name is specified then the lines generated are
+recorded in the file. If the list of spectral line files is shorter than
+the list of input spectra, the last spectral line list file is reused.
+.le
+.ls nlines = 0
+If no spectral line file or a new file is specified then the task will
+generate this number of random spectral lines. The rest wavelengths are
+uniformly random within the limits of the spectrum, the peaks are uniformly
+random between zero and the value of the \fIpeak\fR parameter, the profile
+type is given by \fIprofile\fR, and the widths are fixed at the values of
+the \fIgfhwm\fR ad \fIlfwhm\fR parameters. If a redshift is applied the
+rest wavelengths are shifted and repeated periodically.
+.le
+.ls profile = "gaussian" (gaussian|lorentzian|voigt)
+The default profile type for random lines or when not specified in the
+spectral line file. The profile types are:
+
+.nf
+ gaussian - Gaussian profile
+ lorentzian - Lorentzian profile
+ voigt - Voigt profile
+.fi
+.le
+.ls peak = -0.5
+The maximum spectral line peak value when generating random lines or
+when the peak is missing from the spectral line file.
+This value is relative to the continuum unless the continuum is zero.
+Negative values are absorption lines and positive values are emission lines.
+.le
+.ls gfwhm = 20., lfwhm = 20.
+The default gaussian and lorentzian full widths at half maximum (FWHM), in
+Angstroms, used when generating random lines or when the widths are missing
+from the spectral line file.
+.le
+.ls seed = 1
+Random number seed. If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+.ls comments = yes
+Include comments recording task parameters in the image header?
+.le
+
+PACKAGE PARAMETERS
+.ls nxsub = 10
+Number of pixel subsamples used in computing the gaussian spectral line
+profiles.
+.le
+.ls dynrange = 100000.
+The gaussian line profiles extend to infinity so a dynamic range, the ratio
+of the peak intensity to the cutoff intensity, is imposed to cutoff
+the profiles.
+.le
+.ih
+DESCRIPTION
+This task creates or modifies one dimensional spectra. with a combination
+of blackbody and linear sloped continuum and emission and absorption
+spectral lines. The spectral lines may be gaussian, lorentzian, or voigt
+profiles. A velocity shift may be applied to the underlying artificial
+spectrum which is shifted into the specified observed wavelength region.
+No noise is included but may be added with the task \fBmknoise\fR. New
+spectra are created with the specified number of pixels, wavelength range,
+and real datatype. When \fInlines\fR is greater than 1 then an image with
+the specified number of lines is created though only the line given by the
+\fIap\fR is will have a spectrum. Existing spectra may be modified in
+place or new spectra output. Spectra are modified by adding the continuum
+and lines defined by the parameters.
+
+For new images a set of header keywords may be added by specifying an image
+or data file with the \fIheader\fR parameter (see also \fBmkheader\fR). If
+a data file is specified lines beginning with FITS keywords are entered in
+the image header. Leading whitespace is ignored and any lines beginning
+with words having lowercase and nonvalid FITS keyword characters are
+ignored. In addition to this optional header, parameters for the
+wavelength coordinates are defined. Finally, comments may be added to the
+image header recording the task parameters and any information from the
+line file which are not line definitions.
+
+Initially all spectra are created without a dispersion function; i.e.
+pixel coordinates. For multiple spectra in an image this task must be
+executed for each image line to set the dispersion function and add data.
+When an image line is selected if it has a defined dispersion function that
+is used otherwise the task wavelength parameters are used.
+
+A continuum is defined by the value at the starting wavelength at rest, a
+slope, and a blackbody function of a given temperature. The blackbody
+function is scaled to have the specified continuum value at the starting
+wavelength at rest. The blackbody flux units are per unit wavelength
+(F-lambda). A zero continuum value or a zero temperature will not produce a
+blackbody continuum.
+
+Spectral lines are modeled by gaussian, lorentzian, or voigt profiles of
+specified wavelength, peak, and widths. The lines are defined in a
+spectral line file or generated randomly. A spectral line file consists of
+text lines giving rest wavelength, peak, profile type, gaussian full width
+at half maximum and/or lorentzian full width at half maximum. Only the
+wavelength is required and subsequent fields may be missing or given as
+INDEF. The following table shows the possible formats where wavelength,
+peak, gfwhm, and lfwhm are values of wavelength, peak, gaussian FWHM, and
+lorentzian FWHM. The profile types are as shown though they may be
+abbreviated to one character.
+
+.nf
+ wavelength
+ wavelength peak
+ wavelength peak gaussian
+ wavelength peak gaussian gfwhm
+ wavelength peak gaussian gfwhm
+ wavelength peak lorentzian
+ wavelength peak lorentzian lfwhm
+ wavelength peak lorentzian lfwhm
+ wavelength peak voigt
+ wavelength peak voigt gfwhm
+ wavelength peak voigt gfwhm lfwhm
+ wavelength peak voigt gfwhm lfwhm
+.fi
+
+When a field is missing or INDEF the values given by the parameters
+\fIpeak\fR, \fIprofile\fR, \fIgfwhm\fR, and \fIlfwhm\fR are used. If a
+peak value is missing, random values between zero and the \fIpeak\fR value
+are generated. Note that to get random line intensities with some
+specified profile type and widths the value INDEF would be used for
+the peak field.
+
+If no spectral line file is specified or a new (nonexistent) file is named
+then the number of random lines given by the parameter \fInlines\fR is
+generated. The rest wavelengths are uniformly random within the wavelength
+range of the spectrum and extend periodically outside this range in the
+case of an applied velocity shift, the peaks are uniformly random between
+zero and the \fIpeak\fR parameter, and the profile type and widths are
+given by the \fIprofile\fR, \fIgfwhm\fR, and \fIlfwhm\fR parameters. If a
+new file is named then the parameters of the generated lines will be
+output.
+
+The peak values are taken relative to a positive continuum. In other
+words the generated line profile is multiplied by the continuum (with a
+minimum of zero for fully saturated absorption lines). If the
+continuum is less than or equal to zero, as in the case of an
+artificial arc spectrum or pure emission line spectrum, then the peak
+values are absolute intensities. Positive peak values produce emission
+lines and negative values produce absorption lines. Odd results will
+occur if the continuum has both positive and zero or negative values.
+
+The underlying rest spectrum may be shifted. This is used primarily for
+testing radial velocity measuring algorithms and is not intended as a
+complete model of redshift effects. The starting and ending wavelengths
+are not changed by redshifting; these are the instrumental observed
+wavelengths. Input line wavelengths are specified at rest and then
+shifted into or out of the final spectrum. To be realistic the line
+list should include wavelengths over a great enough range to cover
+all desired redshifts. The peaks and widths are also appropriately
+modified by a redshift. As an example, if the redshift is 1 the
+lines will appear broader by a factor of 2 and the peaks will be down
+by a factor of 2 in order to maintain the same flux.
+
+The random line generation is difficult in that one wants to have the
+same set of lines (for a given seed) observed at different redshifts.
+What is done is that the specified number of random lines is generated
+within the observed wavelength interval taken at rest. This set is
+then repeated periodical over all wavelengths. A redshift will then
+shift these rest lines in to or out of the observed spectrum. If the
+lines are output, they are given at rest. \fBNote that this
+periodicity may be important in interpreting cross correlation redshift
+tests for large shifts between template and object spectra.\fR
+
+The definitions of the continuum are also affected by a redshift.
+The reference point for the continuum level, slope, and blackbody
+continuum is the starting wavelength taken at rest. Shifts will then
+modify the continuum level at the first pixel appropriately. In
+particular a large redshift will shift the blackbody in such a way that
+the flux is still given by the \fIcontinuum\fR parameter at the starting
+wavelength at rest.
+.ih
+EXAMPLES
+1. Create a simple blackbody continuum between the default wavelengths.
+
+.nf
+ cl> mk1dspec bb title=Blackbody
+.fi
+
+2. Create a random absorption spectrum on a blackbody continuum without
+saving the line list.
+
+.nf
+ cl> mk1dspec bbab title=Absorption nlines=100
+.fi
+
+3. Create a random absorption spectrum with noise and cosmic rays.
+
+.nf
+ cl> mk1dspec bbab title=Absorption nlines=100
+ cl> mknoise bbab rdnoise=10 poisson+ ncos=5 energy=1000
+.fi
+
+4. Create a random emission spectrum on a blackbody continuum and save
+the line list.
+
+.nf
+ cl> mk1dspec bbem title=Emission nl=30 peak=0.6 lines=bbem.dat
+.fi
+
+5. Create an artificial random arc line spectrum.
+
+.nf
+ cl> mk1dspec arc title="Arc lines" cont=0 peak=500 nl=30
+.fi
+
+6. Create a test spectrum with a line list.
+
+.nf
+ cl> type linelist
+ 4100 -.1 g 20
+ 4200 -2. g 20
+ 4300 -.3 g 20
+ 5100 -.9 g 2
+ 5200 -.9 g 4
+ 5300 -.9 g 8
+ 6700 .9 g 8
+ 6800 .9 g 2
+ 6900 .9 g 4
+ 7700 .3 g 20
+ 7800 .2 g 20
+ 7900 .1 g 20
+ cl> mk1dspec testspec title=Test cont=500 temp=0 lines=linelist
+.fi
+
+7. Add absorption lines to a spectrum.
+
+.nf
+ cl> mk1dspec bb out=artspec cont=0 lines=STDIN
+ 4300 -60
+ 5000 -200
+ [EOF]
+.fi
+
+Normally the input spectrum would be a real spectrum.
+
+8. Make two spectra taken from the same set of random lines but differing
+in redshift.
+
+.nf
+ cl> mk1dspec restspec nl=30
+ cl> mk1dspec redspec rv=3000 nl=30
+ cl> mk1dspec bluespec rv=-.01 z+ nl=30
+.fi
+
+9. Make a multispec image with 5 apertures and a range of redshifts.
+
+.nf
+ cl> mk1dspec spec.ms ap=1 nl=30 rv=0 naps=5
+ cl> mk1dspec spec.ms ap=2 nl=30 rv=1000
+ cl> mk1dspec spec.ms ap=3 nl=30 rv=2000
+ cl> mk1dspec spec.ms ap=4 nl=30 rv=3000
+ cl> mk1dspec spec.ms ap=5 nl=30 rv=4000
+.fi
+.ih
+REVISIONS
+.ls MK1DSPEC V2.11+
+The random number seed can be set from the clock time by using the value
+"INDEF" to yield different random numbers for each execution.
+.le
+.ls MK1DSPEC V2.11
+Lorentzian and Voigt profiles were added and the parameters and input
+line list format were changed. The widths are now FWHM instead of
+gaussian sigmas.
+.le
+.ls MK1DSPEC V2.10.3
+The format parameter was eliminated and the task updated to produce the
+current coordinate system format.
+.le
+.ih
+SEE ALSO
+mknoise, mk2dspec, mkheader, onedspec.sinterp
+.endhelp
diff --git a/noao/artdata/doc/mk2dspec.hlp b/noao/artdata/doc/mk2dspec.hlp
new file mode 100644
index 00000000..44336400
--- /dev/null
+++ b/noao/artdata/doc/mk2dspec.hlp
@@ -0,0 +1,207 @@
+.help mk2dspec Aug90 noao.artdata
+.ih
+NAME
+mk2dspec -- Make/add 2D spectra using 1D spectra templates
+.ih
+USAGE
+mk2dspec input
+.ih
+PARAMETERS
+.ls input
+Spectra to create or modify.
+.le
+.ls output = ""
+Output spectra when modifying input spectra. If no output spectra are
+given then existing spectra in the input list are modified directly.
+If an output list is given then it must match in number the input list.
+.le
+.ls models = ""
+List of model parameter files. If the list of model files is shorter than the
+list of input images then the last model file is reused. The model
+parameter files contain lines giving one dimensional spectrum template
+name, intensity scale, type of cross dispersion profile, profile
+width in the center line, change of width per line, profile position
+in the center line, and change of position per line (see the DESCRIPTION
+section).
+.le
+.ls comments = yes
+Include comments recording task parameters in the image header?
+.le
+
+WHEN CREATING NEW SPECTRA
+.ls title = ""
+Image title to be given to the spectra. Maximum of 79 characters.
+.le
+.ls ncols = 100, nlines = 512
+Number of columns and lines.
+.le
+.ls header = "artdata$stdheader.dat"
+Image or header keyword data file. If an image is given then the image header
+is copied. If a file is given then the FITS format cards are copied.
+This only applies to new images. The data file consists of lines
+in FITS format with leading whitespace ignored. A FITS card must begin
+with an uppercase/numeric keyword. Lines not beginning with a FITS
+keyword such as comments or lower case are ignored. The user keyword
+output of \fBimheader\fR is an acceptable data file. See \fBmkheader\fR
+for further information.
+.le
+.ih
+DESCRIPTION
+This task creates or modifies two dimensional spectra by taking one
+dimensional spectra, convolving them with a spatial profile across the
+dispersion, and adding them into two dimensional images. The one
+dimensional spectra may be real data or artificial data created with
+the task \fBmk1dspec\fR. No noise is included but may be added with
+the task \fBmknoise\fR. The spatial profile is fully subsampled and
+may vary in width and position along the dispersion axis. The spatial
+axis is along the first dimension and the dispersion is along the
+second dimension.
+
+For new images a set of header keywords may be added by specifying an
+image or data file with the \fIheader\fR parameter (see also \fBmkheader\fR).
+If a data file is specified lines beginning with FITS keywords are
+entered in the image header. Leading whitespace is ignored and any
+lines beginning with words having lowercase and nonvalid FITS keyword
+characters are ignored. In addition, comments may be added to
+the image header recording the model file name and the contents of the
+model file.
+
+The spatial profile models are specified in one or more model parameter
+files. These files contain lines giving a one dimensional spectrum template
+name, intensity scale, type of cross dispersion profile, profile
+width in the center line, change of width per line, profile position
+in the center line, and change of position per line. More specifically:
+
+.ls <template name>
+The one dimensional spectrum template is any one dimensional IRAF image.
+If the spectrum template length is less than the two dimensional spectrum,
+the profile extends only over that number of lines and, if it is longer,
+then only the first part of the spectrum is used.
+.le
+.ls scale
+The template spectrum is scaled by this parameter to define the
+total flux for the two dimensional profile.
+.le
+.ls <profile type>
+The spatial profiles are identified by two keywords, "gaussian"
+or "slit". The profiles are defined by the following formulae,
+
+.nf
+ gaussian: I(x) = exp (-ln(2) * (2*(x-xc)/fwhm)**2)
+ slit: I(x) = exp (-ln(2) * (2*(x-xc)/fwhm)**10)
+.fi
+
+where x is the column coordinate, xc is the profile center, and
+fwhm is the full width at half maximum. The "gaussian" profile
+is the usual gaussian specified in terms of a FWHM. The "slit"
+profile is one which is relatively flat and then rapidly drops
+to zero. The profile is normalized to unit integral so that
+the total flux across the profile is given by the scaled
+1D spectrum flux.
+.le
+.ls fwhm, dfwhm
+The full width at half maximum and derivative with line number. The fwhm is
+defined for the middle of the image. The FWHM as a function
+of line, l, is,
+
+ fwhm + (l - nlines/2) * dfwhm
+.le
+.ls center, dcenter
+The profile center and derivative with line number. The center is
+defined for the middle of the image. The center as a function
+of line, l, is,
+
+ center + (l - nlines/2) * dcenter
+.le
+
+The provision for having the spectra tilted relative to the columns is
+useful for understanding undersampling effects. However, note that the
+spectral lines are not perpendicular to the dispersion but are always
+aligned with the image lines.
+.ih
+EXAMPLES
+1. Create an artificial multifiber spectrum:
+
+.nf
+ cl> type multifiber.dat
+ arc 4 gauss 3 0 20 .01
+ spec1 .5 gauss 3 0 30 .01
+ spec2 .4 gauss 3 0 40 .01
+ spec3 .9 gauss 3 0 50 .01
+ spec4 .2 gauss 3 0 60 .01
+ spec5 .6 gauss 3 0 70 .01
+ spec6 1 gauss 3 0 80 .01
+ spec7 1 gauss 3 0 90 .01
+ cl> mk1dspec arc cont=0 peak=500 nl=30
+ cl> mk1dspec spec1 nlines=99 seed=1
+ cl> mk1dspec spec2 nlines=80 seed=2
+ cl> mk1dspec spec3 nlines=45 seed=3
+ cl> mk1dspec spec4 nlines=95 seed=4
+ cl> mk1dspec spec5 nlines=66 seed=5
+ cl> mk1dspec spec6 nlines=90 seed=6
+ cl> mk1dspec spec7 nlines=85 seed=7
+ cl> mk2dspec multifiber model=multifiber.dat
+.fi
+
+In this example artificial one dimensional spectra are generated with
+\fBmk1dspec\fR.
+
+2. Create an artificial multislit spectrum:
+
+.nf
+ cl> type multislit.dat
+ arc 10 slit 18 0 120 .01
+ sky 2.5 slit 18 0 140 .01
+ sky 2.5 slit 18 0 160 .01
+ sky 2.5 slit 18 0 180 .01
+ sky 2.5 slit 18 0 200 .01
+ sky 2.5 slit 18 0 220 .01
+
+ spec1 .05 gauss 3 0 140 .01
+ spec2 .2 gauss 4 0 161 .01
+ spec3 .1 gauss 3 0 179 .01
+ spec4 .1 gauss 3 0 200 .01
+ spec5 .15 gauss 4 0 220 .01
+ cl> mk1dspec sky peak=1 nl=100
+ cl> mk2dspec multislit model=multislit.dat nc=400
+.fi
+
+Note how two spectra are overlaid to provide a sky spectrum with a
+narrower object spectrum.
+
+3. Create an artificial long slit spectrum:
+
+.nf
+ cl> type longslit.dat
+ sky 22 slit 160 0 220 .01
+ spec5 .05 gauss 3 0 140 .01
+ spec1 .05 gauss 3 0 190 .01
+ spec4 .5 gauss 3 0 220 .01
+ spec2 2 gauss 40 0 220 .01
+ spec5 .1 gauss 3 0 240 .01
+ spec1 .02 gauss 3 0 290 .01
+ cl> mk2dspec longslit model=longslit.dat nc=400
+.fi
+
+Note how objects are overlaid on a long slit sky spectrum. The width
+of the spec2 spectrum is wider simulating a galaxy spectrum.
+
+4. To include noise use the task \fBmknoise\fR:
+
+.nf
+ cl> mk2dspec longslit model=longslit.dat nc=400
+ cl> mknoise longslit rdnoise=10 gain=2 poisson+ ncos=100
+.fi
+
+5. Use a real long slit spectrum and add an object with an artificial spectrum:
+
+.nf
+ cl> mk1dspec artspec1d nlines=50
+ cl> mk2dspec ls005 out=ls005new model=STDIN
+ artspec1d 1 gauss 5 0 125 0
+ [EOF]
+.fi
+.ih
+SEE ALSO
+mk1dspec, mknoise, mkheader
+.endhelp
diff --git a/noao/artdata/doc/mkechelle.hlp b/noao/artdata/doc/mkechelle.hlp
new file mode 100644
index 00000000..34e022c5
--- /dev/null
+++ b/noao/artdata/doc/mkechelle.hlp
@@ -0,0 +1,585 @@
+.help mkechelle Mar93 noao.artdata
+.ih
+NAME
+mkechelle -- Make artificial echelle spectra
+.ih
+USAGE
+mkechelle images [clobber]
+.ih
+PARAMETERS
+.ls images
+List of echelle spectra to create or modify.
+.le
+.ls clobber (query)
+If an existing image is specified the clobber query parameter is used.
+Normally the parameter is not specified on the command line so that
+a query will be made for each image which exists. Putting a value
+on the command line permanently overrides the query. This should be
+done if the task is run in the background.
+.le
+.ls ncols = 512, nlines = 512, norders = 23
+For two dimensional spectra these parameters define the number of columns
+and lines in the final image and the maximum number of orders (there may be
+orders falling outside the image). The dispersion is along the columns
+which is the second or line axis (dispersion axis is 2) so the number of
+columns is the number of pixels across the dispersion and the number of
+lines is the number of pixels along the dispersion per order.
+
+The extracted format turns the number of lines into the number columns
+and the number of orders is the number of lines; i.e the image
+has the specified number of extracted orders, one per image line,
+with the number of pixels along the dispersion specified by the
+\fInlines\fR parameter. This is equivalent to what the \fBapextract\fR
+package would produces for an extracted echelle format with an original
+dispersion axis of 2. There is no check in this case for orders
+which might fall outside the two dimensional format; i.e. exactly
+the number of orders are created.
+.le
+.ls title = "Artificial Echelle Spectrum"
+Image title to be given to the spectra. Maximum of 79 characters.
+.le
+.ls header = "artdata$stdheader.dat"
+Image or header keyword data file. If an image is given then the image
+header is copied. If a file is given then the FITS format cards are
+copied. The data file consists of lines in FITS format with leading
+whitespace ignored. A FITS card must begin with an uppercase/numeric
+keyword. Lines not beginning with a FITS keyword such as comments or lower
+case are ignored. The user keyword output of \fBimheader\fR is an
+acceptable data file. See \fBmkheader\fR for further information.
+.le
+.ls list = no
+List the grating/instrument parameters?
+.le
+.ls make = yes
+Make the artificial spectra? This is set to no if only the grating
+parameter listing is desired.
+.le
+.ls comments = yes
+Include comments recording task parameters in the image header?
+.le
+
+.ce
+FORMAT PARAMETERS
+.ls xc = INDEF, yc = INDEF
+The column and line position of the blaze peak in the reference order (see
+\fIorder\fR parameter. If INDEF then the middle of the dimension is used.
+This allows setting the image center relative to the center of the echelle
+pattern. As with the number of lines and columns the interpretation of
+these numbers relative to the image created depends on whether the format
+is extracted or not.
+.le
+.ls pixsize = 0.027
+Pixel size in millimeters. This is used to convert the focal length
+and dispersion to pixels. If INDEF then these parameters are
+assumed to be in pixels.
+.le
+.ls profile = "gaussian" (extracted|gaussian|slit)
+The order profile across the dispersion. If the value is "extracted"
+then an extracted echelle format spectrum is produced. Otherwise a
+two dimensional format with a gaussian or slit profile is produced.
+See \fBmk2dspec\fR for a discussion of the profile functions.
+.le
+.ls width = 5.
+If two dimensional echelle images are selected this parameter specifies
+the order profile full width at half maximum in pixels. See \fBmk2dspec\fR
+for a fuller discussion.
+.le
+.ls scattered = 0.
+Scattered light peak flux per pixel. A simple scattered light component
+may be included in the two dimensional format. The scattered light has the
+blaze function shape of the central order along the dispersion and the
+crossdisperser blaze function shape across the dispersion with the peak
+value given by this parameter. A value of zero indicates no scattered
+light component.
+.le
+
+.ce
+GRATING PARAMETERS
+
+Any of the following parameters may be specified as INDEF. The missing
+values are resolved using the grating equations described in the
+DESCRIPTION section. If it is not possible to resolve all the grating
+parameters but the order, wavelength, and dispersion are specified
+then a linear dispersion function is used. Also in this case the
+extracted format will include dispersion information.
+.ls f = 590., cf = 590.
+Echelle and crossdisperser focal lengths in millimeters (if \fIpixsize\fR
+is given) or pixels. Technically it is defined by the equation x = f * tan
+(theta) where x is distance from the optical axis on the detector and theta
+is the diffraction angle; i.e. it converts angular measures to millimeters
+or pixels on the detector. If the focal length is specified as INDEF it
+may be computed from the dispersion, which is required in this case, and
+the other parameters.
+.le
+.ls gmm = 31.6, cgmm = 226.
+Echelle and crossdisperser grating grooves per millimeter. If specified as
+INDEF it may be computed from the order, which is required in this case,
+and the other parameters.
+.le
+.ls blaze = 63., cblaze = 4.53
+Echelle and crossdisperser blaze angles in degrees. It is always specified or printed as a positive
+angle relative to the grating normal. If specified as INDEF it is
+computed from the other parameters.
+.le
+.ls theta = 69., ctheta = -11.97
+Echelle and crossdisperser angles of incidence in degrees. The angle of
+incidence must be in the plane perpendicular to face of the grating. The
+angle of incidence may be specified relative to the grating normal or the
+blaze angle though it is always printed relative to the grating normal. To
+specify it relative to the blaze angle add 360 degrees; for example to have
+an angle of 15 degrees less than the blaze angle specify 360 - 15 = 345.
+If the angle of incidence is specified as INDEF it is computed from the
+other parameters.
+.le
+.ls order = 112
+The central or reference echelle order for which the wavelength and
+dispersion are specified. If specified as INDEF it will be computed from
+the grooves per mm, which is required in this case, and the other
+parameters. In combination with the number of orders this defines the
+first and last orders. The highest order is the central order plus
+the integer part of one half the number of orders. However, the
+lowest order is constrained to be at least 1. The
+reference order is also used in the definitions of \fIxc\fR and \fIyc\fR.
+.le
+.ls corder = 1
+The crossdisperser order for which the crossdisperser blaze wavelength and
+dispersion are specified. If specified as INDEF it will be computed from
+the grooves per mm, which is required in this case, and the other
+parameters.
+
+If the order is zero then the other grating parameters are ignored and a
+prism-like dispersion is used with the property that the order spacing is
+constant. Specifically the dispersion varies as the inverse of the
+wavelength with the \fIcwavelength\fR and \fIcdispersion\fR defining the
+function.
+.le
+.ls wavelength = 5007.49 cwavelength = 6700.
+Echelle and crossdisperser blaze wavelengths in Angstroms at the reference
+orders. If specified as INDEF it will be computed from the other parameters.
+.le
+.ls dispersion = 2.61 cdispersion = 70.
+Echelle and crossdisperser blaze dispersions in Angstroms per millimeter
+(if \fIpixsize\fR is specified) or pixels.
+If specified as INDEF it will be computed from the focal length, which is
+required in this case, and the other parameters.
+.le
+
+.ce
+SPECTRA PARAMETERS
+.ls rv = 0.
+Radial velocity (km/s) or redshift, as selected by the parameter \fIz\fR,
+applied to line positions and continuum. Velocities are converted to
+redshift using the relativistic relation 1+z = sqrt ((1+rv/c)/(1-rv/c)).
+Note the shift is not a shift in the dispersion parameters but in the
+underlying artificial spectrum.
+.le
+.ls z = no
+Is the velocity parameter a radial velocity or a redshift?
+.le
+.ls continuum = 1000.
+Continuum at the echelle blaze peak in the reference order.
+.le
+.ls temperature = 5700.
+Blackbody continuum temperature in Kelvin. A value of 0 is used if
+no blackbody continuum is desired. The intensity level is set by
+scaling to the continuum level at blaze peak reference point.
+.le
+
+.ls lines = ""
+List of spectral line files. Spectral line files contain lines of rest
+wavelength, peak, and widths (see the DESCRIPTION section).
+The latter two parameters may be missing in which case they default to
+the task \fIpeak\fR and \fIsigma\fR parameters. If no file or a new
+(nonexistent) file is specified then a number of random lines given by the
+parameter \fInlines\fR is generated. If a new file name is specified then
+the lines generated are recorded in the file. If the list of spectral
+line files is shorter than the list of input spectra, the last
+spectral line list file is reused.
+.le
+.ls nlines = 0
+If no spectral line file or a new file is specified then the task will
+generate this number of random spectral lines. The rest wavelengths are
+uniformly random within the limits of the spectrum, the peaks are
+uniformly random between zero and the value of the \fIpeak\fR parameter
+and the width is fixed at the value of the \fIsigma\fR parameter.
+If a redshift is applied the rest wavelengths are shifted and repeated
+periodically.
+.le
+.ls peak = -0.5
+The maximum spectral line peak value when generating random lines or
+when the peak is missing from the spectral line file.
+This value is relative to the continuum unless the continuum is zero.
+Negative values are absorption lines and positive values are emission lines.
+.le
+.ls sigma = 1.
+The default line width as a gaussian sigma in Angstroms when generating
+random lines or when the width is missing from the spectral line file.
+.le
+.ls seed = 1
+Random number seed.
+.le
+
+PACKAGE PARAMETERS
+.ls nxsub = 10
+Number of pixel subsamples used in computing the gaussian spectral line
+profiles.
+.le
+.ls dynrange = 100000.
+The gaussian line profiles extend to infinity so a dynamic range, the ratio
+of the peak intensity to the cutoff intensity, is imposed to cutoff the
+profiles.
+.le
+.ih
+DESCRIPTION
+This task creates or adds to artificial extracted (one dimensional
+"echelle" format) or two dimensional echelle spectra. The input spectrum
+(before modification by the spectrograph model) may be a combination of
+doppler shifted blackbody or constant continuum and emission and absorption
+gaussian profile spectral lines. The lines may have randomly selected
+parameters or be taken from an input file. Note that the parameters and
+method is similar to the task \fBmk1dspec\fR except that the input line list
+cannot specify a profile type and only Gaussian profiles are currently
+allowed. The input spectrum is then
+separated out into echelle orders and either recorded as extracted one
+dimensional orders or convolved with a spatial profile and crossdispersed
+into a two dimensional image. The properties of the echelle grating,
+crossdisperser, and instrumental configuration are modeled described
+later.
+
+If an existing image is specified the \fIclobber\fR parameter is used
+to determine whether to add the generated artificial echelle spectrum
+to the image. Generally the clobber parameter is not specified on the
+command line to cause a query with the image name to be made for
+each image which already exists. However, it is possible to put
+the clobber parameter on the command line to eliminate the query.
+This is appropriate for running the task in the background.
+
+There is \fIno\fR checking for consistency with an existing image;
+i.e. that it is an echelle image, whether it is an extracted format
+or a two dimensional spectrum, and what it's wavelength and order
+coverage is. The only thing that happens is that the \fIncols\fR,
+\fInlines\fR, and \fInorders\fR parameters are replaced by the appropriate
+dimensions of the image with the choice between \fInlines\fR and
+\fInorders\fR made by the \fIprofile\fR parameter (as discussed below)
+and not by the format of the image.
+
+The created spectra are two dimensional, real datatype, images. A title
+may be given and a set of header keywords be added by specifying an image
+or data file with the \fIheader\fR parameter (see also \fBmkheader\fR). If
+a data file is specified lines beginning with FITS keywords are entered in
+the image header. Leading whitespace is ignored and any lines beginning
+with words having lowercase and nonvalid FITS keyword characters are
+ignored. In addition to this optional header, various parameters which
+occur during reduction of real echelle spectra, such a wavelength
+coordinates for extracted and dispersion corrected spectra, are added.
+Finally, comments may be added to the image header recording the task
+parameters and any information from the line file which are not line
+definitions.
+
+The creation of an artificial echelle spectra has three stages. First a
+true spectrum is generated; i.e. the spectrum which arrives at the
+spectrograph. The spectrum is then separated into orders and the
+dispersion and blaze functions of the echelle and crossdisperser gratings
+(or crossdisperser prism) are applied. Finally, if a two dimensional
+format is desired it is convolved by an spatial profile (either a gaussian
+or a broader slit-like profile) and the orders are placed as required by
+the crossdispersion relation.
+
+Generation of the model spectrum has three parts; defining a continuum,
+adding emission and absorption lines, and applying a doppler shift. The
+continuum has two parameters; an intensity scale set by the \fIcontinuum\fR
+parameter and a shape set by the \fItemperature\fR parameter. The
+intensity scale is set by defining the total, final, extracted intensity in
+a pixel at the blaze peak (rest) wavelength in the reference order; i.e. at
+the wavelength set by the \fIwavelength\fR parameter. Note this means that
+the efficiency of the gratings at that wavelength is included. The shape
+of the continuum may be either a blackbody if a positive temperature is
+specified or constant.
+
+Spectral lines are modeled by gaussian profiles of specified wavelength,
+peak, and sigma. The lines are defined in a spectral line file or
+generated randomly. A spectral line file consists of text lines giving
+rest wavelength, peak, and sigma. The sigma or the sigma and peak may be
+absent in which case the parameters \fIsigma\fR and \fIpeak\fR will be
+used. If peak values are missing random values between zero and the
+\fIpeak\fR value are generated. Thus, a simple list of wavelengths or a
+list of wavelengths and peaks may be used.
+
+If no spectral line file is specified or a new (nonexistent) file is named
+then the number of random lines given by the parameter \fInlines\fR is
+generated. The rest wavelengths are uniformly random within the wavelength
+range of the spectrum and extend periodically outside this range in the
+case of an applied velocity shift, the peaks are uniformly random between
+zero and the \fIpeak\fR parameter, and the widths are given by the
+\fIsigma\fR parameter. If a new file is named then the parameters of the
+generated lines will be output.
+
+The peak values are taken relative to a positive continuum. In other words
+the generated line profile is multiplied by the continuum (with a minimum
+of zero for fully saturated absorption lines). If the continuum is less
+than or equal to zero, as in the case of an artificial arc spectrum or pure
+emission line spectrum, then the peak values are interpreted as absolute
+intensities. Positive peak values produce emission lines and negative
+values produce absorption lines. Odd results will occur if the continuum
+has both positive and zero or negative values.
+
+The width values are gaussian sigmas given in Angstroms.
+
+The underlying rest spectrum may be shifted. This is used primarily for
+testing radial velocity measuring algorithms and is not intended as a
+complete model of redshift effects. The observed wavelength coverage as
+defined by the grating parameters and number of orders is not changed by
+redshifting. Input line wavelengths are specified at rest and then shifted
+into or out of the final spectrum. To be realistic the line list should
+include wavelengths over a great enough range to cover all desired
+redshifts. The peaks and sigma are also appropriately modified by a
+redshift. As an example, if the redshift is 1 the lines will appear
+broader by a factor of 2 and the peaks will be down by a factor of 2 in
+order to maintain the same flux.
+
+The random line generation is complicated because one wants to have the
+same set of lines (for a given seed) observed at different redshifts. What
+is done is that the specified number of random lines is generated within
+the observed wavelength interval taken at rest. This set is then repeated
+periodical over all wavelengths. A redshift will then shift these rest
+lines in to or out of the observed spectrum. If the lines are output to a
+line file, they are given at rest. \fBNote that this periodicity may be
+important in interpreting cross-correlation redshift tests for large shifts
+between template and object spectra.\fR
+
+The definitions of the continuum are also affected by a redshift. The
+reference point for the continuum level and blackbody shape is the starting
+wavelength taken at rest. Shifts will then modify the continuum level at
+the reference pixel appropriately. In particular a large redshift will
+shift the blackbody in such a way that the flux is still given by the
+\fIcontinuum\fR parameter at the reference wavelength at rest.
+
+Once the input spectrum is defined it is modified by the effects of an
+echelle grating and crossdispersion. This includes the dispersion relation
+between pixel and wavelength, the blaze response function of the gratings,
+and separation into orders.
+
+The primary reference for the model of the echelle grating (a
+crossdisperser grating also obeys this model) used in this task is "Echelle
+efficiencies: theory and experiment" by Schroeder and Hilliard in Applied
+Optics, Vol. 19, No. 16, 1980, p. 2833. (The nomenclature below is similar
+to that paper except we use theta for alpha, their theta is theta - blaze,
+the reciprocal of the groove spacing which is the grooves per millimeter,
+and the dispersion per linear distance at the detector rather than per
+radian). This task only treats the case where the incident beam is in the
+plane perpendicular to the grating face (gamma=0). In this case the basic
+equation is
+
+.nf
+(1) m * lambda = (sin(theta) + sin(beta)) / g
+.fi
+
+where m is the order, lambda the wavelength, g the grooves per wavelength
+unit, theta the angle of incidence to the grating normal, and beta the
+angle of diffraction to the normal. The diffraction angle relative to that
+of the blaze maximum, psi, is given by
+
+.nf
+(2) beta = psi + 2 * blaze - theta
+.fi
+
+where blaze is the blaze angle. The diffraction angle psi is related to
+position on the detector, again measured from the blaze peak, by
+
+.nf
+(3) x = f / pixsize * tan(psi)
+.fi
+
+where f is the effective focal length (as defined by this equation) and
+pixsize is the pixel size in millimeters that converts the detector
+positions to pixels. If a pixel size is not specified then f will be
+taken as being in pixels.
+
+The second basic equation is the diffraction pattern or blaze response
+given by
+
+.nf
+(5) I = I0 * (sin(delta) / delta) ** 2
+(6) delta = 2 * pi / lambda * (cos(theta) / g) / cos(epsilon) *
+ sin(psi/2) * cos(epsilon-psi/2)
+(7) epsilon = theta - blaze
+.fi
+
+where epsilon is the angle between the blaze angle and the angle of
+incidence (the theta of Shroeder and Hilliard). When theta = blaze, (6)
+simplifies to
+
+.nf
+(6a) delta = pi / lambda * (cos (blaze) / g) * sin (psi)
+.fi
+
+As discussed by Schroeder and Hilliard, the relative intensity at the blaze
+peak, I0, must be reduced by the fraction of light at the same wavelength
+as the blaze peak which is diffracted into other orders. Furthermore at
+some diffraction angles the light is reflected off the second face of the
+grating giving a different effective diffraction angle to be used in (6).
+This computation is done by the task giving a variation in relative blaze
+response with order and reproducing the calculations of Schroeder and
+Hilliard. The absolute normalization, including the crossdisperser blaze
+function if any, is such that the response at the blaze peak of the
+reference order is unity. This insures that specified continuum level at
+the reference wavelength is produced.
+
+At the blaze maximum psi = x = 0 and the wavelength and dispersion per
+millimeter on the detector are given by (1) and the derivative of (1) with
+respect to x:
+
+.nf
+(8) wavelength = 1E7*(sin(theta)+sin(2*blaze-theta))/(gmm*order)
+(9) dispersion = 1E7*cos(2*blaze-theta)/(gmm*order*f/pixsize)
+.fi
+
+The variable names are the same as the parameters in this task. In
+particular, gmm is the echelle grooves per millimeter with the factors of
+1E7 (10 to the seventh power) to convert to Angstroms, the factor of f /
+pixsize to convert the dispersion to per pixel, and order is the reference
+order for the wavelength and dispersion.
+
+The \fBmkechelle\fR task provides different ways to define the parameters.
+If there is insufficient information to determine all the grating
+parameters but the wavelength, dispersion, order are specified then
+a simplified grating equation is used which is linear with pixel
+position. The approximation is that tan(psi) = sin(psi) = psi so
+that
+
+.nf
+(9) lambda = (order * wavelength + dispersion * x) / m
+ = (a + b * x) / m
+(10) delta = pi * order * dispersion / lambda * x
+ = c / lambda * x
+.fi
+
+Also in this case the extracted format (described later) includes
+wavelength information in the header so that the spectra appear as fully
+dispersion corrected.
+
+If there are at least five of the seven grating parameters specified
+then equations (8) and (9) are used to determine
+unspecified parameters or to override parameters if the equations are
+overspecified. For example, suppose the grooves per millimeter is known
+but not the blaze angle or focal length. Then the wavelength and
+dispersion at the reference order are used to compute these quantities.
+
+The full set of grating parameters derived and used to create the spectra
+are documented in the image header if the \fIcomments\fR parameter is
+specified. Also the \fIlist\fR parameter may be set to print the grating
+parameters and the \fImake\fR parameter may be set to no to check the
+grating parameters without making the spectra.
+
+The crossdisperser grating parameters are treated exactly as above except,
+since only one order is used, the relative blaze efficiency is not
+computed.
+
+There is a variant on the crossdispersion to allow a prism-like separation
+of the echelle orders. If the crossdispersion grating order, \fIcorder\fR
+is set to zero then the other grating parameters are ignored and a
+prism-like dispersion is used with the property that the order spacing is
+constant. Specifically the dispersion varies as the inverse of the
+wavelength with the \fIcwavelength\fR and \fIcdispersion\fR defining the
+function. There is no crossdisperser blaze function in this case either;
+i.e. the relative intensities between orders is solely due to the echelle
+grating blaze response.
+
+There is an interesting effect which follows from the above equations but
+which is not obvious at first glance. When the full grating equation is
+used the dispersion varies with wavelength. This means the size of a pixel
+in wavelength varies and so the flux in a pixel changes. The effect is
+such that the order intensity maximum shifts to the blue from the blaze peak
+because the pixel width in Angstroms increases to the blue faster, for a
+while, than the blaze response decreases.
+
+Once the spectrum has been separated into orders, modified by the
+grating blaze functions, and sampled into pixels in the dispersion
+direction it may be output as an extracted "echelle" format spectrum.
+This occurs when the spatial profile is specified as "extracted".
+The keywords added by the \fBapextract\fR package are included in
+the image header. If the dispersion model is linear
+the keywords are the same as those produced by the dispersion
+correction task \fBecdispcor\fR.
+
+If the spatial profile is specified as "gaussian" or "slit" then the
+orders are convolved by the profile function and the crossdispersion
+relation is used to determine where the order falls at each wavelength.
+The spatial profiles are defined by the formulas:
+
+.nf
+ gaussian: I(x) = exp (-ln(2) * (2*(x-xc(w))/width)**2)
+ slit: I(x) = exp (-ln(2) * (2*(x-xc(w))/width)**10)
+.fi
+
+where x is the spatial coordinate, xc(w) is the order center at
+wavelength w, and width is the full width at half maximum specified by
+the parameter of that name. The "gaussian" profile
+is the usual gaussian specified in terms of a FWHM. The "slit"
+profile is one which is relatively flat and then rapidly drops
+to zero. The profile is normalized to unit integral so that
+the total flux across the profile is given by the scaled
+1D spectrum flux. The profile is fully sampled and then binned to
+the pixel size to correctly include sampling effects as a function
+of where in a pixel the order center falls.
+
+Note that in this model the orders are always tilted with respect
+to the columns and constant wavelength is exactly aligned with the
+image lines.
+.ih
+EXAMPLES
+1. Create an absorption spectrum with blackbody continuum and scattered
+light using the default grating parameters then add noise.
+
+.nf
+ cl> mkechelle ex1 nrand=100 scat=100.
+ cl> mknoise ex1 gain=2 rdnoise=5 poisson+
+.fi
+
+2. Create an arc spectrum using the line list noao$lib/onedstds/thorium.dat.
+
+.nf
+ cl> mkechelle ex2 cont=10 temp=0 \
+ lines=noao$lib/onedstds/thorium.dat peak=1000 sigma=.05
+.fi
+
+Note that the line intensities are random and not realistic. The peak
+intensities range from 0 to 1000 times the continuum or 10000.
+
+3. Create an extracted version of example1.
+
+.nf
+ cl> mkechelle ex1.ec prof=extracted nrand=100 scat=100.
+ cl> mknoise ex1.ec gain=2 rdnoise=5 poisson+
+.fi
+
+Note that the noise is different and greater than would be the case with
+extracting the orders of example 1 because the noise is not summed
+over the order profile but is added after the fact.
+
+4. Create an extracted and dispersion corrected version of example1.
+
+.nf
+ cl> mkechelle ex1.ec prof=extracted nrand=100 scat=100. \
+ gmm=INDEF blaze=INDEF theta=INDEF
+ Echelle grating: Using linear dispersion
+ Warning: Insufficient information to resolve grating parameters
+ cl> mknoise ex1.ec gain=2 rdnoise=5 poisson+
+.fi
+
+The warning is expected. By not specifying all the parameters needed to
+fully model an echelle grating the default action is to use a linear
+dispersion in each order and to set the image header dispersion
+information. When a complete grating model is specified, as in example 3,
+the extracted spectrum is not given dispersion information so that the
+nonlinear behavior of the dispersion can be applied by \fBecidentify\fR and
+\fBdispcor\fR. As with example 3, the noise is different since it is added
+after extraction and dispersion correction.
+.ih
+REVISIONS
+.ls MKECHELLE V2.10.3
+The task was updated to produce the current coordinate system format.
+.le
+.ih
+SEE ALSO mknoise, mk1dspec, mk2dspec, mkheader, astutil.gratings
+.endhelp
diff --git a/noao/artdata/doc/mkexamples.hlp b/noao/artdata/doc/mkexamples.hlp
new file mode 100644
index 00000000..b3b012a0
--- /dev/null
+++ b/noao/artdata/doc/mkexamples.hlp
@@ -0,0 +1,167 @@
+.help mkexamples Mar93 noao.artdata
+.ih
+NAME
+mkexamples - Make artificial data example images
+.ih
+USAGE
+mkexamples name image
+.ih
+PARAMETERS
+.ls name
+Example name (abbreviations are not allowed):
+
+.nf
+ galcluster - Galaxy cluster
+ globular - Globular cluster
+ galfield - Galaxy field
+ starfield - Starfield
+
+ henear - Helium-neon-argon spectrum (uncalibrated)
+ heneardc - Helium-neon-argon spectrum (calibrated)
+
+ ecarc - Echelle thorium-argon spectrum (uncalibrated)
+ ecarcdc - Echelle thorium-argon spectrum (calibrated)
+
+ spectrum - Absorption spectrum (calibrated)
+ echelle - Echelle absorption spectrum (calibrated)
+
+ ecarc2d - Echelle thorium-argon slit spectrum
+ ecobj2d - Echelle object slit spectrum
+
+ lsarc - Long slit helium-neon-argon spectrum
+ lsobj - Long slit object spectrum
+
+ multifiber - Multifiber spectrum
+.fi
+.le
+.ls image
+Output image name.
+.le
+.ls oseed = 1
+Random number seed affecting object generation. Different object seeds
+will produces different examples of objects or spectral lines or number
+of apertures/orders. This
+usually modifies the seed parameters in \fBstarlist\fR, \fBgallist\fR,
+and \fBmk1dspec\fR.
+.le
+.ls nseed = 1
+Random number noise seed. Different noise seeds will produce examples
+with different noise, generally of the same level but simply having
+a different pattern. This is usually the seed parameter in
+\fBmkobjects\fR or \fBmknoise\fR.
+.le
+.ls comments = no
+Add comments to the image header describing various artificial data
+parameters?
+.le
+.ls verbose = yes
+Print message indicating image being created?
+.le
+.ls errors = yes
+Print messages if the image already exists, bad example name, or other
+errors?
+.le
+.ls list = no
+List script used to generate the example rather than create an image?
+.le
+.ih
+DESCRIPTION
+The task is intended to generate a few artificial images of various types to
+be used as examples of the artificial data package and in various
+demonstrations and test procedures for other packages. The examples are not
+exhaustive. The only adjustable parameters are variations of the
+random number seeds. Varying the noise seed allows several observations
+of the same example while varying the object seed allows several observations
+of different "fields", spectral lines, or number of apertures/orders.
+
+If the example name is not given on the command line a menu of example
+names is first printed and then a prompt for the name is given.
+The name may be a submenu or an example. The
+names may not be abbreviated. If desired the simple command
+script used to generate the example may be paged. Otherwise the
+specified image will be generated. Keep in mind that some of the
+examples (particularly those generating galaxy images) may take a
+significant amount of time. On a SPARCstation the examples all run in
+under five minutes. A check is made to see if the image already
+exists. If the image exists then the task exits. If the \fIerrors\fR
+parameter is specified an error message is printed.
+
+A reason for the error output to be turned off is in test scripts and
+demonstrations where the image will be created the first time and reused
+in further tests or demonstrations. In such cases the verbose option is
+generally set so that the user is aware that an image is being created
+and some delay is to be expected.
+
+This task is a procedure script which selects and lists or executes
+any file in the mkexamples$ logical directory with the example name and the
+extension ".cl". Thus, to add additional examples create a simple
+command script (not a procedure script) and place it in the mkexamples
+directory along with an entry in the menu file mkexamples$mkexamples.men.
+.ih
+EXAMPLES
+1. Create a globular cluster example.
+
+.nf
+ ar> mkexample
+ MKEXAMPLE Menu
+
+ galcluster - Galaxy cluster
+ globular - Globular cluster
+ galfield - Galaxy field
+ starfield - Starfield
+
+ onedspec - Menu of one dimensional spectra
+ twodspec - Menu of two dimensional spectra
+ threedspec - Menu of three dimensional spectra
+ Example name: globular
+ Image name: globular
+ Creating example globular in image globular ...
+.fi
+
+2. Try and create the same example again.
+
+.nf
+ ar> mkexample globular globular
+ ERROR: Image globular already exists
+.fi
+
+3. List the script which creates the globular example.
+
+.nf
+ ar> mkexample globular list+
+ # GLOBULAR - Globular cluster
+
+ file image, dat
+
+ image = s1
+ dat = mktemp ("art")
+
+ starlist (dat, 5000, "", "", interactive=no, spatial="hubble",
+ xmin=1., xmax=512., ymin=1., ymax=512., xcenter=INDEF,
+ ycenter=INDEF, core_radius=30., base=0., sseed=i,
+ luminosity="bands", minmag=-7., maxmag=0., mzero=-4., power=0.6,
+ alpha=0.74, beta=0.04, delta=0.294, mstar=1.28, lseed=i,
+ nssample=100, sorder=10, nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, graphics="stdgraph", cursor="")
+
+ mkobjects (image, output="", ncols=512, nlines=512,
+ title="Example artificial globular cluster",
+ header="artdata$stdheader.dat", background=1000., objects=dat,
+ xoffset=0., yoffset=0., star="moffat", radius=1.0, beta=2.5,
+ ar=1., pa=0., distance=1., exptime=1., magzero=7.,
+ gain=3., rdnoise=10., poisson=yes, seed=j)
+
+ delete (dat, verify=no)
+.fi
+.ih
+REVISIONS
+.ls MKEXAMPLES V2.10.3
+The examples have been expanded to include submenus. The submenus organize
+the various types of spectra. Additional spectral examples have been
+added. The oseed parameter selects the number of apertures in the
+onedspec spectra and the number of orders in the echelle examples.
+.le
+.ih
+SEE ALSO
+mkobjects, mknoise, mk1dspec, mk2dspec, mkechelle
+.endhelp
diff --git a/noao/artdata/doc/mkheader.hlp b/noao/artdata/doc/mkheader.hlp
new file mode 100644
index 00000000..43b37895
--- /dev/null
+++ b/noao/artdata/doc/mkheader.hlp
@@ -0,0 +1,84 @@
+.help mkheader Aug90 noao.artdata
+.ih
+NAME
+mkheader - Append/replace image header
+.ih
+USAGE
+mkheader images headers
+.ih
+PARAMETERS
+.ls images
+List of images in which header information is to be added or modified.
+.le
+.ls header = "artdata$stdheader.dat"
+List of images or header keyword data files. If the list is shorter
+than the input image list then the last entry is repeated.
+If an image is given then the image header
+is copied. If a file is given then the FITS format cards are copied.
+This only applies to new images. The data file consists of lines
+in FITS format with leading whitespace ignored. A FITS card must begin
+with an uppercase/numeric keyword. Lines not beginning with a FITS
+keyword such as comments or lower case are ignored. The user keyword
+output of \fBimheader\fR is an acceptable data file.
+.le
+.ls append = yes
+Append to existing keywords? If no then the existing header is replaced.
+.le
+.ls verbose = no
+Verbose output?
+.le
+.ih
+DESCRIPTION
+The image headers in the list of input images may be replaced or appended
+with information from images or data files specified by the \fIheader\fR
+parameter list. If the header list is shorter than the list of images
+to be modified the last header file is repeated. Depending on the
+value of the \fIappend\fR parameter, new parameters will be appended
+or replace the existing image header parameters.
+
+A header keyword data file consists of lines of FITS format cards.
+Leading whitespace is ignored. Lines not recognized as FITS cards
+are ignored. A valid FITS card is defined as beginning with a keyword
+of up to 8 uppercase, digit, hyphen, or underscore characters. If
+less than 8 characters the remaining characters are blanks. The
+ninth character may be an equal sign but must be immediately followed
+by a blank. Such value cards should be in FITS format though no
+attempt is made to enforce this. Any other ninth character is also
+acceptable and the line will be treated as a comment. Note that this
+way of recognizing FITS parameters excludes the case of comments
+in which the first 8 characters are blank. The reason for allowing
+leading whitespace and eliminating the blank keyword case is so that
+the long output of \fBimheader\fR may be used directly as input.
+
+Header files are also used by several of the tasks in the artificial
+data package with a standard default file "artdata$stdheader.dat".
+To edit image headers also see \fBhedit\fR.
+.ih
+EXAMPLES
+1. Add some standard keywords from a file to an image.
+
+.nf
+ ar> type myheader
+ # MY header list
+ INSTRUME= 'bspec mark II' / B Spectrograph
+ LENS = 3 / Lens number
+ FOCRATIO= 5.2 / Focal ratio
+ ar> mkheader *.imh myheader
+.fi
+
+2. Copy an image header.
+
+ ar> mkheader new dev$pix append-
+
+3. Edit the image header with a text editor and replace the old header
+with the edited header.
+
+.nf
+ ar> imheader myimage l+ > temp
+ ar> edit temp
+ ar> mkheader myimage temp append-
+.fi
+.ih
+SEE ALSO
+hedit, mkobjects, mknoise, mk1dspec, mk2dspec
+.endhelp
diff --git a/noao/artdata/doc/mknoise.hlp b/noao/artdata/doc/mknoise.hlp
new file mode 100644
index 00000000..cab14d42
--- /dev/null
+++ b/noao/artdata/doc/mknoise.hlp
@@ -0,0 +1,245 @@
+.help mknoise Aug90 noao.artdata
+.ih
+NAME
+mknoise - Make/add noise and cosmic rays to 1D/2D images
+.ih
+PARAMETERS
+.ls input
+Images to create or modify.
+.le
+.ls output = ""
+Output images when modifying input images. If no output images are
+given then existing images in the input list are modified directly.
+If an output image list is given then it must match in number the
+input list.
+.le
+
+WHEN CREATING NEW IMAGES
+.ls title = ""
+Image title to be given to the images. Maximum of 79 characters.
+.le
+.ls ncols = 512, nlines = 512
+Number of columns and lines.
+.le
+.ls header = "artdata$stdheader.dat"
+Image or header keyword data file. If an image is given then the image header
+is copied. If a file is given then the FITS format cards are copied.
+This only applies to new images. The data file consists of lines
+in FITS format with leading whitespace ignored. A FITS card must begin
+with an uppercase/numeric keyword. Lines not beginning with a FITS
+keyword such as comments or lower case are ignored. The user keyword
+output of \fBimheader\fR is an acceptable data file. See \fBmkheader\fR
+for further information.
+.le
+
+NOISE PARAMETERS
+.ls background = 0.
+Background to add to images before computing Poisson noise.
+.le
+.ls gain = 1.
+Gain in electrons per data number. The gain is used for scaling the
+read noise parameter and in computing poisson noise.
+.le
+.ls rdnoise = 0.
+Gaussian read noise in electrons.
+.le
+.ls poisson = no
+Add poisson noise? Note that any specified background is added to new
+or existing images before computing the Poisson noise.
+.le
+.ls seed = 1
+Random number seed. If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+COSMIC RAYS
+.ls cosrays = ""
+List of cosmic ray files. Cosmic ray files contain lines of cosmic ray
+coordinates and energy (see DESCRIPTION section). If no
+file or a new (nonexistent) file is specified then a number of random
+cosmic rays given by the parameter \fIncosrays\fR is generated. If a
+new file name is specified then the events generated are recorded in the
+file. If the list of cosmic ray files is shorter than the list of
+input images then the last cosmic ray file is reused.
+.le
+.ls ncosrays = 0
+If no cosmic ray file or a new file is specified then the task will
+generate this number of random cosmic rays. The positions are
+uniformly random within the limits of the image and the energy is
+uniformly random between zero and a maximum.
+.le
+.ls energy = 30000.
+When generating random events the cosmic rays will have a uniform energy
+distribution (in electrons) between zero and this maximum.
+.le
+.ls radius = 0.5
+The half-intensity radius of gaussian profile cosmic rays in pixels
+along the major axis.
+.le
+.ls ar = 1.
+Minor to major axial ratio for cosmic rays.
+.le
+.ls pa = 0.
+Position angle in degrees measured counterclockwise from the X axis for
+cosmic rays.
+.le
+
+.ls comments = yes
+Include comments recording task parameters in the image header?
+.le
+
+PACKAGE PARAMETERS
+
+These parameters define certain computational shortcuts which greatly
+affect the computational speed. They should be adjusted with care.
+.ls nxc = 5, nyc = 5
+Number of cosmic ray centers per pixel in X and Y. Rather than evaluate
+cosmic rays precisely at each subpixel coordinate, a set of templates
+with a grid of subpixel centers is computed and then the nearest template to
+the desired position is chosen. The larger the number the more memory
+and startup time required.
+.le
+.ls nxsub = 10, nysub = 10
+Number of pixel subsamples in X and Y used in computing the cosmic
+ray profiles. This is the subsampling in the central
+pixel and the number of subsamples decreases linearly from the center.
+This affects the time required to compute the cosmic ray templates.
+.le
+.ls dynrange = 100000.
+The intensity profile of the gaussian cosmic rays extends to infinity so
+a dynamic range, the ratio of the peak intensity to the cutoff
+intensity, is imposed. Because the cosmic rays are small this parameter
+is not critical.
+.le
+.ls ranbuf = 0
+Random number buffer size. When generating readout and poisson noise,
+evaluation of new random values has an affect on the execution time.
+If truly (or computationally truly) random numbers are not needed
+then this number of random values is stored and a simple
+uniform random number is used to select from the stored values.
+To force evaluation of new random values for every pixel set the
+value of this parameter to zero.
+.le
+.ih
+DESCRIPTION
+This task creates or modifies images with readout noise, poisson noise,
+and cosmic ray events. New images are created with the specified
+dimensions and real datatype. Existing images may be modified in place
+or new images may be created.
+
+If a new image is created it is has the mean level given by the parameter
+\fIbackground\fR. With no noise and no cosmic rays this task can be used to
+create images of constant background value. For existing images the
+background is added before computing any noise. To add noise to an
+existing image without modifying the mean counts set the background
+to zero.
+
+For new images a set of header keywords may be added by specifying an
+image or data file with the \fIheader\fR parameter (see also \fBmkheader\fR).
+If a data file is specified lines beginning with FITS keywords are
+entered in the image header. Leading whitespace is ignored and any
+lines beginning with words having lowercase and nonvalid FITS keyword
+characters are ignored. In addition to this optional header,
+keywords, parameters for the gain and read noise are defined.
+Finally, comments may be added to the image header recording the task
+parameters and any information from the cosmic ray file which are not
+cosmic ray definitions.
+
+Poisson photon noise is generated by setting the \fIpoisson\fR parameter.
+For new images the input data value is the background while for
+existing images the input data value is added to the background value.
+The data value is then multiplied by the gain, a poisson deviate is
+generated, and divided by the gain. Expressed as a formula:
+
+.nf
+ New images: out = P(background * gain) / gain
+ Existing images: out = P((in+background)*gain) / gain
+.fi
+
+where P(x) is a poisson deviate with mean x, in and out are the input
+and final pixel values, and background and gain are the parameter
+values of the same name.
+
+Readout or gaussian noise is generated by specifying a gaussian sigma with
+the parameter \fIrdnoise\fR. The sigma is divided by the specified gain
+to convert to image data units. Gaussian random numbers of mean zero are
+then generated for each pixel and added to the image, or background
+value for new images, after the photon noise is computed.
+
+Generating gaussian and poisson random numbers computationally is
+the main determinant of the execution time in this task.
+Two things are done to speed up the task.
+First, the gaussian approximation is used for data values greater
+than 20 (after applying the background and gain). The square root
+of the data value is used as the gaussian sigma about the data
+value. For values less than 20 a true poisson deviate is generated.
+The second speed up is to allow storing a number of normalized gaussian
+values given by the package parameter \fIranbuf\fR as they are generated. If
+more values than this are desired then a uniform random number is used
+to select one of these stored values. This applies to both the read noise
+and poisson noise gaussian approximation though not the true poisson
+evaluation. For most purposes this approximation is good and one would
+need to look very hard to detect the nonrandomness in the noise.
+However, if one wants to take the extra computational time then
+by setting the \fIranbuf\fR parameter to zero each gaussian
+random number will be generated independently.
+
+The cosmic ray model is an elliptical gaussian of specified
+half-intensity radius, axial ratio, and position angle. Normally the
+radius will be small (smaller than the point spread function) and the
+axial ratio will be 1. The cosmic rays are subsampled and can have the
+number of centers given by the \fInxc/nyc\fR package parameters. The method
+of generating the cosmic rays is that described for the task
+\fBmkobjects\fR. Specifically it is the same as adding gaussian
+profile stars.
+
+The total flux (not the peak) of the cosmic ray is given by the energy
+in electrons so that the value is divided by the gain to produce the
+total flux in the image. Note that this task can be used to add cosmic
+ray spikes to one dimensional images such as spectra but the strengths
+will appear low because of the part of the event which falls outside
+the single line.
+
+The positions and energies of the cosmic rays can be specified in a
+file or the task can generate random events. Specific cosmic rays are
+specified by a file containing lines of x and y positions and energy.
+Positions outside the limits of the image are ignored. If no cosmic
+ray file is given or if a new, nonexistent file is named then the
+number of cosmic rays given by the \fIncosrays\fR parameter is
+generated with uniform spatial distribution within the image and
+uniform energy distribution between zero and that given by the
+\fIenergy\fR parameter. By giving a new file name the randomly
+generated cosmic rays will be recorded for reuse or to allow
+identifying the events while testing tasks and algorithms.
+.ih
+EXAMPLES
+1. Create a new image with a background of 1000, a read noise
+of 10 electrons, a gain of 2, and 50 random cosmic rays. Don't keep a
+record of the cosmic rays.
+
+.nf
+ cl> mknoise testim back=1000 rd=10 gain=2 poisson+ ncos=50
+.fi
+
+2. Add cosmic rays to an image and create a new output image.
+
+.nf
+ cl> head cosfile
+ 20.3 50.1 1000
+ 325.6 99.6 250
+ cl> mknoise dev$pix out=newpix cos=cosfile
+.fi
+.ih
+REVISIONS
+.ls MKNOISE V2.11+
+The random number seed can be set from the clock time by using the value
+"INDEF" to yield different random numbers for each execution.
+.le
+.ls MKNOISE V2.11
+The default value of "ranbuf" was changed to zero.
+.le
+.ih
+SEE ALSO
+mkobjects, mkheader
+.endhelp
diff --git a/noao/artdata/doc/mkobjects.hlp b/noao/artdata/doc/mkobjects.hlp
new file mode 100644
index 00000000..e641635a
--- /dev/null
+++ b/noao/artdata/doc/mkobjects.hlp
@@ -0,0 +1,636 @@
+.help mkobjects Jan92 noao.artdata
+.ih
+NAME
+mkobjects - Make/add artificial stars and galaxies to 2D images
+.ih
+USAGE
+mkobjects input
+.ih
+PARAMETERS
+.ls input
+Images to create or modify.
+.le
+.ls output = ""
+Output images when modifying input images. If no output images are
+given then existing images in the input list are modified directly.
+If an output image list is given then it must match in number the
+input list.
+.le
+
+WHEN CREATING NEW IMAGES
+.ls title = ""
+Image title to be given to the images. Maximum of 79 characters.
+.le
+.ls ncols = 512, nlines = 512
+Number of columns and lines.
+.le
+.ls header = "artdata$stdheader.dat"
+Image or header keyword data file. If an image is given then the image header
+is copied. If a file is given then the FITS format cards are copied.
+This only applies to new images. The data file consists of lines
+in FITS format with leading whitespace ignored. A FITS card must begin
+with an uppercase/numeric keyword. Lines not beginning with a FITS
+keyword such as comments or lower case are ignored. The user keyword
+output of \fBimheader\fR is an acceptable data file. See \fBmkheader\fR
+for further information.
+.le
+.ls background = 1000.
+Default background and poisson noise background. This is in data numbers
+with the conversion to photons determined by the \fIgain\fR parameter.
+.le
+
+OBJECT PARAMETERS
+.ls objects = ""
+List of object files. The number of object files must match the number of
+input images. The object files contain lines of object coordinates,
+magnitudes, and shape parameters (see the DESCRIPTION section).
+.le
+.ls xoffset = 0., yoffset = 0.
+X and Y coordinate offset to be added to the object list coordinates.
+.le
+.ls star = "moffat"
+Type of star and point spread function. The choices are:
+.ls gaussian
+An elliptical Gaussian profile with major axis half-intensity radius
+given by the parameter \fIradius\fR, axial ratio given by the parameter
+\fIar\fR, and position angle given by the parameter \fIpa\fR.
+.le
+.ls moffat
+An elliptical Moffat profile with major axis half-intensity radius
+given by the parameter \fIradius\fR, model parameter \fIbeta\fR,
+axial ratio given by the parameter \fIar\fR, and position angle given
+by the parameter \fIpa\fR.
+.le
+.ls <image>
+If not one of the profiles above, an image of the specified name is
+sought. If found the center of the template image is assumed to be the
+center of the star/psf and the image template is scaled so that the
+radius of the template along the first axis is given by the \fIradius\fR
+parameter. The axial ratio and position angle define an
+elliptical sampling of the template.
+.le
+.ls <profile file>
+If not one of the above, a text file is sought giving either an intensity
+per unit area profile or a cumulative flux profile from the center to the
+edge. The two are differentiated by whether the first profile point is 0
+for a cumulative profile or nonzero for an intensity profile. An intensity
+profile is recommended. If found the profile defines an elliptical star/psf
+with the major axis radius to the last profile point given by the parameter
+\fIradius\fR, axial ratio given by the parameter \fIar\fR, and position
+angle given by the parameter \fIpa\fR.
+.le
+.le
+.ls radius = 1.
+Seeing radius/scale in pixels along the major axis. For the "gaussian"
+and "moffat" profiles this is the half-intensity radius of the major
+axis, for image templates this is the template radius along the x dimension,
+specifically one half the number of columns, and for arbitrary user profiles
+this is the radius to the last profile point.
+.le
+.ls beta = 2.5
+Moffat model parameter. See the DESCRIPTION for a definition of the
+Moffat profile.
+.le
+.ls ar = 1.
+Minor to major axial ratio for the star/psf.
+.le
+.ls pa = 0.
+Position angle in degrees measured counterclockwise from the X axis
+for the star/psf.
+.le
+.ls distance = 1.
+Relative distance to be applied to the object list coordinates,
+magnitudes, and scale sizes. This factor is divided into the
+object coordinates, after adding the offset factors, to allow expanding
+or contracting about any origin. The magnitudes scale as the
+square of the distance and the sizes of the galaxies scale
+linearly. This parameter allows changing image sizes and fluxes
+at a given seeing and sampling with one value.
+.le
+.ls exptime = 1.
+Relative exposure time. The object magnitudes and background
+level are scaled by this parameter. This is comparable to changing the
+magnitude zero point except that it includes changing the background.
+.le
+.ls magzero = 7.
+Magnitude zero point defining the conversion from magnitudes in the
+object list to instrumental/image fluxes.
+.le
+
+NOISE PARAMETERS
+.ls gain = 1.
+Gain in electrons per data number. The gain is used for scaling the
+read noise parameter, the background, and in computing poisson noise.
+.le
+.ls rdnoise = 0.
+Gaussian read noise in electrons. For new images this applies to the
+entire image while for existing images this is added only to the objects.
+.le
+.ls poisson = no
+Add poisson photon noise? For new images this applies to the entire image
+while for existing images this is only applied to the objects. Note
+that in the latter case the background parameter is added before
+computing the new value and then subtracted again.
+.le
+.ls seed = 1
+Random number seed. If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+.ls comments = yes
+Include comments recording task parameters in the image header?
+.le
+
+PACKAGE PARAMETERS
+
+These parameters define certain computational shortcuts which greatly
+affect the computational speed. They should be adjusted with care.
+.ls nxc = 5, nyc = 5
+Number of star and psf centers per pixel in X and Y. Rather than evaluate
+stars and the psf convolution functions precisely at each subpixel
+coordinate, a set of templates with a grid of subpixel centers is
+computed and then the nearest template to the desired position is chosen.
+The larger the number the more memory and startup time required.
+.le
+.ls nxsub = 10, nysub = 10
+Number of pixel subsamples in X and Y used in computing the star and
+psf. This is the subsampling in the central
+pixel and the number of subsamples decreases linearly from the center.
+The larger the numbers the longer it takes to compute the star and psf
+convolution templates.
+.le
+.ls nxgsub = 5, nygsub = 5
+Number of pixel subsamples in X and Y used in computing galaxy images.
+This is the subsampling in the central pixel and the number of
+subsamples decreases linearly from the center. Because galaxy images
+are extended and each subsample is convolved by the psf convolution it
+need not be as finely sampled as the stars. This is a critical
+parameter in the execution time if galaxies are being modeled.
+The larger the numbers the longer the execution time.
+.le
+.ls dynrange = 100000., psfrange = 10.
+The intensity profiles of the analytic functions extend to infinity so
+a dynamic range, the ratio of the peak intensity to the cutoff
+intensity, is imposed to cutoff the profiles. The \fIdynrange\fR
+parameter applies to the stellar templates and to the galaxy profiles.
+The larger this parameter the further the profile extends.
+When modeling galaxies this has a fairly
+strong affect on the time (larger numbers means larger images and more
+execution time). Only for very high signal-to-noise
+objects will the cutoff be noticeable. A correction is made to
+the object magnitudes to reflect light lost by this cutoff.
+
+The psf convolution, used on galaxies, is generally not
+evaluated over as large a dynamic range, given by the parameter
+\fIpsfrange\fR, especially since it has a very strong affect on the
+execution time. The convolution is normalized to unit weight over the
+specified dynamic range.
+.le
+.ls ranbuf = 0
+Random number buffer size. When generating readout and poisson noise,
+evaluation of new random values has an affect on the execution time.
+If truly (or computationally truly) random numbers are not needed
+then this number of random values is stored and a simple
+uniform random number is used to select from the stored values.
+To force evaluation of new random values for every pixel set the
+value of this parameter to zero.
+.le
+.ih
+DESCRIPTION
+This task creates or modifies images by adding models of astronomical
+objects, stars and galaxies, as specified in object lists. New images are
+created with the specified dimensions, background, title, and real datatype.
+Existing images may be modified in place or new images output. The
+task includes the effects of image scale, pixel sampling, atmospheric
+seeing, and noise. The object models may be analytic one dimensional
+profiles, user defined one dimensional profiles, and user defined image
+templates. The profiles and templates are given elliptical shapes by
+specifying a scale radius for the major axis, a minor axis to major
+axis axial ratio, and a position angle.
+
+For new images a set of header keywords may be added by specifying an
+image or data file with the \fIheader\fR parameter (see also \fBmkheader\fR).
+If a data file is specified lines beginning with FITS keywords are
+entered in the image header. Leading whitespace is ignored and any
+lines beginning with words having lowercase and nonvalid FITS keyword
+characters are ignored. In addition to this optional header,
+keywords, parameters for the gain, read noise, and exposure time are
+defined. Finally, comments may be added to the image header recording the task
+parameters and any information from the objects file which are not
+object definitions; in particular, the \fBstarlist\fR and
+\fBgallist\fR parameters are recorded.
+
+A completely accurate simulation of the effects of pixel sampling,
+atmospheric seeing, object appearance, luminosity functions, and noise
+can require a large amount of computer time even on
+supercomputers. This task is intended to allow generation of large
+numbers of objects and images over large image sizes representative of
+current deep optical astronomical images. All this is to be done
+on typical workstations. Thus, there are many approximations and
+subtle algorithms used to make this possible to as high a degree of
+accuracy as practical. The discussion will try to describe these in
+sufficient detail for the user to judge the accuracy of the artificial
+data generated and understand the trade offs with many of the
+parameters.
+
+New images are created with the specified dimensions, title, and real
+datatype. The images have a constant background value given by the
+\fIbackground\fR parameter (in data numbers) before adding objects and
+noise. Noise consists of gaussian and poisson components. For existing
+images, noise is only added to the objects and the background parameter is
+used in the calculation of the poisson noise: specifically, a poisson
+random value with mean given by the sum of the object and the background is
+generated and then the background is subtracted. For more on how the noise
+is computed and approximations used see \fBmknoise\fR.
+
+Objects are specified by a position, magnitude, model, scale, axial
+ratio, and position angle. Since the point spread function (PSF)
+is assumed constant over the image the star model, size, axial ratio,
+and position angle are specified by the task parameters \fIstar\fR,
+\fIradius\fR, \fIar\fR, and \fIpa\fR. For galaxies, where the
+intrinsic shapes vary from object to object, these parameters are
+specified as part of the object lists. For both types of objects the
+positions and magnitudes are specified in the object lists.
+
+There is a great deal of flexibility in defining the object models.
+The models are defined either in terms of a one dimensional radial
+intensity or cumulative flux profile
+or an image template. The flux profiles may be
+analytic functions or a user defined profile given as an equally spaced
+set of values in a text file. The first point is zero at the center
+for a cumulative profile
+and increases monotonically to the edge. Note that intensity profiles
+are to be preferred to avoid artifacts in the conversion from cumulative
+flux. In particular, cumulative flux profiles may give a spike at the
+center. In either case, the profile should be specified fairly finely,
+many points, to avoid interpolation effects.
+
+The functional form of the analytic profiles the user profiles, and
+image template are given below.
+
+.nf
+ gaussian: I = exp (-ln (2) * (R/radius)**2)
+ moffat: I = (1 + (2**(1/beta)-1) * (R/radius)**2) ** -beta
+ sersic<n>: I = exp (-b * (R/radius)**1/n)
+ expdisk: I = exp (-1.6783 * R/radius)
+ devauc: I = exp (-7.67 * (R/radius)**1/4)
+ flux profile: I = intensity (nprofile * R/radius)
+ flux profile: F = flux (nprofile * R/radius)
+image template: I = image (nc/2+nc/2*dX/radius, nl/2+nc/2*dY/radius)
+.fi
+
+where R, dX, and dY are defined below, \fIradius\fR is the scale parameter
+and \fIbeta\fR is the Moffat parameter specified by the user,
+nprofile is the number of profile points in the user profile, and nc and nl
+are the image template column and line dimensions. The Gaussian, "gaussian",
+and Moffat, "moffat", profiles are used for stars and the point spread
+function, while the Sersic (sersic), exponential disk (expdisk), and
+De Vaucouleurs (devauc) profiles are common models for spiral and elliptical
+galaxies. The image templates are intended to model images with
+some complex structure. The usual case is to have a very well sampled
+and high signal-to-noise image be reduced in scale (a more distant
+example), convolved with seeing (loss of detail), and noise (degraded
+signal-to-noise). This also allows for more complex point spread
+functions.
+
+The radial profiles are mapped into two dimensional objects by an elliptical
+transformation. The image templates are also mapped by an elliptical
+transformation to rotate and stretch them. If the output image
+coordinates are given by (x, y), and the specified object center
+coordinates are given by (xc, yc) then the transformation is defined
+as shown below.
+
+.nf
+ dx = x - xc
+ dy = y - yc
+ dX = dx * cos(pa) + dy * sin(pa)
+ dY = (-dx * sin(pa) + dy * cos(pa)) / ar
+ R = sqrt (dX ** 2 + dY ** 2)
+.fi
+
+where dx and dy are the object coordinates relative to the object
+center, dX and dY are the object coordinates in the transformed
+circular coordinates, and R is the circularly symmetric radius.
+The transformation parameters are the axial ratio \fIar\fR
+defined as the ratio of the minor axis to the major axis,
+and the position angle \fIpa\fR defined counterclockwise from
+the x axis.
+
+The \fIradius\fR parameter defines the size, in pixels, of the model
+object (before seeing for the galaxies) in the output image. It
+consistently refers to the major axis of the object but its meaning
+does depend on the model. For the gaussian and moffat profiles it is
+defined as the half-intensity radius. For the sersic, expdisk, and devauc
+profiles it is defined as the half-flux radius. For the user specified
+profiles it is the radius of the last profile point. And for the image
+templates it is the radius of the image along the first or x axis given
+by one-half of the image dimension; i.e. nc/2.
+
+The profiles of the analytic functions extend to infinity so a dynamic
+range, the ratio of the peak intensity to the cutoff intensity, is imposed
+to cutoff the profiles. The \fIdynrange\fR package parameter applies to
+the stellar and galaxy analytic profiles. The larger this parameter the
+further the profile extends, particularly for the large index Sersic and De
+Vaucouleurs models. When modeling large galaxies this has a fairly strong
+affect on the execution time because the overall extent of the images
+becomes rapidly greater. Only for very high signal-to-noise objects will
+the cutoff be noticeable. A correction is made to account for lost light
+(light beyond the modeled dynamic range) so that an aperture magnitude
+will give the correct value for an object of the specified total magnitude.
+This can become quite significant for larger index Sersic profiles and
+for the default dynamic range.
+
+The object models are integrated over the size of the image pixels. This
+is done by subsampling, dividing up a pixel into smaller pieces called
+subpixels. For the image templates a bilinear surface interpolation
+function is used and integrated analytically over the extent of the
+subpixels. The user cumulative one dimensional profiles are first
+converted to intensity profiles. The various intensity profiles are then
+binned into pixel fluxes per subpixel on a grid much finer than the
+subpixel spacing. Then for any particular radius and object center the
+appropriate subpixel flux can be determined quickly and accurately.
+
+The number of subpixels per image pixel is determined by the package
+parameters \fInxsub\fR, \fInysub\fR, \fInxgsub\fR, and \fInygsub\fR. The
+first two apply to the stars and the PSF and the latter two apply to the
+galaxies. Typically the subsampling will be the same in each dimension.
+The galaxies are generally subsampled less since they will have less
+rapidly changing profiles and are convolved by the PSF. Also, the stars
+are computed only a few times and then scaled and moved, as described
+below, while each galaxy needs to be computed separately. Therefore, one
+can afford greater precision in the stars than in the galaxies.
+
+Given an image of several hundred pixels subsampled by a factor of 100
+(10 x 10) this will be a very large number of computations. A
+shortcut to reduce this number of operations is allow the number
+of subpixels to change as a function of distance from the
+profile center. Since the profile center is where the intensity
+changes most rapidly with position, the greatest subsampling is needed for
+the pixel nearest the center. Further from the object center the intensity
+changes more slowly and the number of subpixels may be reduced.
+Thus, the number of subpixels in each dimension in each pixel is
+decreased linearly with distance from the profile center. For example,
+a pixel which is 3.2 pixels from the profile center will have
+\fInxsub\fR - 3 subpixels in the x dimension. There is, of course, a
+minimum of one subpixel per pixel or, in other words, no subsampling
+for the outer parts of the objects. By adjusting the subsampling
+parameters one can set the degree of accuracy desired at the trade off of
+greatly different execution times.
+
+The star shapes are assumed constant over the images and only their
+position and magnitude change. Thus, rather than compute each desired
+star from the model profile or image template, a normalized star
+template is computed once, using the spatial transformation and
+subsampling operations described above, and simply scaled each time to
+achieve the desired magnitude and added at the requested position.
+However, the apparent star shape does vary depending on where its
+center lies within an image pixel. To handle this a set of
+normalized star templates is precomputed over a grid of centers
+relative to the center of a pixel. Then the template with center
+nearest to that requested, relative to a pixel center, is used. The
+number of such templates is set by the package parameters \fInxc\fR and
+\fInyc\fR where the two axis typically have the same values. The
+larger the number of centers the more memory and startup time required
+but the better the representation of this sampling effect. The choice
+also depends on the scale of the stars since the larger the star
+profile compared to a pixel the smaller the subcentering effect is.
+This technique allows generating images with many stars, such as a
+globular cluster or a low galactic latitude field, quite
+efficiently.
+
+Unlike the stars, the galaxies will each have different profiles,
+ellipticities, and position angles and so templates cannot be used (except
+for special test cases as mentioned later). Another difference is that the
+galaxy models need to be convolved by the PSF; i.e. the shapes are defined
+prior to seeing. The PSF convolution must also be subsampled and the
+convolution operation requires as many operations as the number of pixels
+in the PSF for each galaxy subpixel. Thus, computing seeing convolved,
+well subsampled, large galaxy images is the most demanding task of all,
+requiring all the shortcuts described above (larger and variable
+subsampling and the subpixel flux approximation) as well as further ones.
+
+The PSF used for convolving galaxies is truncated at a lower dynamic
+range than the stars according to the package parameter
+\fIpsfrange\fR. This reduces the number of elements in the convolution
+dramatically at the expense of losing only a small amount of the flux
+in the wings. Like the stars, the PSF is precomputed on a grid of
+pixel subcenters and the appropriate PSF template is used for each
+galaxy subpixel convolution. Unlike the stars, the truncated PSF is
+normalized to unit flux in order to conserve the total flux in the
+galaxies. For the extended galaxies this approximation has only a very
+small effect. As with the other approximations one may increase the
+dynamic range of the PSF at the expense of an increase in execution
+time.
+
+There is an exception to using the truncated PSF. If the size of the
+galaxy because very small, 0.01 pixel, then a stellar image is substituted.
+
+
+OBJECT FILES
+
+The object files contain lines defining stars and galaxies. Stars
+are defined by three numbers and galaxies by seven or eight as
+represented symbolically below.
+
+.nf
+ stars: xc yc magnitude
+ galaxies: xc yc magnitude model radius ar pa <save>
+.fi
+
+.ls 6 xc, yc:
+Object center coordinates. These coordinates are transformed to image
+coordinates as follows.
+
+.nf
+ xc in image = xoffset + xc / distance
+ yc in image = yoffset + yc / distance
+.fi
+
+where \fIxoffset\fR and \fIyoffset\fR are the task offset parameters.
+Objects whose image centers fall outside the image dimensions are ignored.
+.le
+.ls magnitude:
+Object magnitude. This is converted to instrumental fluxes as follows.
+
+.nf
+ flux = exptime/distance**2 * 10**(-0.4*(magnitude-magzero))
+.fi
+
+where \fIexptime\fR, \fIdistance\fR, and \fImagzero\fR are task parameters.
+For the analytic star and galaxy models a correction
+is made for lost light due to the finite extent of the image in the
+sense that the flux added to the image will never quite be that
+requested.
+.le
+.ls model:
+The types of galaxy models are as follows:
+.ls 4 sersic<n>
+A Sersic model of index n. The index may real but the value will be rounded
+to the nearest multiple of 0.5 or, equivalently, two times the index value will
+be rounded to an integer. The index must be between 0.5 and 10. The Sersic
+model defined as
+
+.nf
+ I = exp (-b * (R/radius)**1/n)
+.fi
+
+where radius is the major axis scale length corresponding to half of the
+total flux. The value of b is computed using the formula of Ciotti and
+Bertin (AA v352, p447, 1999);
+
+.nf
+ b = 2n - 1/3 + 4/(405n) + 46 / (25515n^2)
+.fi
+.le
+.ls 4 expdisk
+An exponential disk model defined as
+
+.nf
+ I = exp (-b * R/radius)
+.fi
+
+where radius is the major axis scale length corresponding to half of the total
+flux and b is computed as with the Sersic model for n=1. In fact, the
+algorithm is identical with that for the Sersic model using n=1. Note that
+because of this there will be slight differences with the earlier versions.
+.le
+.ls devauc
+A De Vaucouleurs profile defined as
+
+.nf
+ I = exp (-b * (R/radius)**1/4)
+.fi
+
+where radius is the major axis scale length corresponding to half of the total
+flux and b is computed as with the Sersic model for n=4. In fact, the
+algorithm is identical with that for the Sersic model using n=4. Note that
+because of this there will be slight differences with the earlier versions.
+.le
+.ls <image>
+If not one of the profiles above an image of the specified name is
+sought. If found the center of the template image is assumed to be the
+center of the object and the image template is scaled so that the
+radius of the template is given by the major axis scale radius parameter.
+.le
+.ls <profile file>
+If not one of the above a text file giving a cumulative flux profile from
+the center to the edge is sought. If found the profile defines
+a model galaxy of extent to the last profile point given by
+the major axis scale radius parameter.
+.le
+.le
+.ls 6 radius:
+Major axis scale radius parameter in pixels as defined above for the different
+galaxy models. The actual image radius is modified as follows.
+
+ radius in image = radius / distance
+.le
+.ls ar:
+Minor to major axis axial ratio.
+.le
+.ls pa:
+Major axis position angle in degrees measured counterclockwise from the X axis.
+.le
+.ls save:
+If a large number of identically shaped galaxies (size, axial ratio,
+and position angle) located at the same subpixel (the same x and y
+fractional part) but with varying magnitudes is desired then by
+putting the word "yes" as the eighth field the model will be saved
+the first time and reused subsequent times. This speeds up the execution.
+There may certain algorithm testing situations where this might be useful.
+.le
+.ih
+EXAMPLES
+1. Create a galaxy cluster with a power law distribution of field galaxies
+and stars as background/foreground.
+
+.nf
+ ar> gallist galaxies.dat 100 spatial=hubble lum=schecter egal=.8
+ ar> gallist galaxies.dat 500
+ ar> starlist galaxies.dat 100
+ ar> mkobjects galaxies obj=galaxies.dat gain=3 rdnoise=10 poisson+
+.fi
+
+Making the image takes about 5 minutes (2.5 min cpu) on a SPARCstation 1.
+
+2. Create a uniform artificial starfield of 5000 stars for a 512 square image.
+
+.nf
+ ar> starlist starfield.dat 5000
+ ar> mkobjects starfield obj=starfield.dat gain=2 rdnoise=10 poisson+
+.fi
+
+This example takes about a minute on a SPARCstation 1.
+
+3. Create a globular cluster field of 5000 stars for a 512 square image.
+
+.nf
+ ar> starlist gc.dat 5000 spat=hubble lum=bands
+ ar> mkobjects gc obj=gc.dat gain=2 rdnoise=10 poisson+
+.fi
+
+This example takes about a minute on a SPARCstation 1.
+
+4. Add stars to an existing image for test purposes.
+
+.nf
+ ar> mkobjects starfield obj=STDIN gain=2 pois+ magzero=30
+ 100 100 20
+ 100 200 21
+ 200 100 22
+ 200 200 23
+ [EOF]
+.fi
+
+5. Look at the center of the globular cluster with no noise and very
+good seeing.
+
+.nf
+ cl> mkobjects gc1 obj=gc.dat nc=400 nl=400 distance=.5 \
+ >>> xo=-313 yo=-313 radius=.1
+.fi
+
+The offset parameters are used to recenter the cluster from
+(256,256) in the data file to (200,200) in the expanded field.
+This example takes 30 sec (5 sec CPU) on a SPARCstation 1. To expand
+and contract about a fixed point define the object list to have an
+origin at zero.
+
+.nf
+ ar> starlist gc.dat 5000 spat=hubble lum=bands xmin=-256 xmax=256 \
+ >>> ymin=-256 ymax=256
+ ar> mkobjects gc obj=gc.dat xo=257 yo=257 gain=2 rdnoise=10 poisson+
+ ar> mkobjects gc1 obj=gc.dat xo=257 yo=257 gain=2 \
+ >>> distance=.5 rdnoise=10 poisson+
+.fi
+
+6. Make an image of dev$pix at various distances and orientation. First we
+must subtract the background.
+
+.nf
+ cl> imarith dev$pix - 38 pix
+ cl> mkobjects pix1 obj=STDIN nc=200 nl=200 back=1000 \
+ >>> magzero=30 rd=10 poi+
+ 50 50 15.0 pix 40 1 0
+ 150 50 15.6 pix 30 .8 45
+ 50 150 16.5 pix 20 .6 90
+ 150 150 17.1 pix 15 .4 135
+ [EOF]
+.fi
+
+It would be somewhat more efficient to first block average the
+template since the oversampling in this case is very large.
+.ih
+REVISIONS
+.ls MKOBJECTS V2.11+
+The random number seed can be set from the clock time by using the value
+"INDEF" to yield different random numbers for each execution.
+.le
+.ls MKOBJECTS V2.11
+The default value of "ranbuf" was changed to zero.
+.le
+.ih
+SEE ALSO
+gallist, starlist, mknoise, mkheader
+.endhelp
diff --git a/noao/artdata/doc/mkpattern.hlp b/noao/artdata/doc/mkpattern.hlp
new file mode 100644
index 00000000..61bfae09
--- /dev/null
+++ b/noao/artdata/doc/mkpattern.hlp
@@ -0,0 +1,180 @@
+.help mkpattern Jan90 noao.artdata
+.ih
+NAME
+mkpattern - Make/add patterns in images
+.ih
+USAGE
+mkpattern input
+.ih
+PARAMETERS
+.ls input
+Images to create or modify. Image sections are allowed to apply a pattern
+to a portion of an image.
+.le
+.ls output = ""
+Output images when modifying input images. If no output images are
+given then existing images in the input list are modified directly.
+If an output image list is given then it must match in number the
+input list.
+.le
+.ls pattern = "constant"
+Pattern to be used. The patterns are:
+.ls constant
+Constant value v1.
+.le
+.ls grid
+A grid starting with the first pixel and going in steps of the
+pattern size with value v2. Pixels between the grid have value v1.
+A minimum grid size of 2 is enforced.
+.le
+.ls checker
+A checkerboard with squares of the pattern size alternating between values v1
+and v2 starting with v1.
+.le
+.ls coordinates
+Each pixel is numbered sequentially starting with 1 with the column
+dimension varying fastest.
+.le
+.ls slope
+A sloped plane starting with value v1 for the first pixel and value v2 for
+the last pixel in one or two dimensions.
+.le
+.ls square
+A checkerboard pattern in which the size of the squares begin with
+the pattern size and grow as the square of the coordinate.
+.le
+.le
+.ls option = "replace"
+Editing option when modifying existing images. Often this is used
+in conjunction with image sections to modify a part of an image.
+The options are:
+
+.nf
+ replace - Replace the image with the pattern.
+ add - Add the pattern to the image.
+multiply - Multiply the pattern with the image values.
+.fi
+.le
+.ls v1 = 0., v2 = 1.
+Pattern values used as described for each pattern.
+.le
+.ls size = 1
+Pattern size used as described for each pattern.
+.le
+
+WHEN CREATING NEW IMAGES
+.ls title = ""
+Image title to be given to the images. Maximum of 79 characters.
+.le
+.ls pixtype = "real"
+Pixel datatype of new images; one of ushort, short, integer, real, double,
+or complex.
+.le
+.ls ndim = 2
+Number of dimensions between 0 and 7.
+.le
+.ls ncols = 512, nlines = 512
+Number of columns (first dimension) and lines (second dimension).
+.le
+.ls n3 = 1, n4 = 1, n5 = 1, n6 = 1, n7 = 1
+Number of pixels in 3rd-7th dimensions
+.le
+.ls header = "artdata$stdheader.dat"
+Image or header keyword data file. If an image is given then the image header
+is copied. If a file is given then the FITS format cards are copied.
+This only applies to new images. The data file consists of lines
+in FITS format with leading whitespace ignored. A FITS card must begin
+with an uppercase/numeric keyword. Lines not beginning with a FITS
+keyword such as comments or lower case are ignored. The user keyword
+output of \fBimheader\fR is an acceptable data file. See \fBmkheader\fR
+for further information.
+.le
+.ih
+DESCRIPTION
+This task creates or modifies images with a choice of patterns. New images
+are created with the specified dimensions, datatype, and pattern.
+Existing images may have the pattern replace, add, or multiply the
+pixel values. Existing images may be modified in place or new images may be
+created and image sections are allowed.
+
+For new images a set of header keywords may be added by specifying an
+image or data file with the \fIheader\fR parameter (see also \fBmkheader\fR).
+If a data file is specified lines beginning with FITS keywords are
+entered in the image header. Leading whitespace is ignored and any
+lines beginning with words having lowercase and nonvalid FITS keyword
+characters are ignored.
+
+This task is the simplest one for creating empty images to be used for
+mosaicing with \fBimcopy\fR and making patterns for testing display and
+image operators. The replace option is generally used with image sections
+to place constant values in regions. The multiply option is useful
+for making masks of the given pattern when the values are 0 and 1.
+
+Though the patterns make sense extending to higher dimensions they
+are only defined in two dimensions. One dimensional images may be
+thought of as the first line of the two dimensional pattern. Images
+with dimensions greater than 2 simply repeat the two dimensional
+pattern into the higher dimensions. The reason for stopping at
+two dimensions is simplicity.
+
+The patterns have the following precise definitions where P(i,j) is the
+pixel value at column i and line j, v1 and v2 are the pattern
+values, size is the pattern size, ncols and nlines are the number of
+columns and lines in the image, int is the integer function, mod is the
+modulus function, and sqrt is the square root function.
+
+.nf
+ k = int ((i-1)/size), l = int ((j-1)/size)
+ ksr = int (sqrt (k)), lsr = int (sqrt (l))
+ slope = (v2-v1) / ((ncols+nlines-2)/size)
+
+ constant: P(i,j) = v1
+
+ grid: P(i,j) = v2 when mod(i,size)=1 or mod(j,size)=1
+ P(i,j) = v1 otherwise
+
+ coordinates: P(i,j) = i + j * ncols
+
+ checker: P(i,j) = v1 when mod(k,2)=0 and mod(l,2)=0
+ P(i,j) = v2 when mod(k,2)=1 and mod(l,2)=0
+ P(i,j) = v2 when mod(k,2)=0 and mod(l,2)=1
+ P(i,j) = v1 when mod(k,2)=1 and mod(l,2)=1
+
+ slope: P(i,j) = v1 + slope * (k + l)
+
+ square: P(i,j) = v1 when mod(ksr,2)=0 and mod(lsr,2)=0
+ P(i,j) = v2 when mod(ksr,2)=1 and mod(lsr,2)=0
+ P(i,j) = v2 when mod(ksr,2)=0 and mod(lsr,2)=1
+ P(i,j) = v1 when mod(ksr,2)=1 and mod(lsr,2)=1
+.fi
+
+.ih
+EXAMPLES
+1. Create an empty (constant value of zero) three dimensional image.
+
+.nf
+ cl> mkpattern cube ndim=3 nc=100 nl=100 n3=100
+.fi
+
+2. Replace a square region of an image with the value -1000.
+
+.nf
+ cl> mkpat alpha[201:250,1:50] v1=-1000
+.fi
+
+3. Put a grid pattern on an image to create a new image.
+
+.nf
+ cl> mkpat dev$pix out=gridpix pat=grid op=mul v1=1 v2=0
+.fi
+.ih
+REVISIONS
+.ls MKPATTERN V2.11
+Now allows ndim=0 to create dataless header.
+
+Now allows type ushort pixel type.
+.le
+.ih
+SEE ALSO
+imcopy, imreplace
+.endhelp
diff --git a/noao/artdata/doc/starlist.hlp b/noao/artdata/doc/starlist.hlp
new file mode 100644
index 00000000..de3eb8b8
--- /dev/null
+++ b/noao/artdata/doc/starlist.hlp
@@ -0,0 +1,355 @@
+.help starlist Feb90 noao.artdata
+.ih
+TASK
+starlist -- make an artificial star list
+.ih
+USAGE
+starlist starlist nstars
+.ih
+PARAMETERS
+.ls starlist
+The name of the output text file for the x and y coordinates
+and magnitudes of the artificial stars. Output will be appended to this
+file is it exists.
+.le
+.ls nstars = 5000
+The number of stars in the output star list.
+.le
+.ls interactive = no
+Examine plots and change the parameters of the spatial luminosity
+distributions interactively.
+.le
+
+ SPATIAL DISTRIBUTION
+.ls spatial = "uniform"
+Type of spatial distribution. The types are:
+.ls uniform
+The stars are uniformly distributed between \fIxmin\fR, \fIxmax\fR, \fIymin\fR,
+and \fIymax\fR.
+.le
+.ls hubble
+The stars are distributed around the center of symmetry \fIxcenter\fR and
+\fIycenter\fR according to a Hubble density law of core radius
+\fIcore_radius\fR and background density \fIbase\fR.
+.le
+.ls file
+The radial density function is contained in the text file \fIsfile\fR.
+.le
+.le
+.ls xmin = 1., xmax = 512., ymin = 1., ymax = 512.
+The range of output coordinates in x and y.
+.le
+.ls xcenter = INDEF, ycenter = INDEF
+The coordinate of the center of symmetry for the "hubble"
+and "file" radial density functions. The default is the
+midpoint of the coordinate limits.
+.le
+.ls core_radius = 30
+The core radius of the Hubble spatial distribution in pixels.
+.le
+.ls base = 0.0
+The background density relative to the central density of the Hubble
+density distribution.
+.le
+.ls sseed = 1
+The initial value supplied to the random number generator used to
+generate the output x and y coordinates.
+If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+ MAGNITUDE DISTRIBUTION
+.ls luminosity = "powlaw"
+Type of luminosity distribution. The types are:
+.ls uniform
+The stars are uniformly distributed between \fIminmag\fR and \fImaxmag\fR.
+.le
+.ls powlaw
+The stars are distributed according to a power law with coefficient
+\fIpower\fR.
+.le
+.ls salpeter
+The stars are distributed with a Salpeter luminosity function between
+\fIminmag\fR and \fImaxmag\fR.
+.le
+.ls bands
+The stars are distributed with a Bahcall and Soneira luminosity function
+between \fIminmag\fR and \fImaxmag\fR. The function is described
+by the parameters \fIalpha\fR, \fIbeta\fR, \fIdelta\fR and \fImstar\fR
+whose default values give a best fit to the observed main sequence in several
+nearby globular clusters.
+.le
+.ls file
+The luminosity function is contained in the text file \fIlfile\fR.
+.le
+.le
+.ls minmag = -7., maxmag = 0.
+The range of output magnitudes. The "salpeter" luminosity function
+imposes limits of -4 and 16 and the "bands" luminosity function
+imposes limits of -7 and 17 relative to the zero point given by
+\fImzero\fR.
+.le
+.ls mzero = -4.
+The zero point for converting the output relative magnitudes
+to absolute magnitudes for the Salpeter and Bahcall and Soneira
+luminosity functions. For example the default values give an
+absolute magnitude range of -3 to +4.
+.le
+.ls power = 0.6
+Coefficient for the power law magnitude distribution.
+The default value of 0.6 is the value for a homogeneous
+and isotropic distribution with no cutoff in distance.
+.le
+.ls alpha = 0.74, beta = 0.04, delta = 0.294, mstar = 1.28
+The parameters of the Bahcall and Soneira luminosity function.
+.le
+.ls lseed = 1
+The initial value supplied to the random number generator used to
+generate the output magnitudes.
+If a value of "INDEF" is given then the clock
+time (integer seconds since 1980) is used as the seed yielding
+different random numbers for each execution.
+.le
+
+ USER FUNCTIONS
+.ls sfile
+The name of the input text file containing the sampled spatial radial
+density
+function, one sample point per line, with the radius and relative probability
+in columns one and two respectively. The sample points need not be
+uniformly spaced or normalized.
+.le
+.ls nssample = 100
+The number of points at which the \fIspatial\fR density function is
+sampled. If the \fIspatial\fR density function is analytic or approximated
+analytically (the "uniform" and "hubble" options) the function is sampled
+directly. If the function is read from a file (the "file" option) an
+initial smoothing step is performed before sampling.
+.le
+.ls sorder = 10
+The order of the spline fits used to evaluate the integrated spatial
+density function.
+.le
+.ls lfile
+The name of the input text file containing the sampled luminosity
+function, one sample point per line, with the magnitude and relative probability
+in columns one and two respectively. The sample points need not be
+uniformly spaced or normalized.
+.le
+.ls nlsample = 100
+The number of points at which the luminosity function is sampled. If
+the luminosity function is analytic or approximated analytically (the
+"salpeter" and "bands" options) the function is sampled directly. If
+it is read from a file (the "file" option) an initial smoothing step
+is performed before sampling.
+.le
+.ls lorder = 10
+The order of the spline fits used to evaluate the integrated
+\fIluminosity\fR function.
+.le
+
+ INTERACTIVE PARAMETERS
+.ls rbinsize = 10.
+The bin size in pixels of the plotted histogram of the radial density
+distribution.
+.le
+.ls mbinsize = 0.5
+The bin size in magnitudes of the plotted histogram of the luminosity function.
+.le
+.ls graphics = stdgraph
+The default graphics device.
+.le
+.ls cursor = ""
+The graphics cursor.
+.le
+.ih
+DESCRIPTION
+\fBStarlist\fR generates a list of x and y coordinates and magnitudes
+for a sample of \fInstars\fR stars based on a user selected spatial
+density function \fIspatial\fR and luminosity function
+\fIluminosity\fR and writes (appends) the results to the text file
+\fIstarlist\fR. If the \fIinteractive\fR parameter is "yes" the user
+can interactively examine plots of the spatial density function,
+the radial density function, and the luminosity function, and alter the
+parameters of the task until a satisfactory artificial field is
+generated.
+
+The spatial density function generates x and y values around a center
+of symmetry defined by \fIxcenter\fR and \fIycenter\fR within the x and
+y limits \fIxmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR according to
+the spatial density function specified by \fIspatial\fR. The three
+supported spatial density functions are listed below where R is the
+radial distance in pixels, P is the relative spatial density, C is a
+constant and f is the best fitting cubic spline function to the spatial
+density function R(user), P(user) supplied by the user in the text file
+\fIsfile\fR.
+
+.nf
+ uniform: P = C
+ hubble: P = 1.0 / (1 + R / core_radius) ** 2 + base
+ file: P = f (R(user), P(user))
+.fi
+
+The Hubble and user file spatial density function are sampled at
+\fInssample\fR equally spaced points, and integrated to give the
+spatial density probability function at each sampled point. The
+integrated probability function is normalized and approximated by a
+cubic spline of order \fIsorder\fR. The x and y coordinates are
+computed by randomly sampling the integrated probability function until
+\fInstars\fR stars which satisfy the x and y coordinate limits
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR are generated.
+
+The luminosity function generates relative magnitude values between
+\fIminmag\fR and \fImaxmag\fR according to the luminosity function
+specified by \fIluminosity\fR. The four supported luminosity functions
+are defined below where M is the magnitude, P is the relative luminosity
+function, C is a constant and f is the best fitting cubic spline
+function to the luminosity function M(user), P(user) supplied by the
+in the text file \fIlfile\fR.
+
+.nf
+ uniform: P = C
+
+ powlaw: P = C * 10. ** (power * M)
+
+ salpeter: P = C * 10. ** (-3.158 + 1.551e-1*dM - 5.194e-3*dM**2)
+
+ dM = M - mzero
+
+ C * 10. ** (beta * dM)
+ bands: P = --------------------------------------------------
+ (1. + 10. ** ((beta-alpha)*delta*dM))) ** 1. /delta
+
+ dM = M - mstar - mzero
+
+ file: P = f (M(user), P(user))
+.fi
+
+The Salpeter and "bands" functions are defined in terms of absolute
+magnitudes so the parameter \fImzero\fR is used to convert from
+relative magnitudes. Equivalently, one could use absolute magnitudes
+for the magnitude limits while setting the zero point to 0.
+
+The luminosity function is sampled at \fInlsample\fR equally spaced
+points, and integrated to give the luminosity probability function at
+each sampled point. The probablity function is normalized and
+approximated by a cubic spline of order \fIlorder\fR. The magnitudes
+are computed by randomly sampling the integrated probability function
+until \fInstars\fR objects which satisfy the magnitude limits
+\fIminmag\fR and \fImaxmag\fR are generated. The Salpeter luminosity
+is a best fit function to the data of McCuskey (McCuskey, 1966, Vistas
+Astr. 7, 141). The Bahcall and Soneira function and the default values
+of the parameters are discussed by Bahcall and Soneira (Ap.J. Supp. 44, 73).
+.ih
+CURSORS
+The following interactive keystroke commands are available from within the
+STARLIST task.
+
+.nf
+ Starlist Keystroke Commands
+
+? Print options
+f Fit one or more of the following
+ Spatial density function (SDF)
+ Luminosity functions (LF)
+x Plot the x-y spatial density function
+r Plot the histogram of the radial density function
+m Plot the histogram of the luminosity function
+: Colon escape commands (see below)
+q Exit program
+.fi
+
+The following parameters can be shown or set from within the STARLIST task.
+
+
+.nf
+ Starlist Colon Commands
+
+:show Show starlist parameters
+:nstars [value] Number of stars
+
+:spatial [string] Spatial density function (SDF)
+ (uniform|hubble|file)
+:xmin [value] Minimum X value
+:xmax [value] Maximum X value
+:ymin [value] Minimum Y value
+:ymax [value] Maximum Y value
+:xcenter [value] X center for SDF
+:ycenter [value] Y center for SDF
+:core [value] Core radius for Hubble density function
+:base [value] Background density for Hubble density function
+
+:luminosity [string] Luminosity function (LF)
+ (uniform|powlaw|salpeter|bands|file)
+:minmag [value] Minimum magnitude
+:maxmag [value] Maximum magnitude
+:mzero [value] Magnitude zero-point for salpeter and bands LF
+:power [value] Exponent for powlaw LF
+:alpha [value] Alpha parameter for bands LF
+:beta [value] Beta parameter for bands LF
+:delta [value] Delta parameter for bands LF
+:mstar [value] Mstar parameter for bands LF
+
+:sfile [string] File containing the user SDF
+:nssample [value] Number of SDF sample points
+:sorder [value] Order of spline fit to integrated SDF
+:lfile [string] File containing the user LF
+:nlsample [value] Number of LF sample points
+:lorder [value] Order of spline fit to the integrated LF
+
+:rbinsize [value] Resolution of radial profile histogram (pixels)
+:mbinsize [value] Resolution of magnitude histogram (mag)
+.fi
+
+.ih
+EXAMPLES
+1. Create a uniform artificial starfield of 5000 stars for a 512 square image.
+
+.nf
+ ar> starlist starfield.dat 5000
+ ar> mkobjects starfield obj=starfield.dat gain=2 rdnoise=10 poisson+
+.fi
+
+This example takes about a minute on a SPARCstation 1.
+
+2. Create a globular cluster field of 5000 stars for a 512 square image.
+
+.nf
+ ar> starlist gc.dat 5000 spat=hubble lum=bands
+ ar> mkobjects starfield obj=gc.dat gain=2 rdnoise=10 poisson+
+.fi
+
+This example takes about a minute on a SPARCstation 1.
+
+3. Examine the distributions for a Hubble spatial distribution
+and Salpeter magnitude distribution using 1000 stars without
+creating a data file.
+
+.nf
+ ar> starlist dev$null 1000 inter+ spat=hubble lum=salpeter
+ ... an x-y plot will appear on the screen
+ ... type r to examine the radial density function
+ ... type m to examine the luminosity function
+ ... type = to make a copy of any of the plots
+ ... type q to quit
+.fi
+.ih
+REVISIONS
+.ls STARLIST V2.11+
+The random number seeds can be set from the clock time by using the value
+"INDEF" to yield different random numbers for each execution.
+.le
+.ih
+BUGS
+The spline approximation to the spatial density and luminosity
+probability functions can cause wiggles in the output spatial density
+and luminosity functions. Users can examine the results interactively
+and experiment with the spline order and number of sample points if
+they are not satisfied with the results of STARLIST. The default setup
+of 10 sample points per spline piece is generally satisfactory for the
+spatial density and luminosity functions supplied here.
+.ih
+SEE ALSO
+gallist mkobjects
+.endhelp
diff --git a/noao/artdata/doc/version1.1 b/noao/artdata/doc/version1.1
new file mode 100644
index 00000000..b16c4df1
--- /dev/null
+++ b/noao/artdata/doc/version1.1
@@ -0,0 +1,49 @@
+ Changes to the ARTDATA Package: Version 1.1
+
+Since the first release of the ARTDATA package with V2.9 there have been
+several bugs found and documented, as is natural, and some new features
+and tasks have been added. This note summarizes these changes.
+
+The bugs (log numbers 127, 130, 131, and 135) are all in the main task
+MKOBJECTS. Two of these concern the size of the PSF as specified by
+the "radius" parameter. The radius parameter for the gaussian PSF
+is interpreted as a FWHM instead of the intended HWHM (radius at half
+maximum). This leads to very narrow stars until one compensates for the
+error. The size of the moffat PSF are off by a small factor (~10%)
+depending on the beta parameter. Another bug causes objects near the edge
+to be off by 1 pixel from the specified coordinates. The last bug
+prevents use of a user supplied profile function for the PSF. A
+distributed patch for the above problems is planned. Thanks go to
+Lindsey Davis and Steven Perry for the careful studies which identified
+the first three bugs.
+
+The artificial one dimensional spectra produced by MK1DSPEC have new
+parameters allowing addition of a velocity shift specified either as
+a velocity or redshift. This is a useful addition for tests of radial
+velocity programs.
+
+The initial version of the package provided minimal headers; mostly
+just the wavelengths for 1D spectra and the dispersion axis for 2D
+spectra. People then need to HEDIT keywords to make the images
+work well with some packages. Four things have been done to
+make useful headers for the artificial data. First, a detailed
+log of parameters used to generate the images is included as COMMENT
+cards. This includes task parameters and information from data files
+such as those in the star and galaxy list files produced by
+STARLIST and GALLIST. Second, the gain, read noise, exposure
+time, and dispersion correction flag have been added to the header
+when appropriate. Third, each task has a new parameter for specifying
+a header keyword data file containing a list of keywords and values
+to be automatically added. A default file is supplied but any set
+for a particular type of data may be substituted. Finally, a new
+task called MKHEADER has been added which applies header keyword
+data files to images.
+
+A new task called MKEXAMPLES has been added. Given the name of an
+example from a menu an image is created. This task is intended to
+provide examples for the ARTDATA package as well as test and
+demonstration images for the various packages. The initial menu
+includes long slit and multifiber spectra, a globular cluster,
+a star field, a galaxy field, and a galaxy cluster. Additional
+examples will be added as demonstrations and test procedures are
+developed for various packages.
diff --git a/noao/artdata/gallist.par b/noao/artdata/gallist.par
new file mode 100644
index 00000000..e0df91f2
--- /dev/null
+++ b/noao/artdata/gallist.par
@@ -0,0 +1,52 @@
+# GALLIST Parameter File
+
+gallist,f,a,,,,"Output galaxies list file"
+ngals,i,a,100,,,"Number of galaxies in the output list"
+interactive,b,h,no,,,"Interactive mode?
+
+SPATIAL DISTRIBUTION"
+spatial,s,h,"uniform","uniform|hubble|file",,"Spatial density function (uniform|hubble|file)"
+xmin,r,h,1.,,,"Minimum x coordinate value"
+xmax,r,h,512.,,,"Maximum x coordinate value"
+ymin,r,h,1.,,,"Minimum y coordinate value"
+ymax,r,h,512.,,,"Maximum y coordinate value"
+xcenter,r,h,INDEF,,,"X coordinate of center of Hubble distribution"
+ycenter,r,h,INDEF,,,"Y coordinate of center of Hubble distribution"
+core_radius,r,h,50.,,,"Core radius of Hubble distribution"
+base,r,h,0.,,,"Relative background density of the Hubble distribution"
+sseed,i,h,2,,,"Seed for sampling the spatial probability function
+
+MAGNITUDE DISTRIBUTION"
+luminosity,s,h,"powlaw","uniform|powlaw|schecter|file",,"Luminosity function (uniform|powlaw|schecter|file)"
+minmag,r,h,-7.,,,"Minimum magnitude"
+maxmag,r,h,0.,,,"Maximum magnitude"
+mzero,r,h,15.,,,"Magnitude zero point"
+power,r,h,0.6,,,"Power law magnitude distribution coefficient"
+alpha,r,h,-1.24,,,"Schecter luminosity function coefficient"
+mstar,r,h,-21.41,,,"Schecter luminosity function charactersitic mangitude"
+lseed,i,h,2,,,"Seed for sampling the luminosity probability function
+
+MORPHOLOGY DISTRIBUTION"
+egalmix,r,h,0.4,,,"Percentage of elliptical galaxies"
+ar,r,h,0.3,0.3,1.,"Minimum elliptical galaxy axial ratio"
+eradius,r,h,20.,,,"Maximum elliptical half flux radius"
+sradius,r,h,1.,,,"Spiral/ellipical radius at same magnitude"
+absorption,r,h,1.2,,,"Absorption in edge on spirals (mag)"
+z,r,h,0.05,0.0001,,"Minimum redshift
+
+USER FUNCTIONS"
+sfile,f,h,"",,,"File containing spatial density function"
+nssample,i,h,100,,,"Number of spatial density function sampling points"
+sorder,i,h,10,,,"Number of spline pieces for spatial function"
+lfile,f,h,"",,,"File containing luminosity function"
+nlsample,i,h,100,,,"Number of luminosity function sampling points"
+lorder,i,h,10,,,"Number of spline pieces for luminosity probability function
+
+INTERACTIVE PARAMETERS"
+rbinsize,r,h,10.,,,"Bin size of radial density function histogram in pixels"
+mbinsize,r,h,0.5,,,"Bin size of luminosity function in magnitudes"
+dbinsize,r,h,0.5,,,"Bin size of diameter distribution function in pixels"
+ebinsize,r,h,0.1,,,"Bin size of roundness distribution function"
+pbinsize,r,h,20.,,,"Bin size of position angle distribution function in degrees"
+graphics,s,h,stdgraph,,,"Standard graphics device"
+cursor,*gcur,h,"",,,"Graphics cursor"
diff --git a/noao/artdata/lists/gallist.key b/noao/artdata/lists/gallist.key
new file mode 100644
index 00000000..aa9d1822
--- /dev/null
+++ b/noao/artdata/lists/gallist.key
@@ -0,0 +1,63 @@
+ Gallist Keystroke Commands
+
+? Print options
+f Fit one or more of following
+ Spatial density function (SDF)
+ Luminosity function (LF)
+ Distribution of morphological type
+ Diameter distribution
+ Roundness distribution
+ Position angle distribution
+x Plot the x-y spatial density function
+r Plot the histogram of the radial density function
+m Plot the histogram of the luminosity function
+d Plot the histogram of the diameter values
+e Plot the histogram of the roundness values
+p Plot the histogram of the position angle values
+: Colon escape commands (see below)
+q Exit program
+
+
+ Gallist Colon Commands
+
+:show Show gallist parameters
+:ngal [value] Number of galaxies
+
+:spatial [string] Spatial density function (SDF) (uniform|hubble|file)
+:xmin [value] Minimum X value
+:xmax [value] Maximum X value
+:ymin [value] Minimum Y value
+:ymax [value] Maximum Y value
+:xcenter [value] X center for radial density function
+:ycenter [value] Y center for radial density function
+:core [value] Core radius for Hubble density function
+:base [value] Background density for Hubble density function
+
+:luminosity [string] Luminosity function (LF)
+ (uniform|powlaw|shcecter|file)
+:minmag [value] Minimum magnitude
+:maxmag [value] Maximum magnitude
+:power [value] Power law coefficient
+:mzero [value] Zero point for Schecter luminosity function
+:alpha [value] Schecter parameter
+:mstar [value] Characteristic mag for Schecter function
+
+:egalmix [value] Elliptical/Spiral galaxy ratio
+:ar [value] Minimum elliptical galaxy axial ratio
+:eradius [value] Maximum elliptical half flux radius
+:sradius [value] Spiral/elliptical radius at same magnitude
+:absorption [value] Absorption correction for edge-on spirals
+:z [value] Minimum redshift
+
+:lfile [string] Name of the luminosity function file
+:sfile [string] Name of the spatial density function file
+:nlsample [value] Number of LF sample points
+:lorder [value] Order of spline approximation to the integrated LF
+:nssample [value] Number of SDF sample points
+:sorder [value] Order of spline approximation to the integrated SDF
+
+:rbinsize [value] Resolution of radial SDF histogram in pixels
+:mbinsize [value] Resolution of magnitude histogram in magnitudes
+:dbinsize [value] Resolution of diameter histogram in pixels
+:ebinsize [value] Resolution of roundness histogram in pixels
+:pbinsize [value] Resolution of position angle histogram in degrees
diff --git a/noao/artdata/lists/mkpkg b/noao/artdata/lists/mkpkg
new file mode 100644
index 00000000..d8281097
--- /dev/null
+++ b/noao/artdata/lists/mkpkg
@@ -0,0 +1,18 @@
+# ARTDATA STARLIST and GALLIST task libraries
+
+$checkout libpkg.a ../
+$update libpkg.a
+$checkin libpkg.a ../
+$exit
+
+libpkg.a:
+ stcolon.x starlist.h <error.h>
+ stdbio.x starlist.h <pkg/dttext.h>
+ stlum.x <mach.h> <math.h> <math/curfit.h> <math/iminterp.h>
+ stmix.x <math.h> starlist.h
+ stplot.x starlist.h <gset.h> <mach.h> <math.h> <pkg/gtools.h>
+ stshow.x starlist.h
+ stspatial.x <math.h> <math/curfit.h> <math/iminterp.h>
+ t_gallist.x starlist.h <fset.h>
+ t_starlist.x starlist.h <fset.h>
+ ;
diff --git a/noao/artdata/lists/starlist.h b/noao/artdata/lists/starlist.h
new file mode 100644
index 00000000..01498222
--- /dev/null
+++ b/noao/artdata/lists/starlist.h
@@ -0,0 +1,136 @@
+# STARLIST/GALLIST task definitions file
+
+define ST_STARS 1 # Make star list
+define ST_GALAXIES 2 # Make galaxies list
+
+# Spatial distribution functions
+
+define ST_UNIFORM 1 # Uniform spatial distribution
+define ST_HUBBLE 2 # Hubble law
+define ST_SPFILE 3 # User input
+
+# Luminosity distribution function
+
+define ST_UNIFORM 1 # Uniform luminosity function
+define ST_SALPETER 2 # Salpeter luminosity function
+define ST_BANDS 3 # Bahcall and Soneira
+define ST_LFFILE 4 # User input
+define ST_POWLAW 5 # Power law
+define ST_SCHECTER 6 # Schecter luminosity function
+
+# Galaxies types
+
+define ST_DEVAUC 1 # Ellipticals
+define ST_EXP 2 # Spirals
+
+define LEN_STSTRUCT (45 + 4 * SZ_FNAME + 4)
+
+define ST_TYPE Memi[$1] # Stars or galaxies
+define ST_SPATIAL Memi[$1+1] # Spatial function
+define ST_XC Memr[P2R($1+2)] # X center
+define ST_YC Memr[P2R($1+3)] # Y center
+define ST_CORE Memr[P2R($1+4)] # Hubble core radius
+define ST_BASE Memr[P2R($1+5)] # Hubble baseline probability
+define ST_XMIN Memr[P2R($1+6)] # Minimum x value
+define ST_XMAX Memr[P2R($1+7)] # Maximum x value
+define ST_YMIN Memr[P2R($1+8)] # Minimum y value
+define ST_YMAX Memr[P2R($1+9)] # Maximum y value
+
+define ST_LUMINOSITY Memi[$1+10] # Luminosity function
+define ST_POWER Memr[P2R($1+11)]# Power law
+define ST_MZERO Memr[P2R($1+12)]# Zero point of magnitudes
+define ST_ALPHA Memr[P2R($1+13)]# Bands function alpha
+define ST_BETA Memr[P2R($1+14)]# Bands function beta
+define ST_DELTA Memr[P2R($1+15)]# Bands function delta
+define ST_MSTAR Memr[P2R($1+16)]# Bands function mstar
+define ST_MINMAG Memr[P2R($1+17)]# Minimum magnitude
+define ST_MAXMAG Memr[P2R($1+18)]# Maximum magnitude
+
+define ST_Z Memr[P2R($1+19)]# Minimum redshift
+define ST_AR Memr[P2R($1+20)]# Minimum roundness
+define ST_ERADIUS Memr[P2R($1+21)]# Maximum elliptical radius
+define ST_SRADIUS Memr[P2R($1+22)]# Maximum spiral radius
+define ST_EGALMIX Memr[P2R($1+23)]# Egal fraction
+define ST_ABSORPTION Memr[P2R($1+24)]# Absorption
+
+define ST_SSEED Meml[$1+25] # Spatial function seed
+define ST_LSEED Meml[$1+26] # Luminosity function seed
+define ST_NSSAMPLE Memi[$1+27] # Spatial function sampling
+define ST_NLSAMPLE Memi[$1+28] # Luminosity function sampling
+define ST_SORDER Memi[$1+29] # Spatial spline order
+define ST_LORDER Memi[$1+30] # Luminosity spline order
+define ST_NSTARS Memi[$1+31] # Number of stars
+
+define ST_RBINSIZE Memr[P2R($1+32)]# Radial histogram resolution
+define ST_MBINSIZE Memr[P2R($1+33)]# Magnitude histogram resolution
+define ST_DBINSIZE Memr[P2R($1+34)]# Diameter histogram resolution
+define ST_EBINSIZE Memr[P2R($1+35)]# Roundness histogram resolution
+define ST_PBINSIZE Memr[P2R($1+36)]# Posang histogram resolution
+
+define ST_SPSTRING Memc[P2C($1+37)]
+define ST_LFSTRING Memc[P2C($1+37+SZ_FNAME+1)]
+define ST_SFILE Memc[P2C($1+37+2*SZ_FNAME+2)]
+define ST_LFILE Memc[P2C($1+37+3*SZ_FNAME+3)]
+
+define STCMDS "|show|nstars|spatial|xcenter|ycenter|core|base|xmin|xmax|\
+ymin|ymax|luminosity|power|alpha|beta|delta|mstar|minmag|maxmag|||nssample|\
+nlsample|sorder|lorder|sfile|lfile|rbinsize|mbinsize|ar|z|eradius|sradius|\
+egalmix|dbinsize|ebinsize|pbinsize|ngals|mzero|absorption|"
+
+define SPFUNCS "|uniform|hubble|file|"
+define LUMFUNCS "|uniform|salpeter|bands|file|powlaw|"
+define GLUMFUNCS "|uniform|||file|powlaw|schecter|"
+
+define STCMD_SHOW 1
+define STCMD_NSTARS 2
+define STCMD_SPATIAL 3
+define STCMD_XCENTER 4
+define STCMD_YCENTER 5
+define STCMD_CORE 6
+define STCMD_BASE 7
+define STCMD_XMIN 8
+define STCMD_XMAX 9
+define STCMD_YMIN 10
+define STCMD_YMAX 11
+define STCMD_LUMINOSITY 12
+define STCMD_POWER 13
+define STCMD_ALPHA 14
+define STCMD_BETA 15
+define STCMD_DELTA 16
+define STCMD_MSTAR 17
+define STCMD_MINMAG 18
+define STCMD_MAXMAG 19
+define STCMD_SSEED 20
+define STCMD_LSEED 21
+define STCMD_NSSAMPLE 22
+define STCMD_NLSAMPLE 23
+define STCMD_SORDER 24
+define STCMD_LORDER 25
+define STCMD_SFILE 26
+define STCMD_LFILE 27
+define STCMD_RBINSIZE 28
+define STCMD_MBINSIZE 29
+define STCMD_AR 30
+define STCMD_Z 31
+define STCMD_ERADIUS 32
+define STCMD_SRADIUS 33
+define STCMD_EGALMIX 34
+define STCMD_DBINSIZE 35
+define STCMD_EBINSIZE 36
+define STCMD_PBINSIZE 37
+define STCMD_NGALS 38
+define STCMD_MZERO 39
+define STCMD_ABSORPTION 40
+
+# Miscellaneous default values
+
+define DEF_CORE 20.0
+define DEF_BASE 0.00
+
+define DEF_ALPHA 0.74
+define DEF_BETA 0.04
+define DEF_DELTA 0.294
+define DEF_MSTAR 1.28
+
+define DEF_GMSTAR -20.6
+define DEF_GALPHA -1.25
diff --git a/noao/artdata/lists/starlist.key b/noao/artdata/lists/starlist.key
new file mode 100644
index 00000000..45f6c1fc
--- /dev/null
+++ b/noao/artdata/lists/starlist.key
@@ -0,0 +1,47 @@
+ Starlist Keystroke Commands
+
+? Print options
+f Fit one or more of the following
+ Spatial density function (SDF)
+ Luminosity functions (LF)
+x Plot the x-y spatial density function
+r Plot the histogram of the radial density function
+m Plot the histogram of the luminosity function
+: Colon escape commands (see below)
+q Exit program
+
+
+ Starlist Colon Commands
+
+:show Show starlist parameters
+:nstars [value] Number of stars
+
+:spatial [string] Spatial density function (uniform|hubble|file)
+:xmin [value] Minimum X value
+:xmax [value] Maximum X value
+:ymin [value] Minimum Y value
+:ymax [value] Maximum Y value
+:xcenter [value] X center for radial density function
+:ycenter [value] Y center for radial density function
+:core [value] Core radius for Hubble density function
+:base [value] Background density for Hubble density function
+
+:luminosity [string] Luminosity function (uniform|powlaw|salpeter|bands|file)
+:minmag [value] Minimum magnitude
+:maxmag [value] Maximum magnitude
+:power [value] Coefficent for powlaw magnitude distribution
+:mzero [value] Magnitude zero point for salpeter and bands functions
+:alpha [value] Alpha parameter for bands luminosity function
+:beta [value] Beta parameter for bands luminosity function
+:delta [value] Delta parameter for bands luminosity function
+:mstar [value] Mstar parameter for bands luminosity function
+
+:sfile [string] File containing the user spatial function
+:nssample [value] Number of SDF sample points
+:sorder [value] Order of spline fit to spatial function
+:lfile [string] File containing the user luminosity function
+:nlsample [value] Number of LF sample points
+:lorder [value] Order of spline fit to the integrated LF
+
+:rbinsize [value] Resolution of radial profile histogram (pixels)
+:mbinsize [value] Resolution of magnitude histogram (mag)
diff --git a/noao/artdata/lists/stcolon.x b/noao/artdata/lists/stcolon.x
new file mode 100644
index 00000000..9aadd578
--- /dev/null
+++ b/noao/artdata/lists/stcolon.x
@@ -0,0 +1,835 @@
+include <error.h>
+include "starlist.h"
+
+# ST_COLON -- Process colon commands for the STARLIST task.
+
+procedure st_colon (gd, st, sf, lf, cmdstr, newspace, newlum, newplot)
+
+pointer gd # pointer to the graphics stream
+pointer st # pointer to starlist structure
+int sf # spatial density function file descriptor
+int lf # luminosity function file descriptor
+char cmdstr[ARB] # input command string
+int newspace # new spatial distribution function
+int newlum # new luminosity function
+int newplot # new plot
+
+int ival, ncmd, stat
+pointer sp, cmd, str
+long lval
+real rval
+bool streq()
+int strdic(), nscan(), open()
+
+string lumfuncs LUMFUNCS
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Get the command.
+ call sscan (cmdstr)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS)
+ return
+
+ # Process the command.
+ ncmd = strdic (Memc[cmd], Memc[cmd], SZ_LINE, STCMDS)
+ switch (ncmd) {
+
+ case STCMD_SHOW:
+ call gdeactivate (gd, 0)
+ call st_show (st)
+ call greactivate (gd, 0)
+
+ case STCMD_SPATIAL:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (nscan () == 1) {
+ call printf ("spatial = %s\n")
+ call pargstr (ST_SPSTRING(st))
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, SPFUNCS)
+ if (stat > 0) {
+ ST_SPATIAL(st) = stat
+ call strcpy (Memc[cmd], ST_SPSTRING(st), SZ_FNAME)
+ }
+ newspace = YES
+ }
+
+ case STCMD_XCENTER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("xcenter = %g pixels\n")
+ call pargr (ST_XC(st))
+ } else {
+ ST_XC(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_YCENTER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ycenter = %g pixels\n")
+ call pargr (ST_YC(st))
+ } else {
+ ST_YC(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_CORE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("core = %g pixels\n")
+ call pargr (ST_CORE(st))
+ } else {
+ ST_CORE(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_BASE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("base = %g pixels\n")
+ call pargr (ST_BASE(st))
+ } else {
+ ST_BASE(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_XMIN:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("xmin = %g pixels\n")
+ call pargr (ST_XMIN(st))
+ } else {
+ ST_XMIN(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_XMAX:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("xmax = %g pixels\n")
+ call pargr (ST_XMAX(st))
+ } else {
+ ST_XMAX(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_YMIN:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ymin = %g pixels\n")
+ call pargr (ST_YMIN(st))
+ } else {
+ ST_YMIN(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_YMAX:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ymax = %g pixels\n")
+ call pargr (ST_YMAX(st))
+ } else {
+ ST_YMAX(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_SFILE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], ST_SFILE(st))) {
+ call printf ("sfile: %s\n")
+ call pargstr (ST_SFILE(st))
+ } else {
+ if (sf != NULL) {
+ call close (sf)
+ sf = NULL
+ }
+ iferr {
+ sf = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ sf = NULL
+ call erract (EA_WARN)
+ call strcpy ("", ST_SFILE(st), SZ_FNAME)
+ call printf (
+ "Spatial distribution function file is undefined.\n")
+ } else {
+ call strcpy (Memc[cmd], ST_SFILE(st), SZ_FNAME)
+ newspace = YES
+ }
+ }
+
+ case STCMD_LUMINOSITY:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (nscan () == 1) {
+ call printf ("luminosity = %s\n")
+ call pargstr (ST_LFSTRING(st))
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, LUMFUNCS)
+ if (stat > 0) {
+ ST_LUMINOSITY (st) = stat
+ call strcpy (Memc[cmd], ST_LFSTRING(st), SZ_FNAME)
+ }
+ newlum = YES
+ }
+
+ case STCMD_POWER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("power = %g\n")
+ call pargr (ST_POWER(st))
+ } else {
+ ST_POWER(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MZERO:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("mzero = %g\n")
+ call pargr (ST_MZERO(st))
+ } else {
+ ST_MZERO(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_ALPHA:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("alpha = %g\n")
+ call pargr (ST_ALPHA(st))
+ } else {
+ ST_ALPHA(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_BETA:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("beta = %g\n")
+ call pargr (ST_BETA(st))
+ } else {
+ ST_BETA(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_DELTA:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("delta = %g\n")
+ call pargr (ST_DELTA(st))
+ } else {
+ ST_DELTA(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MSTAR:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("mstar = %g\n")
+ call pargr (ST_MSTAR(st))
+ } else {
+ ST_MSTAR(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MINMAG:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("minmag = %g\n")
+ call pargr (ST_MINMAG(st))
+ } else {
+ ST_MINMAG(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MAXMAG:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("maxmag = %g\n")
+ call pargr (ST_MAXMAG(st))
+ } else {
+ ST_MAXMAG(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_LFILE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], ST_LFILE(st))) {
+ call printf ("lfile: %s\n")
+ call pargstr (ST_LFILE(st))
+ } else {
+ if (lf != NULL) {
+ call close (lf)
+ lf = NULL
+ }
+ iferr {
+ lf = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ lf = NULL
+ call erract (EA_WARN)
+ call strcpy ("", ST_LFILE(st), SZ_FNAME)
+ call printf (
+ "Luminosity function file is undefined.\n")
+ } else {
+ call strcpy (Memc[cmd], ST_LFILE(st), SZ_FNAME)
+ newlum = YES
+ }
+ }
+
+ case STCMD_NSTARS:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("nstars = %d\n")
+ call pargi (ST_NSTARS(st))
+ } else {
+ ST_NSTARS(st) = ival
+ newlum = YES
+ newspace = YES
+ }
+
+ case STCMD_NSSAMPLE:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("nssample = %d\n")
+ call pargi (ST_NSSAMPLE(st))
+ } else {
+ ST_NSSAMPLE(st) = ival
+ newspace = YES
+ }
+
+ case STCMD_SORDER:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("sorder = %d\n")
+ call pargl (ST_SORDER(st))
+ } else {
+ ST_SORDER(st) = lval
+ newspace = YES
+ }
+
+ case STCMD_SSEED:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("sseed = %d\n")
+ call pargl (ST_SSEED(st))
+ } else {
+ ST_SSEED(st) = lval
+ newspace = YES
+ }
+
+ case STCMD_NLSAMPLE:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("nlsample = %d\n")
+ call pargi (ST_NLSAMPLE(st))
+ } else {
+ ST_NLSAMPLE(st) = ival
+ newlum = YES
+ }
+
+ case STCMD_LORDER:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("lorder = %d\n")
+ call pargl (ST_LORDER(st))
+ } else {
+ ST_LORDER(st) = lval
+ newlum = YES
+ }
+
+ case STCMD_LSEED:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("lseed = %d\n")
+ call pargl (ST_LSEED(st))
+ } else {
+ ST_LSEED(st) = lval
+ newlum = YES
+ }
+
+ case STCMD_RBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("rbinsize = %g\n")
+ call pargr (ST_RBINSIZE(st))
+ } else {
+ ST_RBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ case STCMD_MBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("mbinsize = %g\n")
+ call pargr (ST_MBINSIZE(st))
+ } else {
+ ST_MBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ default:
+ call printf ("\7\n")
+ }
+
+ call sfree (sp)
+end
+
+
+# ST_GCOLON -- Process colon commands for the GALAXIES task.
+
+procedure st_gcolon (gd, st, sf, lf, cmdstr, newspace, newlum, newmix, newaxis,
+ newround, newphi, newplot)
+
+pointer gd # pointer to the graphics stream
+pointer st # pointer to starlist structure
+int sf # spatial distribution function file descriptor
+int lf # luminosity function file descriptor
+char cmdstr[ARB] # command string
+int newspace # new spatial distribution function
+int newlum # new luminosity function
+int newmix # new E/S galaxy mixture
+int newaxis # new axis parameter
+int newround # compute new roundness parameters
+int newphi # compute new position angles
+int newplot # make a newplot
+
+int ival, ncmd, stat
+pointer sp, cmd, str
+long lval
+real rval
+bool streq()
+int strdic(), nscan(), open()
+
+string lumfuncs LUMFUNCS
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Get the command.
+ call sscan (cmdstr)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS)
+ return
+
+ # Process the command.
+ ncmd = strdic (Memc[cmd], Memc[cmd], SZ_LINE, STCMDS)
+ switch (ncmd) {
+ case STCMD_SHOW:
+ call gdeactivate (gd, 0)
+ call st_gshow (st)
+ call greactivate (gd, 0)
+
+ case STCMD_SPATIAL:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (nscan () == 1) {
+ call printf ("spatial = %s\n")
+ call pargstr (ST_SPSTRING(st))
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, SPFUNCS)
+ if (stat > 0) {
+ ST_SPATIAL(st) = stat
+ call strcpy (Memc[cmd], ST_SPSTRING(st), SZ_FNAME)
+ }
+ newspace = YES
+ }
+
+ case STCMD_XCENTER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("xcenter = %g pixels\n")
+ call pargr (ST_XC(st))
+ } else {
+ ST_XC(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_YCENTER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ycenter = %g pixels\n")
+ call pargr (ST_YC(st))
+ } else {
+ ST_YC(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_CORE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("core = %g pixels\n")
+ call pargr (ST_CORE(st))
+ } else {
+ ST_CORE(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_BASE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("base = %g pixels\n")
+ call pargr (ST_BASE(st))
+ } else {
+ ST_BASE(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_XMIN:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("xmin = %g pixels\n")
+ call pargr (ST_XMIN(st))
+ } else {
+ ST_XMIN(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_XMAX:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("xmax = %g pixels\n")
+ call pargr (ST_XMAX(st))
+ } else {
+ ST_XMAX(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_YMIN:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ymin = %g pixels\n")
+ call pargr (ST_YMIN(st))
+ } else {
+ ST_YMIN(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_YMAX:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ymax = %g pixels\n")
+ call pargr (ST_YMAX(st))
+ } else {
+ ST_YMAX(st) = rval
+ newspace = YES
+ }
+
+ case STCMD_SFILE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], ST_SFILE(st))) {
+ call printf ("sfile: %s\n")
+ call pargstr (ST_SFILE(st))
+ } else {
+ if (sf != NULL) {
+ call close (sf)
+ sf = NULL
+ }
+ iferr {
+ sf = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ sf = NULL
+ call erract (EA_WARN)
+ call strcpy ("", ST_SFILE(st), SZ_FNAME)
+ call printf (
+ "Spatial distribution function file is undefined.\n")
+ } else {
+ call strcpy (Memc[cmd], ST_SFILE(st), SZ_FNAME)
+ newspace = YES
+ }
+ }
+
+ case STCMD_LUMINOSITY:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (nscan () == 1) {
+ call printf ("luminosity = %s\n")
+ call pargstr (ST_LFSTRING(st))
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, GLUMFUNCS)
+ if (stat > 0) {
+ ST_LUMINOSITY (st) = stat
+ call strcpy (Memc[cmd], ST_LFSTRING(st), SZ_FNAME)
+ }
+ newlum = YES
+ }
+
+ case STCMD_POWER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("power = %g\n")
+ call pargr (ST_POWER(st))
+ } else {
+ ST_POWER(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MZERO:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("mzero = %g\n")
+ call pargr (ST_MZERO(st))
+ } else {
+ ST_MZERO(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_ALPHA:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("alpha = %g\n")
+ call pargr (ST_ALPHA(st))
+ } else {
+ ST_ALPHA(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MSTAR:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("mstar = %g\n")
+ call pargr (ST_MSTAR(st))
+ } else {
+ ST_MSTAR(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MINMAG:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("minmag = %g\n")
+ call pargr (ST_MINMAG(st))
+ } else {
+ ST_MINMAG(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_MAXMAG:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("maxmag = %g\n")
+ call pargr (ST_MAXMAG(st))
+ } else {
+ ST_MAXMAG(st) = rval
+ newlum = YES
+ }
+
+ case STCMD_LFILE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], ST_LFILE(st))) {
+ call printf ("lfile: %s\n")
+ call pargstr (ST_LFILE(st))
+ } else {
+ if (lf != NULL) {
+ call close (lf)
+ lf = NULL
+ }
+ iferr {
+ lf = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ lf = NULL
+ call erract (EA_WARN)
+ call strcpy ("", ST_LFILE(st), SZ_FNAME)
+ call printf (
+ "Luminosity function file is undefined.\n")
+ } else {
+ call strcpy (Memc[cmd], ST_LFILE(st), SZ_FNAME)
+ newlum = YES
+ }
+ }
+
+
+ case STCMD_EGALMIX:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("egalmix = %g\n")
+ call pargr (ST_EGALMIX(st))
+ } else {
+ ST_EGALMIX(st) = rval
+ newmix = YES
+ }
+
+ case STCMD_ERADIUS:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("eradius = %g\n")
+ call pargr (ST_ERADIUS(st))
+ } else {
+ ST_ERADIUS(st) = rval
+ newaxis = YES
+ }
+
+ case STCMD_SRADIUS:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("sradius = %g\n")
+ call pargr (ST_SRADIUS(st))
+ } else {
+ ST_SRADIUS(st) = rval
+ newaxis = YES
+ }
+
+ case STCMD_AR:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ar = %g\n")
+ call pargr (ST_AR(st))
+ } else {
+ ST_AR(st) = rval
+ newround = YES
+ }
+
+ case STCMD_Z:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("z = %g\n")
+ call pargr (ST_Z(st))
+ } else {
+ ST_Z(st) = rval
+ newaxis = YES
+ }
+
+ case STCMD_ABSORPTION:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("absorption = %g\n")
+ call pargr (ST_ABSORPTION(st))
+ } else {
+ ST_ABSORPTION(st) = rval
+ newround = YES
+ }
+
+ case STCMD_NGALS:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("ngals = %d\n")
+ call pargi (ST_NSTARS(st))
+ } else {
+ ST_NSTARS(st) = ival
+ newlum = YES
+ newspace = YES
+ newmix = YES
+ newaxis = YES
+ newround = YES
+ newphi = YES
+ }
+
+ case STCMD_NSSAMPLE:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("nssample = %d\n")
+ call pargi (ST_NSSAMPLE(st))
+ } else {
+ ST_NSSAMPLE(st) = ival
+ newspace = YES
+ }
+
+ case STCMD_SORDER:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("sorder = %d\n")
+ call pargl (ST_SORDER(st))
+ } else {
+ ST_SORDER(st) = lval
+ newspace = YES
+ }
+
+ case STCMD_SSEED:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("sseed = %d\n")
+ call pargl (ST_SSEED(st))
+ } else {
+ ST_SSEED(st) = lval
+ newspace = YES
+ }
+
+ case STCMD_NLSAMPLE:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("nlsample = %d\n")
+ call pargi (ST_NLSAMPLE(st))
+ } else {
+ ST_NLSAMPLE(st) = ival
+ newlum = YES
+ }
+
+ case STCMD_LORDER:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("lorder = %d\n")
+ call pargl (ST_LORDER(st))
+ } else {
+ ST_LORDER(st) = lval
+ newlum = YES
+ }
+
+ case STCMD_LSEED:
+ call gargl (lval)
+ if (nscan() == 1) {
+ call printf ("lseed = %d\n")
+ call pargl (ST_LSEED(st))
+ } else {
+ ST_LSEED(st) = lval
+ newlum = YES
+ }
+
+ case STCMD_RBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("rbinsize = %g\n")
+ call pargr (ST_RBINSIZE(st))
+ } else {
+ ST_RBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ case STCMD_MBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("mbinsize = %g\n")
+ call pargr (ST_MBINSIZE(st))
+ } else {
+ ST_MBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ case STCMD_DBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("dbinsize = %g\n")
+ call pargr (ST_DBINSIZE(st))
+ } else {
+ ST_DBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ case STCMD_EBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("ebinsize = %g\n")
+ call pargr (ST_EBINSIZE(st))
+ } else {
+ ST_EBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ case STCMD_PBINSIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("pbinsize = %g\n")
+ call pargr (ST_PBINSIZE(st))
+ } else {
+ ST_PBINSIZE(st) = rval
+ newplot = YES
+ }
+
+ default:
+ call printf ("\7\n")
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/artdata/lists/stdbio.x b/noao/artdata/lists/stdbio.x
new file mode 100644
index 00000000..1ec411b8
--- /dev/null
+++ b/noao/artdata/lists/stdbio.x
@@ -0,0 +1,350 @@
+include <pkg/dttext.h>
+include "starlist.h"
+
+# ST_DTINIT -- Write the header to the database.
+
+procedure st_dtinit (dt, st, starlist, sseed, lseed)
+
+pointer dt # pointer to output database
+pointer st # pointer to structure
+char starlist[ARB] # name of output text file
+long sseed # spatial function seed
+long lseed # luminsosity function seed
+
+begin
+ call dtptime (dt)
+ call dtput (dt, "# begin\t%s\n")
+ call pargstr (starlist)
+
+ # Write out the spatial density function parameters.
+ call dtput (dt, "#\tspatial\t\t%s\n")
+ switch (ST_SPATIAL(st)) {
+ case ST_UNIFORM:
+ call pargstr ("uniform")
+ case ST_HUBBLE:
+ call pargstr ("hubble")
+ call dtput (dt, "#\txcenter\t\t%g\n")
+ call pargr (ST_XC(st))
+ call dtput (dt, "#\tycenter\t\t%g\n")
+ call pargr (ST_YC(st))
+ call dtput (dt, "#\tcoreradius\t%g\n")
+ call pargr (ST_CORE(st))
+ call dtput (dt, "#\tbaseline\t%g\n")
+ call pargr (ST_BASE(st))
+ case ST_SPFILE:
+ call pargstr (ST_SFILE(st))
+ call dtput (dt, "#\txcenter\t\t%g\n")
+ call pargr (ST_XC(st))
+ call dtput (dt, "#\tycenter\t\t%g\n")
+ call pargr (ST_YC(st))
+ }
+ call dtput (dt, "#\txmin\t\t%g\n")
+ call pargr (ST_XMIN(st))
+ call dtput (dt, "#\txmax\t\t%g\n")
+ call pargr (ST_XMAX(st))
+ call dtput (dt, "#\tymin\t\t%g\n")
+ call pargr (ST_YMIN(st))
+ call dtput (dt, "#\tymax\t\t%g\n")
+ call pargr (ST_YMAX(st))
+
+ # Write out the luminsosity function parameters.
+ call dtput (dt, "#\tluminosity\t%s\n")
+ switch (ST_LUMINOSITY(st)) {
+ case ST_UNIFORM:
+ call pargstr ("uniform")
+ case ST_POWLAW:
+ call pargstr ("powlaw")
+ call dtput (dt, "#\tpower\t\t%g\n")
+ call pargr (ST_POWER(st))
+ case ST_SALPETER:
+ call pargstr ("salpeter")
+ call dtput (dt, "#\tmzero\t\t%g\n")
+ call pargr (ST_MZERO(st))
+ case ST_BANDS:
+ call pargstr ("bands")
+ call dtput (dt, "#\tmzero\t\t%g\n")
+ call pargr (ST_MZERO(st))
+ call dtput (dt, "#\talpha\t\t%g\n")
+ call pargr (ST_ALPHA(st))
+ call dtput (dt, "#\tbeta\t\t%g\n")
+ call pargr (ST_BETA(st))
+ call dtput (dt, "#\tdelta\t\t%g\n")
+ call pargr (ST_DELTA(st))
+ call dtput (dt, "#\tmstar\t\t%g\n")
+ call pargr (ST_MSTAR(st))
+ case ST_LFFILE:
+ call pargstr (ST_LFILE(st))
+ }
+ call dtput (dt, "#\tminmag\t\t%g\n")
+ call pargr (ST_MINMAG(st))
+ call dtput (dt, "#\tmaxmag\t\t%g\n")
+ call pargr (ST_MAXMAG(st))
+
+ # Save the spatial density function fitting parameters.
+ call dtput (dt, "#\tnssample\t%d\n")
+ call pargi (ST_NSSAMPLE(st))
+ call dtput (dt, "#\tsorder\t\t%d\n")
+ call pargi (ST_SORDER(st))
+ call dtput (dt, "#\tsseed\t\t%d\n")
+ call pargl (sseed)
+
+ # Save the luminosity function fitting parameters.
+ call dtput (dt, "#\tnlsample\t%d\n")
+ call pargi (ST_NLSAMPLE(st))
+ call dtput (dt, "#\tlorder\t\t%d\n")
+ call pargi (ST_LORDER(st))
+ call dtput (dt, "#\tlseed\t\t%d\n")
+ call pargl (lseed)
+
+ # Save the number of stars.
+ call dtput (dt, "#\tnstars\t\t%d\n")
+ call pargi (ST_NSTARS(st))
+end
+
+
+# ST_DTGINIT -- Write the GALLIST header to the database.
+
+procedure st_dtginit (dt, st, galaxies, sseed, lseed)
+
+pointer dt # pointer to database
+pointer st # pointer to starlist structure
+char galaxies[ARB] # name of output text file
+long sseed # spatial function seed
+long lseed # luminsosity function seed
+
+begin
+ call dtptime (dt)
+ call dtput (dt, "# begin\t%s\n")
+ call pargstr (galaxies)
+
+ # Save the spatial distribution function parameters.
+ call dtput (dt, "#\tspatial\t\t%s\n")
+ switch (ST_SPATIAL(st)) {
+ case ST_UNIFORM:
+ call pargstr ("uniform")
+ case ST_HUBBLE:
+ call pargstr ("hubble")
+ call dtput (dt, "#\txcenter\t\t%g\n")
+ call pargr (ST_XC(st))
+ call dtput (dt, "#\tycenter\t\t%g\n")
+ call pargr (ST_YC(st))
+ call dtput (dt, "#\tcoreradius\t%g\n")
+ call pargr (ST_CORE(st))
+ call dtput (dt, "#\tbaseline\t%g\n")
+ call pargr (ST_BASE(st))
+ case ST_SPFILE:
+ call pargstr (ST_SFILE(st))
+ call dtput (dt, "#\txcenter\t\t%g\n")
+ call pargr (ST_XC(st))
+ call dtput (dt, "#\tycenter\t\t%g\n")
+ call pargr (ST_YC(st))
+ }
+ call dtput (dt, "#\txmin\t\t%g\n")
+ call pargr (ST_XMIN(st))
+ call dtput (dt, "#\txmax\t\t%g\n")
+ call pargr (ST_XMAX(st))
+ call dtput (dt, "#\tymin\t\t%g\n")
+ call pargr (ST_YMIN(st))
+ call dtput (dt, "#\tymax\t\t%g\n")
+ call pargr (ST_YMAX(st))
+
+ # Save the luminsosity function parameters.
+ call dtput (dt, "#\tluminosity\t%s\n")
+ switch (ST_LUMINOSITY(st)) {
+ case ST_UNIFORM:
+ call pargstr ("uniform")
+ case ST_POWLAW:
+ call pargstr ("powlaw")
+ call dtput (dt, "#\tpower\t\t%g\n")
+ call pargr (ST_POWER(st))
+ case ST_SCHECTER:
+ call pargstr ("shechter")
+ call dtput (dt, "#\tmzero\t\t%g\n")
+ call pargr (ST_MZERO(st))
+ call dtput (dt, "#\talpha\t\t%g\n")
+ call pargr (ST_ALPHA(st))
+ call dtput (dt, "#\tmstar\t\t%g\n")
+ call pargr (ST_MSTAR(st))
+ case ST_LFFILE:
+ call pargstr (ST_LFILE(st))
+ }
+ call dtput (dt, "#\tminmag\t\t%g\n")
+ call pargr (ST_MINMAG(st))
+ call dtput (dt, "#\tmaxmag\t\t%g\n")
+ call pargr (ST_MAXMAG(st))
+ call dtput (dt, "#\teradius\t\t%g\n")
+ call pargr (ST_ERADIUS(st))
+ call dtput (dt, "#\tsradius\t\t%g\n")
+ call pargr (ST_SRADIUS(st))
+
+ call dtput (dt, "#\tegalmix\t\t%g\n")
+ call pargr (ST_EGALMIX(st))
+ call dtput (dt, "#\tar\t\t%g\n")
+ call pargr (ST_AR(st))
+ call dtput (dt, "#\tabsorption\t%g\n")
+ call pargr (ST_ABSORPTION(st))
+ call dtput (dt, "#\tz\t\t%g\n")
+ call pargr (ST_Z(st))
+
+ # Save the spatial distribution fitting parameters.
+ call dtput (dt, "#\tnssample\t%d\n")
+ call pargi (ST_NSSAMPLE(st))
+ call dtput (dt, "#\tsorder\t\t%d\n")
+ call pargi (ST_SORDER(st))
+ call dtput (dt, "#\tsseed\t\t%d\n")
+ call pargl (sseed)
+
+ # Save the spatial function fitting parameters.
+ call dtput (dt, "#\tnlsample\t%d\n")
+ call pargi (ST_NLSAMPLE(st))
+ call dtput (dt, "#\tlorder\t\t%d\n")
+ call pargi (ST_LORDER(st))
+ call dtput (dt, "#\tlseed\t\t%d\n")
+ call pargl (lseed)
+
+ # Save the number of stars.
+ call dtput (dt, "#\tngals\t\t%d\n")
+ call pargi (ST_NSTARS(st))
+end
+
+
+# ST_DTWRITE -- Write the starlist to the database.
+
+procedure st_dtwrite (dt, x, y, mag, nstars)
+
+pointer dt # pointer to the output database
+real x[ARB] # array of x coordinates
+real y[ARB] # array of y coordinates
+real mag[ARB] # array of magnitude values
+int nstars # number of stars
+
+int i, j
+pointer sp, index
+
+begin
+ call smark (sp)
+ call salloc (index, nstars, TY_INT)
+ call st_qsort (y, Memi[index], Memi[index], nstars)
+
+ do i = 1, nstars {
+ j = Memi[index+i-1]
+ call dtput (dt, "\t%8.3f %8.3f %7.3f\n")
+ call pargr (x[j])
+ call pargr (y[j])
+ call pargr (mag[j])
+ }
+
+ call sfree (sp)
+end
+
+
+# ST_DTGWRITE -- Procedure to write the galaxy list to the database
+
+procedure st_dtgwrite (dt, x, y, mag, egal, axis, round, phi, nstars)
+
+pointer dt # pointer to database
+real x[ARB] # x values
+real y[ARB] # y values
+real mag[ARB] # magnitude values
+int egal[ARB] # galaxy types
+real axis[ARB] # galaxy diameters
+real round[ARB] # galaxy roundness
+real phi[ARB] # galaxy position angles
+int nstars # number of stars
+
+int i, j
+pointer sp, index
+
+begin
+ call smark (sp)
+ call salloc (index, nstars, TY_INT)
+ call st_qsort (y, Memi[index], Memi[index], nstars)
+
+ do i = 1, nstars {
+ j = Memi[index+i-1]
+ call dtput (dt,
+ "\t%8.3f %8.3f %7.3f %7s %7.2f %5.3f %5.1f\n")
+ call pargr (x[j])
+ call pargr (y[j])
+ call pargr (mag[j])
+ if (egal[j] == ST_DEVAUC)
+ call pargstr ("devauc")
+ else
+ call pargstr ("expdisk")
+ call pargr (axis[j])
+ call pargr (round[j])
+ call pargr (phi[j])
+ }
+
+ call sfree (sp)
+end
+
+
+# ST_QSORT -- Vector Quicksort. In this version the index array is
+# sorted.
+
+define LOGPTR 20 # log2(maxpts) (1e6)
+
+procedure st_qsort (data, a, b, npix)
+
+real data[ARB] # data array
+int a[ARB], b[ARB] # index array
+int npix # number of pixels
+
+int i, j, lv[LOGPTR], p, uv[LOGPTR], temp
+real pivot
+
+begin
+ # Initialize the indices for an inplace sort.
+ do i = 1, npix
+ a[i] = i
+ call amovi (a, b, npix)
+
+ # Initialize.
+ p = 1
+ lv[1] = 1
+ uv[1] = npix
+
+ # Sort.
+ while (p > 0) {
+
+ # If only one elem in subset pop stack otherwise pivot line.
+ if (lv[p] >= uv[p])
+ p = p - 1
+ else {
+ i = lv[p] - 1
+ j = uv[p]
+ pivot = data[b[j]]
+
+ while (i < j) {
+ for (i=i+1; data[b[i]] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (data[b[j]] <= pivot)
+ break
+ if (i < j) { # Out of order pair
+ temp = b[j] # Interchange elements
+ b[j] = b[i]
+ b[i] = temp
+ }
+ }
+
+ j = uv[p] # Move pivot to position i
+ temp = b[j] # Interchange elements
+ b[j] = b[i]
+ b[i] = temp
+
+ if (i-lv[p] < uv[p] - i) { # Stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ p = p + 1 # Push onto stack
+ }
+ }
+end
diff --git a/noao/artdata/lists/stlum.x b/noao/artdata/lists/stlum.x
new file mode 100644
index 00000000..0abe9e1e
--- /dev/null
+++ b/noao/artdata/lists/stlum.x
@@ -0,0 +1,342 @@
+include <mach.h>
+include <math.h>
+include <math/curfit.h>
+include <math/iminterp.h>
+
+
+# ST_MAGUNIFORM -- Compute a set of magnitude values which are uniformly
+# distributed between minmag and maxmag.
+
+procedure st_maguniform (mag, nstars, minmag, maxmag, seed)
+
+real mag[ARB] # output array of magnitudes
+int nstars # number of stars
+real minmag, maxmag # minimum and maximum magnitude values
+long seed # seed for random number generator
+
+int i
+real urand()
+
+begin
+ # Get values between 0 and 1.
+ do i = 1, nstars
+ mag[i] = urand (seed)
+
+ # Map values into data range.
+ call amapr (mag, mag, nstars, 0.0, 1.0, minmag, maxmag)
+end
+
+
+# ST_POWER -- Compute a set of magnitude values which are power law
+# distributed between minmag and maxmag.
+
+procedure st_power (mag, nstars, power, minmag, maxmag, seed)
+
+real mag[ARB] # output array of magnitudes
+int nstars # number of stars
+real power # power law exponent
+real minmag, maxmag # minimum and maximum magnitude values
+long seed # seed for random number generator
+
+int i
+real a, urand()
+
+begin
+ # Get values between 0 and 1.
+ a = 10. ** (power * (maxmag - minmag)) - 1
+ do i = 1, nstars
+ mag[i] = minmag + log10 (a * urand (seed) + 1) / power
+end
+
+
+define MIN_BANDS -6.
+define MAX_BANDS 19.
+define MID_BANDS 15.
+
+# ST_BANDS -- Compute the Bahcall and Soneira luminosity function.
+
+procedure st_bands (mag, nstars, alpha, beta, delta, mstar, minmag, maxmag,
+ mzero, nsample, order, seed)
+
+real mag[ARB] # array of output magnitudes
+int nstars # number of stars
+real alpha, beta # Bahcall and Soneira parameters
+real delta, mstar # Bahcall and Soneira parameters
+real minmag, maxmag # minimum and maximum magnitude values
+real mzero # zero point between relative and absolute mags.
+int nsample # number of points in sampling function
+int order # order of the spline fit
+long seed # value of the seed
+
+int i, ier
+pointer sp, m, iprob, w, cv, asi
+real dmag, magval, mtemp, temp1, temp2, imin, imax
+real cveval(), asigrl(), urand()
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (m, nsample, TY_REAL)
+ call salloc (iprob, nsample, TY_REAL)
+ call salloc (w, nsample, TY_REAL)
+
+ # Compute the probability function.
+ magval = max (minmag - mzero, MIN_BANDS)
+ dmag = (min (maxmag - mzero, MAX_BANDS) - magval) / (nsample - 1)
+ do i = 1, nsample {
+ Memr[m+i-1] = magval
+ if (magval > MID_BANDS)
+ mtemp = MID_BANDS
+ else
+ mtemp = magval - mstar
+ temp1 = 10.0 ** (beta * mtemp)
+ temp2 = (1.0 + 10.0 ** ((beta - alpha) * delta * mtemp)) **
+ (1.0 / delta)
+ Memr[iprob+i-1] = temp1 / temp2
+ magval = magval + dmag
+ }
+
+ # Integrate the probablity function.
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[iprob], nsample)
+ Memr[iprob] = 0.0
+ do i = 2, nsample
+ Memr[iprob+i-1] = Memr[iprob+i-2] + asigrl (asi, real (i-1),
+ real (i))
+ call alimr (Memr[iprob], nsample, imin, imax)
+ call amapr (Memr[iprob], Memr[iprob], nsample, imin, imax, 0.0, 1.0)
+ call asifree (asi)
+
+ # Fit the inverse of the integral of the probability function.
+ call cvinit (cv, SPLINE3, order, 0.0, 1.0)
+ call cvfit (cv, Memr[iprob], Memr[m], Memr[w], nsample, WTS_UNIFORM,
+ ier)
+
+ # Compute the magnitudes.
+ if (ier == OK) {
+ do i = 1, nstars
+ mag[i] = cveval (cv, urand (seed)) + mzero
+ } else {
+ call printf ("Error computing the bands luminosity function.\n")
+ call amovkr ((minmag + maxmag) / 2.0, mag, nstars)
+ }
+ call cvfree (cv)
+
+ # Free space.
+ call sfree (sp)
+end
+
+
+define MIN_SALPETER -4.0
+define MAX_SALPETER 16.0
+
+# ST_SALPETER -- Compute the Salpter luminosity function.
+
+procedure st_salpeter (mag, nstars, minmag, maxmag, mzero, nsample, order, seed)
+
+real mag[ARB] # array of output magnitudes
+int nstars # number of stars
+real minmag, maxmag # minimum and maximum magnitude values
+real mzero # zero point between relative and absolute mags.
+int nsample # number of points in sampling function
+int order # order of the spline fit
+long seed # value of the seed
+
+int i, ier
+pointer sp, m, iprob, w, cv, asi
+real dmag, magval, imin, imax
+real cveval(), asigrl(), urand()
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (m, nsample, TY_REAL)
+ call salloc (iprob, nsample, TY_REAL)
+ call salloc (w, nsample, TY_REAL)
+
+ # Compute the probability function.
+ magval = max (minmag - mzero, MIN_SALPETER)
+ dmag = (min (maxmag - mzero, MAX_SALPETER) - magval) / (nsample - 1)
+ do i = 1, nsample {
+ Memr[m+i-1] = magval
+ Memr[iprob+i-1] = 10.0 ** (-3.158375 + 1.550629e-1 * magval -
+ 5.19388e-3 * magval ** 2)
+ magval = magval + dmag
+ }
+
+ # Integrate the probablity function.
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[iprob], nsample)
+ Memr[iprob] = 0.0
+ do i = 2, nsample
+ Memr[iprob+i-1] = Memr[iprob+i-2] + asigrl (asi, real (i-1),
+ real (i))
+ call alimr (Memr[iprob], nsample, imin, imax)
+ call amapr (Memr[iprob], Memr[iprob], nsample, imin, imax, 0.0, 1.0)
+ call asifree (asi)
+
+ # Fit the inverse of the integral of the probability function.
+ call cvinit (cv, SPLINE3, order, 0.0, 1.0)
+ call cvfit (cv, Memr[iprob], Memr[m], Memr[w], nsample, WTS_UNIFORM,
+ ier)
+
+ # Compute the magnitudes.
+ if (ier == OK) {
+ do i = 1, nstars
+ mag[i] = cveval (cv, urand (seed)) + mzero
+ } else {
+ call printf ("Error computing the Salpeter luminosity function.\n")
+ call amovkr ((minmag + maxmag) / 2.0, mag, nstars)
+ }
+ call cvfree (cv)
+
+ # Free space.
+ call sfree (sp)
+end
+
+
+# ST_SCHECTER -- Compute the Schecter luminosity function.
+
+procedure st_schecter (mag, nstars, alpha, mstar, minmag, maxmag, mzero,
+ nsample, order, seed)
+
+real mag[ARB] # array of output magnitudes
+int nstars # number of stars
+real alpha, mstar # Schecter luminosity function parameters
+real minmag, maxmag # minimum and maximum magnitude values
+real mzero # zero point between relative and absolute mags.
+int nsample # number of points in the sampling function
+int order # order of the spline fit
+long seed # value of the seed
+
+int i, ier
+pointer sp, m, iprob, w, cv, asi
+real dmag, magval, temp, imin, imax
+real cveval(), asigrl(), urand()
+
+begin
+ # Allocate space for fitting.
+ call smark (sp)
+ call salloc (m, nsample, TY_REAL)
+ call salloc (iprob, nsample, TY_REAL)
+ call salloc (w, nsample, TY_REAL)
+
+ # Sample the luminosity function.
+ magval = minmag - mzero
+ dmag = (maxmag - minmag) / (nsample - 1)
+ do i = 1, nsample {
+ Memr[m+i-1] = magval
+ temp = 0.4 * (mstar - magval)
+ Memr[iprob+i-1] = 10.0 ** ((alpha + 1) * temp) *
+ exp (- 10.0 ** temp)
+ magval = magval + dmag
+ }
+
+ # Integrate the sampling function.
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[iprob], nsample)
+ Memr[iprob] = 0.0
+ do i = 2, nsample
+ Memr[iprob+i-1] = Memr[iprob+i-2] + asigrl (asi, real (i-1),
+ real(i))
+ call alimr (Memr[iprob],nsample, imin, imax)
+ call amapr (Memr[iprob], Memr[iprob], nsample, imin, imax, 0.0, 1.0)
+ call asifree (asi)
+
+ # Fit the inverse of the integral of the probability function.
+ call cvinit (cv, SPLINE3, order, 0.0, 1.0)
+ call cvfit (cv, Memr[iprob], Memr[m], Memr[w], nsample, WTS_UNIFORM,
+ ier)
+ if (ier == OK) {
+ do i = 1, nstars
+ mag[i] = cveval (cv, urand (seed)) + mzero
+ } else {
+ call printf ("Error fitting the Schecter luminosity function.\n")
+ call amovkr ((minmag + maxmag) / 2.0, mag, nstars)
+ }
+ call cvfree (cv)
+
+ # Free space.
+ call sfree (sp)
+end
+
+
+# ST_LFSAMPLE -- Compute the luminosity function using a user supplied
+# function.
+
+procedure st_lfsample (smag, mprob, nlf, mag, nstars, minmag, maxmag, nsample,
+ order, seed)
+
+real smag[ARB] # input array of magnitudes
+real mprob[ARB] # input array of relative probabilities
+int nlf # number of input points
+real mag[ARB] # output magnitude array
+int nstars # number of stars
+real minmag, maxmag # minimum and maximum magnitude values
+int nsample # number of sample points
+int order # order of the spline fit
+long seed # value of the seed
+
+int npts, i, ier
+pointer sp, m, w, iprob, cv, asi
+real mval, dm, sfmin, sfmax, imin, imax
+real cveval(), asigrl(), urand()
+
+begin
+ # Allocate space for fitting.
+ npts = max (nlf, nsample)
+ call smark (sp)
+ call salloc (m, nsample, TY_REAL)
+ call salloc (iprob, nsample, TY_REAL)
+ call salloc (w, npts, TY_REAL)
+
+ # Smooth the relative probability function.
+ call alimr (smag, nlf, sfmin, sfmax)
+ call cvinit (cv, SPLINE3, max (1, nlf / 4), sfmin, sfmax)
+ call cvfit (cv, smag, mprob, Memr[w], nlf, WTS_UNIFORM, ier)
+
+ # Evaluate the smoothed function at equal intervals in r.
+ if (ier == OK) {
+ mval = max (minmag, sfmin)
+ dm = (min (maxmag, sfmax) - mval) / (nsample - 1)
+ do i = 1, nsample {
+ Memr[m+i-1] = mval
+ Memr[iprob+i-1] = cveval (cv, mval)
+ mval = mval + dm
+ }
+ call cvfree (cv)
+ } else {
+ call printf ("Error smoothing user luminosity function.\n")
+ call amovkr ((minmag + maxmag) / 2.0, mag, nstars)
+ call cvfree (cv)
+ call sfree (sp)
+ }
+
+ # Evaluate the integral.
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[iprob], nsample)
+ Memr[iprob] = 0.0
+ do i = 2, nsample
+ Memr[iprob+i-1] = Memr[iprob+i-2] + asigrl (asi, real(i-1), real(i))
+ call alimr (Memr[iprob], nsample, imin, imax)
+ call amapr (Memr[iprob], Memr[iprob], nsample, imin, imax, 0.0, 1.0)
+ call asifree (asi)
+
+ # Fit the inverse of the integral of the probability function.
+ call cvinit (cv, SPLINE3, order, 0.0, 1.0)
+ call cvfit (cv, Memr[iprob], Memr[m], Memr[w], nsample, WTS_UNIFORM,
+ ier)
+
+ # Sample the computed function.
+ if (ier == OK) {
+ do i = 1, nstars
+ mag[i] = cveval (cv, urand (seed))
+ } else {
+ call printf ("Error computing the user luminosity function.\n")
+ call amovkr ((minmag + maxmag) / 2.0, mag, nstars)
+ }
+ call cvfree (cv)
+
+ # Free space.
+ call sfree (sp)
+end
diff --git a/noao/artdata/lists/stmix.x b/noao/artdata/lists/stmix.x
new file mode 100644
index 00000000..4254e8eb
--- /dev/null
+++ b/noao/artdata/lists/stmix.x
@@ -0,0 +1,144 @@
+include <math.h>
+include "starlist.h"
+
+# ST_ESMIX -- Compute the percentage of elliptical galaxies.
+
+procedure st_esmix (egal, nstar, esmix, seed)
+
+int egal[ARB] # array of types
+int nstar # number of objects
+real esmix # fraction of elliptical galaxies
+long seed # seed for random number generator
+
+int i
+real fraction
+real urand()
+
+begin
+ do i = 1, nstar {
+ fraction = urand (seed)
+ if (fraction <= esmix)
+ egal[i] = ST_DEVAUC
+ else
+ egal[i] = ST_EXP
+ }
+end
+
+
+# ST_ROUND -- Compute an array of roundness parameters.
+# For ellipical models use a uniform distribution of axial ratios.
+# For spiral models use a uniform inclination distribution. Compute
+# the axial ratio with a .1 flatness parameter from Holmberg (Stars
+# and Stellar Systems). Then add cosecant absorption factor to the
+# the magnitudes with a limit of 10 in the cosecant.
+
+procedure st_round (egal, mag, round, nstars, ar, alpha, seed)
+
+int egal[ARB] # array of galaxy types
+real mag[ARB] # magnitudes
+real round[ARB] # array of roundness values
+int nstars # number of stars
+real ar # minimum roundness value
+real alpha # absorption coefficent
+long seed # seed for the random number generator
+
+int i
+real dr, s, urand()
+
+begin
+ dr = (1. - ar)
+ do i = 1, nstars {
+ if (egal[i] == ST_DEVAUC)
+ round[i] = ar + dr * urand (seed)
+ else {
+ s = sin (HALFPI * urand (seed))
+ round[i] = sqrt (s**2 * .99 + .01)
+ mag[i] = mag[i] + alpha * (1 / max (0.1, s) - 1) / 9.
+ }
+ }
+
+end
+
+
+# ST_PHI -- Compute an array of position angles.
+
+procedure st_phi (phi, nstars, seed)
+
+real phi[ARB] # array of position angles
+int nstars # number of stars
+long seed # seed for random number generator
+
+int i
+real urand()
+
+begin
+ do i = 1, nstars
+ phi[i] = urand (seed)
+ call amapr (phi, phi, nstars, 0.0, 1.0, 0.0, 360.0)
+end
+
+
+# ST_DIAMETERS -- Compute the effective diameters of the galaxies based
+# on their magnitudes. The relation used is from Holmberg (Stars and
+# Stellar Systems). A uniform dispersion of 20% is added.
+
+procedure st_diameters (mag, egal, axis, nstars, minmag, maxmag, eradius,
+ sradius, seed)
+
+real mag[ARB] # input array of magnitudes
+int egal[ARB] # array of galaxy types
+real axis[ARB] # output array of diameters
+int nstars # number of stars
+real minmag # minimum magnitude
+real maxmag # maximum magnitude
+real eradius # maximum elliptical radius
+real sradius # maximum spiral radius
+long seed # seed for random number generator
+
+int i
+real urand()
+
+begin
+ do i = 1, nstars {
+ if (egal[i] == ST_DEVAUC)
+ axis[i] = eradius * 10.0 ** ((minmag - mag[i]) / 6.)
+ else
+ axis[i] = sradius * eradius * 10.0 ** ((minmag - mag[i]) / 6.)
+ axis[i] = axis[i] * (0.8 + 0.4 * urand (seed))
+ }
+end
+
+
+# ST_ZDIAMETERS -- Compute the effective diameters of the galaxies based
+# on their magnitudes and redshift. The relation used is that the redshift
+# is proportional to the luminousity and the diameters include the
+# factor of (1+z)**2. A uniform dispersion of 50% is added.
+
+procedure st_zdiameters (mag, egal, axis, nstars, minmag, maxmag,
+ z, eradius, sradius, seed)
+
+real mag[ARB] # input array of magnitudes
+int egal[ARB] # array of galaxy types
+real axis[ARB] # output array of diameters
+int nstars # number of stars
+real minmag # minimum magnitude
+real maxmag # maximum magnitude
+real z # minumum redshift
+real eradius # maximum elliptical radius
+real sradius # maximum spiral radius
+long seed # seed for random number generator
+
+int i
+real z0, z1, urand()
+
+begin
+ z0 = z / (1 + z) ** 2
+ do i = 1, nstars {
+ z1 = z * 10.0 ** (-0.2 * (minmag - mag[i]))
+ if (egal[i] == ST_DEVAUC)
+ axis[i] = eradius * z0 * (1 + z1) ** 2 / z1
+ else
+ axis[i] = sradius * eradius * z0 * (1 + z1) ** 2 / z1
+ axis[i] = axis[i] * (0.5 + urand (seed))
+ }
+end
diff --git a/noao/artdata/lists/stplot.x b/noao/artdata/lists/stplot.x
new file mode 100644
index 00000000..2e79b6c1
--- /dev/null
+++ b/noao/artdata/lists/stplot.x
@@ -0,0 +1,1102 @@
+include <mach.h>
+include <math.h>
+include <gset.h>
+include <pkg/gtools.h>
+include "starlist.h"
+
+define HELPFILE1 "artdata$lists/starlist.key"
+
+# ST_PLOTS -- Interactively examine the spatial density and luminosity
+# functions.
+
+procedure st_plots (sf, lf, gd, st, x, y, mag)
+
+int sf # spatial density file descriptor
+int lf # luminsosity function file descriptor
+pointer gd # graphics stream pointer
+pointer st # pointer to starlist structure
+pointer x # pointer to x array
+pointer y # pointer to y array
+pointer mag # pointer to mag array
+
+int wcs, key, plottype, newplot, newspace, newlum
+pointer sp, cmd, gt1, gt2, gt3
+real wx, wy
+int gt_gcur()
+pointer gt_init()
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Intialize the plots.
+ gt1 = gt_init()
+ gt2 = gt_init()
+ gt3 = gt_init()
+
+ newspace = NO
+ newlum = NO
+ newplot = NO
+ plottype = 1
+
+ # Draw the first plot.
+ call st_pfield (gd, gt1, st, Memr[x], Memr[y], Memr[mag], ST_NSTARS(st))
+
+ while (gt_gcur ("cursor", wx, wy, wcs, key, Memc[cmd], SZ_LINE) !=
+ EOF) {
+ switch (key) {
+
+ case '?':
+ call gpagefile (gd, HELPFILE1, "")
+
+ case ':':
+ call st_colon (gd, st, sf, lf, Memc[cmd], newspace, newlum,
+ newplot)
+ switch (plottype) {
+ case 1:
+ call gt_colon (Memc[cmd], gd, gt1, newplot)
+ case 2:
+ call gt_colon (Memc[cmd], gd, gt2, newplot)
+ case 3:
+ call gt_colon (Memc[cmd], gd, gt3, newplot)
+ }
+
+ case 'q':
+ break
+
+ case 'f':
+
+ if (newspace == YES) {
+ call st_mkspatial (sf, st, x, y, mag)
+ newspace = NO
+ newplot = YES
+ }
+
+ if (newlum == YES) {
+ call st_mklum (lf, st, x, y, mag)
+ newlum = NO
+ newplot = YES
+ }
+
+ if (newplot == YES) {
+ switch (plottype) {
+ case 1:
+ call st_pfield (gd, gt1, st, Memr[x], Memr[y],
+ Memr[mag], ST_NSTARS(st))
+ case 2:
+ call st_prhist (gd, gt2, st, Memr[x], Memr[y],
+ ST_NSTARS(st))
+ case 3:
+ call st_pmhist (gd, gt3, st, Memr[mag], ST_NSTARS(st))
+ default:
+ call st_pfield (gd, gt1, st, Memr[x], Memr[y],
+ Memr[mag], ST_NSTARS(st))
+ }
+ newplot = NO
+ }
+
+ case 'x':
+ if (newspace == YES || newlum == YES)
+ call printf ("Type the f key to remake the star list\n")
+ else if (plottype != 1 || newplot == YES) {
+ call st_pfield (gd, gt1, st, Memr[x], Memr[y], Memr[mag],
+ ST_NSTARS(st))
+ plottype = 1
+ newplot = NO
+ }
+
+ case 'r':
+ if (newspace == YES)
+ call printf ("Type the f key to remake the star list\n")
+ else if (plottype != 2 || newplot == YES) {
+ call st_prhist (gd, gt2, st, Memr[x], Memr[y],
+ ST_NSTARS(st))
+ plottype = 2
+ newplot = NO
+ }
+
+ case 'm':
+ if (newlum == YES)
+ call printf ("Type the f key to remake the star list\n")
+ else if (plottype != 3 || newplot == YES) {
+ call st_pmhist (gd, gt3, st, Memr[mag], ST_NSTARS(st))
+ plottype = 3
+ newplot = NO
+ }
+
+ default:
+ }
+ }
+
+ # Recompute the results if necessary.
+ if (newspace == YES)
+ call st_mkspatial (sf, st, x, y, mag)
+ if (newlum == YES)
+ call st_mklum (lf, st, x, y, mag)
+
+ # Free space for the plots.
+ call gt_free (gt1)
+ call gt_free (gt2)
+ call gt_free (gt3)
+
+ call sfree (sp)
+end
+
+
+define HELPFILE2 "artdata$lists/gallist.key"
+
+# ST_GPLOTS -- Fit the luminosity function and make plots.
+
+procedure st_gplots (sf, lf, gd, st, x, y, mag, egal, axis, round, phi)
+
+int sf # spatial distribution file descriptor
+int lf # luminsosity distribution file descriptor
+pointer gd # graphics stream pointer
+pointer st # pointer to starlist structure
+pointer x # pointer to x array
+pointer y # pointer to y array
+pointer mag # pointer to mag array
+pointer egal # pointer to type array
+pointer axis # pointer to the diameters array
+pointer round # pointer to the roundness array
+pointer phi # pointer to the position angle array
+
+int wcs, key, plottype
+int newplot, newspace, newlum, newmix, newaxis, newround, newphi
+pointer sp, cmd, gt1, gt2, gt3, gt4, gt5, gt6
+real wx, wy
+int gt_gcur()
+pointer gt_init()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Intialize the plots.
+ gt1 = gt_init()
+ gt2 = gt_init()
+ gt3 = gt_init()
+ gt4 = gt_init()
+ gt5 = gt_init()
+ gt6 = gt_init()
+
+ newspace = NO
+ newlum = NO
+ newmix = NO
+ newaxis = NO
+ newround = NO
+ newplot = NO
+ newphi = NO
+ plottype = 1
+
+ # Draw the first plot.
+ call st_pgfield (gd, gt1, st, Memr[x], Memr[y], Memr[mag], Memi[egal],
+ Memr[axis], Memr[round], ST_NSTARS(st))
+
+ while (gt_gcur ("cursor", wx, wy, wcs, key, Memc[cmd], SZ_LINE) !=
+ EOF) {
+ switch (key) {
+
+ case '?':
+ call gpagefile (gd, HELPFILE2, "")
+
+ case ':':
+ call st_gcolon (gd, st, sf, lf, Memc[cmd], newspace, newlum,
+ newmix, newaxis, newround, newphi, newplot)
+ switch (plottype) {
+ case 1:
+ call gt_colon (Memc[cmd], gd, gt1, newplot)
+ case 2:
+ call gt_colon (Memc[cmd], gd, gt2, newplot)
+ case 3:
+ call gt_colon (Memc[cmd], gd, gt3, newplot)
+ case 4:
+ call gt_colon (Memc[cmd], gd, gt4, newplot)
+ case 5:
+ call gt_colon (Memc[cmd], gd, gt5, newplot)
+ case 6:
+ call gt_colon (Memc[cmd], gd, gt6, newplot)
+ }
+
+ case 'q':
+ break
+
+ case 'f':
+
+
+ if (newphi == YES) {
+ call st_gmkphi (st, x, y, mag, egal, axis, round, phi)
+ newphi = NO
+ newplot = YES
+ }
+
+ if (newspace == YES) {
+ call st_gmkspatial (sf, st, x, y, mag, egal, axis, round,
+ phi)
+ newspace = NO
+ newplot = YES
+ }
+
+ if (newmix == YES) {
+ call st_gmkmix (st, x, y, mag, egal, axis, round, phi)
+ newmix = NO
+ newplot = YES
+ }
+
+ if (newlum == YES) {
+ call st_gmklum (lf, st, x, y, mag, egal, axis, round, phi)
+ call st_gmkround (st, x, y, mag, egal, axis, round, phi)
+ call st_gmkaxis (st, x, y, mag, egal, axis, round, phi)
+ newlum = NO
+ newaxis = NO
+ newround = NO
+ newplot = YES
+ }
+
+ if (newround == YES) {
+ call st_gmkround (st, x, y, mag, egal, axis, round, phi)
+ newround = NO
+ newplot = YES
+ }
+
+ if (newaxis == YES) {
+ call st_gmkaxis (st, x, y, mag, egal, axis, round, phi)
+ newaxis = NO
+ newplot = YES
+ }
+
+ if (newplot == YES) {
+ switch (plottype) {
+ case 1:
+ call st_pgfield (gd, gt1, st, Memr[x], Memr[y],
+ Memr[mag], Memi[egal], Memr[axis], Memr[round],
+ ST_NSTARS(st))
+ case 2:
+ call st_prhist (gd, gt2, st, Memr[x], Memr[y],
+ ST_NSTARS(st))
+ case 3:
+ call st_pmhist (gd, gt3, st, Memr[mag], ST_NSTARS(st))
+ case 4:
+ call st_pdhist (gd, gt4, st, Memr[axis], ST_NSTARS(st))
+ case 5:
+ call st_pehist (gd, gt5, st, Memr[round], ST_NSTARS(st))
+ case 6:
+ call st_pphist (gd, gt6, st, Memr[phi], ST_NSTARS(st))
+ default:
+ call st_pgfield (gd, gt1, st, Memr[x], Memr[y],
+ Memr[mag], Memi[egal], Memr[axis], Memr[round],
+ ST_NSTARS(st))
+ }
+ newplot = NO
+ }
+
+ case 'x':
+ if (newspace == YES || newlum == YES || newmix == YES ||
+ newround == YES)
+ call printf ("Type the f key to refit the galaxies list\n")
+ else if (plottype != 1 || newplot == YES) {
+ call st_pgfield (gd, gt1, st, Memr[x], Memr[y], Memr[mag],
+ Memi[egal], Memr[axis], Memr[round], ST_NSTARS(st))
+ plottype = 1
+ newplot = NO
+ }
+
+ case 'r':
+ if (newspace == YES)
+ call printf ("Type the f key to refit the galaxies list\n")
+ else if (plottype != 2 || newplot == YES) {
+ call st_prhist (gd, gt2, st, Memr[x], Memr[y],
+ ST_NSTARS(st))
+ plottype = 2
+ newplot = NO
+ }
+
+ case 'm':
+ if (newlum == YES)
+ call printf ("Type the f key to refit the galaxies list\n")
+ else if (plottype != 3 || newplot == YES) {
+ call st_pmhist (gd, gt3, st, Memr[mag], ST_NSTARS(st))
+ plottype = 3
+ newplot = NO
+ }
+
+ case 'd':
+ if (newlum == YES)
+ call printf ("Type the f key to refit the galaxies list\n")
+ else if (plottype != 4 || newplot == YES) {
+ call st_pdhist (gd, gt4, st, Memr[axis], ST_NSTARS(st))
+ plottype = 4
+ newplot = NO
+ }
+
+ case 'e':
+ if (newround == YES)
+ call printf ("Type the f key to refit the galaxies list\n")
+ else if (plottype != 5 || newplot == YES) {
+ call st_pehist (gd, gt5, st, Memr[round], ST_NSTARS(st))
+ plottype = 5
+ newplot = NO
+ }
+
+ case 'p':
+ if (newphi == YES)
+ call printf ("Type the f key to refit the galaxies list\n")
+ else if (plottype != 6 || newplot == YES) {
+ call st_pphist (gd, gt6, st, Memr[phi], ST_NSTARS(st))
+ plottype = 6
+ newplot = NO
+ }
+
+ default:
+ }
+ }
+
+ # Recompute the functions if necessary.
+ if (newphi == YES)
+ call st_gmkphi (st, x, y, mag, egal, axis, round, phi)
+ if (newspace == YES)
+ call st_gmkspatial (sf, st, x, y, mag, egal, axis, round, phi)
+ if (newmix == YES)
+ call st_gmkmix (st, x, y, mag, egal, axis, round, phi)
+ if (newlum == YES) {
+ call st_gmklum (lf, st, x, y, mag, egal, axis, round, phi)
+ call st_gmkaxis (st, x, y, mag, egal, axis, round, phi)
+ call st_gmkround (st, x, y, mag, egal, axis, round, phi)
+ } else {
+ if (newaxis == YES)
+ call st_gmkaxis (st, x, y, mag, egal, axis, round, phi)
+ if (newround == YES)
+ call st_gmkround (st, x, y, mag, egal, axis, round, phi)
+ }
+
+ # Free space for the plots.
+ call gt_free (gt1)
+ call gt_free (gt2)
+ call gt_free (gt3)
+ call gt_free (gt4)
+ call gt_free (gt5)
+ call gt_free (gt6)
+
+ call sfree (sp)
+end
+
+
+# ST_PFIELD -- Plot distribution of stars in the x-y plane.
+
+procedure st_pfield (gd, gt, st, x, y, mag, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to graphics descriptor
+pointer st # pointer to starlist structure
+real x[ARB] # x coords
+real y[ARB] # y coords
+real mag[ARB] # magnitudes
+int npts # number of points
+
+int i
+pointer sp, sizes, par1, par2, title
+bool fp_equalr()
+
+begin
+ call smark (sp)
+ call salloc (sizes, npts, TY_REAL)
+ call salloc (par1, SZ_LINE, TY_CHAR)
+ call salloc (par2, SZ_LINE, TY_CHAR)
+ call salloc (title, 3 * SZ_LINE, TY_CHAR)
+
+ # Clear screen.
+ call gclear (gd)
+
+ # Set the labels.
+ call gt_sets (gt, GTXLABEL, "X coordinate")
+ call gt_sets (gt, GTYLABEL, "Y coordinate")
+
+ # Set the title.
+ call sprintf (Memc[par1], SZ_LINE,
+ "Nstars: %d Spatial model: %s Luminosity model: %s\n")
+ call pargi (ST_NSTARS(st))
+ if (ST_SPATIAL(st) == ST_SPFILE)
+ call pargstr (ST_SFILE(st))
+ else
+ call pargstr (ST_SPSTRING(st))
+ if (ST_LUMINOSITY(st) == ST_LFFILE)
+ call pargstr (ST_LFILE(st))
+ else
+ call pargstr (ST_LFSTRING(st))
+ call sprintf (Memc[par2], SZ_LINE,
+ "X range: %g to %g Yrange: %g to %g Mag range: %g to %g\n")
+ call pargr (ST_XMIN(st))
+ call pargr (ST_XMAX(st))
+ call pargr (ST_YMIN(st))
+ call pargr (ST_YMAX(st))
+ call pargr (ST_MINMAG(st))
+ call pargr (ST_MAXMAG(st))
+ call sprintf (Memc[title], 3 * SZ_LINE, "%s%s%s")
+ call pargstr ("MAP OF STARLIST\n")
+ call pargstr (Memc[par1])
+ call pargstr (Memc[par2])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the plot axes min and max values.
+ call gt_setr (gt, GTXMIN, ST_XMIN(st))
+ call gt_setr (gt, GTXMAX, ST_XMAX(st))
+ call gt_setr (gt, GTYMIN, ST_YMIN(st))
+ call gt_setr (gt, GTYMAX, ST_YMAX(st))
+
+ # Set the window and labels.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Plot.
+ if (fp_equalr (ST_MAXMAG(st), ST_MINMAG(st)))
+ call amovkr (2.0, Memr[sizes], npts)
+ else
+ call amapr (mag, Memr[sizes], npts, ST_MAXMAG(st), ST_MINMAG(st),
+ 1.0, 4.0)
+ call gt_sets (gt, GTTYPE, "mark")
+ call gt_sets (gt, GTMARK, "box")
+ do i = 1, npts
+ call gmark (gd, x[i], y[i], GM_BOX, Memr[sizes+i-1],
+ Memr[sizes+i-1])
+
+ call sfree (sp)
+end
+
+
+# ST_PGFIELD -- Plot distribution of stars in the x-y plane.
+
+procedure st_pgfield (gd, gt, st, x, y, mag, egal, axis, round, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to plot descriptor
+pointer st # pointer to starlist structure
+real x[ARB] # array of x coordinates
+real y[ARB] # array of y coordinates
+real mag[ARB] # array of magnitudes
+int egal[ARB] # array of galaxy types
+real axis[ARB] # array of diameters
+real round[ARB] # array of roundness values
+int npts # number of points
+
+int i
+pointer sp, par1, par2, title, xsizes
+real amin, amax
+bool fp_equalr()
+
+begin
+ call smark (sp)
+ call salloc (par1, SZ_LINE, TY_CHAR)
+ call salloc (par2, SZ_LINE, TY_CHAR)
+ call salloc (title, 3 * SZ_LINE, TY_CHAR)
+ call salloc (xsizes, npts, TY_REAL)
+
+ # Clear screen.
+ call gclear (gd)
+
+ # Set the labels.
+ call gt_sets (gt, GTXLABEL, "X coordinate")
+ call gt_sets (gt, GTYLABEL, "Y coordinate")
+
+ # Set the title.
+ call sprintf (Memc[par1], SZ_LINE,
+ "Ngals: %d Spatial model: %s Luminosity model: %s\n")
+ call pargi (ST_NSTARS(st))
+ if (ST_SPATIAL(st) == ST_SPFILE)
+ call pargstr (ST_SFILE(st))
+ else
+ call pargstr (ST_SPSTRING(st))
+ if (ST_LUMINOSITY(st) == ST_LFFILE)
+ call pargstr (ST_LFILE(st))
+ else
+ call pargstr (ST_LFSTRING(st))
+ call sprintf (Memc[par2], SZ_LINE,
+ "X range: %g to %g Yrange: %g to %g Mag range: %g to %g\n")
+ call pargr (ST_XMIN(st))
+ call pargr (ST_XMAX(st))
+ call pargr (ST_YMIN(st))
+ call pargr (ST_YMAX(st))
+ call pargr (ST_MINMAG(st))
+ call pargr (ST_MAXMAG(st))
+ call sprintf (Memc[title], 3 * SZ_LINE, "%s%s%s")
+ call pargstr ("MAP OF GALLIST\n")
+ call pargstr (Memc[par1])
+ call pargstr (Memc[par2])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the x and y axis minimums and maximums.
+ call gt_setr (gt, GTXMIN, ST_XMIN(st))
+ call gt_setr (gt, GTXMAX, ST_XMAX(st))
+ call gt_setr (gt, GTYMIN, ST_YMIN(st))
+ call gt_setr (gt, GTYMAX, ST_YMAX(st))
+
+ # Set the window and labels.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Compute the marksizes.
+ call alimr (axis, npts, amin, amax)
+ if (fp_equalr (amin, amax))
+ call amovkr (2.0, Memr[xsizes], npts)
+ else
+ call amapr (axis, Memr[xsizes], npts, amin, amax, 1.0, 4.0)
+
+ #call amulr (axis, round, Memr[ysizes], npts)
+ #call alimr (Memr[ysizes], npts, amin, amax)
+ #call amapr (Memr[ysizes], Memr[ysizes], npts, amin, amax, 1.0, 4.0)
+
+ # Plot.
+ call gt_sets (gt, GTTYPE, "mark")
+ do i = 1, npts {
+ if (egal[i] == ST_DEVAUC)
+ call gmark (gd, x[i], y[i], GM_CIRCLE, Memr[xsizes+i-1],
+ Memr[xsizes+i-1])
+ else
+ call gmark (gd, x[i], y[i], GM_DIAMOND, Memr[xsizes+i-1],
+ Memr[xsizes+i-1])
+ }
+
+ call sfree (sp)
+end
+
+
+# ST_PMHIST -- Plot luminosity function of the stars or galaxies.
+
+procedure st_pmhist (gd, gt, st, mag, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to the plot descriptor
+pointer st # pointer to starlist structure
+real mag[ARB] # array of magnitudes
+int npts # number of points
+
+int i, nbins
+pointer sp, par, title, hx, hgm
+real mval, mmin, mmax, dm, hmin, hmax
+
+begin
+ # Compute the parameters of the histogram to be plotted.
+ mmin = ST_MINMAG(st)
+ if (ST_MBINSIZE(st) <= 0.0) {
+ nbins = 1
+ mmax = ST_MAXMAG(st)
+ dm = mmax - mmin
+ } else {
+ dm = ST_MBINSIZE(st)
+ mval = (ST_MAXMAG(st) - mmin) / dm
+ i = int (mval)
+ if (abs (mval -i ) <= 100.0 * EPSILONR) {
+ nbins = i + 1
+ mmax = ST_MAXMAG(st)
+ } else {
+ mmax = mmin + (i + 1) * dm
+ nbins = i + 2
+ }
+ }
+
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (par, SZ_LINE, TY_CHAR)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (hx, nbins, TY_REAL)
+ call salloc (hgm, nbins, TY_REAL)
+
+ # Make the histogram.
+ call aclrr (Memr[hgm], nbins)
+ call st_hgmr (mag, npts, Memr[hgm], nbins, mmin, mmax)
+ call alimr (Memr[hgm], nbins, hmin, hmax)
+
+ # Make the histogram x scale.
+ mval = mmin + dm / 2.0
+ do i = 1, nbins {
+ Memr[hx+i-1] = mval
+ mval = mval + dm
+ }
+ mval = mmin + nbins * dm
+
+ # Clear the screen.
+ call gclear (gd)
+
+ # Set the labels.
+ call gt_sets (gt, GTXLABEL, "Magnitude")
+ call gt_sets (gt, GTYLABEL, "N(M)")
+
+ # Set the title.
+ call sprintf (Memc[par], SZ_LINE,
+ "%s: %d Model: %s\nMag: %g to %g in steps of %g\n")
+ if (ST_TYPE(st) == ST_STARS)
+ call pargstr ("Nstars")
+ else
+ call pargstr ("Ngals")
+ call pargi (ST_NSTARS(st))
+ if (ST_LUMINOSITY(st) == ST_LFFILE)
+ call pargstr (ST_LFILE(st))
+ else
+ call pargstr (ST_LFSTRING(st))
+ call pargr (mmin)
+ call pargr (mmax)
+ call pargr (dm)
+ call sprintf (Memc[title], 2 * SZ_LINE, "%s%s")
+ call pargstr ("LUMINOSITY FUNCTION\n")
+ call pargstr (Memc[par])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the mins and maxs.
+ call gt_setr (gt, GTXMIN, mmin)
+ call gt_setr (gt, GTXMAX, mval)
+ call gt_setr (gt, GTYMIN, 0.0)
+ call gt_setr (gt, GTYMAX, hmax)
+
+ # Set the window.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Plot.
+ call gt_sets (gt, GTTYPE, "histogram")
+ call gt_plot (gd, gt, Memr[hx], Memr[hgm], nbins)
+ call gline (gd, mmin, Memr[hgm], Memr[hx], Memr[hgm])
+ call gline (gd, Memr[hx+nbins-1], Memr[hgm+nbins-1], mval,
+ Memr[hgm+nbins-1])
+
+ call sfree (sp)
+end
+
+
+# ST_PRHIST -- Plot radial density distribution of the stars.
+
+procedure st_prhist (gd, gt, st, x, y, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to plot descriptor
+pointer st # pointer to starlist structure
+real x[ARB] # array of x values
+real y[ARB] # array of y values
+int npts # number of points
+
+int i, nbins
+pointer sp, par, title, r, hx, hgm
+real rval, rmin, rmax, dr, hmin, hmax
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (r, npts, TY_REAL)
+ call salloc (par, SZ_LINE, TY_CHAR)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+
+ # Compute the radial coordinate values.
+ do i = 1, npts {
+ Memr[r+i-1] = sqrt ((x[i] - ST_XC(st)) ** 2 +
+ (y[i] - ST_YC(st)) ** 2)
+ }
+ call alimr (Memr[r], npts, rmin, rmax)
+
+ # Compute the size of the histogram.
+ rmin = 0.0
+ if (ST_RBINSIZE(st) <= 0) {
+ nbins = 1
+ dr = rmax - rmin
+ } else {
+ dr = ST_RBINSIZE(st)
+ rval = (rmax - rmin) / dr
+ i = int (rval)
+ if (abs (rval - i) <= 100.0 * EPSILONR)
+ nbins = i + 1
+ else {
+ rmax = rmin + (i + 1) * dr
+ nbins = i + 2
+ }
+ }
+
+ # Make the histogram and normalize by area.
+ call salloc (hx, nbins, TY_REAL)
+ call salloc (hgm, nbins, TY_REAL)
+ call aclrr (Memr[hgm], nbins)
+ call st_hgmr (Memr[r], npts, Memr[hgm], nbins, rmin, rmax)
+ rval = rmin
+ do i = 1, nbins {
+ Memr[hgm+i-1] = Memr[hgm+i-1] / ( PI * dr * (2.0 * rval + dr))
+ rval = rval + dr
+ }
+ call alimr (Memr[hgm], nbins, hmin, hmax)
+
+ # Make the histogram x scale.
+ rval = rmin + dr / 2.0
+ do i = 1, nbins {
+ Memr[hx+i-1] = rval
+ rval = rval + dr
+ }
+ rval = rmin + nbins * dr
+
+ # Clear the screen.
+ call gclear (gd)
+
+ # Set the labels.
+ call gt_sets (gt, GTXLABEL, "Radial Distance")
+ call gt_sets (gt, GTYLABEL, "N(R) / Area")
+
+ # Set the title.
+ call sprintf (Memc[par], SZ_LINE,
+ "Model: %s %s: %d\nRadius: %g to %g in steps of %g\n")
+ if (ST_SPATIAL(st) == ST_SPFILE)
+ call pargstr (ST_SFILE(st))
+ else
+ call pargstr (ST_SPSTRING(st))
+ if (ST_TYPE(st) == ST_STARS)
+ call pargstr ("Nstars")
+ else
+ call pargstr ("Ngals")
+ call pargi (ST_NSTARS(st))
+ call pargr (rmin)
+ call pargr (rmax)
+ call pargr (dr)
+ call sprintf (Memc[title], 2 * SZ_LINE, "%s%s")
+ call pargstr ("RADIAL DENSITY FUNCTION\n")
+ call pargstr (Memc[par])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the x and y axes minimum and maximum values.
+ call gt_setr (gt, GTXMIN, rmin)
+ call gt_setr (gt, GTXMAX, rval)
+ call gt_setr (gt, GTYMIN, 0.0)
+ call gt_setr (gt, GTYMAX, hmax)
+
+ # Set the window.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Plot.
+ call gt_sets (gt, GTTYPE, "histogram")
+ call gt_plot (gd, gt, Memr[hx], Memr[hgm], nbins)
+ call gline (gd, rmin, Memr[hgm], Memr[hx], Memr[hgm])
+ call gline (gd, Memr[hx+nbins-1], Memr[hgm+nbins-1], rval,
+ Memr[hgm+nbins-1])
+
+ call sfree (sp)
+end
+
+
+# ST_PDHIST -- Plot the distribution of galaxy diameters.
+
+procedure st_pdhist (gd, gt, st, axis, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to plot descriptor
+pointer st # pointer to starlist structure
+real axis[ARB] # array of diameters
+int npts # number of points
+
+int i, nbins
+pointer sp, par, title, hx, hgm
+real aval, amin, amax, da, hmin, hmax
+
+begin
+
+ # Allocate space for the histogram.
+ call alimr (axis, npts, amin, amax)
+ amax = max (ST_ERADIUS(st), ST_SRADIUS(st))
+ if (ST_DBINSIZE(st) <= 0) {
+ nbins = 1
+ da = amax - amin
+ } else {
+ da = ST_DBINSIZE(st)
+ aval = (amax - amin) / da
+ i = int (aval)
+ if (abs (aval - i) <= 100.0 * EPSILONR)
+ nbins = i + 1
+ else {
+ amin = amax - (i + 1) * da
+ nbins = i + 2
+ }
+ }
+
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (par, SZ_LINE, TY_CHAR)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (hx, nbins, TY_REAL)
+ call salloc (hgm, nbins, TY_REAL)
+
+ # Make the histogram.
+ call aclrr (Memr[hgm], nbins)
+ call st_hgmr (axis, npts, Memr[hgm], nbins, amin, amax)
+ call alimr (Memr[hgm], nbins, hmin, hmax)
+
+ # Make the histogram x scale.
+ aval = amin + da / 2.0
+ do i = 1, nbins {
+ Memr[hx+i-1] = aval
+ aval = aval + da
+ }
+ aval = amin + nbins * da
+
+ # Clear the screen.
+ call gclear (gd)
+
+ # Set the labels.
+ call gt_sets (gt, GTXLABEL, "Half-Flux Radius")
+ call gt_sets (gt, GTYLABEL, "N(radius)")
+
+ # Set the title.
+ call sprintf (Memc[par], SZ_LINE,
+ "Luminosity function: %s Ngals: %d\nDiameter: %g to %g in steps of %g\n")
+ if (ST_LUMINOSITY(st) == ST_LFFILE)
+ call pargstr (ST_LFILE(st))
+ else
+ call pargstr (ST_LFSTRING(st))
+ call pargi (ST_NSTARS(st))
+ call pargr (amin)
+ call pargr (amax)
+ call pargr (da)
+ call sprintf (Memc[title], 2 * SZ_LINE, "%s%s")
+ call pargstr ("HALF-FLUX RADIUS DISTRIBUTION\n")
+ call pargstr (Memc[par])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the mins and maxs.
+ call gt_setr (gt, GTXMIN, amin)
+ call gt_setr (gt, GTXMAX, aval)
+ call gt_setr (gt, GTYMIN, 0.0)
+ call gt_setr (gt, GTYMAX, hmax)
+
+ # Set the window.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Plot.
+ call gt_sets (gt, GTTYPE, "histogram")
+ call gt_plot (gd, gt, Memr[hx], Memr[hgm], nbins)
+ call gline (gd, amin, Memr[hgm], Memr[hx], Memr[hgm])
+ call gline (gd, Memr[hx+nbins-1], Memr[hgm+nbins-1], aval,
+ Memr[hgm+nbins-1])
+
+ call sfree (sp)
+end
+
+
+# ST_PEHIST -- Plot the distribution of galaxy diameters.
+
+procedure st_pehist (gd, gt, st, round, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to plot descriptor
+pointer st # pointer to starlist structure
+real round[ARB] # array of roundness values
+int npts # number of points
+
+int i, nbins
+pointer sp, par, title, hx, hgm
+real eval, emin, emax, de, hmin, hmax
+
+begin
+ # Compute the size of the histogram.
+ emin = .1
+ if (ST_EBINSIZE(st) <= 0) {
+ nbins = 1
+ emax = 1.
+ de = emax - emin
+ } else {
+ de = ST_EBINSIZE(st)
+ eval = (1. - emin) / de
+ i = int (eval)
+ if (abs (eval - i) <= 100.0 * EPSILONR) {
+ nbins = i + 1
+ emax = 1.
+ } else {
+ nbins = i + 2
+ emax = emin + (i + 1) * de
+ }
+ }
+
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (par, SZ_LINE, TY_CHAR)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (hx, nbins, TY_REAL)
+ call salloc (hgm, nbins, TY_REAL)
+
+ # Make the histogram and normalize by area.
+ call aclrr (Memr[hgm], nbins)
+ call st_hgmr (round, npts, Memr[hgm], nbins, emin, emax)
+ call alimr (Memr[hgm], nbins, hmin, hmax)
+
+ # Make the histogram x scale.
+ eval = emin + de / 2.0
+ do i = 1, nbins {
+ Memr[hx+i-1] = eval
+ eval = eval + de
+ }
+ eval = emin + nbins * de
+
+ # Clear the screen.
+ call gclear (gd)
+
+ # Set the labels.
+ call gt_sets (gt, GTXLABEL, "Axial Ratio")
+ call gt_sets (gt, GTYLABEL, "N(Axial Ratio)")
+
+ # Set the title.
+ call sprintf (Memc[par], SZ_LINE,
+ "Ngals: %d\nRoundness: %g to %g in steps of %g\n")
+ call pargi (ST_NSTARS(st))
+ call pargr (emin)
+ call pargr (emax)
+ call pargr (de)
+ call sprintf (Memc[title], 2 * SZ_LINE, "%s%s")
+ call pargstr ("AXIAL RATIO DISTRIBUTION\n")
+ call pargstr (Memc[par])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the mins and maxs.
+ call gt_setr (gt, GTXMIN, emin)
+ call gt_setr (gt, GTXMAX, eval)
+ call gt_setr (gt, GTYMIN, 0.0)
+ call gt_setr (gt, GTYMAX, hmax)
+
+ # Set the window.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Plot.
+ call gt_sets (gt, GTTYPE, "histogram")
+ call gt_plot (gd, gt, Memr[hx], Memr[hgm], nbins)
+ call gline (gd, emin, Memr[hgm], Memr[hx], Memr[hgm])
+ call gline (gd, Memr[hx+nbins-1], Memr[hgm+nbins-1], eval,
+ Memr[hgm+nbins-1])
+
+ call sfree (sp)
+end
+
+
+define MIN_ANGLE 0.0
+define MAX_ANGLE 360.0
+
+# ST_PPHIST -- Plot the distribution of galaxy diameters.
+
+procedure st_pphist (gd, gt, st, phi, npts)
+
+pointer gd # pointer to graphics stream
+pointer gt # pointer to plot descriptor
+pointer st # pointer to starlist structure
+real phi[ARB] # array of position angle values
+int npts # number of points
+
+int i, nbins
+pointer sp, par, title, hx, hgm
+real pval, pmin, pmax, dp, hmin, hmax
+
+begin
+ # Compute the size of the histogram.
+ pmin = MIN_ANGLE
+ if (ST_PBINSIZE(st) <= 0) {
+ nbins = 1
+ pmax = MAX_ANGLE
+ dp = pmax - pmin
+ } else {
+ dp = ST_PBINSIZE(st)
+ pval = (MAX_ANGLE - pmin) / dp
+ i = int (pval)
+ if (abs (pval - i) <= 100.0 * EPSILONR) {
+ nbins = i + 1
+ pmax = MAX_ANGLE
+ } else {
+ pmax = MIN_ANGLE + (i + 1) * dp
+ nbins = i + 2
+ }
+ }
+
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (par, SZ_LINE, TY_CHAR)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (hx, nbins, TY_REAL)
+ call salloc (hgm, nbins, TY_REAL)
+
+ # Make the histogram.
+ call aclrr (Memr[hgm], nbins)
+ call st_hgmr (phi, npts, Memr[hgm], nbins, pmin, pmax)
+ call alimr (Memr[hgm], nbins, hmin, hmax)
+
+
+ # Make the histogram x scale.
+ pval = dp / 2.0
+ do i = 1, nbins {
+ Memr[hx+i-1] = pval
+ pval = pval + dp
+ }
+ pval = pmin + nbins * dp
+
+ # Clear the screen.
+ call gclear (gd)
+
+ # Set the axis labels.
+ call gt_sets (gt, GTXLABEL, "Position Angle")
+ call gt_sets (gt, GTYLABEL, "N(Position Angle)")
+
+ # Set the title.
+ call sprintf (Memc[par], SZ_LINE,
+ "Ngals: %d\nPosition angle: %g to %g in steps of %g\n")
+ call pargi (ST_NSTARS(st))
+ call pargr (pmin)
+ call pargr (pmax)
+ call pargr (dp)
+ call sprintf (Memc[title], 2 * SZ_LINE, "%s%s")
+ call pargstr ("POSITION ANGLE DISTRIBUTION\n")
+ call pargstr (Memc[par])
+ call gt_sets (gt, GTTITLE, Memc[title])
+
+ # Set the axis mins and maxs.
+ call gt_setr (gt, GTXMIN, pmin)
+ call gt_setr (gt, GTXMAX, pval)
+ call gt_setr (gt, GTYMIN, 0.0)
+ call gt_setr (gt, GTYMAX, hmax)
+
+ # Set the window.
+ call gt_swind (gd, gt)
+ call gt_labax (gd, gt)
+
+ # Plot.
+ call gt_sets (gt, GTTYPE, "histogram")
+ call gt_plot (gd, gt, Memr[hx], Memr[hgm], nbins)
+ call gline (gd, pmin, Memr[hgm], Memr[hx], Memr[hgm])
+ call gline (gd, Memr[hx+nbins-1], Memr[hgm+nbins-1], pval,
+ Memr[hgm+nbins-1])
+
+ call sfree (sp)
+end
+
+
+# ST_HGMR -- Accumulate the histogram of the input vector. The output vector
+# hmg (the histogram) should be cleared prior to the first call.
+
+procedure st_hgmr (data, npix, hgm, nbins, z1, z2)
+
+real data[ARB] # data vector
+int npix # number of pixels
+real hgm[ARB] # output histogram
+int nbins # number of bins in histogram
+real z1, z2 # greyscale values of first and last bins
+
+int bin, i
+real z, dz
+
+begin
+ dz = real (nbins - 1) / real (z2 - z1)
+ if (abs (dz - 1.0) < (EPSILONR * 2.0)) {
+ do i = 1, npix {
+ z = data[i]
+ if (z < z1 || z > z2)
+ next
+ bin = int (z - z1) + 1
+ hgm[bin] = hgm[bin] + 1.0
+ }
+ } else {
+ do i = 1, npix {
+ z = data[i]
+ if (z < z1 || z > z2)
+ next
+ bin = int ((z - z1) * dz) + 1
+ hgm[bin] = hgm[bin] + 1.0
+ }
+ }
+end
diff --git a/noao/artdata/lists/stshow.x b/noao/artdata/lists/stshow.x
new file mode 100644
index 00000000..4aa8d853
--- /dev/null
+++ b/noao/artdata/lists/stshow.x
@@ -0,0 +1,142 @@
+include "starlist.h"
+
+procedure st_show (st)
+
+pointer st # pointer to starlist structure
+
+begin
+ call printf ("nstars = %d\n")
+ call pargi (ST_NSTARS(st))
+
+ call printf ("spatial: %s\n")
+ call pargstr (ST_SPSTRING(st))
+ call printf (" xmin = %g xmax = %g\n")
+ call pargr (ST_XMIN(st))
+ call pargr (ST_XMAX(st))
+ call printf (" ymin = %g ymax = %g\n")
+ call pargr (ST_YMIN(st))
+ call pargr (ST_YMAX(st))
+ call printf (" xc = %g yc = %g\n")
+ call pargr (ST_XC(st))
+ call pargr (ST_YC(st))
+ if (ST_SPATIAL(st) == ST_HUBBLE) {
+ call printf (" core = %g base = %g\n")
+ call pargr (ST_CORE(st))
+ call pargr (ST_BASE(st))
+ } else if (ST_SPATIAL(st) == ST_SPFILE) {
+ call printf (" file: %s\n")
+ call pargstr (ST_SFILE(st))
+ }
+ call printf (" nssample = %d sorder = %d\n")
+ call pargi (ST_NSSAMPLE(st))
+ call pargi (ST_SORDER(st))
+ call printf (" rbinsize = %d\n")
+ call pargr (ST_RBINSIZE(st))
+
+ call printf ("luminosity = %s\n")
+ call pargstr (ST_LFSTRING(st))
+ call printf (" minmag = %g maxmag = %g\n")
+ call pargr (ST_MINMAG(st))
+ call pargr (ST_MAXMAG(st))
+ if (ST_LUMINOSITY(st) == ST_BANDS) {
+ call printf ( " bands: alpha = %g beta = %g\n")
+ call pargr (ST_ALPHA(st))
+ call pargr (ST_BETA(st))
+ call printf (" delta = %g mstar = %g mzero = %g\n")
+ call pargr (ST_DELTA(st))
+ call pargr (ST_MSTAR(st))
+ call pargr (ST_MZERO(st))
+ } else if (ST_LUMINOSITY(st) == ST_SALPETER) {
+ call printf (" mzero = %g\n")
+ call pargr (ST_MZERO(st))
+ } else if (ST_LUMINOSITY(st) == ST_POWLAW) {
+ call printf (" power = %g\n")
+ call pargr (ST_POWER(st))
+ } else if (ST_LUMINOSITY(st) == ST_LFFILE) {
+ call printf (" file: %s\n")
+ call pargstr (ST_LFILE(st))
+ }
+ call printf ( " nlsample = %d lorder = %d\n")
+ call pargl (ST_NLSAMPLE(st))
+ call pargi (ST_LORDER(st))
+ call printf (" mbinsize = %g\n")
+ call pargr (ST_MBINSIZE(st))
+end
+
+
+# ST_GSHOW -- Display the GALAXIES parameters.
+
+procedure st_gshow (st)
+
+pointer st # pointer to starfield structure
+
+begin
+ call printf ("ngals = %d\n")
+ call pargi (ST_NSTARS(st))
+
+ call printf ("spatial: %s\n")
+ call pargstr (ST_SPSTRING(st))
+ call printf (" xmin = %g xmax = %g\n")
+ call pargr (ST_XMIN(st))
+ call pargr (ST_XMAX(st))
+ call printf (" ymin = %g ymax = %g\n")
+ call pargr (ST_YMIN(st))
+ call pargr (ST_YMAX(st))
+ call printf (" xc = %g yc = %g\n")
+ call pargr (ST_XC(st))
+ call pargr (ST_YC(st))
+ if (ST_SPATIAL(st) == ST_HUBBLE) {
+ call printf (" core = %g base = %g\n")
+ call pargr (ST_CORE(st))
+ call pargr (ST_BASE(st))
+ } else if (ST_SPATIAL(st) == ST_SPFILE) {
+ call printf (" file: %s\n")
+ call pargstr (ST_SFILE(st))
+ }
+ call printf (" nssample = %d sorder = %d\n")
+ call pargi (ST_NSSAMPLE(st))
+ call pargi (ST_SORDER(st))
+ call printf (" rbinsize = %d\n")
+ call pargr (ST_RBINSIZE(st))
+
+ call printf ("luminosity = %s\n")
+ call pargstr (ST_LFSTRING(st))
+ call printf (" minmag = %g maxmag = %g\n")
+ call pargr (ST_MINMAG(st))
+ call pargr (ST_MAXMAG(st))
+ if (ST_LUMINOSITY(st) == ST_POWLAW) {
+ call printf ( " powlaw: power = %g\n")
+ call pargr (ST_POWER(st))
+ } else if (ST_LUMINOSITY(st) == ST_SCHECTER) {
+ call printf ( " schecter: alpha = %g mstar = %g mzero = %g\n")
+ call pargr (ST_ALPHA(st))
+ call pargr (ST_MSTAR(st))
+ call pargr (ST_MZERO(st))
+ } else if (ST_LUMINOSITY(st) == ST_LFFILE) {
+ call printf (" file: %s\n")
+ call pargstr (ST_LFILE(st))
+ }
+ call printf (" nlsample = %d lorder = %d\n")
+ call pargi (ST_NLSAMPLE(st))
+ call pargi (ST_LORDER(st))
+ call printf (" mbinsize = %d\n")
+ call pargr (ST_MBINSIZE(st))
+ call printf (" eradius = %g sradius = %g dbinsize = %g\n")
+ call pargr (ST_ERADIUS(st))
+ call pargr (ST_SRADIUS(st))
+ call pargr (ST_DBINSIZE(st))
+ call printf (" z = %g\n")
+ call pargr (ST_Z(st))
+ call printf (" absorption = %g\n")
+ call pargr (ST_ABSORPTION(st))
+
+ call eprintf ("egalmix = %g\n")
+ call pargr (ST_EGALMIX(st))
+ call printf ("ar = %g\n")
+ call pargr (ST_AR(st))
+ call printf (" ebinsize = %g\n")
+ call pargr (ST_EBINSIZE(st))
+ call printf ("posmin = 0.0 posmax = 360.0\n")
+ call printf (" pbinsize = %g\n")
+ call pargr (ST_PBINSIZE(st))
+end
diff --git a/noao/artdata/lists/stspatial.x b/noao/artdata/lists/stspatial.x
new file mode 100644
index 00000000..1171dfda
--- /dev/null
+++ b/noao/artdata/lists/stspatial.x
@@ -0,0 +1,264 @@
+include <math.h>
+include <math/curfit.h>
+include <math/iminterp.h>
+
+
+# ST_XYUNIFORM -- Compute a set of x and y values uniformly distributed in x and
+# y between xmin, xmax, ymin and ymax.
+
+procedure st_xyuniform (x, y, nstars, xmin, xmax, ymin, ymax, seed)
+
+real x[ARB] # output array of x values
+real y[ARB] # output array of y values
+int nstars # number of stars
+real xmin, xmax # x coordinate limits
+real ymin, ymax # y coordinate limits
+long seed # seed for random number generator
+
+int i
+real urand()
+
+begin
+ # Compute x and y values between 0 and 1.
+ do i = 1, nstars {
+ x[i] = urand (seed)
+ y[i] = urand (seed)
+ }
+
+ # Map these values into the data range.
+ call amapr (x, x, nstars, 0.0, 1.0, xmin, xmax)
+ call amapr (y, y, nstars, 0.0, 1.0, ymin, ymax)
+end
+
+
+# ST_HBSAMPLE -- Compute a set of x and y values with a Hubble density
+# distribution.
+
+procedure st_hbsample (x, y, nstars, core, base, xc, yc, xmin, xmax, ymin, ymax,
+ nsample, order, seed)
+
+real x[ARB] # output array of x values
+real y[ARB] # output array of y values
+int nstars # number of stars
+real core # Hubble core radius
+real base # baseline density
+real xc, yc # x and y center coordinates
+real xmin, xmax # x range
+real ymin, ymax # y range
+int nsample # number of sample points
+int order # order of spline fit
+long seed # seed for random number generator
+
+int i, ier
+pointer sp, rad, prob, w, cv
+real r1, r2, r3, r4, rmin, rmax, rval, dr, theta
+real urand(), cveval()
+
+begin
+ # Allocate space for the fits.
+ call smark (sp)
+ call salloc (rad, nsample, TY_REAL)
+ call salloc (prob, nsample, TY_REAL)
+ call salloc (w, nsample, TY_REAL)
+
+ # Compute the maximum radial distance from the center and
+ # the sampling interval.
+
+ r1 = (xmin - xc) ** 2 + (ymin - yc) ** 2
+ r2 = (xmax - xc) ** 2 + (ymin - yc) ** 2
+ r3 = (xmax - xc) ** 2 + (ymax - yc) ** 2
+ r4 = (xmin - xc) ** 2 + (ymax - yc) ** 2
+ if (xc >= xmin && xc <= xmax && yc >= ymin && yc <= ymax)
+ rmin = 0.0
+ else if (yc >= ymin && yc <= ymax)
+ rmin = min (abs (xmin - xc), abs (xmax - xc))
+ else if (xc >= xmin && xc <= xmax)
+ rmin = min (abs (ymin - yc), abs (ymax - yc))
+ else
+ rmin = sqrt (min (r1, r2, r3, r4))
+ rmax = sqrt (max (r1, r2, r3, r4))
+ dr = (rmax - rmin) / (nsample - 1)
+
+ # Compute the integral of the sampling function.
+ r1 = core ** 2
+ rval = rmin
+ do i = 1, nsample {
+ Memr[rad+i-1] = rval
+ r2 = (core + rval) / core
+ Memr[prob+i-1] = r1 * (log (r2) + 1.0 / r2 - 1.0) +
+ base * rval ** 2 / 2.0
+ rval = rval + dr
+ }
+
+ # Normalize the probability function.
+ call alimr (Memr[prob], nsample, rmin, rmax)
+ call amapr (Memr[prob], Memr[prob], nsample, rmin, rmax, 0.0, 1.0)
+
+ # Fit the inverse of the integral of the probability function
+ call cvinit (cv, SPLINE3, order, 0.0, 1.0)
+ call cvfit (cv, Memr[prob], Memr[rad], Memr[w], nsample, WTS_UNIFORM,
+ ier)
+
+ # Sample the computed function.
+ if (ier == OK) {
+ i = 0
+ repeat {
+ rval = cveval (cv, urand (seed))
+ theta = DEGTORAD (360.0 * urand (seed))
+ x[i+1] = rval * cos (theta) + xc
+ y[i+1] = rval * sin (theta) + yc
+ if (x[i+1] >= xmin && x[i+1] <= xmax && y[i+1] >= ymin &&
+ y[i+1] <= ymax)
+ i = i + 1
+ } until (i >= nstars)
+ } else {
+ call amovkr ((xmin + xmax) / 2.0, x, nstars)
+ call amovkr ((ymin + ymax) / 2.0, y, nstars)
+ call printf ("Error computing the spatial probability function.\n")
+ }
+
+ # Free up the space.
+ call cvfree (cv)
+ call sfree (sp)
+end
+
+
+# ST_SFSAMPLE -- Compute a sample of x and y coordinate values based
+# on a user supplied spatial density function.
+
+procedure st_sfsample (r, rprob, nsf, x, y, nstars, nsample, order, xc, yc,
+ xmin, xmax, ymin, ymax, seed)
+
+real r[ARB] # input array of radii
+real rprob[ARB] # input array of relative probabilities
+int nsf # number of input points
+real x[ARB] # output x coordinate array
+real y[ARB] # output y coordinate array
+int nstars # number of stars
+int nsample # number of sample points
+int order # order of the spline fit
+real xc, yc # x and y center coordiantes
+real xmin, xmax # min and max x values
+real ymin, ymax # min and max y values
+long seed # value of the seed
+
+int itemp, i, ier
+pointer sp, w, rad, iprob, cv, asi
+real rfmin, rfmax, dr, rval, theta, imin, imax
+real cveval(), asigrl(), urand()
+
+begin
+ # Allocate space for fitting.
+ itemp = max (nsf, nsample)
+ call smark (sp)
+ call salloc (rad, nsample, TY_REAL)
+ call salloc (iprob, nsample, TY_REAL)
+ call salloc (w, itemp, TY_REAL)
+
+ # Smooth the relative probability function function.
+ call alimr (r, nsf, rfmin, rfmax)
+ itemp = min (order, max (1, nsf / 4))
+ call cvinit (cv, SPLINE3, itemp, rfmin, rfmax)
+ call cvfit (cv, r, rprob, Memr[w], nsf, WTS_UNIFORM, ier)
+
+ # Evaluate the smoothed function at equal intervals in r,
+ # Multiplying by r to prepare for the area integration.
+ if (ier == OK) {
+ rval = rfmin
+ dr = (rfmax - rfmin) / (nsample - 1)
+ do i = 1, nsample {
+ Memr[rad+i-1] = rval
+ Memr[iprob+i-1] = rval * cveval (cv, rval)
+ rval = rval + dr
+ }
+ call cvfree (cv)
+ } else {
+ call printf ("Error smoothing the user spatial density function.\n")
+ call amovkr ((xmin + xmax) / 2.0, x, nstars)
+ call amovkr ((ymin + ymax) / 2.0, y, nstars)
+ call cvfree (cv)
+ call sfree (sp)
+ return
+ }
+
+
+ # Evaluate the integral.
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[iprob], nsample)
+ Memr[iprob] = 0.0
+ do i = 2, nsample
+ Memr[iprob+i-1] = Memr[iprob+i-2] + asigrl (asi, real (i - 1),
+ real (i))
+ call alimr (Memr[iprob], nsample, imin, imax)
+ call amapr (Memr[iprob], Memr[iprob], nsample, imin, imax, 0.0, 1.0)
+ call asifree (asi)
+
+ # Fit the inverse of the integral of the probability function.
+ call cvinit (cv, SPLINE3, order, 0.0, 1.0)
+ call cvfit (cv, Memr[iprob], Memr[rad], Memr[w], nsample, WTS_UNIFORM,
+ ier)
+
+ # Sample the computed function.
+ if (ier == OK) {
+ i = 0
+ repeat {
+ rval = cveval (cv, urand (seed))
+ theta = DEGTORAD (360.0 * urand (seed))
+ x[i+1] = rval * cos (theta) + xc
+ y[i+1] = rval * sin (theta) + yc
+ if (x[i+1] >= xmin && x[i+1] <= xmax && y[i+1] >= ymin &&
+ y[i+1] <= ymax)
+ i = i + 1
+ } until (i >= nstars)
+ } else {
+ call printf (
+ "Error fitting the spatial probability function.\n")
+ call amovkr ((xmin + xmax) / 2.0, x, nstars)
+ call amovkr ((ymin + ymax) / 2.0, y, nstars)
+ }
+ call cvfree (cv)
+
+ # Free space.
+ call sfree (sp)
+end
+
+
+define BUFSIZE 200
+
+# ST_GFETCHXY -- Fetch two real values from a text file.
+
+int procedure st_gfetchxy (sf, x, y)
+
+int sf # input text file descriptor
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+
+int bufsize, npts
+int fscan(), nscan()
+
+begin
+ call seek (sf, BOF)
+
+ call malloc (x, BUFSIZE, TY_REAL)
+ call malloc (y, BUFSIZE, TY_REAL)
+ bufsize = BUFSIZE
+
+ npts = 0
+ while (fscan (sf) != EOF) {
+ call gargr (Memr[x+npts])
+ call gargr (Memr[y+npts])
+ if (nscan () != 2)
+ next
+ npts = npts + 1
+ if (npts < bufsize)
+ next
+ bufsize = bufsize + BUFSIZE
+ call realloc (x, bufsize, TY_REAL)
+ call realloc (y, bufsize, TY_REAL)
+ }
+
+ call realloc (x, npts, TY_REAL)
+ call realloc (y, npts, TY_REAL)
+
+ return (npts)
+end
diff --git a/noao/artdata/lists/t_gallist.x b/noao/artdata/lists/t_gallist.x
new file mode 100644
index 00000000..48f1f333
--- /dev/null
+++ b/noao/artdata/lists/t_gallist.x
@@ -0,0 +1,453 @@
+include <fset.h>
+include "starlist.h"
+
+procedure t_gallist()
+
+pointer galaxies # pointer to the name of the output file
+pointer graphics # poionter to graphics device name
+
+int sf, lf
+long seed, sseed, lseed
+long sseed1, lseed1
+pointer sp, str, x, y, mag, egal, axis, round, phi, dt, st, gd
+
+bool clgetb()
+int clgeti(), clgwrd(), open()
+long clgetl(), clktime()
+pointer dtmap(), gopen()
+real clgetr()
+
+begin
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (galaxies, SZ_FNAME, TY_CHAR)
+ call salloc (graphics, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Allocate the starlist / galaxies structure.
+ call malloc (st, LEN_STSTRUCT, TY_STRUCT)
+ ST_TYPE(st) = ST_GALAXIES
+
+ # Get the parameters.
+ call clgstr ("gallist", Memc[galaxies], SZ_FNAME)
+ ST_NSTARS(st) = clgeti ("ngals")
+
+ # Get the parameters of the spatial density function.
+ ST_SPATIAL(st) = clgwrd ("spatial", ST_SPSTRING(st), SZ_FNAME, SPFUNCS)
+ ST_XMIN(st) = clgetr ("xmin")
+ ST_XMAX(st) = clgetr ("xmax")
+ ST_YMIN(st) = clgetr ("ymin")
+ ST_YMAX(st) = clgetr ("ymax")
+ ST_SFILE(st) = EOS
+ switch (ST_SPATIAL(st)) {
+ case ST_UNIFORM:
+ sf = NULL
+ ST_XC(st) = (ST_XMAX(st) + ST_XMIN(st)) / 2.0
+ ST_YC(st) = (ST_YMAX(st) + ST_YMIN(st)) / 2.0
+ case ST_HUBBLE:
+ sf = NULL
+ ST_XC(st) = clgetr ("xcenter")
+ if (IS_INDEFR(ST_XC(st)))
+ ST_XC(st) = (ST_XMAX(st) + ST_XMIN(st)) / 2.0
+ ST_YC(st) = clgetr ("ycenter")
+ if (IS_INDEFR(ST_YC(st)))
+ ST_YC(st) = (ST_YMAX(st) + ST_YMIN(st)) / 2.0
+ case ST_SPFILE:
+ call clgstr ("sfile", ST_SFILE(st), SZ_FNAME)
+ sf = open (ST_SFILE(st), READ_ONLY, TEXT_FILE)
+ ST_XC(st) = clgetr ("xcenter")
+ if (IS_INDEFR(ST_XC(st)))
+ ST_XC(st) = (ST_XMAX(st) + ST_XMIN(st)) / 2.0
+ ST_YC(st) = clgetr ("ycenter")
+ if (IS_INDEFR(ST_YC(st)))
+ ST_YC(st) = (ST_YMAX(st) + ST_YMIN(st)) / 2.0
+ }
+ ST_CORE(st) = clgetr ("core_radius")
+ ST_BASE(st) = clgetr ("base")
+
+
+ # Get the parameters of the luminosity function.
+ ST_LUMINOSITY(st) = clgwrd ("luminosity", ST_LFSTRING(st), SZ_FNAME,
+ GLUMFUNCS)
+ ST_MINMAG(st) = clgetr ("minmag")
+ ST_MAXMAG(st) = clgetr ("maxmag")
+ ST_LFILE(st) = EOS
+ switch (ST_LUMINOSITY(st)) {
+ case ST_UNIFORM, ST_POWLAW, ST_SCHECTER:
+ lf = NULL
+ case ST_LFFILE:
+ call clgstr ("lfile", ST_LFILE(st), SZ_FNAME)
+ lf = open (ST_LFILE(st), READ_ONLY, TEXT_FILE)
+ }
+ ST_POWER(st) = clgetr ("power")
+ ST_MZERO(st) = clgetr ("mzero")
+ ST_ALPHA(st) = clgetr ("alpha")
+ ST_MSTAR(st) = clgetr ("mstar")
+
+ # Get the remaining parameters.
+ ST_Z(st) = clgetr ("z")
+ ST_AR(st) = clgetr ("ar")
+ ST_ERADIUS(st) = clgetr ("eradius")
+ ST_SRADIUS(st) = clgetr ("sradius")
+ ST_EGALMIX(st) = clgetr ("egalmix")
+ ST_ABSORPTION(st) = clgetr ("absorption")
+
+ # Get the spatial density and luminosity function sampling parameters.
+ seed = clktime (long (0))
+ sseed1 = clgetl ("sseed")
+ if (IS_INDEFL(sseed1))
+ sseed = sseed + seed
+ else
+ sseed = sseed1
+ ST_SSEED(st) = sseed
+ lseed1 = clgetl ("lseed")
+ if (IS_INDEFL(lseed1))
+ lseed = lseed + seed + 1
+ else
+ lseed = lseed1
+ ST_LSEED(st) = lseed
+ ST_NSSAMPLE(st) = clgeti ("nssample")
+ ST_NLSAMPLE(st) = clgeti ("nlsample")
+ ST_SORDER(st) = clgeti ("sorder")
+ ST_LORDER(st) = clgeti ("lorder")
+ ST_RBINSIZE(st) = clgetr ("rbinsize")
+ ST_MBINSIZE(st) = clgetr ("mbinsize")
+ ST_DBINSIZE(st) = clgetr ("dbinsize")
+ ST_EBINSIZE(st) = clgetr ("ebinsize")
+ ST_PBINSIZE(st) = clgetr ("pbinsize")
+
+ x = NULL
+ y = NULL
+ mag = NULL
+ egal = NULL
+ axis = NULL
+ round = NULL
+ phi = NULL
+
+ # Compute the spatial and luminosity functions.
+ call st_gmkspatial (sf, st, x, y, mag, egal, axis, round, phi)
+ call st_gmklum (lf, st, x, y, mag, egal, axis, round, phi)
+ call st_gmkmix (st, x, y, mag, egal, axis, round, phi)
+ call st_gmkaxis (st, x, y, mag, egal, axis, round, phi)
+ call st_gmkround (st, x, y, mag, egal, axis, round, phi)
+ call st_gmkphi (st, x, y, mag, egal, axis, round, phi)
+
+ # Plot the results.
+ if (clgetb ("interactive")) {
+ call clgstr ("graphics", Memc[graphics], SZ_FNAME)
+ if (Memc[graphics] != EOS) {
+ gd = gopen (Memc[graphics], NEW_FILE, STDGRAPH)
+ call st_gplots (sf, lf, gd, st, x, y, mag, egal, axis, round,
+ phi)
+ call gclose (gd)
+ }
+ }
+
+ # Write the database.
+ dt = dtmap (Memc[galaxies], APPEND)
+ call st_dtginit (dt, st, Memc[galaxies], sseed, lseed)
+ call st_dtgwrite (dt, Memr[x], Memr[y], Memr[mag], Memi[egal],
+ Memr[axis], Memr[round], Memr[phi], ST_NSTARS(st))
+ call dtunmap (dt)
+
+ # Free up memory.
+ if (x != NULL)
+ call mfree (x, TY_REAL)
+ if (y != NULL)
+ call mfree (y, TY_REAL)
+ if (mag != NULL)
+ call mfree (mag, TY_REAL)
+ if (egal != NULL)
+ call mfree (egal, TY_INT)
+ if (axis != NULL)
+ call mfree (axis, TY_REAL)
+ if (round != NULL)
+ call mfree (round, TY_REAL)
+ if (phi != NULL)
+ call mfree (phi, TY_REAL)
+ call mfree (st, TY_STRUCT)
+
+ # Close files.
+ if (sf != NULL)
+ call close (sf)
+ if (lf != NULL)
+ call close (lf)
+
+ call sfree (sp)
+end
+
+
+# ST_GMKSPATIAL -- Compute the galactic spatial density function.
+
+procedure st_gmkspatial (sf, st, x, y, mag, egal, axis, round, phi)
+
+int sf # spatial density function file descriptor
+pointer st # pointer to the starlist strucuture
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to the magnitude array
+pointer egal # pointer to the galaxy type array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+int nsf
+pointer r, rprob
+int st_gfetchxy()
+
+begin
+ # Check the sizes of the arrays.
+ call st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+ # Compute the x and y values.
+ switch (ST_SPATIAL(st)) {
+ case ST_UNIFORM:
+ call st_xyuniform (Memr[x], Memr[y], ST_NSTARS(st), ST_XMIN(st),
+ ST_XMAX(st), ST_YMIN(st), ST_YMAX(st), ST_SSEED(st))
+
+ case ST_HUBBLE:
+ call st_hbsample (Memr[x], Memr[y], ST_NSTARS(st), ST_CORE(st),
+ ST_BASE(st), ST_XC(st), ST_YC(st), ST_XMIN(st), ST_XMAX(st),
+ ST_YMIN(st), ST_YMAX(st), ST_NSSAMPLE(st), ST_SORDER(st),
+ ST_SSEED(st))
+
+ case ST_SPFILE:
+ if (sf == NULL) {
+ call printf ("The spatial density file is not open.\n")
+ call amovkr ((ST_XMIN(st) + ST_XMAX(st)) / 2.0, Memr[x],
+ ST_NSTARS(st))
+ call amovkr ((ST_YMIN(st) + ST_YMAX(st)) / 2.0, Memr[y],
+ ST_NSTARS(st))
+ } else {
+ nsf = st_gfetchxy (sf, r, rprob)
+ if (nsf > 0) {
+ call st_sfsample (Memr[r], Memr[rprob], nsf, Memr[x],
+ Memr[y], ST_NSTARS(st), ST_NSSAMPLE(st), ST_SORDER(st),
+ ST_XC(st), ST_YC(st), ST_XMIN(st), ST_XMAX(st),
+ ST_YMIN(st), ST_YMAX(st), ST_SSEED(st))
+ } else {
+ call printf (
+ "The spatial density function file is empty.\n")
+ call amovkr ((ST_XMIN(st) + ST_XMAX(st)) / 2.0, Memr[x],
+ ST_NSTARS(st))
+ call amovkr ((ST_YMIN(st) + ST_YMAX(st)) / 2.0, Memr[y],
+ ST_NSTARS(st))
+ }
+ call mfree (r, TY_REAL)
+ call mfree (rprob, TY_REAL)
+ }
+
+ default:
+ call printf ("Unknown spatial density function.\n")
+ }
+end
+
+
+# ST_GMKLUM -- Compute the luminosity function and the diameter distribution
+# function.
+
+procedure st_gmklum (lf, st, x, y, mag, egal, axis, round, phi)
+
+int lf # luminsosity function file descriptor
+pointer st # pointer to starlist structure
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to magnitude array
+pointer egal # pointer to the galaxy type array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+int nlf
+pointer m, mprob
+int st_gfetchxy()
+
+begin
+ # Check the sizes of the arrays.
+ call st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+ # Compute the luminosity function.
+ switch (ST_LUMINOSITY(st)) {
+ case ST_UNIFORM:
+ call st_maguniform (Memr[mag], ST_NSTARS(st), ST_MINMAG(st),
+ ST_MAXMAG(st), ST_LSEED(st))
+
+ case ST_POWLAW:
+ call st_power (Memr[mag], ST_NSTARS(st), ST_POWER(st),
+ ST_MINMAG(st), ST_MAXMAG(st), ST_LSEED(st))
+
+ case ST_SCHECTER:
+ call st_schecter (Memr[mag], ST_NSTARS(st), ST_ALPHA(st),
+ ST_MSTAR(st), ST_MINMAG(st), ST_MAXMAG(st), ST_MZERO(st),
+ ST_NLSAMPLE(st), ST_LORDER(st), ST_LSEED(st))
+
+ case ST_LFFILE:
+ if (lf == NULL) {
+ call printf ("The luminosity function file is not open.\n")
+ call amovkr ((ST_MINMAG(st) + ST_MAXMAG(st)) / 2.0, Memr[mag],
+ ST_NSTARS(st))
+ } else {
+ nlf = st_gfetchxy (lf, m, mprob)
+ if (nlf > 0) {
+ call st_lfsample (Memr[m], Memr[mprob], nlf, Memr[mag],
+ ST_NSTARS(st), ST_MINMAG(st), ST_MAXMAG(st),
+ ST_NLSAMPLE(st), ST_LORDER(st), ST_LSEED(st))
+ } else {
+ call printf (
+ "The luminosity function file is empty.\n")
+ call amovkr ((ST_MINMAG(st) + ST_MAXMAG(st)) / 2.0,
+ Memr[mag], ST_NSTARS(st))
+ }
+ call mfree (m, TY_REAL)
+ call mfree (mprob, TY_REAL)
+ }
+
+ default:
+ call printf ("The luminosity function is unknown.\n")
+ }
+end
+
+
+# ST_GMKMIX -- Compute the percentage of elliptical versus spiral galaxies.
+
+procedure st_gmkmix (st, x, y, mag, egal, axis, round, phi)
+
+pointer st # pointer to the starlist structure
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to magnitude array
+pointer egal # pointer to the galaxy type array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+begin
+ # Check the sizes of the arrays.
+ call st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+ # Compute the elliptical / spiral galaxy mix.
+ call st_esmix (Memi[egal], ST_NSTARS(st), ST_EGALMIX(st), ST_SSEED(st))
+end
+
+
+# ST_GMKROUND -- Compute the roundness values.
+
+procedure st_gmkround (st, x, y, mag, egal, axis, round, phi)
+
+pointer st # pointer to the starlist structure
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to magnitude array
+pointer egal # pointer to galaxy type array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+begin
+ # Check the sizes of the arrays.
+ call st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+ # Compute the roundness values.
+ call st_round (Memi[egal], Memr[mag], Memr[round], ST_NSTARS(st),
+ ST_AR(st), ST_ABSORPTION(st), ST_SSEED(st))
+end
+
+
+# ST_GMKPHI -- Compute the position angles.
+
+procedure st_gmkphi (st, x, y, mag, egal, axis, round, phi)
+
+pointer st # pointer to the starlist structure
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to mag array
+pointer egal # pointer to the galaxy type array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+begin
+ # Check the sizes of the arrays.
+ call st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+ # Compute the position angles.
+ call st_phi (Memr[phi], ST_NSTARS(st), ST_SSEED(st))
+end
+
+
+# ST_GMKAXIS -- Compute the semi-major axes.
+
+procedure st_gmkaxis (st, x, y, mag, egal, axis, round, phi)
+
+pointer st # pointer to the starlist structure
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to magnitude array
+pointer egal # pointer to E or S galaxy array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+begin
+ # Check the sizes of the arrays.
+ call st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+ # Make the effective diameters array.
+ switch (ST_LUMINOSITY(st)) {
+ case ST_POWLAW:
+ call st_zdiameters (Memr[mag], Memi[egal], Memr[axis],
+ ST_NSTARS(st), ST_MINMAG(st), ST_MAXMAG(st), ST_Z(st),
+ ST_ERADIUS(st), ST_SRADIUS(st), ST_LSEED(st))
+ default:
+ call st_diameters (Memr[mag], Memi[egal], Memr[axis],
+ ST_NSTARS(st), ST_MINMAG(st), ST_MAXMAG(st),
+ ST_ERADIUS(st), ST_SRADIUS(st), ST_LSEED(st))
+ }
+end
+
+
+# ST_GMALLOC -- Allocate array space for the gallist task.
+
+procedure st_gmalloc (st, x, y, mag, egal, axis, round, phi)
+
+pointer st # pointer to the starlist structure
+pointer x # pointer to the x array
+pointer y # pointer to the y array
+pointer mag # pointer to magnitude array
+pointer egal # pointer to galaxy type array
+pointer axis # pointer to half-power diameter array
+pointer round # pointer to roundness array
+pointer phi # pointer to an array of position angles
+
+begin
+ if (x == NULL)
+ call malloc (x, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (x, ST_NSTARS(st), TY_REAL)
+ if (y == NULL)
+ call malloc (y, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (y, ST_NSTARS(st), TY_REAL)
+ if (mag == NULL)
+ call malloc (mag, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (mag, ST_NSTARS(st), TY_REAL)
+ if (egal == NULL)
+ call malloc (egal, ST_NSTARS(st), TY_INT)
+ else
+ call realloc (egal, ST_NSTARS(st), TY_INT)
+ if (axis == NULL)
+ call malloc (axis, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (axis, ST_NSTARS(st), TY_REAL)
+ if (round == NULL)
+ call malloc (round, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (round, ST_NSTARS(st), TY_REAL)
+ if (phi == NULL)
+ call malloc (phi, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (phi, ST_NSTARS(st), TY_REAL)
+end
diff --git a/noao/artdata/lists/t_starlist.x b/noao/artdata/lists/t_starlist.x
new file mode 100644
index 00000000..f110a27c
--- /dev/null
+++ b/noao/artdata/lists/t_starlist.x
@@ -0,0 +1,303 @@
+include <fset.h>
+include "starlist.h"
+
+procedure t_starlist ()
+
+pointer starlist # pointer to the name of the output file
+pointer graphics # pointer to the graphics device name
+
+int sf, lf
+long seed, sseed, lseed
+long sseed1, lseed1
+pointer sp, str, x, y, mag, dt, st, gd
+
+bool clgetb()
+int clgeti(), clgwrd(), open()
+long clgetl(), clktime()
+pointer dtmap(), gopen()
+real clgetr()
+
+begin
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (starlist, SZ_FNAME, TY_CHAR)
+ call salloc (graphics, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Allocate the starlist structure.
+ call malloc (st, LEN_STSTRUCT, TY_STRUCT)
+ ST_TYPE(st) = ST_STARS
+
+ # Get the principal parameters.
+ call clgstr ("starlist", Memc[starlist], SZ_FNAME)
+ ST_NSTARS(st) = clgeti ("nstars")
+
+ # Get the charactersitics of the spatial density function.
+ ST_SPATIAL(st) = clgwrd ("spatial", ST_SPSTRING(st), SZ_FNAME, SPFUNCS)
+ ST_XMIN(st) = clgetr ("xmin")
+ ST_XMAX(st) = clgetr ("xmax")
+ ST_YMIN(st) = clgetr ("ymin")
+ ST_YMAX(st) = clgetr ("ymax")
+ ST_SFILE(st) = EOS
+ switch (ST_SPATIAL(st)) {
+ case ST_UNIFORM:
+ sf = NULL
+ ST_XC(st) = (ST_XMAX(st) + ST_XMIN(st)) / 2.0
+ ST_YC(st) = (ST_YMAX(st) + ST_YMIN(st)) / 2.0
+ case ST_HUBBLE:
+ sf = NULL
+ ST_XC(st) = clgetr ("xcenter")
+ if (IS_INDEFR(ST_XC(st)))
+ ST_XC(st) = (ST_XMAX(st) + ST_XMIN(st)) / 2.0
+ ST_YC(st) = clgetr ("ycenter")
+ if (IS_INDEFR(ST_YC(st)))
+ ST_YC(st) = (ST_YMAX(st) + ST_YMIN(st)) / 2.0
+ case ST_SPFILE:
+ call clgstr ("sfile", ST_SFILE(st), SZ_FNAME)
+ sf = open (ST_SFILE(st), READ_ONLY, TEXT_FILE)
+ ST_XC(st) = clgetr ("xcenter")
+ if (IS_INDEFR(ST_XC(st)))
+ ST_XC(st) = (ST_XMAX(st) + ST_XMIN(st)) / 2.0
+ ST_YC(st) = clgetr ("ycenter")
+ if (IS_INDEFR(ST_YC(st)))
+ ST_YC(st) = (ST_YMAX(st) + ST_YMIN(st)) / 2.0
+ }
+ ST_CORE(st) = clgetr ("core_radius")
+ ST_BASE(st) = clgetr ("base")
+
+
+ # Get the luminosity function parameters.
+ ST_LUMINOSITY(st) = clgwrd ("luminosity", ST_LFSTRING(st), SZ_FNAME,
+ LUMFUNCS)
+ ST_MINMAG(st) = clgetr ("minmag")
+ ST_MAXMAG(st) = clgetr ("maxmag")
+ ST_LFILE(st) = EOS
+ switch (ST_LUMINOSITY(st)) {
+ case ST_UNIFORM, ST_SALPETER, ST_BANDS, ST_POWLAW:
+ lf = NULL
+ case ST_LFFILE:
+ call clgstr ("lfile", ST_LFILE(st), SZ_FNAME)
+ lf = open (ST_LFILE(st), READ_ONLY, TEXT_FILE)
+ }
+ ST_POWER(st) = clgetr ("power")
+ ST_MZERO(st) = clgetr ("mzero")
+ ST_ALPHA(st) = clgetr ("alpha")
+ ST_BETA(st) = clgetr ("beta")
+ ST_DELTA(st) = clgetr ("delta")
+ ST_MSTAR(st) = clgetr ("mstar")
+
+ # Get the function sampling parameters.
+ seed = clktime (long (0))
+ sseed1 = clgetl ("sseed")
+ if (IS_INDEFL(sseed1))
+ sseed = sseed + seed
+ else
+ sseed = sseed1
+ ST_SSEED(st) = sseed
+ lseed1 = clgetl ("lseed")
+ if (IS_INDEFL(lseed1))
+ lseed = lseed + seed + 1
+ else
+ lseed = lseed1
+ ST_LSEED(st) = lseed
+ ST_NSSAMPLE(st) = clgeti ("nssample")
+ ST_NLSAMPLE(st) = clgeti ("nlsample")
+ ST_SORDER(st) = clgeti ("sorder")
+ ST_LORDER(st) = clgeti ("lorder")
+
+
+ # Compute the initial spatial and luminosity functions.
+ x = NULL
+ y = NULL
+ mag = NULL
+ call st_mkspatial (sf, st, x, y, mag)
+ call st_mklum (lf, st, x, y, mag)
+
+ # Plot the results.
+ if (clgetb ("interactive")) {
+ call clgstr ("graphics", Memc[graphics], SZ_FNAME)
+ ST_RBINSIZE(st) = clgetr ("rbinsize")
+ ST_MBINSIZE(st) = clgetr ("mbinsize")
+ if (Memc[graphics] != EOS) {
+ gd = gopen (Memc[graphics], NEW_FILE, STDGRAPH)
+ call st_plots (sf, lf, gd, st, x, y, mag)
+ call gclose (gd)
+ }
+ }
+
+ # Write the database.
+ dt = dtmap (Memc[starlist], APPEND)
+ call st_dtinit (dt, st, Memc[starlist], sseed, lseed)
+ call st_dtwrite (dt, Memr[x], Memr[y], Memr[mag], ST_NSTARS(st))
+ call dtunmap (dt)
+
+ # Free up memory.
+ if (x != NULL)
+ call mfree (x, TY_REAL)
+ if (y != NULL)
+ call mfree (y, TY_REAL)
+ if (mag != NULL)
+ call mfree (mag, TY_REAL)
+ call mfree (st, TY_STRUCT)
+
+ # Close files.
+ if (sf != NULL)
+ call close (sf)
+ if (lf != NULL)
+ call close (lf)
+
+ call sfree (sp)
+end
+
+
+# ST_MKSPATIAL -- Compute the spatial density function.
+
+procedure st_mkspatial (sf, st, x, y, mag)
+
+int sf # spatial function file descriptor
+pointer st # pointer to the starlist structure
+pointer x # pointer to the x coordinate array
+pointer y # pointer to the y coordinate array
+pointer mag # pointer to the magnitude array
+
+int nsf
+pointer r, rprob
+int st_gfetchxy()
+
+begin
+ # Dynamically reallocate the x, y and magnitude arrays.
+ if (x == NULL)
+ call malloc (x, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (x, ST_NSTARS(st), TY_REAL)
+ if (y == NULL)
+ call malloc (y, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (y, ST_NSTARS(st), TY_REAL)
+ if (mag == NULL)
+ call malloc (mag, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (mag, ST_NSTARS(st), TY_REAL)
+
+ # Compute the x and y values.
+ switch (ST_SPATIAL(st)) {
+ case ST_UNIFORM:
+ call st_xyuniform (Memr[x], Memr[y], ST_NSTARS(st), ST_XMIN(st),
+ ST_XMAX(st), ST_YMIN(st), ST_YMAX(st), ST_SSEED(st))
+
+ case ST_HUBBLE:
+ call st_hbsample (Memr[x], Memr[y], ST_NSTARS(st), ST_CORE(st),
+ ST_BASE(st), ST_XC(st), ST_YC(st), ST_XMIN(st), ST_XMAX(st),
+ ST_YMIN(st), ST_YMAX(st), ST_NSSAMPLE(st), ST_SORDER(st),
+ ST_SSEED(st))
+
+ case ST_SPFILE:
+ if (sf == NULL) {
+ call printf ("The spatial density file is not open.\n")
+ call amovkr ((ST_XMIN(st) + ST_XMAX(st)) / 2.0, Memr[x],
+ ST_NSTARS(st))
+ call amovkr ((ST_YMIN(st) + ST_YMAX(st)) / 2.0, Memr[y],
+ ST_NSTARS(st))
+ } else {
+ nsf = st_gfetchxy (sf, r, rprob)
+ if (nsf > 0) {
+ call st_sfsample (Memr[r], Memr[rprob], nsf, Memr[x],
+ Memr[y], ST_NSTARS(st), ST_NSSAMPLE(st), ST_SORDER(st),
+ ST_XC(st), ST_YC(st), ST_XMIN(st), ST_XMAX(st),
+ ST_YMIN(st), ST_YMAX(st), ST_SSEED(st))
+ } else {
+ call printf (
+ "The spatial density function file is empty.\n")
+ call amovkr ((ST_XMIN(st) + ST_XMAX(st)) / 2.0, Memr[x],
+ ST_NSTARS(st))
+ call amovkr ((ST_YMIN(st) + ST_YMAX(st)) / 2.0, Memr[y],
+ ST_NSTARS(st))
+ }
+ call mfree (r, TY_REAL)
+ call mfree (rprob, TY_REAL)
+ }
+
+ default:
+ call printf ("Unknown spatial density function.\n")
+ }
+end
+
+
+# ST_MKLUM -- Compute the luminosity function.
+
+procedure st_mklum (lf, st, x, y, mag)
+
+int lf # luminsosity function file descriptor
+pointer st # pointer to starlist structure
+pointer x # pointer to the x coordinate array
+pointer y # pointer to the y coordinate array
+pointer mag # pointer to the magnitude array
+
+int nlf
+pointer m, mprob
+int st_gfetchxy()
+
+begin
+ # Dynamically reallocate the array space.
+ if (x == NULL)
+ call malloc (x, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (x, ST_NSTARS(st), TY_REAL)
+ if (y == NULL)
+ call malloc (y, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (y, ST_NSTARS(st), TY_REAL)
+ if (mag == NULL)
+ call malloc (mag, ST_NSTARS(st), TY_REAL)
+ else
+ call realloc (mag, ST_NSTARS(st), TY_REAL)
+
+ # Compute the magnitudes.
+ switch (ST_LUMINOSITY(st)) {
+ case ST_UNIFORM:
+ call st_maguniform (Memr[mag], ST_NSTARS(st), ST_MINMAG(st),
+ ST_MAXMAG(st), ST_LSEED(st))
+
+ case ST_POWLAW:
+ call st_power (Memr[mag], ST_NSTARS(st), ST_POWER(st),
+ ST_MINMAG(st), ST_MAXMAG(st), ST_LSEED(st))
+
+ case ST_SALPETER:
+ call st_salpeter (Memr[mag], ST_NSTARS(st),
+ ST_MINMAG(st), ST_MAXMAG(st), ST_MZERO(st),
+ ST_NLSAMPLE(st), ST_LORDER(st), ST_LSEED(st))
+
+ case ST_BANDS:
+ call st_bands (Memr[mag], ST_NSTARS(st), ST_ALPHA(st),
+ ST_BETA(st), ST_DELTA(st), ST_MSTAR(st),
+ ST_MINMAG(st), ST_MAXMAG(st), ST_MZERO(st),
+ ST_NLSAMPLE(st), ST_LORDER(st), ST_LSEED(st))
+
+ case ST_LFFILE:
+ if (lf == NULL) {
+ call printf ("The luminosity function file is not open.\n")
+ call amovkr ((ST_MINMAG(st) + ST_MAXMAG(st)) / 2.0, Memr[mag],
+ ST_NSTARS(st))
+ } else {
+ nlf = st_gfetchxy (lf, m, mprob)
+ if (nlf > 0) {
+ call st_lfsample (Memr[m], Memr[mprob], nlf, Memr[mag],
+ ST_NSTARS(st),
+ ST_MINMAG(st), ST_MAXMAG(st),
+ ST_NLSAMPLE(st), ST_LORDER(st), ST_LSEED(st))
+ } else {
+ call printf (
+ "The luminosity function file is empty.\n")
+ call amovkr ((ST_MINMAG(st) + ST_MAXMAG(st)) / 2.0,
+ Memr[mag], ST_NSTARS(st))
+ }
+ call mfree (m, TY_REAL)
+ call mfree (mprob, TY_REAL)
+ }
+
+ default:
+ call printf ("The luminosity function is unknown.\n")
+ }
+end
diff --git a/noao/artdata/mk1dspec.par b/noao/artdata/mk1dspec.par
new file mode 100644
index 00000000..3faed74b
--- /dev/null
+++ b/noao/artdata/mk1dspec.par
@@ -0,0 +1,31 @@
+input,s,a,,,,"List of input spectra"
+output,s,h,"",,,"List of output spectra"
+ap,i,h,1,1,,"Image line"
+rv,r,h,0.,,,"Radial velocity (km/s) or redshift"
+z,b,h,no,,,"Is velocity a redshift?
+
+IF NEW SPECTRUM"
+title,s,h,"",,,"Title of spectrum"
+ncols,i,h,512,1,,"Number of columns"
+naps,i,h,1,1,,"Number of lines (apertures)"
+header,f,h,"artdata$stdheader.dat",,,"Image or header keyword file"
+wstart,r,h,4000.,,,"Starting wavelength (Angstroms)"
+wend,r,h,8000.,,,"Ending wavelength (Angstroms)
+
+CONTINUUM"
+continuum,r,h,1000.,,,"Continuum at first pixel"
+slope,r,h,0.,,,"Continuum slope per pixel"
+temperature,r,h,5700.,0.,,"Blackbody temperture (Kelvin)"
+fnu,b,h,no,,,"F-nu or F-lamda?
+
+LINES"
+lines,s,h,"",,,"List of files containing lines"
+nlines,i,h,0,0,,"Number of random lines (if new line list)"
+profile,s,h,"gaussian",|gaussian|lorentzian|voigt,,"Default profile type"
+peak,r,h,-0.5,,,"Default peak strength (relative to continuum)"
+gfwhm,r,h,20.,,,"Default Gaussian FWHM (Angstroms)"
+lfwhm,r,h,20.,,,"Default Lorentzian FWHM (Angstroms)"
+seed,i,h,1,,,"Random number seed
+"
+comments,b,h,yes,,,"Add comments to image?"
+mode,s,h,"ql",,,
diff --git a/noao/artdata/mk2dspec.par b/noao/artdata/mk2dspec.par
new file mode 100644
index 00000000..df78e38a
--- /dev/null
+++ b/noao/artdata/mk2dspec.par
@@ -0,0 +1,10 @@
+input,f,a,"",,,"Input image list"
+output,f,h,"",,,"Input image list"
+models,f,h,"",,,"List of model parameter files"
+comments,b,h,yes,,,"Add comments to image?
+
+IF NEW IMAGE"
+title,s,h,"",,,"Title of spectrum"
+ncols,i,h,100,1,,"Number of columns"
+nlines,i,h,512,1,,"Number of lines"
+header,f,h,"artdata$stdheader.dat",,,"Image or header keyword file"
diff --git a/noao/artdata/mkechelle.par b/noao/artdata/mkechelle.par
new file mode 100644
index 00000000..e312203c
--- /dev/null
+++ b/noao/artdata/mkechelle.par
@@ -0,0 +1,48 @@
+images,s,a,,,,"List of echelle spectra to be created"
+clobber,b,q,no,,,"Modify existing image?"
+ncols,i,h,512,1,,"Number of columns (across dispersion)"
+nlines,i,h,512,1,,"Number of lines (along dispersion)"
+norders,i,h,23,1,,"Number of orders"
+title,s,h,"Artificial Echelle Spectrum",,,"Title of spectrum"
+header,f,h,"artdata$stdheader.dat",,,"Image or header keyword file"
+list,b,h,no,,,"List grating parameters?"
+make,b,h,yes,,,"Make spectra?"
+comments,b,h,yes,,,"Add comments to image?
+
+FORMAT PARAMETERS"
+xc,r,h,INDEF,,,"Blaze peak column (pixel)"
+yc,r,h,INDEF,,,"Blaze peak line (pixel)"
+pixsize,r,h,0.027,,,"Pixel size (mm)"
+profile,s,h,"gaussian","extracted|gaussian|slit",,"Type of order profile"
+width,r,h,5.,0.,,"Width of orders (pixels)"
+scattered,r,h,0.,0.,,"Scattered light peak flux
+
+ECHELLE GRATING PARAMETERS"
+f,r,h,590.,,,"Focal length (mm|pixel)"
+gmm,r,h,31.6,,,"Grooves per mm"
+blaze,r,h,63.,,,"Blaze angle (degrees)"
+theta,r,h,69.,,,"Incidence angle (degrees)"
+order,i,h,112,,,"Reference order"
+wavelength,r,h,5007.49,,,"Blaze wavelength (Angstroms)"
+dispersion,r,h,2.61,,,"Blaze dispersion (Angstroms/(mm|pixel))
+
+CROSSDISPERSER PARAMETERS"
+cf,r,h,590.,,,"Focal length (mm|pixel)"
+cgmm,r,h,226.,,,"Grooves per mm"
+cblaze,r,h,4.53,,,"Blaze angle (degrees)"
+ctheta,r,h,-11.97,,,"Incidence angle (degrees)"
+corder,i,h,1,,,"Order (0 = prism)"
+cwavelength,r,h,6700.,,,"Blaze wavelength (Angstroms)"
+cdispersion,r,h,70.,,,"Blaze dispersion (Angstroms/(mm|pixel))
+
+SPECTRA PARAMETERS"
+rv,r,h,0.,,,"Radial velocity (km/s) or redshift"
+z,b,h,no,,,"Is velocity a redshift?"
+continuum,r,h,1000.,,,"Continuum at reference wavelength"
+temperature,r,h,5700,0.,,"Blackbody temperture (Kelvin)
+"
+lines,s,h,"",,,"List of files containing lines"
+nrandom,i,h,0,0,,"Number of random spectral lines"
+peak,r,h,-0.5,,,"Peak of lines (relative to continuum)"
+sigma,r,h,1.,,,"Sigma of lines (Angstroms)"
+seed,i,h,1,,,"Random number seed"
diff --git a/noao/artdata/mkexamples/archdr.dat b/noao/artdata/mkexamples/archdr.dat
new file mode 100644
index 00000000..b52dcc32
--- /dev/null
+++ b/noao/artdata/mkexamples/archdr.dat
@@ -0,0 +1,19 @@
+OBSERVER= 'IRAF ' / observers
+OBSERVAT= 'KPNO ' / observatory
+INSTRUME= 'IRAF ARTDATA ' / instrument
+TELESCOP= 'kpcdf ' / telescope name
+DETECTOR= 'te1k ' / detector
+EXPTIME = 60. / actual integration time
+DARKTIME= 60. / total elapsed time
+IMAGETYP= 'comp ' / object, dark, bias, etc.
+DATE-OBS= '1991-11-26T12:11:30.00' / date (dd/mm/yy) of obs.
+UT = '12:11:30.00 ' / universal time
+ST = '09:04:54.00 ' / sidereal time
+RA = '06:37:02.00 ' / right ascension
+DEC = '06:09:03.00 ' / declination
+EPOCH = 1991.9 / epoch of ra and dec
+ZD = '48.760 ' / zenith distance
+AIRMASS = 0. / airmass
+APERTURE= '250micron slit ' / aperture
+GRATPOS = 4624.3 / grating position
+CCDPROC = 'Nov 26 5:44 CCD processing done'
diff --git a/noao/artdata/mkexamples/ecarc.cl b/noao/artdata/mkexamples/ecarc.cl
new file mode 100644
index 00000000..174eba2f
--- /dev/null
+++ b/noao/artdata/mkexamples/ecarc.cl
@@ -0,0 +1,20 @@
+# ecarc - Echelle extracted thorium arc (calibrated)
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$archdr.dat"
+i = 10
+
+mkechelle (out, yes, ncols=512, nlines=512, norders=i,
+ title="Artificial Echelle Spectrum", header=hdr,
+ list=no, make=yes, comments=b1, xc=235.5, yc=INDEF, pixsize=0.027,
+ profile="extracted", width=20., scattered=0., f=590., gmm=31.6, blaze=63.,
+ theta=69., order=112, wavelength=5007.49, dispersion=2.61, cf=590.,
+ cgmm=226., cblaze=4.53, ctheta=-11.97, corder=1, cwavelength=6700.,
+ cdispersion=70., rv=0., z=no, continuum=20., temperature=0.,
+ lines="mkexamples$ecthorium.dat", sigma=0.05, >& "dev$null")
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/ecarc2d.cl b/noao/artdata/mkexamples/ecarc2d.cl
new file mode 100644
index 00000000..8261e84a
--- /dev/null
+++ b/noao/artdata/mkexamples/ecarc2d.cl
@@ -0,0 +1,20 @@
+# ecarc2d - Echelle thorium-argon slit spectrum
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$archdr.dat"
+i = 10
+
+mkechelle (out, yes, ncols=512, nlines=512, norders=i,
+ title="Artificial Echelle Spectrum", header=hdr,
+ list=no, make=yes, comments=b1, xc=235.5, yc=INDEF, pixsize=0.027,
+ profile="slit", width=20., scattered=10., f=590., gmm=31.6, blaze=63.,
+ theta=69., order=112, wavelength=5007.49, dispersion=2.61, cf=590.,
+ cgmm=226., cblaze=4.53, ctheta=-11.97, corder=1, cwavelength=6700.,
+ cdispersion=70., rv=0., z=no, continuum=20., temperature=0.,
+ lines="mkexamples$ecthorium.dat", sigma=0.05, >& "dev$null")
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/ecarcdc.cl b/noao/artdata/mkexamples/ecarcdc.cl
new file mode 100644
index 00000000..d188d52f
--- /dev/null
+++ b/noao/artdata/mkexamples/ecarcdc.cl
@@ -0,0 +1,21 @@
+# ecarcdc - Echelle thorium-argon arc (calibrated)
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$archdr.dat"
+i = 10
+
+mkechelle (out, yes, ncols=512, nlines=512, norders=i,
+ title="Artificial Echelle Spectrum", header=hdr,
+ list=no, make=yes, comments=b1, xc=235.5, yc=INDEF, pixsize=0.027,
+ profile="extracted", width=20., scattered=0., f=590., gmm=INDEF,
+ blaze=INDEF, theta=INDEF, order=112, wavelength=5007.49,
+ dispersion=2.61, cf=590., cgmm=226., cblaze=4.53, ctheta=-11.97,
+ corder=1, cwavelength=6700., cdispersion=70., rv=0., z=no,
+ continuum=20., temperature=0., lines="mkexamples$ecthorium.dat",
+ sigma=0.05, >& "dev$null")
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/echelle.cl b/noao/artdata/mkexamples/echelle.cl
new file mode 100644
index 00000000..4e6dc197
--- /dev/null
+++ b/noao/artdata/mkexamples/echelle.cl
@@ -0,0 +1,22 @@
+# echelle - Echelle absorption spectrum (calibrated)
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$objhdr.dat"
+i = 10
+k = 10 * i
+
+mkechelle (out, yes, ncols=512, nlines=512, norders=i,
+ title="Artificial Echelle Spectrum", header=hdr,
+ list=no, make=yes, comments=b1, xc=235.5, yc=INDEF, pixsize=0.027,
+ profile="extracted", width=20., scattered=0., f=590., gmm=INDEF,
+ blaze=INDEF, theta=INDEF, order=112, wavelength=5007.49,
+ dispersion=2.61, cf=590., cgmm=226., cblaze=4.53, ctheta=-11.97,
+ corder=1, cwavelength=6700., cdispersion=70., rv=0., z=no,
+ continuum=1000., temperature=5700., lines="", nrandom=k, peak=-0.5,
+ sigma=0.5, seed=i, >& "dev$null")
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/ecobj2d.cl b/noao/artdata/mkexamples/ecobj2d.cl
new file mode 100644
index 00000000..303dbb69
--- /dev/null
+++ b/noao/artdata/mkexamples/ecobj2d.cl
@@ -0,0 +1,29 @@
+# ecobj2d - Echelle object slit spectrum
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$objhdr.dat"
+i = 10
+
+mkechelle (out, yes, ncols=512, nlines=512, norders=i,
+ title="Artificial Echelle Spectrum", header=hdr,
+ list=no, make=yes, comments=b1, xc=235.5, yc=INDEF, pixsize=0.027,
+ profile="gaussian", width=4., scattered=25., f=590., gmm=31.6, blaze=63.,
+ theta=69., order=112, wavelength=5007.49, dispersion=2.61, cf=590.,
+ cgmm=226., cblaze=4.53, ctheta=-11.97, corder=1, cwavelength=6700.,
+ cdispersion=70., rv=0., z=no, continuum=500., temperature=7700.,
+ lines="", nrandom=500, peak=-0.5, sigma=0.5, seed=i, >& "dev$null")
+
+mkechelle (out, yes, ncols=512, nlines=512, norders=i,
+ title="Artificial Echelle Spectrum", header="",
+ list=no, make=yes, comments=b1, xc=235.5, yc=INDEF, pixsize=0.027,
+ profile="slit", width=20., scattered=10., f=590., gmm=31.6, blaze=63.,
+ theta=69., order=112, wavelength=5007.49, dispersion=2.61, cf=590.,
+ cgmm=226., cblaze=4.53, ctheta=-11.97, corder=1, cwavelength=6700.,
+ cdispersion=70., rv=0., z=no, continuum=200., temperature=5700.,
+ lines="", nrandom=20, peak=5.0, sigma=0.1, seed=i+1, >& "dev$null")
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/ecthorium.dat b/noao/artdata/mkexamples/ecthorium.dat
new file mode 100644
index 00000000..2624bca7
--- /dev/null
+++ b/noao/artdata/mkexamples/ecthorium.dat
@@ -0,0 +1,655 @@
+4588.4273 65.5462
+4589.8978 784.211
+4592.666 56.8004
+4593.6437 4.85515
+4595.4206 222.271
+4596.0967 64.2832
+4596.3078 7.56086
+4598.7621 119.88
+4603.1444 15.7948
+4607.9347 11.4279
+4608.6218 11.6267
+4609.372 7.02327
+4609.5673 1221.28
+4611.2433 5.10087
+4612.5435 4.90891
+4613.6044 17.7617
+4615.024 10.0449
+4615.3351 17.7089
+4619.4794 6.41386
+4621.1629 18.3723
+4624.314 6.47323
+4627.2973 9.49829
+4628.1991 16.5995
+4628.4409 35.8668
+4631.6382 7.29758
+4633.7628 18.198
+4637.233 85.9085
+4637.629 3.94716
+4638.6849 26.9969
+4640.0462 6.61454
+4641.2418 8.27625
+4644.7072 4.26447
+4646.6861 10.9471
+4647.2546 26.3496
+4650.2343 17.455
+4650.9195 4.13841
+4651.9895 6.17074
+4655.2088 5.39692
+4657.9041 836.603
+4659.5698 5.48127
+4663.2026 61.9759
+4666.0027 16.1667
+4666.5155 40.8868
+4666.7985 47.1163
+4668.172 81.6867
+4669.9853 117.237
+4673.0535 5.4883
+4673.6621 545.703
+4676.0555 82.2877
+4676.133 4.29782
+4680.2377 5.75225
+4680.646 5.52363
+4683.353 30.5339
+4686.1946 153.856
+4689.2523 41.9807
+4690.6219 49.3453
+4691.3452 8.5765
+4691.6354 46.0446
+4694.0914 54.4282
+4694.9323 12.2334
+4695.0381 168.923
+4695.4542 13.1503
+4700.7837 4.93863
+4702.3161 93.1766
+4703.3581 10.2278
+4703.9898 817.83
+4705.7606 21.5926
+4706.2383 3.97915
+4708.294 31.0609
+4708.5366 9.58955
+4712.4814 53.4481
+4712.8408 15.1739
+4715.4308 4.88338
+4719.9888 4.53178
+4720.4586 13.3998
+4721.2765 33.1405
+4721.591 22.633
+4722.0886 44.4139
+4723.4398 734.51
+4723.784 64.7118
+4726.8705 1064.39
+4728.133 6.98479
+4729.1293 92.2413
+4729.8795 7.27099
+4730.6614 5.23277
+4732.0532 180.301
+4735.9079 506.782
+4739.6764 45.7522
+4740.5292 63.9671
+4740.9585 48.6428
+4741.2967 14.9477
+4742.1174 17.1227
+4742.5535 5.73107
+4743.6923 23.7768
+4745.3322 16.1008
+4746.8239 3.38238
+4748.04 4.84055
+4749.2002 121.145
+4749.7935 9.05726
+4749.9713 47.3657
+4752.4141 99.979
+4757.2196 10.6699
+4761.1101 8.57456
+4764.3463 78.5935
+4764.8646 2661.86
+4765.5959 28.4925
+4766.6006 135.507
+4768.6814 12.8589
+4773.241 13.2449
+4774.2607 35.9558
+4775.313 14.2325
+4775.794 17.3488
+4777.1933 14.2463
+4778.297 563.167
+4779.7306 15.7418
+4780.422 10.1095
+4780.7505 8.35713
+4781.526 6.25546
+4782.7608 11.4082
+4783.8617 23.7332
+4784.0396 9.64609
+4786.1434 3.9864
+4786.531 31.3448
+4787.1476 53.7384
+4789.3868 271.327
+4792.0819 9.75035
+4793.2446 20.983
+4795.9131 29.3319
+4800.1741 10.6862
+4805.6064 6.48416
+4806.0205 1231.64
+4808.1351 498.92
+4809.6161 150.796
+4812.3755 16.1879
+4813.0075 18.7566
+4813.7204 51.2083
+4813.8963 88.0909
+4817.0206 7.78436
+4818.6458 7.23017
+4819.193 19.4464
+4819.5674 3.51181
+4820.4649 13.8117
+4820.8847 93.4916
+4821.2631 4.19723
+4821.385 7.22092
+4821.5878 111.817
+4821.8592 4.93727
+4822.8548 294.935
+4823.1692 8.09662
+4823.6058 110.695
+4823.9967 56.2398
+4826.7004 149.96
+4829.7973 18.3661
+4831.1225 423.617
+4831.5975 126.894
+4832.8029 71.37
+4833.8317 4.28069
+4840.4744 26.8947
+4840.8492 521.946
+4843.9395 65.1344
+4844.1653 12.8414
+4845.1626 32.4845
+4847.3208 7.04908
+4847.8095 577.432
+4847.9985 17.7145
+4848.3646 300.478
+4849.1429 27.1511
+4849.8617 28.7808
+4850.4408 55.1639
+4852.8696 40.0447
+4858.3344 15.3578
+4861.2167 44.0273
+4861.7173 34.9526
+4863.1724 227.657
+4865.4775 441.647
+4865.91 91.5246
+4867.5558 12.5734
+4868.5254 4.98434
+4868.8814 47.874
+4871.289 48.0372
+4872.0309 6.36617
+4872.9169 292.235
+4874.3662 116.992
+4876.2611 46.5136
+4876.4923 19.5098
+4877.0035 8.01475
+4877.8121 13.1595
+4878.0106 127.864
+4878.7344 565.658
+4879.3501 9.17893
+4879.8635 2699.79
+4880.8509 7.95572
+4881.2046 54.3629
+4881.8531 10.9968
+4882.2455 36.8137
+4887.9516 42.1399
+4889.0422 412.134
+4889.4903 19.0889
+4890.4582 20.1678
+4892.7592 24.1195
+4893.4436 15.9623
+4894.6906 16.6734
+4894.957 1132.26
+4898.4563 4.74024
+4898.8044 4.04661
+4899.2441 20.2471
+4902.0556 68.2129
+4902.7943 19.836
+4904.7533 127.776
+4907.2093 6.81641
+4909.8328 4.27015
+4910.1576 51.7022
+4910.7929 26.4061
+4911.3787 23.3431
+4912.5293 30.4426
+4919.8157 381.301
+4920.0226 45.5284
+4921.6134 25.5825
+4922.938 7.03937
+4924.4223 10.6983
+4925.9501 11.3703
+4927.298 9.44349
+4927.7803 74.3743
+4929.086 12.2534
+4929.9754 5.72947
+4932.0473 11.9265
+4933.2136 214.685
+4933.8521 14.704
+4936.7746 57.1255
+4937.8295 95.4003
+4938.5071 19.9503
+4939.2703 6.0869
+4939.6422 301.318
+4941.416 5.25372
+4943.0642 168.915
+4945.4614 456.276
+4946.6637 44.9907
+4947.5751 22.4592
+4949.4037 4.26514
+4950.2513 13.8656
+4950.6264 33.1245
+4952.6953 11.0807
+4954.6546 11.0296
+4955.1204 7.72677
+4956.7647 15.5955
+4957.2963 8.85339
+4958.0988 9.11654
+4960.4279 14.3728
+4961.726 24.3599
+4963.1881 7.4831
+4963.7664 6.96135
+4964.1157 8.74783
+4964.2533 4.31933
+4965.0812 1356.08
+4965.7315 71.1095
+4968.7556 21.4262
+4970.0766 30.2119
+4972.1597 95.446
+4973.398 8.93496
+4975.9439 3.991
+4980.1859 57.2975
+4980.9513 11.0146
+4982.4875 99.3208
+4985.3725 251.563
+4985.9469 6.5111
+4987.1505 11.3022
+4987.5582 9.65165
+4989.3086 78.4742
+4989.9557 6.99063
+4992.1257 6.07045
+4992.6372 5.15659
+4993.7488 38.5607
+4994.1058 11.8688
+4997.7993 3.54205
+4999.9398 6.91979
+5000.2516 5.5321
+5002.0972 624.716
+5003.6785 99.5414
+5009.3344 351.003
+5009.9424 7.14161
+5010.9532 6.36927
+5011.4774 4.75069
+5015.8892 19.6963
+5016.5356 11.4415
+5017.1673 698.923
+5017.5084 36.3313
+5017.6235 10.7912
+5018.0568 8.5097
+5019.3336 8.20173
+5019.8062 151.717
+5020.5335 4.04989
+5021.2531 4.14241
+5022.0051 33.1907
+5022.1698 11.5854
+5023.7059 12.9307
+5026.9532 3.96646
+5028.6556 325.768
+5029.0102 9.39148
+5029.6295 5.87679
+5029.8916 84.2817
+5035.3516 2.98555
+5039.2303 360.229
+5039.5265 19.1678
+5040.5393 6.15313
+5040.6805 10.461
+5041.1224 18.8219
+5041.5971 6.75403
+5043.5131 9.80939
+5044.7195 678.079
+5045.2481 89.4546
+5046.3518 13.9731
+5046.6372 8.14425
+5047.0434 191.836
+5048.8139 30.8297
+5048.9354 35.1897
+5049.5543 8.69854
+5049.796 240.343
+5050.7842 203.828
+5051.8887 76.8459
+5054.1776 19.4488
+5055.3473 69.0541
+5056.5358 3.91915
+5057.9866 43.4318
+5059.8611 118.26
+5060.0871 36.3014
+5061.2199 9.60687
+5061.6562 41.2795
+5062.0395 408.861
+5062.9325 18.4902
+5063.5157 40.647
+5063.9988 33.0461
+5064.602 265.528
+5064.9463 369.346
+5066.1355 57.7505
+5066.7773 29.6794
+5067.1371 24.0714
+5067.9737 968.248
+5069.3384 10.6411
+5073.0746 9.28652
+5074.6637 4.94064
+5077.7903 221.971
+5081.4462 36.1646
+5084.6987 5.1124
+5084.9935 20.9643
+5087.1131 7.51694
+5090.0513 10.254
+5090.5148 191.383
+5095.0639 20.4182
+5096.4848 193.562
+5098.0432 32.5449
+5098.932 11.7805
+5100.6211 223.131
+5101.1299 43.7184
+5101.3395 2.88295
+5109.7207 5.92458
+5110.5493 7.00854
+5110.8709 7.06356
+5111.2781 56.3139
+5115.0477 467.92
+5116.3016 4.97391
+5117.2923 10.4614
+5118.205 18.4299
+5122.4995 18.2438
+5125.4895 15.4357
+5125.9502 109.096
+5126.279 6.87144
+5128.4906 54.4392
+5130.2338 4.79469
+5131.0699 7.36472
+5133.1051 6.47732
+5134.746 110.644
+5136.121 4.46243
+5137.4733 29.6407
+5140.7736 45.7047
+5141.7827 335.653
+5143.2673 55.4042
+5143.9165 128.674
+5145.3083 191.136
+5146.0544 8.24467
+5148.2087 18.9162
+5149.207 9.03956
+5150.7064 4.93751
+5151.612 422.456
+5151.858 9.02414
+5154.243 378.949
+5158.6059 1084.03
+5159.4574 5.80872
+5159.6246 14.2469
+5160.7163 83.4544
+5161.5396 95.0381
+5162.2892 207.227
+5162.7493 10.0091
+5163.4604 233.684
+5165.066 4.84967
+5165.7728 37.3169
+5166.6553 7.52561
+5168.9225 21.3244
+5170.2227 3.38792
+5173.0794 17.3811
+5173.6757 25.3887
+5174.2217 4.48851
+5174.7988 12.5656
+5175.3248 46.0539
+5175.9115 14.3605
+5176.2292 30.8526
+5176.4037 24.8192
+5176.961 228.93
+5177.6228 30.8468
+5178.4726 6.05432
+5180.7209 3.79134
+5182.5269 9.67873
+5183.0965 6.48001
+5183.9896 13.7039
+5184.4541 16.2505
+5186.4132 20.5776
+5187.1693 9.6076
+5187.3374 80.8798
+5187.4583 11.2173
+5187.7491 277.819
+5190.872 19.9127
+5191.3463 3.51862
+5193.8256 39.5266
+5193.9316 6.88467
+5194.4576 80.7526
+5197.2361 7.67376
+5198.7999 130.392
+5199.1637 605.975
+5201.9993 8.19701
+5203.8474 10.9719
+5205.1522 29.1488
+5206.6722 4.99346
+5207.8015 5.52073
+5209.7246 76.0411
+5210.492 6.73147
+5211.2343 435.898
+5213.3492 103.41
+5214.7867 14.0768
+5215.6977 18.0725
+5216.6014 42.831
+5218.5271 11.4526
+5219.1137 181.613
+5220.7068 11.6886
+5220.9301 24.6622
+5221.271 89.5286
+5221.8618 5.7256
+5228.2246 28.1778
+5228.9951 6.50876
+5231.1597 1030.95
+5233.2254 25.7881
+5234.1087 13.1727
+5238.8137 112.491
+5239.552 108.328
+5240.1968 32.4576
+5245.3797 5.56783
+5247.1977 20.5721
+5247.6548 133.966
+5250.8771 17.9253
+5254.2588 12.0692
+5254.4699 12.9062
+5258.3602 414.673
+5260.1041 61.1699
+5261.472 18.3194
+5264.7865 9.64064
+5266.7145 215.526
+5267.1597 5.01696
+5272.927 29.2593
+5273.1317 9.19802
+5274.1188 69.5206
+5277.1433 14.8433
+5277.5002 22.9387
+5278.1166 40.8362
+5280.3546 7.23929
+5281.069 53.1053
+5281.6257 8.22585
+5286.887 11.7575
+5289.8929 4.02498
+5294.3997 36.0776
+5295.7567 40.0391
+5296.2811 50.8073
+5297.7431 67.7718
+5298.2824 62.5283
+5300.5234 34.4504
+5301.3968 4.29366
+5303.4845 9.75274
+5305.699 10.4784
+5306.9963 9.24972
+5307.4632 10.0681
+5309.5983 4.18063
+5310.2665 23.0678
+5312.0063 273.93
+5312.5288 106.566
+5312.9045 58.8661
+5315.2278 5.62052
+5317.4983 42.7434
+5320.7757 8.43051
+5325.1438 10.8274
+5326.2774 26.3838
+5326.9756 139.316
+5329.3745 10.7514
+5329.7088 6.99339
+5330.0804 20.0113
+5340.4982 7.20135
+5343.5812 484.175
+5345.3079 4.87436
+5347.9714 27.1696
+5351.1294 27.9601
+5351.8297 7.16019
+5354.6016 10.7326
+5355.6465 8.3333
+5358.3826 13.1422
+5359.8269 8.53931
+5360.1501 53.6849
+5361.1593 12.9852
+5362.5751 24.8142
+5369.2819 22.9514
+5369.447 24.2969
+5370.7096 21.5016
+5372.7067 15.7941
+5373.4943 8.9765
+5374.8219 65.5878
+5375.3587 20.0024
+5375.7688 5.72737
+5376.1304 6.71117
+5376.7809 19.7164
+5378.8349 44.3788
+5379.1105 92.3125
+5382.9276 12.0274
+5384.0356 10.4107
+5384.3682 4.78892
+5386.6148 178.392
+5387.8266 3.94538
+5388.0507 10.9853
+5390.4375 108.219
+5392.5726 25.4667
+5393.6047 3.81093
+5393.9744 31.1239
+5394.7608 173.502
+5397.5144 13.2397
+5398.2047 5.77056
+5398.7015 46.0962
+5398.9219 28.3115
+5399.1745 46.9412
+5399.6218 7.48724
+5400.1455 6.34557
+5402.6048 15.4506
+5403.1993 8.38744
+5407.6535 119.875
+5410.4885 8.56801
+5410.7687 119.322
+5415.5226 45.915
+5417.4926 350.868
+5419.1234 4.06181
+5421.3517 35.6623
+5421.8359 8.0436
+5424.0079 60.1299
+5425.6783 33.8477
+5426.4141 11.7312
+5431.112 141.196
+5435.1229 4.50729
+5435.892 13.6085
+5437.3877 10.8337
+5438.1488 7.10681
+5439.9891 29.8005
+5440.6015 21.9521
+5442.2477 6.35966
+5443.2326 17.9646
+5443.6938 7.1111
+5447.1536 13.7033
+5449.4733 14.3251
+5451.652 105.803
+5452.2187 132.988
+5454.3015 13.2046
+5457.2985 12.3395
+5457.7973 5.71497
+5461.7402 6.77458
+5462.6057 6.94785
+5464.2123 83.3744
+5467.1465 7.21199
+5469.0844 4.43358
+5470.7591 40.2228
+5473.4541 14.1218
+5479.0751 15.313
+5484.1367 8.1378
+5488.6363 14.6626
+5488.9151 8.21965
+5489.0833 5.74745
+5490.1267 4.08873
+5492.3362 11.3461
+5493.2043 35.802
+5494.3327 41.7922
+5495.8738 274.697
+5496.1382 96.6002
+5498.175 9.52024
+5499.2567 644.75
+5499.6461 26.91
+5500.3125 4.43996
+5501.2784 15.8737
+5501.9367 7.14131
+5504.3018 114.524
+5506.1152 55.5132
+5506.9034 5.49701
+5507.5376 26.0302
+5507.7676 4.82248
+5508.5564 12.7312
+5509.9938 158.421
+5514.8721 102.678
+5518.9841 15.0484
+5519.2615 5.45086
+5524.5824 27.0831
+5524.957 24.5668
+5527.3022 13.4549
+5528.2376 12.4776
+5533.2452 8.99002
+5534.4989 7.95551
+5535.9572 5.60565
+5537.1194 5.84807
+5537.5563 15.7154
+5538.608 23.1786
+5539.2614 388.735
+5539.9104 65.4704
+5539.933 78.3845
+5541.1446 17.2482
+5541.5833 12.5174
+5541.94 3.13988
+5542.8911 66.4701
+5546.122 4.80963
+5548.1774 189.504
+5551.3701 12.0377
+5552.6221 14.6156
+5554.0219 29.5953
+5554.1319 6.26146
+5555.5278 8.09759
+5557.0459 149.272
+5558.3407 206.791
+5558.702 263.229
+5559.6677 11.9343
+5559.8912 48.3765
+5567.9986 17.0399
+5570.9235 11.7199
+5571.1937 136.006
+5572.4866 145.08
+5573.3535 302.095
+5576.2053 63.5346
+5577.689 25.2927
+5578.5506 4.47167
+5579.3594 168.85
+5580.0795 14.7368
+5580.7436 5.80429
+5581.8644 7.58005
+5582.8565 6.70252
+5583.7635 13.0022
+5587.026 752.61
+5588.7244 16.4156
+5593.2794 2.48508
+5593.6193 28.1029
diff --git a/noao/artdata/mkexamples/galcluster.cl b/noao/artdata/mkexamples/galcluster.cl
new file mode 100644
index 00000000..8a6e1e7f
--- /dev/null
+++ b/noao/artdata/mkexamples/galcluster.cl
@@ -0,0 +1,39 @@
+# GALCLUSTER - Galaxy cluster
+
+file image, dat
+
+image = s1
+dat = mktemp ("art")
+
+gallist (dat, 100, interactive=no, spatial="hubble", xmin=1., xmax=512.,
+ ymin=1., ymax=512., xcenter=INDEF, ycenter=INDEF, core_radius=50.,
+ base=0., sseed=i+1, luminosity="schecter", minmag=-7., maxmag=0.,
+ mzero=15., power=0.6, alpha=-1.24, mstar=-21.41, lseed=i+1, egalmix=0.8,
+ ar=0.7, eradius=20., sradius=1., absorption=1.2, z=0.05, sfile="",
+ nssample=100, sorder=10, lfile="", nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, dbinsize=0.5, ebinsize=0.1, pbinsize=20.,
+ graphics="stdgraph", cursor="")
+
+gallist (dat, 500, interactive=no, spatial="uniform", xmin=1.,
+ xmax=512., ymin=1., ymax=512., xcenter=INDEF, ycenter=INDEF,
+ core_radius=50., base=0., sseed=i+1, luminosity="powlaw", minmag=-7.,
+ maxmag=0., mzero=15., power=0.6, alpha=-1.24, mstar=-21.41, lseed=i+1,
+ egalmix=0.4, ar=0.7, eradius=20., sradius=1., absorption=1.2, z=0.05,
+ sfile="", nssample=100, sorder=10, lfile="", nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, dbinsize=0.5, ebinsize=0.1, pbinsize=20.,
+ graphics="stdgraph", cursor="")
+
+starlist (dat, 100, "", "", interactive=no, spatial="uniform", xmin=1.,
+ xmax=512., ymin=1., ymax=512., xcenter=INDEF, ycenter=INDEF,
+ core_radius=30., base=0., sseed=i, luminosity="powlaw", minmag=-7.,
+ maxmag=0., mzero=-4., power=0.6, alpha=0.74, beta=0.04, delta=0.294,
+ mstar=1.28, lseed=i, nssample=100, sorder=10, nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, graphics="stdgraph", cursor="")
+
+mkobjects (image, output="", ncols=512, nlines=512,
+ title="Example artificial galaxy cluster", header="artdata$stdheader.dat",
+ background=1000., objects=dat, xoffset=0., yoffset=0., star="moffat",
+ radius=1.0, beta=2.5, ar=1., pa=0., distance=1., exptime=1., magzero=7.,
+ gain=3., rdnoise=10., poisson=yes, seed=j, comments=b1)
+
+delete (dat, verify=no)
diff --git a/noao/artdata/mkexamples/galfield.cl b/noao/artdata/mkexamples/galfield.cl
new file mode 100644
index 00000000..168972fe
--- /dev/null
+++ b/noao/artdata/mkexamples/galfield.cl
@@ -0,0 +1,30 @@
+# GALFIELD - Uniform galaxy field
+
+file image, dat
+
+image = s1
+dat = mktemp ("art")
+
+gallist (dat, 1000, interactive=no, spatial="uniform", xmin=1.,
+ xmax=512., ymin=1., ymax=512., xcenter=INDEF, ycenter=INDEF,
+ core_radius=50., base=0., sseed=i+1, luminosity="powlaw", minmag=-7.,
+ maxmag=0., mzero=15., power=0.45, alpha=-1.24, mstar=-21.41, lseed=i+1,
+ egalmix=0.4, ar=0.7, eradius=10., sradius=1., absorption=1.2, z=0.05,
+ sfile="", nssample=100, sorder=10, lfile="", nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, dbinsize=0.5, ebinsize=0.1, pbinsize=20.,
+ graphics="stdgraph", cursor="")
+
+starlist (dat, 100, "", "", interactive=no, spatial="uniform", xmin=1.,
+ xmax=512., ymin=1., ymax=512., xcenter=INDEF, ycenter=INDEF,
+ core_radius=30., base=0., sseed=i, luminosity="powlaw", minmag=-7.,
+ maxmag=0., mzero=-4., power=0.6, alpha=0.74, beta=0.04, delta=0.294,
+ mstar=1.28, lseed=i, nssample=100, sorder=10, nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, graphics="stdgraph", cursor="")
+
+mkobjects (image, output="", ncols=512, nlines=512,
+ title="Example artificial galaxy field", header="artdata$stdheader.dat",
+ background=400., objects=dat, xoffset=0., yoffset=0., star="moffat",
+ radius=1.0, beta=2.5, ar=1., pa=0., distance=1., exptime=1., magzero=5.5,
+ gain=5., rdnoise=10., poisson=yes, seed=j, comments=b1)
+
+delete (dat, verify=no)
diff --git a/noao/artdata/mkexamples/globular.cl b/noao/artdata/mkexamples/globular.cl
new file mode 100644
index 00000000..f70f3f1e
--- /dev/null
+++ b/noao/artdata/mkexamples/globular.cl
@@ -0,0 +1,23 @@
+# GLOBULAR - Globular cluster
+
+file image, dat
+
+image = s1
+dat = mktemp ("art")
+
+starlist (dat, 5000, "", "", interactive=no, spatial="hubble",
+ xmin=1., xmax=512., ymin=1., ymax=512., xcenter=INDEF,
+ ycenter=INDEF, core_radius=30., base=0., sseed=i,
+ luminosity="bands", minmag=-7., maxmag=0., mzero=-4., power=0.6,
+ alpha=0.74, beta=0.04, delta=0.294, mstar=1.28, lseed=i,
+ nssample=100, sorder=10, nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, graphics="stdgraph", cursor="")
+
+mkobjects (image, output="", ncols=512, nlines=512,
+ title="Example artificial globular cluster",
+ header="artdata$stdheader.dat", background=1000., objects=dat,
+ xoffset=0., yoffset=0., star="moffat", radius=1.0, beta=2.5,
+ ar=1., pa=0., distance=1., exptime=1., magzero=7.,
+ gain=3., rdnoise=10., poisson=yes, seed=j, comments=b1)
+
+delete (dat, verify=no)
diff --git a/noao/artdata/mkexamples/henear.cl b/noao/artdata/mkexamples/henear.cl
new file mode 100644
index 00000000..cec48b98
--- /dev/null
+++ b/noao/artdata/mkexamples/henear.cl
@@ -0,0 +1,20 @@
+# henear - Helium-Neon-Argon spectrum (uncalibrated)
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$archdr.dat"
+
+for (k=1; k<=i; k+=1) {
+ mk1dspec (out, output="", ap=k, rv=0., z=no, ncols=512, naps=i,
+ wstart=4209.0+k, wend=7361.7+k, title="Helium-Neon-Argon Arc Example",
+ header="", continuum=0.5, slope=0., temperature=0.,
+ lines="mkexamples$henear2.dat", profile="gaussian", gfwhm=14,
+ comments=b1)
+}
+
+mkheader (out, hdr, append=no, verbose=no)
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/henear1.dat b/noao/artdata/mkexamples/henear1.dat
new file mode 100644
index 00000000..6dfd4bdf
--- /dev/null
+++ b/noao/artdata/mkexamples/henear1.dat
@@ -0,0 +1,56 @@
+# HENEAR TRUE WAVELENGTHS
+4259.361 456.992
+4300.4 151.516
+4426.01 304.683
+4471.477 808.484
+4510.733 197.966
+4545.08 371.968
+4579.39 334.567
+4657.94 269.339
+4713.143 365.173
+4764.89 628.846
+4806.07 308.678
+4879.9 418.101
+4921.929 644.809
+4965.12 234.898
+5015.675 2573.67
+5187.746 292.801
+5221.27 193.722
+5400.562 326.122
+5495.872 454.027
+5572.548 335.12
+5606.732 326.878
+5650.703 283.252
+5748.299 224.797
+5764.418 269.445
+5852.4878 20334.6
+5875.618 15259.1
+5944.8342 2891.43
+6029.9971 1970.72
+6074.3377 2573.29
+6096.163 4871.86
+6143.0623 7662.59
+6163.5939 2296.45
+6217.2813 1579.43
+6266.495 4593.41
+6304.7892 1604.55
+6334.4279 3425.67
+6382.9914 6427.61
+6402.246 10708.7
+6506.5279 6053.5
+6532.8824 2271.5
+6598.9529 3574.04
+6678.2 30911.1
+6717.0428 4317.68
+6752.832 3451.33
+6871.29 2877.43
+6929.468 5863.05
+6965.43 35198.7
+7032.4127 14128.7
+7065.188 32334.6
+7107.496 1037.61
+7147.041 3887.49
+7173.939 1277.6
+7206.986 1433.43
+7245.167 4155.93
+7281.349 11982.2
diff --git a/noao/artdata/mkexamples/henear1d.cl b/noao/artdata/mkexamples/henear1d.cl
new file mode 100644
index 00000000..2595ad6a
--- /dev/null
+++ b/noao/artdata/mkexamples/henear1d.cl
@@ -0,0 +1,4 @@
+# HENEAR1D - 1D Helium-Neon-Argon Arc Spectrum
+
+i = 1
+cl < "mkexamples$henear.cl"
diff --git a/noao/artdata/mkexamples/henear2.dat b/noao/artdata/mkexamples/henear2.dat
new file mode 100644
index 00000000..a8fb3236
--- /dev/null
+++ b/noao/artdata/mkexamples/henear2.dat
@@ -0,0 +1,56 @@
+# HENEAR WAVELENGTHS WITH 10A QUADRATIC DISPERSION FUNCTION
+4268.73 456.992
+4309.26 151.516
+4433.41 304.683
+4478.38 808.484
+4517.22 197.966
+4551.22 371.968
+4585.18 334.567
+4662.99 269.339
+4717.69 365.173
+4769.00 628.846
+4809.85 308.678
+4883.11 418.101
+4924.84 644.809
+4967.74 234.898
+5017.97 2573.67
+5189.10 292.801
+5222.47 193.722
+5401.09 326.122
+5496.16 454.027
+5572.69 335.12
+5606.83 326.878
+5650.75 283.252
+5748.30 224.797
+5764.42 269.445
+5852.53 20334.6
+5875.68 15259.1
+5944.98 2891.43
+6030.31 1970.72
+6074.76 2573.29
+6096.65 4871.86
+6143.69 7662.59
+6164.29 2296.45
+6218.17 1579.43
+6267.58 4593.41
+6306.05 1604.55
+6335.83 3425.67
+6384.63 6427.61
+6403.99 10708.7
+6508.88 6053.5
+6535.40 2271.5
+6601.92 3574.04
+6681.75 30911.1
+6720.90 4317.68
+6756.98 3451.33
+6876.49 2877.43
+6935.22 5863.05
+6971.54 35198.7
+7039.22 14128.7
+7072.35 32334.6
+7115.13 1037.61
+7155.13 3887.49
+7182.34 1277.6
+7215.79 1433.43
+7254.44 4155.93
+7291.07 11982.2
diff --git a/noao/artdata/mkexamples/heneardc.cl b/noao/artdata/mkexamples/heneardc.cl
new file mode 100644
index 00000000..8e1ab75f
--- /dev/null
+++ b/noao/artdata/mkexamples/heneardc.cl
@@ -0,0 +1,18 @@
+# heneardc - Helium-Neon-Argon spectrum (calibrated)
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$archdr.dat"
+
+for (k=1; k<=i; k+=1) {
+ mk1dspec (out, output="", ap=k, rv=0., z=no, ncols=512, naps=i,
+ wstart=4210.0, wend=7362.7, title="Helium-Neon-Argon Arc Example",
+ header=hdr, continuum=0.5, slope=0., temperature=0.,
+ lines="mkexamples$henear1.dat", profile="gaussian",
+ gfwhm=14, comments=b1)
+}
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=10., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/longslit.cl b/noao/artdata/mkexamples/longslit.cl
new file mode 100644
index 00000000..5be43bcd
--- /dev/null
+++ b/noao/artdata/mkexamples/longslit.cl
@@ -0,0 +1,77 @@
+# LONGSLIT - Long slit example
+
+real w1, w2
+file out, obj, bkg, dat, hdr
+
+out = s1
+hdr = s2
+obj = mktemp ("art")
+bkg = mktemp ("art")
+dat = mktemp ("art")
+
+w1 = 4209 + i
+w2 = 7361 + i
+
+if (i == 2) { # Featureless hot continuum + sky
+ mk1dspec (obj, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=10000., lines="", nlines=0, peak=-0.5, profile="gaussian",
+ gfwhm=23.5, seed=1, comments=b1)
+ mk1dspec (bkg, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=5800., lines="", nlines=20, peak=1., profile="gaussian",
+ gfwhm=12, seed=1, comments=b1)
+ print (obj, " 1 gauss 3 0 50 .002", > dat)
+ print (bkg, " 10 slit 90 0 50 0", >> dat)
+} else if (i == 3) { # Sky only
+ mk1dspec (bkg, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=5800., lines="", nlines=20, peak=1., profile="gaussian",
+ gfwhm=12, seed=1, comments=b1)
+ print (bkg, " 10 slit 90 0 50 0", > dat)
+} else if (i == 4) { # Flat field
+ mk1dspec (bkg, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=8000., lines="", nlines=0, peak=-0.5, profile="gaussian",
+ gfwhm=23.5, seed=1, comments=b1)
+ print (bkg, " 10 slit 90 0 50 0", > dat)
+} else if (i == 5) { # HE-NE-AR
+ mk1dspec (bkg, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=0.5, slope=0.,
+ temperature=0., lines="mkexamples$henear2.dat", profile="gaussian",
+ gfwhm=14, comments=b1)
+ print (bkg, " 100 slit 90 0 50 0", > dat)
+} else if (i == 6) { # Galaxy absorption line spectrum + sky
+ mk1dspec (obj, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=7000., lines="", nlines=50, peak=-0.5, profile="gaussian",
+ gfwhm=23.5, seed=i+1, comments=b1)
+ mk1dspec (bkg, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=5800., lines="", nlines=20, peak=1., profile="gaussian",
+ gfwhm=12, seed=1, comments=b1)
+ print (obj, " 10 gauss 30 0 50 .002", > dat)
+ print (bkg, " 10 slit 200 0 50 0", >> dat)
+} else { # Star absorption line spectrum + sky
+ mk1dspec (obj, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=7000., lines="", nlines=50, peak=-0.5, profile="gaussian",
+ gfwhm=23.5, seed=i+1, comments=b1)
+ mk1dspec (bkg, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=w1, wend=w2, continuum=1000., slope=0.,
+ temperature=5800., lines="", nlines=20, peak=1., profile="gaussian",
+ gfwhm=12, seed=1, comments=b1)
+ print (obj, " 1 gauss 3 0 50 .002", > dat)
+ print (bkg, " 10 slit 90 0 50 0", >> dat)
+}
+
+mk2dspec (out, output="", model=dat, comments=b1, ncols=100, nlines=512,
+ title="Example artificial long slit image", header=hdr)
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=3., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
+
+imdelete (obj, verify=no, >& "dev$null")
+imdelete (bkg, verify=no, >& "dev$null")
+delete (dat, verify=no, >& "dev$null")
diff --git a/noao/artdata/mkexamples/lsarc.cl b/noao/artdata/mkexamples/lsarc.cl
new file mode 100644
index 00000000..2ca4afdc
--- /dev/null
+++ b/noao/artdata/mkexamples/lsarc.cl
@@ -0,0 +1,5 @@
+# LSARC - Longslit Helium-Neon-Argon Arc Spectrum
+
+i = 5
+s2 = "mkexamples$archdr.dat"
+cl < "mkexamples$longslit.cl"
diff --git a/noao/artdata/mkexamples/lsgal.cl b/noao/artdata/mkexamples/lsgal.cl
new file mode 100644
index 00000000..0c9aaad7
--- /dev/null
+++ b/noao/artdata/mkexamples/lsgal.cl
@@ -0,0 +1,5 @@
+# LSGAL - Long slit galaxy spectrum (uncalibrated)
+
+i = 6
+s2 = "mkexamples$objhdr.dat"
+cl < "mkexamples$longslit.cl"
diff --git a/noao/artdata/mkexamples/lsobj.cl b/noao/artdata/mkexamples/lsobj.cl
new file mode 100644
index 00000000..fe00bf9b
--- /dev/null
+++ b/noao/artdata/mkexamples/lsobj.cl
@@ -0,0 +1,5 @@
+# LSOBJ - Long slit object spectrum (uncalibrated)
+
+i = 1
+s2 = "mkexamples$objhdr.dat"
+cl < "mkexamples$longslit.cl"
diff --git a/noao/artdata/mkexamples/mkexamples.cl b/noao/artdata/mkexamples/mkexamples.cl
new file mode 100644
index 00000000..ed9150e4
--- /dev/null
+++ b/noao/artdata/mkexamples/mkexamples.cl
@@ -0,0 +1,68 @@
+# MKEXAMPLES -- Make ARTDATA examples.
+# The example script files use variable s1 to pass the image name,
+# variable s2 to pass the image header file, variable i to pass an object
+# seed, variable j to pass a noise seed, and variable b1 to pass the comment
+# flag.
+
+procedure mkexamples (name, image)
+
+string name {prompt="Example name"}
+string image {prompt="Image name"}
+
+int oseed=1 {prompt="Object seed"}
+int nseed=1 {prompt="Noise seed"}
+bool comments=no {prompt="Add comments to image?"}
+bool verbose=yes {prompt="Print operation?"}
+bool errors=yes {prompt="Report errors?"}
+bool list=no {prompt="List example script file only?"}
+
+begin
+ string name1, name2, example
+
+
+ # Get and check parameters.
+ if ($nargs < 1 && mode != "h") {
+ name1 = "mkexamples"
+ name2 = ""
+ while (name1 != name2) {
+ example = "mkexamples$" // name1 // ".men"
+ if (!access (example))
+ break
+ type (example)
+ name2 = name1
+ name1 = name
+ }
+ if (name1 == name2)
+ return
+ } else
+ name1 = name
+
+ example = "mkexamples$" // name1 // ".cl"
+ if (!access (example)) {
+ if (errors)
+ error (2, "Unknown example " // name1)
+ return
+ }
+
+ # Make or list the example.
+ if (list)
+ page (example)
+ else {
+ s1 = image
+ s2 = "artdata$stdheader.dat"
+ i = oseed
+ j = nseed
+ b1 = comments
+ if (s1 == "" || name1 == "")
+ return
+ if ((access (s1) || access (s1//"."//envget("imtype")))) {
+ if (errors)
+ error (1, "Image " // s1 // " already exists")
+ return
+ }
+
+ if (verbose)
+ print ("Creating example ", name1, " in image ", s1, " ...")
+ cl (< example)
+ }
+end
diff --git a/noao/artdata/mkexamples/mkexamples.men b/noao/artdata/mkexamples/mkexamples.men
new file mode 100644
index 00000000..feb89380
--- /dev/null
+++ b/noao/artdata/mkexamples/mkexamples.men
@@ -0,0 +1,10 @@
+ MKEXAMPLE Menu
+
+ galcluster - Galaxy cluster
+ globular - Globular cluster
+ galfield - Galaxy field
+ starfield - Starfield
+
+ onedspec - Menu of one dimensional spectra
+ twodspec - Menu of two dimensional spectra
+ threedspec - Menu of three dimensional spectra
diff --git a/noao/artdata/mkexamples/multifiber.cl b/noao/artdata/mkexamples/multifiber.cl
new file mode 100644
index 00000000..af48c4e7
--- /dev/null
+++ b/noao/artdata/mkexamples/multifiber.cl
@@ -0,0 +1,31 @@
+# MULTIFIBER - Multifiber example
+
+file out, obj, dat
+
+out = s1
+obj = mktemp ("art")
+dat = mktemp ("art")
+
+mk1dspec (obj, output="", ap=1, rv=0., z=no, title="", header="",
+ ncols=512, naps=1, wstart=4000., wend=8000., continuum=1000., slope=0.,
+ temperature=5700., lines="", nlines=50, peak=-0.5, profile="gaussian",
+ gfwhm=24, seed=i, comments=b1)
+
+print (obj, " .1 gauss 3 0 10.1 .002", > dat)
+print (obj, " .2 gauss 3 0 20.2 .002", >> dat)
+print (obj, " .3 gauss 3 0 30.3 .002", >> dat)
+print (obj, " .4 gauss 3 0 40.4 .002", >> dat)
+print (obj, " .5 gauss 3 0 50.5 .002", >> dat)
+print (obj, " .6 gauss 3 0 60.6 .002", >> dat)
+print (obj, " .7 gauss 3 0 70.7 .002", >> dat)
+print (obj, " .8 gauss 3 0 80.8 .002", >> dat)
+print (obj, " .9 gauss 3 0 90.9 .002", >> dat)
+mk2dspec (out, output="", model=dat, comments=b1, ncols=100, nlines=512,
+ title="Example artificial multifiber image", header="artdata$stdheader.dat")
+
+mknoise (out, output="", title="", header="", ncols=512, nlines=512,
+ background=0., gain=1., rdnoise=3., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
+
+imdelete (obj, verify=no)
+delete (dat, verify=no)
diff --git a/noao/artdata/mkexamples/objhdr.dat b/noao/artdata/mkexamples/objhdr.dat
new file mode 100644
index 00000000..eabebc5f
--- /dev/null
+++ b/noao/artdata/mkexamples/objhdr.dat
@@ -0,0 +1,19 @@
+OBSERVER= 'IRAF ' / observers
+OBSERVAT= 'KPNO ' / observatory
+INSTRUME= 'IRAF ARTDATA ' / instrument
+TELESCOP= 'kpcdf ' / telescope name
+DETECTOR= 'te1k ' / detector
+EXPTIME = 1200. / actual integration time
+DARKTIME= 1200. / total elapsed time
+IMAGETYP= 'object ' / object, dark, bias, etc.
+DATE-OBS= '1991-11-26T12:19:55.00' / date (dd/mm/yy) of obs.
+UT = '12:19:55.00 ' / universal time
+ST = '09:13:15.00 ' / sidereal time
+RA = '06:37:02.00 ' / right ascension
+DEC = '06:08:52.00 ' / declination
+EPOCH = 1991.9 / epoch of ra and dec
+ZD = '44.580 ' / zenith distance
+AIRMASS = 0. / airmass
+APERTURE= '250micron slit ' / aperture
+GRATPOS = 4624.3 / grating position
+CCDPROC = 'Nov 26 5:44 CCD processing done'
diff --git a/noao/artdata/mkexamples/onedspec.men b/noao/artdata/mkexamples/onedspec.men
new file mode 100644
index 00000000..903eb45a
--- /dev/null
+++ b/noao/artdata/mkexamples/onedspec.men
@@ -0,0 +1,10 @@
+ ONEDSPEC Menu
+
+ henear - Helium-neon-argon spectrum (uncalibrated)
+ heneardc - Helium-neon-argon spectrum (calibrated)
+
+ ecarc - Echelle thorium-argon spectrum (uncalibrated)
+ ecarcdc - Echelle thorium-argon spectrum (calibrated)
+
+ spectrum - Absorption spectrum (calibrated)
+ echelle - Echelle absorption spectrum (calibrated)
diff --git a/noao/artdata/mkexamples/spectrum.cl b/noao/artdata/mkexamples/spectrum.cl
new file mode 100644
index 00000000..a5045b8f
--- /dev/null
+++ b/noao/artdata/mkexamples/spectrum.cl
@@ -0,0 +1,19 @@
+# spectrum - Object spectrum (calibrated)
+
+file out, hdr
+
+out = s1
+hdr = "mkexamples$objhdr.dat"
+
+for (k=1; k<=i; k+=1) {
+ x = k * 100.
+ mk1dspec (out, output="", ap=k, rv=0., z=no, ncols=512, naps=i,
+ wstart=4210.0, wend=7362.7, title="Artificial Spectrum",
+ header=hdr, continuum=x, slope=0., temperature=5700.,
+ lines="", nlines=50, peak=-0.5, profile="gaussian", gfwhm=24,
+ seed=i, comments=b1)
+}
+
+mknoise (out, output="", ncols=512, nlines=512, title="", header="",
+ background=0., gain=1., rdnoise=3., poisson=yes, seed=j, cosrays="",
+ ncosrays=0, energy=30000., radius=0.5, ar=1., pa=0., comments=b1)
diff --git a/noao/artdata/mkexamples/starfield.cl b/noao/artdata/mkexamples/starfield.cl
new file mode 100644
index 00000000..3e1c19b9
--- /dev/null
+++ b/noao/artdata/mkexamples/starfield.cl
@@ -0,0 +1,22 @@
+# STARFIELD - Uniform star field
+
+file image, dat
+
+image = s1
+dat = mktemp ("art")
+
+starlist (dat, 1000, "", "", interactive=no, spatial="uniform", xmin=1.,
+ xmax=512., ymin=1., ymax=512., xcenter=INDEF, ycenter=INDEF,
+ core_radius=30., base=0., sseed=i, luminosity="powlaw", minmag=-7.,
+ maxmag=0., mzero=-4., power=0.6, alpha=0.74, beta=0.04, delta=0.294,
+ mstar=1.28, lseed=i, nssample=100, sorder=10, nlsample=100, lorder=10,
+ rbinsize=10., mbinsize=0.5, graphics="stdgraph", cursor="")
+
+mkobjects (image, output="", ncols=512, nlines=512,
+ title="Example artificial uniform star field",
+ header="artdata$stdheader.dat",
+ background=1000., objects=dat, xoffset=0., yoffset=0., star="moffat",
+ radius=1.0, beta=2.5, ar=1., pa=0., distance=1., exptime=1., magzero=7.,
+ gain=3., rdnoise=10., poisson=yes, seed=j, comments=b1)
+
+delete (dat, verify=no)
diff --git a/noao/artdata/mkexamples/threedspec.men b/noao/artdata/mkexamples/threedspec.men
new file mode 100644
index 00000000..8aba4892
--- /dev/null
+++ b/noao/artdata/mkexamples/threedspec.men
@@ -0,0 +1,3 @@
+ THREEDSPEC Menu
+
+ None available
diff --git a/noao/artdata/mkexamples/twodspec.men b/noao/artdata/mkexamples/twodspec.men
new file mode 100644
index 00000000..08ebff20
--- /dev/null
+++ b/noao/artdata/mkexamples/twodspec.men
@@ -0,0 +1,10 @@
+ TWODSPEC Menu
+
+ ecarc2d - Echelle thorium-argon slit spectrum
+ ecobj2d - Echelle object slit spectrum
+
+ lsarc - Long slit helium-neon-argon spectrum
+ lsobj - Long slit object spectrum
+ lsgal - Long slit galaxy spectrum
+
+ multifiber - Multifiber spectrum
diff --git a/noao/artdata/mkheader.par b/noao/artdata/mkheader.par
new file mode 100644
index 00000000..327d5ac8
--- /dev/null
+++ b/noao/artdata/mkheader.par
@@ -0,0 +1,4 @@
+images,s,a,,,,List of images to be modified
+headers,s,a,,,,List of images or header keyword files
+append,b,h,yes,,,Append to existing header?
+verbose,b,h,no,,,Verbose output?
diff --git a/noao/artdata/mkheader.x b/noao/artdata/mkheader.x
new file mode 100644
index 00000000..aeee042a
--- /dev/null
+++ b/noao/artdata/mkheader.x
@@ -0,0 +1,229 @@
+include <error.h>
+include <imhdr.h>
+include <imio.h>
+include <ctype.h>
+
+define LEN_COMMENT 70 # Maximum comment length
+define IDB_RECLEN 80 # Length of FITS record (card)
+define COMMENT "COMMENT" # Comment key
+define IS_FITS (IS_DIGIT($1)||IS_UPPER($1)||($1=='-')||($1=='_'))
+
+# MKH_HEADER -- Append or substitute new image header from an image or file.
+# Only the legal FITS cards (ignoring leading whitespace) will be copied
+# from a file.
+
+procedure mkh_header (im, fname, append, verbose)
+
+pointer im # IMIO pointer
+char fname[ARB] # Image or data file name
+bool append # Append to existing header?
+bool verbose # Verbose?
+
+int i, j, curlen, buflen, max_lenuserarea
+pointer ua, fd, out
+pointer sp, str
+
+int open(), getline(), nowhite(), strlen(), stropen()
+pointer immap()
+errchk open, stropen
+
+begin
+ if (nowhite (fname, fname, SZ_FNAME) == 0)
+ return
+
+ # Open the user area string for appending. 'buflen' is the malloc-ed
+ # buffer length in struct units; IMU is the struct offset to the user
+ # area, i.e., the size of that part of the image descriptor preceding
+ # the user area. If the buffer fills we must allow one extra char for
+ # the EOS delimiter; since storage for the image descriptor was
+ # allocated in struct units the storage allocator will not have
+ # allocated space for the extra EOS char.
+
+ ua = IM_USERAREA(im)
+ if (!append)
+ Memc[ua] = EOS
+ curlen = strlen (Memc[ua])
+ buflen = LEN_IMDES + IM_LENHDRMEM(im)
+ max_lenuserarea = (buflen - IMU) * SZ_STRUCT - 1
+
+ # Append from an image header.
+ ifnoerr (fd = immap (fname, READ_ONLY, 0)) {
+ iferr {
+ i = strlen (Memc[IM_USERAREA(fd)]) + strlen (Memc[ua])
+ call strcat (Memc[IM_USERAREA(fd)], Memc[ua], max_lenuserarea)
+ if (i > max_lenuserarea)
+ call error (1, "Possibly failed to add all the keywords")
+ } then {
+ call erract (EA_WARN)
+
+ # Check for truncated card.
+ for (i=ua+max_lenuserarea-1; i > ua; i=i-1) {
+ if (Memc[i] == '\n') {
+ Memc[i+1] = EOS
+ break
+ }
+ }
+ }
+ call imunmap (fd)
+
+ # Append from a text file.
+ } else {
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+ out = stropen (Memc[ua+curlen], max_lenuserarea - curlen, APPEND)
+
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ iferr {
+ while (getline (fd, Memc[str]) != EOF) {
+ for (i=str; IS_WHITE(Memc[i]); i=i+1)
+ ;
+ for (j=i; IS_FITS(Memc[j]); j=j+1)
+ ;
+ for (; j<i+8 && Memc[j]==' '; j=j+1)
+ ;
+ if (j<i+8 && (Memc[j] != EOS || Memc[j] != '\n'))
+ next
+ if (Memc[j] == '=' && Memc[j+1] != ' ')
+ next
+ for (; j<i+IDB_RECLEN && Memc[j] != EOS; j=j+1)
+ ;
+ if (Memc[j-1] == '\n')
+ Memc[j-1] = EOS
+ if (j == i + IDB_RECLEN || Memc[j] == '\n')
+ Memc[j] = EOS
+ if (strlen (Memc[ua]) + IDB_RECLEN >= max_lenuserarea)
+ call error (1,
+ "Possibly failed to add all the keywords")
+ call fprintf (out, "%s%*t\n")
+ call pargstr (Memc[i])
+ call pargi (IDB_RECLEN+1)
+ }
+ } then {
+ call erract (EA_WARN)
+
+ # Check for truncated card.
+ call close (out)
+ for (i=ua+max_lenuserarea-1; i > ua; i=i-1) {
+ if (Memc[i] == '\n') {
+ Memc[i+1] = EOS
+ break
+ }
+ }
+ }
+ call close (out)
+ call close (fd)
+ call sfree (sp)
+ }
+ if (verbose) {
+ if (append) {
+ call printf ("%s: Image header from %s appended\n")
+ call pargstr (IM_HDRFILE(im))
+ call pargstr (fname)
+ } else {
+ call printf ("%s: Image header from %s substituted\n")
+ call pargstr (IM_HDRFILE(im))
+ call pargstr (fname)
+ }
+ }
+end
+
+
+# MKH_COMMENT -- Add comment to header.
+# Remove any tab characters.
+
+procedure mkh_comment (im, comment)
+
+pointer im # image descriptor
+char comment[ARB] # comment
+
+begin
+ call imputh (im, COMMENT, comment)
+end
+
+## MKH_COMMENT -- Add comment to header.
+## Remove any tab characters.
+#
+#define MAX_COMMENT 72
+#
+#procedure mkh_comment (im, comment)
+#
+#pointer im # image descriptor
+#char comment[ARB] # comment
+#
+#int i, j, k, stridxs()
+#pointer sp, cmmt
+#
+#begin
+# if (stridxs ("\t", comment) == 0) {
+# call imputh (im, COMMENT, comment)
+# return
+# }
+#
+# call smark (sp)
+# call salloc (cmmt, MAX_COMMENT, TY_CHAR)
+# j = 0
+# for (i=1; comment[i]!=EOS; i=i+1) {
+# if (comment[i] == '\t') {
+# k = min (j + 8 - mod (j, 8), MAX_COMMENT)
+# for (; j < k; j=j+1)
+# Memc[cmmt+j] = ' '
+# } else {
+# Memc[cmmt+j] = comment[i]
+# j = j + 1
+# }
+# if (j == MAX_COMMENT)
+# break
+# }
+# Memc[cmmt+j] = EOS
+# call imputh (im, COMMENT, Memc[cmmt])
+# call sfree (sp)
+#end
+
+
+# MKH_COMMENT1 -- Make comment out of CL parameter.
+
+procedure mkh_comment1 (im, param, type)
+
+pointer im # image descriptor
+char param[ARB] # parameter name
+int type # datatype
+
+bool clgetb()
+int clgeti()
+real clgetr()
+double clgetd()
+pointer sp, comment, str
+
+begin
+ call smark (sp)
+ call salloc (comment, LEN_COMMENT, TY_CHAR)
+
+ switch (type) {
+ case 'b':
+ call sprintf (Memc[comment], LEN_COMMENT, "%9t%s%24t%b")
+ call pargstr (param)
+ call pargb (clgetb (param))
+ case 'i':
+ call sprintf (Memc[comment], LEN_COMMENT, "%9t%s%24t%d")
+ call pargstr (param)
+ call pargi (clgeti (param))
+ case 'r':
+ call sprintf (Memc[comment], LEN_COMMENT, "%9t%s%24t%g")
+ call pargstr (param)
+ call pargr (clgetr (param))
+ case 'd':
+ call sprintf (Memc[comment], LEN_COMMENT, "%9t%s%24t%g")
+ call pargstr (param)
+ call pargd (clgetd (param))
+ case 's':
+ call salloc (str, SZ_FNAME, TY_CHAR)
+ call clgstr (param, Memc[str], SZ_FNAME)
+ call sprintf (Memc[comment], LEN_COMMENT, "%9t%s%24t%s")
+ call pargstr (param)
+ call pargstr (Memc[str])
+ }
+
+ call mkh_comment (im, Memc[comment])
+ call sfree (sp)
+end
diff --git a/noao/artdata/mknoise.par b/noao/artdata/mknoise.par
new file mode 100644
index 00000000..f8c13ff3
--- /dev/null
+++ b/noao/artdata/mknoise.par
@@ -0,0 +1,25 @@
+input,s,a,"",,,"List of input images"
+output,s,h,"",,,"List of output images
+
+IF NEW IMAGE"
+title,s,h,"",,,"Title of image"
+ncols,i,h,512,1,,"Number of columns"
+nlines,i,h,512,1,,"Number of lines"
+header,f,h,"artdata$stdheader.dat",,,"Image or header keyword file
+
+NOISE PARAMETERS"
+background,r,h,0.,0.,,"Default background and Poisson background"
+gain,r,h,1.,1.e-9,,"Gain (electrons/DN)"
+rdnoise,r,h,0.,0.,,"Read noise (electrons)"
+poisson,b,h,no,,,"Add Poisson noise?"
+seed,i,h,1,,,"Random number seed
+
+COSMIC RAYS"
+cosrays,s,h,"",,,"List of cosmic ray files"
+ncosrays,i,h,0,0,,"Number of random events (if none in file)"
+energy,r,h,30000.,0.,,"Maximum random energy (electroms)"
+radius,r,h,0.5,0.,,"Radius of cosmic ray (pixels)"
+ar,r,h,1.,0.,1.,"Axial ratio (minor/major)"
+pa,r,h,0.,,,"Position angle (degrees)
+"
+comments,b,h,yes,,,"Add comments to image?"
diff --git a/noao/artdata/mkobjects.par b/noao/artdata/mkobjects.par
new file mode 100644
index 00000000..919a48d1
--- /dev/null
+++ b/noao/artdata/mkobjects.par
@@ -0,0 +1,30 @@
+input,s,a,"",,,"List of input images"
+output,s,h,"",,,"List of output images
+
+IF NEW IMAGE"
+title,s,h,"",,,"Image title"
+ncols,i,h,512,1,,"Number of columns"
+nlines,i,h,512,1,,"Number of lines"
+header,f,h,"artdata$stdheader.dat",,,"Image or header keyword file"
+background,r,h,1000.,0.,,"Default background (in ADU)
+
+OBJECT PARAMETERS"
+objects,s,h,"",,,"List of objects files"
+xoffset,r,h,0.,,,"X coordinate offset"
+yoffset,r,h,0.,,,"Y coordinate offset"
+star,s,h,"moffat",,,"Star"
+radius,r,h,1.,0.,,"Seeing radius/scale (pixels)"
+beta,r,h,2.5,1.01,,"Moffat parameter"
+ar,r,h,1.,0.,1.,"Axial ratio (minor/major)"
+pa,r,h,0.,,,"Position angle (degrees)"
+distance,r,h,1.,1e-6,,"Relative distance"
+exptime,r,h,1.,1e-6,,"Exposure time"
+magzero,r,h,7.,,,"Magnitude zero point
+
+NOISE PARAMETERS"
+gain,r,h,1.,1.e-9,,"Gain (electrons/ADU)"
+rdnoise,r,h,0.,0.,,"Read noise (electrons)"
+poisson,b,h,no,,,"Add Poisson noise?"
+seed,i,h,1,,,"Random number seed
+"
+comments,b,h,yes,,,"Add comments to image?"
diff --git a/noao/artdata/mkpattern.par b/noao/artdata/mkpattern.par
new file mode 100644
index 00000000..1577558c
--- /dev/null
+++ b/noao/artdata/mkpattern.par
@@ -0,0 +1,20 @@
+input,s,a,"",,,"Images to create or modify"
+output,s,h,"",,,"Output images"
+pattern,s,h,"constant","constant|grid|checker|coordinates|slope|square",,"Pattern"
+option,s,h,"replace","replace|add|multiply",,"Editing option"
+v1,r,h,0.,,,"Pattern value"
+v2,r,h,1.,,,"Pattern value"
+size,i,h,1,1,,"Pattern size
+
+IF NEW IMAGE"
+title,s,h,"",,,"Image title"
+pixtype,s,h,"real","ushort|short|integer|long|real|double|complex",,"Pixel datatype"
+ndim,i,h,2,0,7,"Number of dimensions"
+ncols,i,h,512,1,,"Number of columns"
+nlines,i,h,512,1,,"Number of lines"
+n3,i,h,1,1,,"Number of pixels in 3rd dimension"
+n4,i,h,1,1,,"Number of pixels in 4th dimension"
+n5,i,h,1,1,,"Number of pixels in 5th dimension"
+n6,i,h,1,1,,"Number of pixels in 6th dimension"
+n7,i,h,1,1,,"Number of pixels in 7th dimension"
+header,f,h,"",,,"Image or header keyword file"
diff --git a/noao/artdata/mkpkg b/noao/artdata/mkpkg
new file mode 100644
index 00000000..e1f8a640
--- /dev/null
+++ b/noao/artdata/mkpkg
@@ -0,0 +1,40 @@
+# ARTDATA
+
+$call relink
+$exit
+
+update:
+ $call relink
+ $call install
+ ;
+
+relink:
+ $update libpkg.a
+ $call artdata
+ ;
+
+install:
+ $move xx_artdata.e noaobin$x_artdata.e
+ ;
+
+artdata:
+ $set LIBS = "-lsmw -lxtools -lcurfit -liminterp"
+ $omake x_artdata.x
+ $link x_artdata.o libpkg.a $(LIBS) -o xx_artdata.e
+ ;
+
+libpkg.a:
+ @lists
+ mkheader.x <ctype.h> <imhdr.h> <imio.h>
+ mktemplates.x mktemplates.com <error.h> <imhdr.h> <math.h>\
+ <math/iminterp.h>
+ numrecipes.x <math.h>
+ t_mk1dspec.x <error.h> <imhdr.h>
+ t_mk2dspec.x <error.h> <imhdr.h> <math/iminterp.h>
+ t_mkechelle.x <error.h> <imhdr.h> <math.h>
+ t_mkheader.x <ctype.h> <error.h> <imhdr.h>
+ t_mknoise.x <error.h> <imhdr.h> <mach.h>
+ t_mkobjects.x <error.h> <imhdr.h> <math.h> <mach.h>
+ t_mkpattern.x <error.h> <imhdr.h>
+ voigt.x
+ ;
diff --git a/noao/artdata/mktemplates.com b/noao/artdata/mktemplates.com
new file mode 100644
index 00000000..e2c18b19
--- /dev/null
+++ b/noao/artdata/mktemplates.com
@@ -0,0 +1,9 @@
+int nxc, nyc # Number of PSF centers
+int nxssub, nyssub # Number of star subsamples
+int nxgsub, nygsub # Number of galaxy subsamples
+real dynrange # Profile intensity dynamic range
+real psfrange # PSF convolution dynamic range
+pointer stp, star, see
+
+common /mktcom/ dynrange, psfrange, nxc, nyc, nxssub, nyssub, nxgsub, nygsub,
+ stp, star, see
diff --git a/noao/artdata/mktemplates.x b/noao/artdata/mktemplates.x
new file mode 100644
index 00000000..08fec295
--- /dev/null
+++ b/noao/artdata/mktemplates.x
@@ -0,0 +1,1390 @@
+include <error.h>
+include <imhdr.h>
+include <math.h>
+include <math/iminterp.h>
+
+# Template data structure
+define LEN_MKT 18
+define MKT_PROF Memi[$1] # Pointer to profile
+define MKT_MSI Memi[$1+1] # MSI interpolation pointer
+define MKT_NXM Memi[$1+2] # Number of X points in model
+define MKT_NYM Memi[$1+3] # Number of Y points in model
+define MKT_F Memr[P2R($1+4)] # Fraction of total flux in profile
+define MKT_SCALE Memr[P2R($1+5)] # Radius scale
+
+define MKT_NALLOC Memi[$1+6] # Allocated space for saved templates
+define MKT_N Memi[$1+7] # Number of saved templates
+
+define MKT_DATA Memi[$1+8] # Data pointer
+define MKT_PTRS Memi[$1+9] # Data pointers
+define MKT_NX Memi[$1+10] # Number of X pixels
+define MKT_NY Memi[$1+11] # Number of Y pixels
+define MKT_XC Memi[$1+12] # Subpixel X center
+define MKT_YC Memi[$1+13] # Subpixel Y center
+define MKT_FLUX Memi[$1+14] # Flux
+define MKT_R Memi[$1+15] # Radius
+define MKT_AR Memi[$1+16] # Axial ratio
+define MKT_PA Memi[$1+17] # Position angle
+
+define NALLOC 25 # Allocation block for saved templates
+define NPROF 5001 # Profile length
+define NY 11 # BINPROF binning parameter
+
+
+# MKT_INIT -- Initialize template memory.
+# MKT_FREE -- Free template memory.
+# MKT_SAVE -- Save a template
+# MKT_GET -- Get a template
+# MKT_STAR -- Set star and seeing templates.
+# MKT_OBJECT -- Set object profiles.
+# MKT_GOBJECT -- Get image raster.
+# MKT_BINPROF -- Bin intensity profile
+# MKT_GSTAR -- Get the precomputed template with center nearest (x,y).
+# MKT_PROFILE -- Make template from profile.
+# MKT_MSI -- Make template from image interpolation function.
+
+
+# MKT_INIT -- Initialize template memory.
+# The symbol table is used as a simple way to store the object types by name.
+
+procedure mkt_init ()
+
+int clgeti()
+real clgetr()
+
+pointer stopen()
+include "mktemplates.com"
+
+begin
+ nxc = clgeti ("nxc")
+ nyc = clgeti ("nyc")
+ nxssub = clgeti ("nxsub")
+ nyssub = clgeti ("nysub")
+ nxgsub = clgeti ("nxgsub")
+ nygsub = clgeti ("nygsub")
+ dynrange = clgetr ("dynrange")
+ psfrange = clgetr ("psfrange")
+ stp = stopen ("mkt", 10, 10, 10*SZ_FNAME)
+end
+
+
+# MKT_FREE -- Free template memory.
+
+procedure mkt_free ()
+
+int i
+pointer sym, mkt, sthead(), stnext()
+include "mktemplates.com"
+
+begin
+ # For each object type free the profile and image interpolator data,
+ # the last unsaved data buffer, all saved templates, and the object
+ # structure. Finally free the symbol table.
+
+ for (sym = sthead (stp); sym != NULL; sym = stnext (stp, sym)) {
+ mkt = Memi[sym]
+ if (mkt != NULL) {
+ call mfree (MKT_PROF(mkt), TY_REAL)
+ if (MKT_MSI(mkt) != NULL)
+ call msifree (MKT_MSI(mkt))
+ call mfree (MKT_DATA(mkt), TY_REAL)
+ if (MKT_NALLOC(mkt) > 0) {
+ do i = 0, MKT_N(mkt)-1
+ call mfree (Memi[MKT_PTRS(mkt)+i], TY_REAL)
+ call mfree (MKT_PTRS(mkt), TY_POINTER)
+ call mfree (MKT_NX(mkt), TY_INT)
+ call mfree (MKT_NY(mkt), TY_INT)
+ call mfree (MKT_XC(mkt), TY_REAL)
+ call mfree (MKT_YC(mkt), TY_REAL)
+ call mfree (MKT_FLUX(mkt), TY_REAL)
+ call mfree (MKT_R(mkt), TY_REAL)
+ call mfree (MKT_AR(mkt), TY_REAL)
+ call mfree (MKT_PA(mkt), TY_REAL)
+ }
+ call mfree (mkt, TY_STRUCT)
+ }
+ }
+ call stclose (stp)
+end
+
+
+# MKT_SAVE -- Save a template
+# If a template may be used more than once it may be saved upon direction of
+# the user in the object list. Otherwise the last unsaved template is
+# freed for the next object.
+
+procedure mkt_save (mkt, data, nx, ny, xc, yc, flux, r, ar, pa, save)
+
+pointer mkt #I MKT pointer
+pointer data #I Pointer to template data
+int nx, ny #I Size of template
+real xc, yc #I Subpixel center
+real flux #I Flux
+real r #I Effective radius
+real ar #I Axial ratio
+real pa #I Position angle
+int save #I Save data?
+
+int i
+
+begin
+ if (save == NO) {
+ MKT_DATA(mkt) = data
+ return
+ }
+
+ if (MKT_NALLOC(mkt) == 0) {
+ i = NALLOC
+ call malloc (MKT_PTRS(mkt), i, TY_POINTER)
+ call malloc (MKT_NX(mkt), i, TY_INT)
+ call malloc (MKT_NY(mkt), i, TY_INT)
+ call malloc (MKT_XC(mkt), i, TY_REAL)
+ call malloc (MKT_YC(mkt), i, TY_REAL)
+ call malloc (MKT_FLUX(mkt), i, TY_REAL)
+ call malloc (MKT_R(mkt), i, TY_REAL)
+ call malloc (MKT_AR(mkt), i, TY_REAL)
+ call malloc (MKT_PA(mkt), i, TY_REAL)
+ MKT_NALLOC(mkt) = i
+ } else if (MKT_N(mkt) == MKT_NALLOC(mkt)) {
+ i = MKT_NALLOC(mkt) + NALLOC
+ call realloc (MKT_PTRS(mkt), i, TY_POINTER)
+ call realloc (MKT_NX(mkt), i, TY_INT)
+ call realloc (MKT_NY(mkt), i, TY_INT)
+ call realloc (MKT_XC(mkt), i, TY_REAL)
+ call realloc (MKT_YC(mkt), i, TY_REAL)
+ call realloc (MKT_FLUX(mkt), i, TY_REAL)
+ call realloc (MKT_R(mkt), i, TY_REAL)
+ call realloc (MKT_AR(mkt), i, TY_REAL)
+ call realloc (MKT_PA(mkt), i, TY_REAL)
+ MKT_NALLOC(mkt) = i
+ }
+ i = MKT_N(mkt)
+ Memi[MKT_PTRS(mkt)+i] = data
+ Memi[MKT_NX(mkt)+i] = nx
+ Memi[MKT_NY(mkt)+i] = ny
+ Memr[MKT_XC(mkt)+i] = xc
+ Memr[MKT_YC(mkt)+i] = yc
+ Memr[MKT_FLUX(mkt)+i] = flux
+ Memr[MKT_R(mkt)+i] = r
+ Memr[MKT_AR(mkt)+i] = ar
+ Memr[MKT_PA(mkt)+i] = pa
+ MKT_N(mkt) = i + 1
+end
+
+
+# MKT_GET -- Get a template
+# If not a saved template just free last unsaved template.
+# If saved search for match with position, size, axial ratio, and pa.
+# Return null if not found.
+
+procedure mkt_get (mkt, data, nx, ny, xc, yc, flux, r, ar, pa, save)
+
+pointer mkt #I MKT pointer
+pointer data #O Pointer to template data
+int nx, ny #O Size of template
+real xc, yc #I Subpixel center
+real flux #I Flux
+real r #I Effective radius
+real ar #I Axial ratio
+real pa #I Position angle
+int save #I Get saved template?
+
+int i
+real f
+
+begin
+ data = NULL
+ call mfree (MKT_DATA(mkt), TY_REAL)
+ if (save == NO)
+ return
+
+ for (i=0; i<MKT_N(mkt); i=i+1) {
+ if (xc != Memr[MKT_XC(mkt)+i])
+ next
+ if (yc != Memr[MKT_YC(mkt)+i])
+ next
+ if (r != Memr[MKT_R(mkt)+i])
+ next
+ if (ar != Memr[MKT_AR(mkt)+i])
+ next
+ if (pa != Memr[MKT_PA(mkt)+i])
+ next
+ data = Memi[MKT_PTRS(mkt)+i]
+ nx = Memi[MKT_NX(mkt)+i]
+ ny = Memi[MKT_NY(mkt)+i]
+ f = Memr[MKT_FLUX(mkt)+i]
+ if (f != flux) {
+ call amulkr (Memr[data], flux/f, Memr[data], nx*ny)
+ Memr[MKT_FLUX(mkt)+i] = flux
+ }
+ return
+ }
+end
+
+
+# MKT_STAR -- Define star and seeing templates.
+# The seeing template has a smaller range for efficiency.
+# THe star templates are determined once over a grid of centers and
+# then not evaluated again.
+
+pointer procedure mkt_star (name)
+
+char name[ARB] # Profile name or file
+
+# Star and seeing parameters obatined through CLIO.
+real r # Major axis sigma (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle (radians)
+
+int i, j, nxm, nym, nx, ny, fd
+real dr, flux, radius, seeing, beta, xc, yc, dxc, dyc, der[2]
+pointer sym, mkt1, mkt2, prof, prof1, asi, msi, data, im
+
+bool streq()
+real clgetr(), asieval()
+int open(), fscan(), nscan()
+pointer immap(), imgs2r(), stfind(), stenter()
+errchk immap, open, imgs2r, asifit, asieval, asider
+
+include "mktemplates.com"
+
+begin
+ # Check if previously defined.
+ sym = stfind (stp, "star")
+ if (sym != NULL)
+ return (Memi[sym])
+
+ # Select type of star profile and set intensity profile array.
+ # Compute the fraction of the total flux in the profile.
+ # Insure that the profile subsamples a subpixel.
+
+ star = NULL
+ see = NULL
+ prof = NULL
+ msi = NULL
+ if (streq (name, "gaussian")) {
+ r = clgetr ("radius") / sqrt (log (2.))
+ radius = sqrt (log (dynrange))
+ seeing = sqrt (log (psfrange))
+ nxm = max (NPROF, 1 + nint (radius*r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1
+ Memr[prof+i] = exp (-(i * dr) ** 2)
+ flux = 1 - Memr[prof+nxm-1]
+
+ r = sqrt (log (2.))
+ radius = radius / r
+ seeing = seeing / r
+ } else if (streq (name, "moffat")) {
+ beta = clgetr ("beta")
+ r = clgetr ("radius") / sqrt (2. ** (1/beta) - 1.)
+ radius = sqrt ((dynrange) ** (1/beta) - 1)
+ seeing = sqrt ((psfrange) ** (1/beta) - 1)
+ nxm = max (NPROF, 1 + nint (radius*r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+
+ dr = radius / (nxm - 1)
+ flux = 0
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = 1. / ((1 + r**2) ** beta)
+ flux = flux + r * Memr[prof+i]
+ }
+
+ # Compute the fraction of the total flux in the profile.
+ # The last part of the total flux below is computed by expanding
+ # (1+r**2) --> r**2 under the approximation that r >> 1.
+ # Note that it is possible to explicitly compute the total
+ # flux F(total) = beta / (2 * beta - 2) (CRC 53rd edition)
+ # I found errors in other versions of CRC for this integral!
+
+ r = r + dr / 2
+ xc = 2 * beta - 2
+ flux = flux / (flux + 1. / (xc * r ** xc))
+
+ r = sqrt (2. ** (1/beta) - 1.)
+ radius = radius / r
+ seeing = seeing / r
+ } else ifnoerr (im = immap (name, READ_ONLY, 0)) {
+ iferr {
+ nxm = IM_LEN(im,1)
+ nym = IM_LEN(im,2)
+ data = imgs2r (im, 1, nxm, 1, nym)
+ call msiinit (msi, II_BILINEAR)
+ call msifit (msi, Memr[data], nxm, nym, nxm)
+ } then
+ call erract (EA_WARN)
+ call imunmap (im)
+
+ flux = 1.
+ radius = 1
+ seeing = 0.8
+ } else ifnoerr (fd = open (name, READ_ONLY, TEXT_FILE)) {
+ nxm = NPROF
+ call malloc (prof1, nxm, TY_REAL)
+
+ j = 0
+ while (fscan (fd) != EOF) {
+ call gargr (flux)
+ if (nscan() < 1)
+ next
+ if (j == nxm) {
+ nxm = nxm + NPROF
+ call realloc (prof1, nxm, TY_REAL)
+ }
+ Memr[prof1+j] = flux
+ j = j + 1
+ }
+ call close (fd)
+ if (j == 0) {
+ call mfree (prof1, TY_REAL)
+ call error (1, "PSF template not found")
+ }
+
+ r = clgetr ("radius")
+ nxm = max (NPROF, 1 + nint (r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+ dr = 1. / (nxm - 1)
+ j = j - 1
+
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[prof1], j)
+ xc = Memr[prof1]
+ call mfree (prof1, TY_REAL)
+
+ if (xc == 0.) {
+ flux = 0.
+ do i = 1, nxm - 1 {
+ r = i * dr
+ call asider (asi, 1+j*r, der, 2)
+ Memr[prof+i] = max (0., der[2] / r)
+ flux = flux + r * Memr[prof+i]
+ }
+ Memr[prof] = max (0., 2 * Memr[prof+1] - Memr[prof+2])
+ } else {
+ flux = 0.
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = asieval (asi, 1+j*r)
+ flux = flux + r * Memr[prof+i]
+ }
+ }
+ call asifree (asi)
+
+ xc = 0.9 * flux
+ flux = 0.
+ for (i=1; i<nxm && flux<xc; i=i+1)
+ flux = flux + i * dr * Memr[prof+i]
+
+ flux = 1.
+ radius = 1.
+ seeing = (i - 1.) * dr
+ } else
+ call error (1, "PSF template not found")
+
+ # Set size and orientation parameters.
+ r = clgetr ("radius")
+ ar = clgetr ("ar")
+ pa = DEGTORAD (clgetr ("pa"))
+ radius = r * radius
+ seeing = r * seeing
+
+ # Compute templates with subsampling over a grid of centers.
+ # Do this for the full star profile and a smaller region for
+ # convolving extended objects.
+
+ # Seeing kernel.
+ sym = stenter (stp, "seeing", 1)
+ call calloc (Memi[sym], LEN_MKT, TY_STRUCT)
+ mkt1 = Memi[sym]
+
+ nx = 2 * nint (seeing) + 1
+ ny = nx
+ dxc = 1. / nxc
+ dyc = 1. / nyc
+ if (prof != NULL) {
+ nym = 1 + nint (radius * nxssub * nyssub)
+ call malloc (prof1, nym, TY_REAL)
+ call mkt_binprof (Memr[prof], nxm, Memr[prof1], nym, radius, nxssub)
+ for (yc = -0.5+dyc/2; yc < 0.5; yc = yc+dyc) {
+ for (xc = -0.5+dxc/2; xc < 0.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_profile (data, nx, ny, xc, yc, 1., Memr[prof1],
+ nym, radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt1, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ } else {
+ for (yc = -0.5+dyc/2; yc < 0.5; yc = yc+dyc) {
+ for (xc = -0.5+dxc/2; xc < 0.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_msi (data, nx, ny, xc, yc, 1., msi, nxm, nym,
+ radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt1, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ }
+
+ # Full star templates.
+ sym = stenter (stp, "star", 1)
+ call calloc (Memi[sym], LEN_MKT, TY_STRUCT)
+ mkt2 = Memi[sym]
+
+ nx = 2 * nint (radius) + 1
+ ny = nx
+ dxc = 1. / nxc
+ dyc = 1. / nyc
+ if (prof != NULL) {
+ for (yc = 0.5+dyc/2; yc < 1.5; yc = yc+dyc) {
+ for (xc = 0.5+dxc/2; xc < 1.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_profile (data, nx, ny, xc, yc, flux, Memr[prof1],
+ nym, radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt2, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ call mfree (prof, TY_REAL)
+ call mfree (prof1, TY_REAL)
+ } else {
+ for (yc = 0.5+dyc/2; yc < 1.5; yc = yc+dyc) {
+ for (xc = 0.5+dxc/2; xc < 1.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_msi (data, nx, ny, xc, yc, flux, msi, nxm, nym,
+ radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt2, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ call msifree (msi)
+ }
+
+ see = mkt1
+ star = mkt2
+ return (star)
+end
+
+
+# MKT_OBJECT -- Set object profiles.
+
+pointer procedure mkt_object (name)
+
+char name[ARB] # Profile name or file
+
+int i, j, n, nxm, nym, fd
+real radius, r, dr, s, b, flux, der[2]
+pointer sym, mkt, prof, asi, msi, buf, im
+
+real c3, c4, c5, c6, c7
+
+real asieval()
+double uigamma()
+int open(), fscan(), nscan(), strncmp(), ctor()
+pointer immap(), imgs2r(), stfind(), stenter()
+bool streq()
+errchk open, immap, asifit, asieval, asider
+
+include "mktemplates.com"
+
+begin
+ # Check if previously defined.
+ sym = stfind (stp, name)
+ if (sym != NULL)
+ return (Memi[sym])
+
+ # Select type of profile and set intensity profile array.
+ # Compute the fraction of the total flux in the profile.
+
+ prof = NULL
+ msi = NULL
+ if (strncmp (name, "sersic", 6) == 0) {
+ i = 7
+ if (ctor (name, i, s) == 0) {
+ call eprintf ("WARNING: Bad sersic profile syntax (%s).\n")
+ call pargstr (name)
+ return (NULL)
+ }
+ n = nint (2 * (s + 0.01))
+ s = n / 2.
+ if (n < 1 || n > 20) {
+ call eprintf (
+ "WARNING: Sersic index out of allowed range (%.1f).\n")
+ call pargi (s)
+ return (NULL)
+ }
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange) ** s
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** (1/s)
+ Memr[prof+i] = exp (-r)
+ }
+
+ flux = 1 - uigamma (n, r)
+ r = n - 1./3. + 4./(405.*s) + 46./(25515.*s*s)
+ radius = radius / r ** s
+ } else if (streq (name, "expdisk")) {
+ s = 1.
+ n = nint (2 * s)
+
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange) ** s
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** (1/s)
+ Memr[prof+i] = exp (-r)
+ }
+
+ flux = 1 - uigamma (n, r)
+ r = n - 1./3. + 4./(405.*s) + 46./(25515.*s*s)
+ radius = radius / r ** s
+ } else if (streq (name, "devauc")) {
+ s = 4.
+ n = nint (2 * s)
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange) ** s
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** (1/s)
+ Memr[prof+i] = exp (-r)
+ }
+
+ flux = 1 - uigamma (n, r)
+ r = n - 1./3. + 4./(405.*s) + 46./(25515.*s*s)
+ radius = radius / r ** s
+ } else ifnoerr (im = immap (name, READ_ONLY, 0)) {
+ iferr {
+ nxm = IM_LEN(im,1)
+ nym = IM_LEN(im,2)
+ buf = imgs2r (im, 1, nxm, 1, nym)
+ call msiinit (msi, II_BILINEAR)
+ call msifit (msi, Memr[buf], nxm, nym, nxm)
+ } then
+ call erract (EA_WARN)
+ call imunmap (im)
+
+ flux = 1.
+ radius = 1.
+ } else ifnoerr (fd = open (name, READ_ONLY, TEXT_FILE)) {
+ nxm = NPROF
+ call malloc (buf, nxm, TY_REAL)
+
+ j = 0
+ while (fscan (fd) != EOF) {
+ call gargr (flux)
+ if (nscan() < 1)
+ next
+ if (j == nxm) {
+ nxm = nxm + NPROF
+ call realloc (buf, nxm, TY_REAL)
+ }
+ Memr[buf+j] = flux
+ j = j + 1
+ }
+ call close (fd)
+ if (j == 0) {
+ call mfree (buf, TY_REAL)
+ nxm = 0
+ call error (1, "PSF template not found")
+ }
+
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ dr = 1. / (nxm - 1)
+ j = j - 1
+
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[buf], j)
+ s = Memr[buf]
+ call mfree (buf, TY_REAL)
+
+ if (s == 0.) {
+ do i = 1, nxm - 1 {
+ r = i * dr
+ call asider (asi, 1+j*r, der, 2)
+ Memr[prof+i] = max (0., der[2] / r)
+ }
+ Memr[prof] = max (0., 2 * Memr[prof+1] - Memr[prof+2])
+ } else {
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = asieval (asi, 1+j*r)
+ }
+ }
+ call asifree (asi)
+
+ flux = 1.
+ radius = 1.
+ } else {
+ call eprintf ("WARNING: Object template %s not found.\n")
+ call pargstr (name)
+ return (NULL)
+ }
+
+ # Create the template structure if a model is defined..
+ if (prof == NULL && msi == NULL)
+ mkt = NULL
+ else {
+ call calloc (mkt, LEN_MKT, TY_STRUCT)
+ MKT_PROF(mkt) = prof
+ MKT_MSI(mkt) = msi
+ MKT_NXM(mkt) = nxm
+ MKT_NYM(mkt) = nym
+ MKT_F(mkt) = flux
+ MKT_SCALE(mkt) = radius
+#call eprintf ("flux = %g, radius = %g\n")
+#call pargr (MKT_F(mkt))
+#call pargr (MKT_SCALE(mkt))
+#do i = 0, nxm {
+#call eprintf ("%d: %g\n")
+#call pargi (i)
+#call pargr (Memr[prof+i])
+#}
+ }
+
+ # Enter object model name in symbol table.
+ sym = stenter (stp, name, 1)
+ Memi[sym] = mkt
+ return (mkt)
+end
+
+
+# MKT_GOBJECT -- Get image raster.
+# The profile type is set by the template structure.
+
+procedure mkt_gobject (mkt, data, nx, ny, x, y, z, r, ar, pa, save)
+
+pointer mkt # Object template
+pointer data # Data
+int nx, ny # Size of returned data
+real x, y # Position of object
+real z # Flux of object
+real r # Major axis scale (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle (radians)
+int save # Use/save template?
+
+real xc, yc, radius
+int nprof
+pointer prof
+
+include "mktemplates.com"
+
+begin
+ data = NULL
+ if (mkt == NULL)
+ return
+
+ # Stars are predefined. Return the nearest template center.
+ # Other objects are computed with or without seeing.
+
+ if (mkt == star)
+ call mkt_gstar (star, data, nx, ny, x, y, z)
+ else {
+ xc = x + 0.5 - int (x + 0.5)
+ yc = y + 0.5 - int (y + 0.5)
+ call mkt_get (mkt, data, nx, ny, xc, yc, z, r, ar, pa, save)
+ if (data != NULL)
+ return
+
+ radius = r * MKT_SCALE(mkt)
+ nx = max (3, 2 * nint (radius) + 1)
+ ny = nx
+ if (see != NULL) {
+ nx = nx + Memi[MKT_NX(see)] / 2
+ ny = ny + Memi[MKT_NY(see)] / 2
+ }
+ call malloc (data, nx * ny, TY_REAL)
+
+ if (MKT_PROF(mkt) != 0) {
+ nprof = 1 + nint (radius * nxgsub * nxgsub)
+ call malloc (prof, nprof, TY_REAL)
+ call mkt_binprof (Memr[MKT_PROF(mkt)], MKT_NXM(mkt),
+ Memr[prof], nprof, radius, nxgsub)
+ call mkt_profile (data, nx, ny, x, y, z*MKT_F(mkt), Memr[prof],
+ nprof, radius, ar, pa, nxgsub, nygsub)
+ call mfree (prof, TY_REAL)
+ } else {
+ call mkt_msi (data, nx, ny, x, y, z, MKT_MSI(mkt),
+ MKT_NXM(mkt), MKT_NYM(mkt), radius, ar, pa,
+ nxgsub, nygsub)
+ }
+
+ call mkt_save (mkt, data, nx, ny, xc, yc, z, r, ar, pa, save)
+ }
+end
+
+
+# MKT_BINPROF -- Bin intensity profile into subpixels
+
+procedure mkt_binprof (prof, nprof, prof1, nprof1, radius, nsub)
+
+real prof[nprof] # Input intensity profile
+int nprof # Number of input points
+real prof1[nprof] # Output binned intensity profile
+int nprof1 # Number of output points
+real radius # Radius of profile
+int nsub # Maximum subsampling
+
+int i, j, k, k1, k2, l, dx
+real scale, dy, val
+
+int debug, open()
+data debug/0/
+
+begin
+ if (radius < 0.1) {
+ call amovkr (1., prof1, nprof1)
+ return
+ } else
+ call aclrr (prof1, nprof1)
+
+ # Set binning parameters
+ scale = (nprof - 1.) / (nprof1 - 1.)
+ dx = nint ((nprof1 - 1.) / nsub / radius / 2.)
+ dy = dx / (NY - 1.)
+
+ # Bin central pixels
+ do i = -dx, 2*dx {
+ k = abs (i)
+ k1 = max (1, i - dx + 1)
+ k2 = i + dx + 1
+ do j = 0, NY-1 {
+ if (j == 0)
+ val = k
+ else if (k == 0)
+ val = dy * j
+ else
+ val = k * sqrt (1. + (dy * j / k) ** 2)
+ l = nint (scale * val + 1)
+ if (l > nprof)
+ next
+ val = prof[l] / NY
+ do l = k1, k2
+ prof1[l] = prof1[l] + val
+ }
+ }
+
+ # Now bin remainder of pixels more crudely
+ do i = 2*dx+1, nprof1-1 {
+ k1 = i - dx + 1
+ k2 = min (nprof1, i + dx + 1)
+ val = prof[nint (scale * i + 1)]
+ do k = k1, k2
+ prof1[k] = prof1[k] + val
+ }
+
+ if (debug == YES) {
+ j = open ("debug1.dat", APPEND, TEXT_FILE)
+ do i = 1, nprof {
+ call fprintf (j, "%d %g\n")
+ call pargi (i)
+ call pargr (prof[i])
+ }
+ call close (j)
+
+ j = open ("debug2.dat", APPEND, TEXT_FILE)
+ do i = 1, nprof1 {
+ call fprintf (j, "%d %g\n")
+ call pargi (i)
+ call pargr (prof1[i])
+ }
+ call close (j)
+ }
+end
+
+
+# MKT_GSTAR -- Get the precomputed template with center nearest (x,y).
+
+procedure mkt_gstar (mkt, data, nx, ny, x, y, z)
+
+pointer mkt # Template
+pointer data # Data
+int nx, ny # Size of data
+real x, y # Position of object
+real z # Flux of object
+
+int i, j
+real f
+
+include "mktemplates.com"
+
+begin
+ i = (x + 0.5 - int (x + 0.5)) * nxc
+ j = (y + 0.5 - int (y + 0.5)) * nyc
+ i = j * nxc + i
+ data = Memi[MKT_PTRS(mkt)+i]
+ nx = Memi[MKT_NX(mkt)+i]
+ ny = Memi[MKT_NY(mkt)+i]
+ f = Memr[MKT_FLUX(mkt)+i]
+ if (f != z) {
+ call amulkr (Memr[data], z/f, Memr[data], nx*ny)
+ Memr[MKT_FLUX(mkt)+i] = z
+ }
+end
+
+
+# MKT_PROFILE -- Make template from profile.
+
+procedure mkt_profile (data, nx, ny, xc, yc, flux, prof, npts, radius,
+ ar, pa, nxsub, nysub)
+
+pointer data # Pointer to data array
+int nx, ny # Size of template
+real xc, yc # Model center
+real flux # Model flux
+real prof[npts] # 1D profile
+int npts # Number of points in profile
+real radius # Major axis radius of profile (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle relative to major axis (radians)
+int nxsub, nysub # Number of subpixel samples
+
+int i, n, nxs, nys, nxs2, nys2, xs1, xs2, ys1, ys2
+int x, y, x2, y2
+real a, b, c, r, s, t, z, sum, sum1, asumr()
+real dx, dy, dsub, dsub2
+real x1, y1, xc1, yc1
+pointer ptr, ptr1, ptr2, see1, see2
+
+include "mktemplates.com"
+
+define see_ 99
+
+begin
+ # Switch on the size of the seeing templates.
+ if (see != NULL)
+ if (Memi[MKT_NX(see)] * Memi[MKT_NY(see)] > 1)
+ goto see_
+
+# NO SEEING:
+
+ # If the radius is very small return delta function.
+ if (radius < 0.1) {
+ call aclrr (Memr[data], nx*ny)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ return
+ }
+
+ # Compute elliptical scale factors for entry into profile array.
+ r = ((npts - 1) / radius) ** 2
+ t = ((npts - 1) / (ar * radius)) ** 2
+ c = cos (pa)
+ s = sin (pa)
+ a = r * c * c + t * s * s
+ b = 2 * (r - t) * c * s
+ c = r * s * s + t * c * c
+
+ # Subsample the profile and sum into each pixel.
+ n = nx * ny
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+
+ sum1 = 0.
+ ptr = data
+ do y = 0, ny-1 {
+ t = (y - yc1) ** 2
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + t)
+ dsub = 1. / max (1, nxsub - i)
+ sum = 0.
+ for (y1 = y-0.5+dsub/2; y1 < y+0.5; y1 = y1+dsub) {
+ dy = (y1 - yc1)
+ s = c * dy**2
+ for (x1=x-0.5+dsub/2; x1<x+0.5; x1=x1+dsub) {
+ dx = (x1-xc1)
+ i = sqrt (a * dx**2 + b * dx * dy + s) + 1.5
+ if (i <= npts)
+ sum = sum + prof[i]
+ }
+ }
+ sum = sum * dsub ** 2
+ sum1 = sum1 + sum
+ Memr[ptr] = sum
+ ptr = ptr + 1
+ }
+ }
+
+ # If the subsamples completely miss and signal is zero then return
+ # delta function otherwise scale to requested flux.
+
+ if (sum1 == 0.)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ else
+ call amulkr (Memr[data], flux/sum1, Memr[data], n)
+ return
+
+# WITH SEEING:
+
+see_ n = nx * ny
+ call aclrr (Memr[data], n)
+ sum = 0.
+
+ nxs = Memi[MKT_NX(see)]
+ nys = Memi[MKT_NY(see)]
+ nxs2 = nxs/2
+ nys2 = nys/2
+
+ # If the profile is very small return full star image rather than
+ # convolution with truncated seeing template.
+
+ if (radius > 0.01) {
+ r = ((npts - 1) / radius) ** 2
+ t = ((npts - 1) / (ar * radius)) ** 2
+ c = cos (pa)
+ s = sin (pa)
+ a = r * c * c + t * s * s
+ b = 2 * (r - t) * c * s
+ c = r * s * s + t * c * c
+
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+ ptr = data-nys2*nx-nxs2
+ do y = 0, ny-1 {
+ t = (y - yc1) ** 2
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + t)
+ dsub = 1. / max (1, nxsub - i)
+ dsub2 = dsub ** 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ptr1 = ptr + xs1
+ for (y1=y-0.5+dsub/2; y1<y+0.5; y1=y1+dsub) {
+ dy = (y1 - yc1)
+ s = c * dy**2
+ for (x1=x-0.5+dsub/2; x1<x+0.5; x1=x1+dsub) {
+ dx = (x1-xc1)
+ i = sqrt (a * dx**2 + b * dx * dy + s) + 1.5
+ if (i <= npts) {
+ z = prof[i] * dsub2
+ call mkt_gstar (see, see1, nxs, nys, x1, y1, 1.)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] +
+ z * Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ }
+ }
+ }
+ ptr = ptr + 1
+ }
+ }
+ sum = asumr (Memr[data], n)
+ }
+
+ # If no flux is accumulated or radius is very small return star image.
+ # Otherwise scale to requested flux.
+
+ if (sum == 0.) {
+ call mkt_gstar (star, see1, nxs, nys, xc, yc, flux)
+ x = nx/2
+ y = ny/2
+ nxs2 = nxs / 2
+ nys2 = nys / 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ ptr1 = data-nys2*nx-nxs2+(y*nx+x+xs1)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ } else
+ call amulkr (Memr[data], flux/sum, Memr[data], n)
+end
+
+
+# MKT_MSI -- Make template from image interpolation function.
+
+procedure mkt_msi (data, nx, ny, xc, yc, flux, model, nxm, nym, radius,
+ ar, pa, nxsub, nysub)
+
+pointer data # Pointer to data array
+int nx, ny # Size of template
+real xc, yc # Model center
+real flux # Model flux
+pointer model # Surface interpolation pointer for image template
+int nxm, nym # Number of points in image template
+real radius # Major axis radius of profile (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle relative to major axis (radians)
+int nxsub, nysub # Number of subpixel samples
+
+int i, n, nxs, nys, nxs2, nys2, xs1, xs2, ys1, ys2, x, y, x2, y2
+real a, b, c, s, xcm, ycm, x1, y1, xc1, yc1, dsub, sum, sum1
+real ac, as, bc, bs, acdx1, acdx2, bsdx1, bsdx2, bcdy1, bcdy2, asdy1, asdy2
+real val1, val2, val3, val4, minval, maxval, xm[5], ym[5]
+real asumr(), msigrl(), msisqgrl()
+pointer ptr, ptr1, ptr2, see1, see2
+
+include "mktemplates.com"
+
+define see_ 99
+
+begin
+ # Switch on the size of the seeing templates.
+ if (see != NULL)
+ if (Memi[MKT_NX(see)] * Memi[MKT_NY(see)] > 1)
+ goto see_
+
+# NO SEEING:
+
+ # If the radius is very small return delta function.
+ if (radius < 0.1) {
+ call aclrr (Memr[data], nx*ny)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ return
+ }
+
+ a = (nxm / 2.) / radius
+ b = (nym / 2.) / (ar * radius)
+ c = cos (pa)
+ s = sin (pa)
+ ac = a * c
+ as = a * s
+ bc = b * c
+ bs = b * s
+ a = nxm
+ b = nym
+ xcm = nxm / 2 + 1.
+ ycm = nym / 2 + 1.
+
+ # Subsample the profile and sum into each pixel.
+ n = nx * ny
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+
+ sum1 = 0.
+ ptr = data
+ do y = 0, ny-1 {
+ c = (y - yc1) ** 2
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + c)
+ dsub = 1. / max (1, nxsub - i)
+ sum = 0.
+ for (y1 = y-0.5; y1 < y+0.5-dsub/2; y1 = y1+dsub) {
+ asdy1 = (y1 - yc1)
+ asdy2 = asdy1 + dsub
+ bcdy1 = bc * asdy1 + ycm
+ bcdy2 = bc * asdy2 + ycm
+ if (pa == 0.) {
+ val3 = max (1., bcdy1)
+ if (val3 >= nym)
+ next
+ val4 = min (b, bcdy2)
+ if (val3 >= val4)
+ next
+ }
+ asdy1 = as * asdy1
+ asdy2 = as * asdy2
+ for (x1=x-0.5; x1<x+0.5-dsub/2; x1=x1+dsub) {
+ bsdx1 = (x1-xc1)
+ bsdx2 = bsdx1 + dsub
+ acdx1 = ac * bsdx1 + xcm
+ acdx2 = ac * bsdx2 + xcm
+ if (pa == 0.) {
+ val1 = max (1., acdx1)
+ if (val1 >= nxm)
+ next
+ val2 = min (a, acdx2)
+ if (val1 >= val2)
+ next
+ s = msisqgrl (model, val1, val2, val3, val4)
+ } else {
+ bsdx1 = bs * bsdx1
+ bsdx2 = bs * bsdx2
+
+ val1 = acdx1 + asdy1
+ val2 = acdx2 + asdy1
+ val3 = acdx2 + asdy2
+ val4 = acdx1 + asdy2
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= a || maxval <= 1.)
+ next
+ xm[1] = max (1., min (a, val1))
+ xm[2] = max (1., min (a, val2))
+ xm[3] = max (1., min (a, val3))
+ xm[4] = max (1., min (a, val4))
+ xm[5] = xm[1]
+
+ val1 = bcdy1 - bsdx1
+ val2 = bcdy1 - bsdx2
+ val3 = bcdy2 - bsdx2
+ val4 = bcdy2 - bsdx1
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= b || maxval <= 1.)
+ next
+ ym[1] = max (1., min (b, val1))
+ ym[2] = max (1., min (b, val2))
+ ym[3] = max (1., min (b, val3))
+ ym[4] = max (1., min (b, val4))
+ ym[5] = ym[1]
+ y2 = 1
+ do x2 = 2, 4
+ if (ym[x2] < ym[y2])
+ y2 = x2
+ switch (y2) {
+ case 2:
+ xm[1] = xm[2]; ym[1] = ym[2]
+ xm[2] = xm[3]; ym[2] = ym[3]
+ xm[3] = xm[4]; ym[3] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 3:
+ xm[1] = xm[3]; ym[1] = ym[3]
+ xm[3] = xm[5]; ym[3] = ym[5]
+ xm[5] = xm[2]; ym[5] = ym[2]
+ xm[2] = xm[4]; ym[2] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 4:
+ xm[5] = xm[4]; ym[5] = ym[4]
+ xm[4] = xm[3]; ym[4] = ym[3]
+ xm[3] = xm[2]; ym[3] = ym[2]
+ xm[2] = xm[1]; ym[2] = ym[1]
+ xm[1] = xm[5]; ym[1] = ym[5]
+ }
+
+ s = msigrl (model, xm, ym, 5)
+ }
+ sum = sum + s
+ }
+ }
+ sum1 = sum1 + sum
+ Memr[ptr] = sum
+ ptr = ptr + 1
+ }
+ }
+
+ call amulkr (Memr[data], flux/sum1, Memr[data], n)
+ return
+
+# SEEING:
+
+see_ n = nx * ny
+ call aclrr (Memr[data], n)
+ sum = 0.
+
+ nxs = Memi[MKT_NX(see)]
+ nys = Memi[MKT_NY(see)]
+ nxs2 = nxs/2
+ nys2 = nys/2
+
+ # If the profile is very small return full star image rather than
+ # convolution with truncated seeing template.
+
+ if (radius > 0.01) {
+ a = (nxm / 2.) / radius
+ b = (nym / 2.) / (ar * radius)
+ c = cos (pa)
+ s = sin (pa)
+ ac = a * c
+ as = a * s
+ bc = b * c
+ bs = b * s
+ a = nxm
+ b = nym
+ xcm = nxm / 2 + 1.
+ ycm = nym / 2 + 1.
+
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+ ptr = data-nys2*nx-nxs2
+ do y = 0, ny-1 {
+ c = (y - yc1) ** 2
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + c)
+ dsub = 1. / max (1, nxsub - i)
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ptr1 = ptr + xs1
+ for (y1=y-0.5; y1<y+0.5-dsub/2; y1=y1+dsub) {
+ asdy1 = (y1 - yc1)
+ asdy2 = asdy1 + dsub
+ bcdy1 = bc * asdy1 + ycm
+ bcdy2 = bc * asdy2 + ycm
+ if (pa == 0.) {
+ val3 = max (1., bcdy1)
+ if (val3 >= nym)
+ next
+ val4 = min (b, bcdy2)
+ if (val3 >= val4)
+ next
+ }
+ asdy1 = as * asdy1
+ asdy2 = as * asdy2
+ for (x1=x-0.5; x1<x+0.5-dsub/2; x1=x1+dsub) {
+ bsdx1 = (x1-xc1)
+ bsdx2 = bsdx1 + dsub
+ acdx1 = ac * bsdx1 + xcm
+ acdx2 = ac * bsdx2 + xcm
+ if (pa == 0.) {
+ val1 = max (1., acdx1)
+ if (val1 >= nxm)
+ next
+ val2 = min (a, acdx2)
+ if (val1 >= val2)
+ next
+ sum = msisqgrl (model, val1, val2, val3, val4)
+ } else {
+ bsdx1 = bs * bsdx1
+ bsdx2 = bs * bsdx2
+
+ val1 = acdx1 + asdy1
+ val2 = acdx2 + asdy1
+ val3 = acdx2 + asdy2
+ val4 = acdx1 + asdy2
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= a || maxval <= 1.)
+ next
+ xm[1] = max (1., min (a, val1))
+ xm[2] = max (1., min (a, val2))
+ xm[3] = max (1., min (a, val3))
+ xm[4] = max (1., min (a, val4))
+ xm[5] = xm[1]
+
+ val1 = bcdy1 - bsdx1
+ val2 = bcdy1 - bsdx2
+ val3 = bcdy2 - bsdx2
+ val4 = bcdy2 - bsdx1
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= b || maxval <= 1.)
+ next
+ ym[1] = max (1., min (b, val1))
+ ym[2] = max (1., min (b, val2))
+ ym[3] = max (1., min (b, val3))
+ ym[4] = max (1., min (b, val4))
+ ym[5] = ym[1]
+
+# The following is put in to work around a bug in msigrl in V2.8. When
+# V2.8 is long gone we can take this stuff out since msigrl will do the
+# rotating of the coordinates itself.
+
+ minval = max (1., minval)
+ y2 = 1
+ do x2 = 2, 4
+ if (ym[x2] < ym[y2])
+ y2 = x2
+ switch (y2) {
+ case 2:
+ xm[1] = xm[2]; ym[1] = ym[2]
+ xm[2] = xm[3]; ym[2] = ym[3]
+ xm[3] = xm[4]; ym[3] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 3:
+ xm[1] = xm[3]; ym[1] = ym[3]
+ xm[3] = xm[5]; ym[3] = ym[5]
+ xm[5] = xm[2]; ym[5] = ym[2]
+ xm[2] = xm[4]; ym[2] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 4:
+ xm[5] = xm[4]; ym[5] = ym[4]
+ xm[4] = xm[3]; ym[4] = ym[3]
+ xm[3] = xm[2]; ym[3] = ym[2]
+ xm[2] = xm[1]; ym[2] = ym[1]
+ xm[1] = xm[5]; ym[1] = ym[5]
+ }
+
+ sum = msigrl (model, xm, ym, 5)
+ }
+ call mkt_gstar (see, see1, nxs, nys, x1, y1, 1.)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + sum * Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ }
+ }
+ ptr = ptr + 1
+ }
+ }
+ sum = asumr (Memr[data], n)
+ }
+
+ # If no flux is accumulated or radius is very small return star image.
+ # Otherwise scale to requested flux.
+
+ if (sum == 0.) {
+ call mkt_gstar (star, see1, nxs, nys, xc, yc, flux)
+ x = nx/2
+ y = ny/2
+ nxs2 = nxs / 2
+ nys2 = nys / 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ ptr1 = data-nys2*nx-nxs2+(y*nx+x+xs1)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ } else
+ call amulkr (Memr[data], flux/sum, Memr[data], n)
+end
+
+
+# UIGAMMA -- Upper Incomplete Gamma Function ratioed to Complete.
+#
+# uigamma(n,x) = e^(x) sum (x^k/k!) for k=0, n-1
+
+double procedure uigamma (n, x)
+
+int n #I argument
+real x #I argument
+
+int i
+double uigamma, numerator, denominator
+
+begin
+ numerator = exp(-x)
+ denominator = 1
+ uigamma = numerator / denominator
+ do i = 1, n-1 {
+ numerator = numerator * x
+ denominator = denominator * i
+ uigamma = uigamma + numerator / denominator
+ }
+ return (uigamma)
+end
diff --git a/noao/artdata/mktemplates.xBAK b/noao/artdata/mktemplates.xBAK
new file mode 100644
index 00000000..ba8cac01
--- /dev/null
+++ b/noao/artdata/mktemplates.xBAK
@@ -0,0 +1,1326 @@
+include <error.h>
+include <imhdr.h>
+include <math.h>
+include <math/iminterp.h>
+
+# Template data structure
+define LEN_MKT 18
+define MKT_PROF Memi[$1] # Pointer to profile
+define MKT_MSI Memi[$1+1] # MSI interpolation pointer
+define MKT_NXM Memi[$1+2] # Number of X points in model
+define MKT_NYM Memi[$1+3] # Number of Y points in model
+define MKT_F Memr[P2R($1+4)] # Fraction of total flux in profile
+define MKT_SCALE Memr[P2R($1+5)] # Radius scale
+
+define MKT_NALLOC Memi[$1+6] # Allocated space for saved templates
+define MKT_N Memi[$1+7] # Number of saved templates
+
+define MKT_DATA Memi[$1+8] # Data pointer
+define MKT_PTRS Memi[$1+9] # Data pointers
+define MKT_NX Memi[$1+10] # Number of X pixels
+define MKT_NY Memi[$1+11] # Number of Y pixels
+define MKT_XC Memi[$1+12] # Subpixel X center
+define MKT_YC Memi[$1+13] # Subpixel Y center
+define MKT_FLUX Memi[$1+14] # Flux
+define MKT_R Memi[$1+15] # Radius
+define MKT_AR Memi[$1+16] # Axial ratio
+define MKT_PA Memi[$1+17] # Position angle
+
+define NALLOC 25 # Allocation block for saved templates
+define NPROF 5001 # Profile length
+define NY 11 # BINPROF binning parameter
+
+
+# MKT_INIT -- Initialize template memory.
+# MKT_FREE -- Free template memory.
+# MKT_SAVE -- Save a template
+# MKT_GET -- Get a template
+# MKT_STAR -- Set star and seeing templates.
+# MKT_OBJECT -- Set object profiles.
+# MKT_GOBJECT -- Get image raster.
+# MKT_BINPROF -- Bin intensity profile
+# MKT_GSTAR -- Get the precomputed template with center nearest (x,y).
+# MKT_PROFILE -- Make template from profile.
+# MKT_MSI -- Make template from image interpolation function.
+
+
+# MKT_INIT -- Initialize template memory.
+# The symbol table is used as a simple way to store the object types by name.
+
+procedure mkt_init ()
+
+int clgeti()
+real clgetr()
+
+pointer stopen()
+include "mktemplates.com"
+
+begin
+ nxc = clgeti ("nxc")
+ nyc = clgeti ("nyc")
+ nxssub = clgeti ("nxsub")
+ nyssub = clgeti ("nysub")
+ nxgsub = clgeti ("nxgsub")
+ nygsub = clgeti ("nygsub")
+ dynrange = clgetr ("dynrange")
+ psfrange = clgetr ("psfrange")
+ stp = stopen ("mkt", 10, 10, 10*SZ_FNAME)
+end
+
+
+# MKT_FREE -- Free template memory.
+
+procedure mkt_free ()
+
+int i
+pointer sym, mkt, sthead(), stnext()
+include "mktemplates.com"
+
+begin
+ # For each object type free the profile and image interpolator data,
+ # the last unsaved data buffer, all saved templates, and the object
+ # structure. Finally free the symbol table.
+
+ for (sym = sthead (stp); sym != NULL; sym = stnext (stp, sym)) {
+ mkt = Memi[sym]
+ if (mkt != NULL) {
+ call mfree (MKT_PROF(mkt), TY_REAL)
+ if (MKT_MSI(mkt) != NULL)
+ call msifree (MKT_MSI(mkt))
+ call mfree (MKT_DATA(mkt), TY_REAL)
+ if (MKT_NALLOC(mkt) > 0) {
+ do i = 0, MKT_N(mkt)-1
+ call mfree (Memi[MKT_PTRS(mkt)+i], TY_REAL)
+ call mfree (MKT_PTRS(mkt), TY_POINTER)
+ call mfree (MKT_NX(mkt), TY_INT)
+ call mfree (MKT_NY(mkt), TY_INT)
+ call mfree (MKT_XC(mkt), TY_REAL)
+ call mfree (MKT_YC(mkt), TY_REAL)
+ call mfree (MKT_FLUX(mkt), TY_REAL)
+ call mfree (MKT_R(mkt), TY_REAL)
+ call mfree (MKT_AR(mkt), TY_REAL)
+ call mfree (MKT_PA(mkt), TY_REAL)
+ }
+ call mfree (mkt, TY_STRUCT)
+ }
+ }
+ call stclose (stp)
+end
+
+
+# MKT_SAVE -- Save a template
+# If a template may be used more than once it may be saved upon direction of
+# the user in the object list. Otherwise the last unsaved template is
+# freed for the next object.
+
+procedure mkt_save (mkt, data, nx, ny, xc, yc, flux, r, ar, pa, save)
+
+pointer mkt #I MKT pointer
+pointer data #I Pointer to template data
+int nx, ny #I Size of template
+real xc, yc #I Subpixel center
+real flux #I Flux
+real r #I Effective radius
+real ar #I Axial ratio
+real pa #I Position angle
+int save #I Save data?
+
+int i
+
+begin
+ if (save == NO) {
+ MKT_DATA(mkt) = data
+ return
+ }
+
+ if (MKT_NALLOC(mkt) == 0) {
+ i = NALLOC
+ call malloc (MKT_PTRS(mkt), i, TY_POINTER)
+ call malloc (MKT_NX(mkt), i, TY_INT)
+ call malloc (MKT_NY(mkt), i, TY_INT)
+ call malloc (MKT_XC(mkt), i, TY_REAL)
+ call malloc (MKT_YC(mkt), i, TY_REAL)
+ call malloc (MKT_FLUX(mkt), i, TY_REAL)
+ call malloc (MKT_R(mkt), i, TY_REAL)
+ call malloc (MKT_AR(mkt), i, TY_REAL)
+ call malloc (MKT_PA(mkt), i, TY_REAL)
+ MKT_NALLOC(mkt) = i
+ } else if (MKT_N(mkt) == MKT_NALLOC(mkt)) {
+ i = MKT_NALLOC(mkt) + NALLOC
+ call realloc (MKT_PTRS(mkt), i, TY_POINTER)
+ call realloc (MKT_NX(mkt), i, TY_INT)
+ call realloc (MKT_NY(mkt), i, TY_INT)
+ call realloc (MKT_XC(mkt), i, TY_REAL)
+ call realloc (MKT_YC(mkt), i, TY_REAL)
+ call realloc (MKT_FLUX(mkt), i, TY_REAL)
+ call realloc (MKT_R(mkt), i, TY_REAL)
+ call realloc (MKT_AR(mkt), i, TY_REAL)
+ call realloc (MKT_PA(mkt), i, TY_REAL)
+ MKT_NALLOC(mkt) = i
+ }
+ i = MKT_N(mkt)
+ Memi[MKT_PTRS(mkt)+i] = data
+ Memi[MKT_NX(mkt)+i] = nx
+ Memi[MKT_NY(mkt)+i] = ny
+ Memr[MKT_XC(mkt)+i] = xc
+ Memr[MKT_YC(mkt)+i] = yc
+ Memr[MKT_FLUX(mkt)+i] = flux
+ Memr[MKT_R(mkt)+i] = r
+ Memr[MKT_AR(mkt)+i] = ar
+ Memr[MKT_PA(mkt)+i] = pa
+ MKT_N(mkt) = i + 1
+end
+
+
+# MKT_GET -- Get a template
+# If not a saved template just free last unsaved template.
+# If saved search for match with position, size, axial ratio, and pa.
+# Return null if not found.
+
+procedure mkt_get (mkt, data, nx, ny, xc, yc, flux, r, ar, pa, save)
+
+pointer mkt #I MKT pointer
+pointer data #O Pointer to template data
+int nx, ny #O Size of template
+real xc, yc #I Subpixel center
+real flux #I Flux
+real r #I Effective radius
+real ar #I Axial ratio
+real pa #I Position angle
+int save #I Get saved template?
+
+int i
+real f
+
+begin
+ data = NULL
+ call mfree (MKT_DATA(mkt), TY_REAL)
+ if (save == NO)
+ return
+
+ for (i=0; i<MKT_N(mkt); i=i+1) {
+ if (xc != Memr[MKT_XC(mkt)+i])
+ next
+ if (yc != Memr[MKT_YC(mkt)+i])
+ next
+ if (r != Memr[MKT_R(mkt)+i])
+ next
+ if (ar != Memr[MKT_AR(mkt)+i])
+ next
+ if (pa != Memr[MKT_PA(mkt)+i])
+ next
+ data = Memi[MKT_PTRS(mkt)+i]
+ nx = Memi[MKT_NX(mkt)+i]
+ ny = Memi[MKT_NY(mkt)+i]
+ f = Memr[MKT_FLUX(mkt)+i]
+ if (f != flux) {
+ call amulkr (Memr[data], flux/f, Memr[data], nx*ny)
+ Memr[MKT_FLUX(mkt)+i] = flux
+ }
+ return
+ }
+end
+
+
+# MKT_STAR -- Define star and seeing templates.
+# The seeing template has a smaller range for efficiency.
+# THe star templates are determined once over a grid of centers and
+# then not evaluated again.
+
+pointer procedure mkt_star (name)
+
+char name[ARB] # Profile name or file
+
+# Star and seeing parameters obatined through CLIO.
+real r # Major axis sigma (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle (radians)
+
+int i, j, nxm, nym, nx, ny, fd
+real dr, flux, radius, seeing, beta, xc, yc, dxc, dyc, der[2]
+pointer sym, mkt1, mkt2, prof, prof1, asi, msi, data, im
+
+bool streq()
+real clgetr(), asieval()
+int open(), fscan(), nscan()
+pointer immap(), imgs2r(), stfind(), stenter()
+errchk immap, open, imgs2r, asifit, asieval, asider
+
+include "mktemplates.com"
+
+begin
+ # Check if previously defined.
+ sym = stfind (stp, "star")
+ if (sym != NULL)
+ return (Memi[sym])
+
+ # Select type of star profile and set intensity profile array.
+ # Compute the fraction of the total flux in the profile.
+ # Insure that the profile subsamples a subpixel.
+
+ star = NULL
+ see = NULL
+ prof = NULL
+ msi = NULL
+ if (streq (name, "gaussian")) {
+ r = clgetr ("radius") / sqrt (log (2.))
+ radius = sqrt (log (dynrange))
+ seeing = sqrt (log (psfrange))
+ nxm = max (NPROF, 1 + nint (radius*r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1
+ Memr[prof+i] = exp (-(i * dr) ** 2)
+ flux = 1 - Memr[prof+nxm-1]
+
+ r = sqrt (log (2.))
+ radius = radius / r
+ seeing = seeing / r
+ } else if (streq (name, "moffat")) {
+ beta = clgetr ("beta")
+ r = clgetr ("radius") / sqrt (2. ** (1/beta) - 1.)
+ radius = sqrt ((dynrange) ** (1/beta) - 1)
+ seeing = sqrt ((psfrange) ** (1/beta) - 1)
+ nxm = max (NPROF, 1 + nint (radius*r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+
+ dr = radius / (nxm - 1)
+ flux = 0
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = 1. / ((1 + r**2) ** beta)
+ flux = flux + r * Memr[prof+i]
+ }
+
+ # Compute the fraction of the total flux in the profile.
+ # The last part of the total flux below is computed by expanding
+ # (1+r**2) --> r**2 under the approximation that r >> 1.
+ # Note that it is possible to explicitly compute the total
+ # flux F(total) = beta / (2 * beta - 2) (CRC 53rd edition)
+ # I found errors in other versions of CRC for this integral!
+
+ r = r + dr / 2
+ xc = 2 * beta - 2
+ flux = flux / (flux + 1. / (xc * r ** xc))
+
+ r = sqrt (2. ** (1/beta) - 1.)
+ radius = radius / r
+ seeing = seeing / r
+ } else ifnoerr (im = immap (name, READ_ONLY, 0)) {
+ iferr {
+ nxm = IM_LEN(im,1)
+ nym = IM_LEN(im,2)
+ data = imgs2r (im, 1, nxm, 1, nym)
+ call msiinit (msi, II_BILINEAR)
+ call msifit (msi, Memr[data], nxm, nym, nxm)
+ } then
+ call erract (EA_WARN)
+ call imunmap (im)
+
+ flux = 1.
+ radius = 1
+ seeing = 0.8
+ } else ifnoerr (fd = open (name, READ_ONLY, TEXT_FILE)) {
+ nxm = NPROF
+ call malloc (prof1, nxm, TY_REAL)
+
+ j = 0
+ while (fscan (fd) != EOF) {
+ call gargr (flux)
+ if (nscan() < 1)
+ next
+ if (j == nxm) {
+ nxm = nxm + NPROF
+ call realloc (prof1, nxm, TY_REAL)
+ }
+ Memr[prof1+j] = flux
+ j = j + 1
+ }
+ call close (fd)
+ if (j == 0) {
+ call mfree (prof1, TY_REAL)
+ call error (1, "PSF template not found")
+ }
+
+ r = clgetr ("radius")
+ nxm = max (NPROF, 1 + nint (r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+ dr = 1. / (nxm - 1)
+ j = j - 1
+
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[prof1], j)
+ xc = Memr[prof1]
+ call mfree (prof1, TY_REAL)
+
+ if (xc == 0.) {
+ flux = 0.
+ do i = 1, nxm - 1 {
+ r = i * dr
+ call asider (asi, 1+j*r, der, 2)
+ Memr[prof+i] = max (0., der[2] / r)
+ flux = flux + r * Memr[prof+i]
+ }
+ Memr[prof] = max (0., 2 * Memr[prof+1] - Memr[prof+2])
+ } else {
+ flux = 0.
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = asieval (asi, 1+j*r)
+ flux = flux + r * Memr[prof+i]
+ }
+ }
+ call asifree (asi)
+
+ xc = 0.9 * flux
+ flux = 0.
+ for (i=1; i<nxm && flux<xc; i=i+1)
+ flux = flux + i * dr * Memr[prof+i]
+
+ flux = 1.
+ radius = 1.
+ seeing = (i - 1.) * dr
+ } else
+ call error (1, "PSF template not found")
+
+ # Set size and orientation parameters.
+ r = clgetr ("radius")
+ ar = clgetr ("ar")
+ pa = DEGTORAD (clgetr ("pa"))
+ radius = r * radius
+ seeing = r * seeing
+
+ # Compute templates with subsampling over a grid of centers.
+ # Do this for the full star profile and a smaller region for
+ # convolving extended objects.
+
+ # Seeing kernel.
+ sym = stenter (stp, "seeing", 1)
+ call calloc (Memi[sym], LEN_MKT, TY_STRUCT)
+ mkt1 = Memi[sym]
+
+ nx = 2 * nint (seeing) + 1
+ ny = nx
+ dxc = 1. / nxc
+ dyc = 1. / nyc
+ if (prof != NULL) {
+ nym = 1 + nint (radius * nxssub * nyssub)
+ call malloc (prof1, nym, TY_REAL)
+ call mkt_binprof (Memr[prof], nxm, Memr[prof1], nym, radius, nxssub)
+ for (yc = -0.5+dyc/2; yc < 0.5; yc = yc+dyc) {
+ for (xc = -0.5+dxc/2; xc < 0.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_profile (data, nx, ny, xc, yc, 1., Memr[prof1],
+ nym, radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt1, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ } else {
+ for (yc = -0.5+dyc/2; yc < 0.5; yc = yc+dyc) {
+ for (xc = -0.5+dxc/2; xc < 0.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_msi (data, nx, ny, xc, yc, 1., msi, nxm, nym,
+ radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt1, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ }
+
+ # Full star templates.
+ sym = stenter (stp, "star", 1)
+ call calloc (Memi[sym], LEN_MKT, TY_STRUCT)
+ mkt2 = Memi[sym]
+
+ nx = 2 * nint (radius) + 1
+ ny = nx
+ dxc = 1. / nxc
+ dyc = 1. / nyc
+ if (prof != NULL) {
+ for (yc = 0.5+dyc/2; yc < 1.5; yc = yc+dyc) {
+ for (xc = 0.5+dxc/2; xc < 1.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_profile (data, nx, ny, xc, yc, flux, Memr[prof1],
+ nym, radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt2, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ call mfree (prof, TY_REAL)
+ call mfree (prof1, TY_REAL)
+ } else {
+ for (yc = 0.5+dyc/2; yc < 1.5; yc = yc+dyc) {
+ for (xc = 0.5+dxc/2; xc < 1.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_msi (data, nx, ny, xc, yc, flux, msi, nxm, nym,
+ radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt2, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ call msifree (msi)
+ }
+
+ see = mkt1
+ star = mkt2
+ return (star)
+end
+
+
+# MKT_OBJECT -- Set object profiles.
+
+pointer procedure mkt_object (name)
+
+char name[ARB] # Profile name or file
+
+int i, j, nxm, nym, fd
+real radius, r, dr, flux, c3, c4, c5, c6, c7, der[2]
+pointer sym, mkt, prof, asi, msi, buf, im
+
+real asieval()
+int open(), fscan(), nscan()
+pointer immap(), imgs2r(), stfind(), stenter()
+bool streq()
+errchk open, immap, asifit, asieval, asider
+
+include "mktemplates.com"
+
+begin
+ # Check if previously defined.
+ sym = stfind (stp, name)
+ if (sym != NULL)
+ return (Memi[sym])
+
+ # Select type of profile and set intensity profile array.
+ # Compute the fraction of the total flux in the profile.
+
+ prof = NULL
+ msi = NULL
+ if (streq (name, "expdisk")) {
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange)
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = exp (-r)
+ }
+
+ flux = 1 - Memr[prof+nxm-1] * (1 + r)
+ radius = radius / 1.6783
+ } else if (streq (name, "devauc")) {
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange) ** 4
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** 0.25
+ Memr[prof+i] = exp (-r)
+ }
+
+ c3 = 1. / 6
+ c4 = c3 / 4
+ c5 = c4 / 5
+ c6 = c5 / 6
+ c7 = c6 / 7
+ flux = 1 - Memr[prof+nxm-1] *
+ (1+r*(1+r*(.5+r*(c3+r*(c4+r*(c5+r*(c6+c7*r)))))))
+ radius = radius / 7.67 ** 4
+ } else ifnoerr (im = immap (name, READ_ONLY, 0)) {
+ iferr {
+ nxm = IM_LEN(im,1)
+ nym = IM_LEN(im,2)
+ buf = imgs2r (im, 1, nxm, 1, nym)
+ call msiinit (msi, II_BILINEAR)
+ call msifit (msi, Memr[buf], nxm, nym, nxm)
+ } then
+ call erract (EA_WARN)
+ call imunmap (im)
+
+ flux = 1.
+ radius = 1.
+ } else ifnoerr (fd = open (name, READ_ONLY, TEXT_FILE)) {
+ nxm = NPROF
+ call malloc (buf, nxm, TY_REAL)
+
+ j = 0
+ while (fscan (fd) != EOF) {
+ call gargr (flux)
+ if (nscan() < 1)
+ next
+ if (j == nxm) {
+ nxm = nxm + NPROF
+ call realloc (buf, nxm, TY_REAL)
+ }
+ Memr[buf+j] = flux
+ j = j + 1
+ }
+ call close (fd)
+ if (j == 0) {
+ call mfree (buf, TY_REAL)
+ nxm = 0
+ call error (1, "PSF template not found")
+ }
+
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ dr = 1. / (nxm - 1)
+ j = j - 1
+
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[buf], j)
+ c3 = Memr[buf]
+ call mfree (buf, TY_REAL)
+
+ if (c3 == 0.) {
+ do i = 1, nxm - 1 {
+ r = i * dr
+ call asider (asi, 1+j*r, der, 2)
+ Memr[prof+i] = max (0., der[2] / r)
+ }
+ Memr[prof] = max (0., 2 * Memr[prof+1] - Memr[prof+2])
+ } else {
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = asieval (asi, 1+j*r)
+ }
+ }
+ call asifree (asi)
+
+ flux = 1.
+ radius = 1.
+ } else {
+ call eprintf ("WARNING: Object template %s not found.\n")
+ call pargstr (name)
+ return (NULL)
+ }
+
+ # Create the template structure if a model is defined..
+ if (prof == NULL && msi == NULL)
+ mkt = NULL
+ else {
+ call calloc (mkt, LEN_MKT, TY_STRUCT)
+ MKT_PROF(mkt) = prof
+ MKT_MSI(mkt) = msi
+ MKT_NXM(mkt) = nxm
+ MKT_NYM(mkt) = nym
+ MKT_F(mkt) = flux
+ MKT_SCALE(mkt) = radius
+ }
+
+ # Enter object model name in symbol table.
+ sym = stenter (stp, name, 1)
+ Memi[sym] = mkt
+ return (mkt)
+end
+
+
+# MKT_GOBJECT -- Get image raster.
+# The profile type is set by the template structure.
+
+procedure mkt_gobject (mkt, data, nx, ny, x, y, z, r, ar, pa, save)
+
+pointer mkt # Object template
+pointer data # Data
+int nx, ny # Size of returned data
+real x, y # Position of object
+real z # Flux of object
+real r # Major axis scale (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle (radians)
+int save # Use/save template?
+
+real xc, yc, radius
+int nprof
+pointer prof
+
+include "mktemplates.com"
+
+begin
+ data = NULL
+ if (mkt == NULL)
+ return
+
+ # Stars are predefined. Return the nearest template center.
+ # Other objects are computed with or without seeing.
+
+ if (mkt == star)
+ call mkt_gstar (star, data, nx, ny, x, y, z)
+ else {
+ xc = x + 0.5 - int (x + 0.5)
+ yc = y + 0.5 - int (y + 0.5)
+ call mkt_get (mkt, data, nx, ny, xc, yc, z, r, ar, pa, save)
+ if (data != NULL)
+ return
+
+ radius = r * MKT_SCALE(mkt)
+ nx = max (3, 2 * nint (radius) + 1)
+ ny = nx
+ if (see != NULL) {
+ nx = nx + Memi[MKT_NX(see)] / 2
+ ny = ny + Memi[MKT_NY(see)] / 2
+ }
+ call malloc (data, nx * ny, TY_REAL)
+
+ if (MKT_PROF(mkt) != 0) {
+ nprof = 1 + nint (radius * nxgsub * nxgsub)
+ call malloc (prof, nprof, TY_REAL)
+ call mkt_binprof (Memr[MKT_PROF(mkt)], MKT_NXM(mkt),
+ Memr[prof], nprof, radius, nxgsub)
+ call mkt_profile (data, nx, ny, x, y, z*MKT_F(mkt), Memr[prof],
+ nprof, radius, ar, pa, nxgsub, nygsub)
+ call mfree (prof, TY_REAL)
+ } else {
+ call mkt_msi (data, nx, ny, x, y, z, MKT_MSI(mkt),
+ MKT_NXM(mkt), MKT_NYM(mkt), radius, ar, pa,
+ nxgsub, nygsub)
+ }
+
+ call mkt_save (mkt, data, nx, ny, xc, yc, z, r, ar, pa, save)
+ }
+end
+
+
+# MKT_BINPROF -- Bin intensity profile into subpixels
+
+procedure mkt_binprof (prof, nprof, prof1, nprof1, radius, nsub)
+
+real prof[nprof] # Input intensity profile
+int nprof # Number of input points
+real prof1[nprof] # Output binned intensity profile
+int nprof1 # Number of output points
+real radius # Radius of profile
+int nsub # Maximum subsampling
+
+int i, j, k, k1, k2, l, dx
+real scale, dy, val
+
+int debug, open()
+data debug/0/
+
+begin
+ if (radius < 0.1) {
+ call amovkr (1., prof1, nprof1)
+ return
+ } else
+ call aclrr (prof1, nprof1)
+
+ # Set binning parameters
+ scale = (nprof - 1.) / (nprof1 - 1.)
+ dx = nint ((nprof1 - 1.) / nsub / radius / 2.)
+ dy = dx / (NY - 1.)
+
+ # Bin central pixels
+ do i = -dx, 2*dx {
+ k = abs (i)
+ k1 = max (1, i - dx + 1)
+ k2 = i + dx + 1
+ do j = 0, NY-1 {
+ if (j == 0)
+ val = k
+ else if (k == 0)
+ val = dy * j
+ else
+ val = k * sqrt (1. + (dy * j / k) ** 2)
+ l = nint (scale * val + 1)
+ if (l > nprof)
+ next
+ val = prof[l] / NY
+ do l = k1, k2
+ prof1[l] = prof1[l] + val
+ }
+ }
+
+ # Now bin remainder of pixels more crudely
+ do i = 2*dx+1, nprof1-1 {
+ k1 = i - dx + 1
+ k2 = min (nprof1, i + dx + 1)
+ val = prof[nint (scale * i + 1)]
+ do k = k1, k2
+ prof1[k] = prof1[k] + val
+ }
+
+ if (debug == YES) {
+ j = open ("debug1.dat", APPEND, TEXT_FILE)
+ do i = 1, nprof {
+ call fprintf (j, "%d %g\n")
+ call pargi (i)
+ call pargr (prof[i])
+ }
+ call close (j)
+
+ j = open ("debug2.dat", APPEND, TEXT_FILE)
+ do i = 1, nprof1 {
+ call fprintf (j, "%d %g\n")
+ call pargi (i)
+ call pargr (prof1[i])
+ }
+ call close (j)
+ }
+end
+
+
+# MKT_GSTAR -- Get the precomputed template with center nearest (x,y).
+
+procedure mkt_gstar (mkt, data, nx, ny, x, y, z)
+
+pointer mkt # Template
+pointer data # Data
+int nx, ny # Size of data
+real x, y # Position of object
+real z # Flux of object
+
+int i, j
+real f
+
+include "mktemplates.com"
+
+begin
+ i = (x + 0.5 - int (x + 0.5)) * nxc
+ j = (y + 0.5 - int (y + 0.5)) * nyc
+ i = j * nxc + i
+ data = Memi[MKT_PTRS(mkt)+i]
+ nx = Memi[MKT_NX(mkt)+i]
+ ny = Memi[MKT_NY(mkt)+i]
+ f = Memr[MKT_FLUX(mkt)+i]
+ if (f != z) {
+ call amulkr (Memr[data], z/f, Memr[data], nx*ny)
+ Memr[MKT_FLUX(mkt)+i] = z
+ }
+end
+
+
+# MKT_PROFILE -- Make template from profile.
+
+procedure mkt_profile (data, nx, ny, xc, yc, flux, prof, npts, radius,
+ ar, pa, nxsub, nysub)
+
+pointer data # Pointer to data array
+int nx, ny # Size of template
+real xc, yc # Model center
+real flux # Model flux
+real prof[npts] # 1D profile
+int npts # Number of points in profile
+real radius # Major axis radius of profile (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle relative to major axis (radians)
+int nxsub, nysub # Number of subpixel samples
+
+int i, n, nxs, nys, nxs2, nys2, xs1, xs2, ys1, ys2
+int x, y, x2, y2
+real a, b, c, r, s, t, z, sum, sum1, asumr()
+real dx, dy, dsub, dsub2
+real x1, y1, xc1, yc1
+pointer ptr, ptr1, ptr2, see1, see2
+
+include "mktemplates.com"
+
+define see_ 99
+
+begin
+ # Switch on the size of the seeing templates.
+ if (see != NULL)
+ if (Memi[MKT_NX(see)] * Memi[MKT_NY(see)] > 1)
+ goto see_
+
+# NO SEEING:
+
+ # If the radius is very small return delta function.
+ if (radius < 0.1) {
+ call aclrr (Memr[data], nx*ny)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ return
+ }
+
+ # Compute elliptical scale factors for entry into profile array.
+ r = ((npts - 1) / radius) ** 2
+ t = ((npts - 1) / (ar * radius)) ** 2
+ c = cos (pa)
+ s = sin (pa)
+ a = r * c * c + t * s * s
+ b = 2 * (r - t) * c * s
+ c = r * s * s + t * c * c
+
+ # Subsample the profile and sum into each pixel.
+ n = nx * ny
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+
+ sum1 = 0.
+ ptr = data
+ do y = 0, ny-1 {
+ t = (y - yc1) ** 2
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + t)
+ dsub = 1. / max (1, nxsub - i)
+ sum = 0.
+ for (y1 = y-0.5+dsub/2; y1 < y+0.5; y1 = y1+dsub) {
+ dy = (y1 - yc1)
+ s = c * dy**2
+ for (x1=x-0.5+dsub/2; x1<x+0.5; x1=x1+dsub) {
+ dx = (x1-xc1)
+ i = sqrt (a * dx**2 + b * dx * dy + s) + 1.5
+ if (i <= npts)
+ sum = sum + prof[i]
+ }
+ }
+ sum = sum * dsub ** 2
+ sum1 = sum1 + sum
+ Memr[ptr] = sum
+ ptr = ptr + 1
+ }
+ }
+
+ # If the subsamples completely miss and signal is zero then return
+ # delta function otherwise scale to requested flux.
+
+ if (sum1 == 0.)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ else
+ call amulkr (Memr[data], flux/sum1, Memr[data], n)
+ return
+
+# WITH SEEING:
+
+see_ n = nx * ny
+ call aclrr (Memr[data], n)
+ sum = 0.
+
+ nxs = Memi[MKT_NX(see)]
+ nys = Memi[MKT_NY(see)]
+ nxs2 = nxs/2
+ nys2 = nys/2
+
+ # If the profile is very small return full star image rather than
+ # convolution with truncated seeing template.
+
+ if (radius > 0.01) {
+ r = ((npts - 1) / radius) ** 2
+ t = ((npts - 1) / (ar * radius)) ** 2
+ c = cos (pa)
+ s = sin (pa)
+ a = r * c * c + t * s * s
+ b = 2 * (r - t) * c * s
+ c = r * s * s + t * c * c
+
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+ ptr = data-nys2*nx-nxs2
+ do y = 0, ny-1 {
+ t = (y - yc1) ** 2
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + t)
+ dsub = 1. / max (1, nxsub - i)
+ dsub2 = dsub ** 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ptr1 = ptr + xs1
+ for (y1=y-0.5+dsub/2; y1<y+0.5; y1=y1+dsub) {
+ dy = (y1 - yc1)
+ s = c * dy**2
+ for (x1=x-0.5+dsub/2; x1<x+0.5; x1=x1+dsub) {
+ dx = (x1-xc1)
+ i = sqrt (a * dx**2 + b * dx * dy + s) + 1.5
+ if (i <= npts) {
+ z = prof[i] * dsub2
+ call mkt_gstar (see, see1, nxs, nys, x1, y1, 1.)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] +
+ z * Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ }
+ }
+ }
+ ptr = ptr + 1
+ }
+ }
+ sum = asumr (Memr[data], n)
+ }
+
+ # If no flux is accumulated or radius is very small return star image.
+ # Otherwise scale to requested flux.
+
+ if (sum == 0.) {
+ call mkt_gstar (star, see1, nxs, nys, xc, yc, flux)
+ x = nx/2
+ y = ny/2
+ nxs2 = nxs / 2
+ nys2 = nys / 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ ptr1 = data-nys2*nx-nxs2+(y*nx+x+xs1)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ } else
+ call amulkr (Memr[data], flux/sum, Memr[data], n)
+end
+
+
+# MKT_MSI -- Make template from image interpolation function.
+
+procedure mkt_msi (data, nx, ny, xc, yc, flux, model, nxm, nym, radius,
+ ar, pa, nxsub, nysub)
+
+pointer data # Pointer to data array
+int nx, ny # Size of template
+real xc, yc # Model center
+real flux # Model flux
+pointer model # Surface interpolation pointer for image template
+int nxm, nym # Number of points in image template
+real radius # Major axis radius of profile (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle relative to major axis (radians)
+int nxsub, nysub # Number of subpixel samples
+
+int i, n, nxs, nys, nxs2, nys2, xs1, xs2, ys1, ys2, x, y, x2, y2
+real a, b, c, s, xcm, ycm, x1, y1, xc1, yc1, dsub, sum, sum1
+real ac, as, bc, bs, acdx1, acdx2, bsdx1, bsdx2, bcdy1, bcdy2, asdy1, asdy2
+real val1, val2, val3, val4, minval, maxval, xm[5], ym[5]
+real asumr(), msigrl(), msisqgrl()
+pointer ptr, ptr1, ptr2, see1, see2
+
+include "mktemplates.com"
+
+define see_ 99
+
+begin
+ # Switch on the size of the seeing templates.
+ if (see != NULL)
+ if (Memi[MKT_NX(see)] * Memi[MKT_NY(see)] > 1)
+ goto see_
+
+# NO SEEING:
+
+ # If the radius is very small return delta function.
+ if (radius < 0.1) {
+ call aclrr (Memr[data], nx*ny)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ return
+ }
+
+ a = (nxm / 2.) / radius
+ b = (nym / 2.) / (ar * radius)
+ c = cos (pa)
+ s = sin (pa)
+ ac = a * c
+ as = a * s
+ bc = b * c
+ bs = b * s
+ a = nxm
+ b = nym
+ xcm = nxm / 2 + 1.
+ ycm = nym / 2 + 1.
+
+ # Subsample the profile and sum into each pixel.
+ n = nx * ny
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+
+ sum1 = 0.
+ ptr = data
+ do y = 0, ny-1 {
+ c = (y - yc1) ** 2
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + c)
+ dsub = 1. / max (1, nxsub - i)
+ sum = 0.
+ for (y1 = y-0.5; y1 < y+0.5-dsub/2; y1 = y1+dsub) {
+ asdy1 = (y1 - yc1)
+ asdy2 = asdy1 + dsub
+ bcdy1 = bc * asdy1 + ycm
+ bcdy2 = bc * asdy2 + ycm
+ if (pa == 0.) {
+ val3 = max (1., bcdy1)
+ if (val3 >= nym)
+ next
+ val4 = min (b, bcdy2)
+ if (val3 >= val4)
+ next
+ }
+ asdy1 = as * asdy1
+ asdy2 = as * asdy2
+ for (x1=x-0.5; x1<x+0.5-dsub/2; x1=x1+dsub) {
+ bsdx1 = (x1-xc1)
+ bsdx2 = bsdx1 + dsub
+ acdx1 = ac * bsdx1 + xcm
+ acdx2 = ac * bsdx2 + xcm
+ if (pa == 0.) {
+ val1 = max (1., acdx1)
+ if (val1 >= nxm)
+ next
+ val2 = min (a, acdx2)
+ if (val1 >= val2)
+ next
+ s = msisqgrl (model, val1, val2, val3, val4)
+ } else {
+ bsdx1 = bs * bsdx1
+ bsdx2 = bs * bsdx2
+
+ val1 = acdx1 + asdy1
+ val2 = acdx2 + asdy1
+ val3 = acdx2 + asdy2
+ val4 = acdx1 + asdy2
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= a || maxval <= 1.)
+ next
+ xm[1] = max (1., min (a, val1))
+ xm[2] = max (1., min (a, val2))
+ xm[3] = max (1., min (a, val3))
+ xm[4] = max (1., min (a, val4))
+ xm[5] = xm[1]
+
+ val1 = bcdy1 - bsdx1
+ val2 = bcdy1 - bsdx2
+ val3 = bcdy2 - bsdx2
+ val4 = bcdy2 - bsdx1
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= b || maxval <= 1.)
+ next
+ ym[1] = max (1., min (b, val1))
+ ym[2] = max (1., min (b, val2))
+ ym[3] = max (1., min (b, val3))
+ ym[4] = max (1., min (b, val4))
+ ym[5] = ym[1]
+ y2 = 1
+ do x2 = 2, 4
+ if (ym[x2] < ym[y2])
+ y2 = x2
+ switch (y2) {
+ case 2:
+ xm[1] = xm[2]; ym[1] = ym[2]
+ xm[2] = xm[3]; ym[2] = ym[3]
+ xm[3] = xm[4]; ym[3] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 3:
+ xm[1] = xm[3]; ym[1] = ym[3]
+ xm[3] = xm[5]; ym[3] = ym[5]
+ xm[5] = xm[2]; ym[5] = ym[2]
+ xm[2] = xm[4]; ym[2] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 4:
+ xm[5] = xm[4]; ym[5] = ym[4]
+ xm[4] = xm[3]; ym[4] = ym[3]
+ xm[3] = xm[2]; ym[3] = ym[2]
+ xm[2] = xm[1]; ym[2] = ym[1]
+ xm[1] = xm[5]; ym[1] = ym[5]
+ }
+
+ s = msigrl (model, xm, ym, 5)
+ }
+ sum = sum + s
+ }
+ }
+ sum1 = sum1 + sum
+ Memr[ptr] = sum
+ ptr = ptr + 1
+ }
+ }
+
+ call amulkr (Memr[data], flux/sum1, Memr[data], n)
+ return
+
+# SEEING:
+
+see_ n = nx * ny
+ call aclrr (Memr[data], n)
+ sum = 0.
+
+ nxs = Memi[MKT_NX(see)]
+ nys = Memi[MKT_NY(see)]
+ nxs2 = nxs/2
+ nys2 = nys/2
+
+ # If the profile is very small return full star image rather than
+ # convolution with truncated seeing template.
+
+ if (radius > 0.01) {
+ a = (nxm / 2.) / radius
+ b = (nym / 2.) / (ar * radius)
+ c = cos (pa)
+ s = sin (pa)
+ ac = a * c
+ as = a * s
+ bc = b * c
+ bs = b * s
+ a = nxm
+ b = nym
+ xcm = nxm / 2 + 1.
+ ycm = nym / 2 + 1.
+
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+ ptr = data-nys2*nx-nxs2
+ do y = 0, ny-1 {
+ c = (y - yc1) ** 2
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + c)
+ dsub = 1. / max (1, nxsub - i)
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ptr1 = ptr + xs1
+ for (y1=y-0.5; y1<y+0.5-dsub/2; y1=y1+dsub) {
+ asdy1 = (y1 - yc1)
+ asdy2 = asdy1 + dsub
+ bcdy1 = bc * asdy1 + ycm
+ bcdy2 = bc * asdy2 + ycm
+ if (pa == 0.) {
+ val3 = max (1., bcdy1)
+ if (val3 >= nym)
+ next
+ val4 = min (b, bcdy2)
+ if (val3 >= val4)
+ next
+ }
+ asdy1 = as * asdy1
+ asdy2 = as * asdy2
+ for (x1=x-0.5; x1<x+0.5-dsub/2; x1=x1+dsub) {
+ bsdx1 = (x1-xc1)
+ bsdx2 = bsdx1 + dsub
+ acdx1 = ac * bsdx1 + xcm
+ acdx2 = ac * bsdx2 + xcm
+ if (pa == 0.) {
+ val1 = max (1., acdx1)
+ if (val1 >= nxm)
+ next
+ val2 = min (a, acdx2)
+ if (val1 >= val2)
+ next
+ sum = msisqgrl (model, val1, val2, val3, val4)
+ } else {
+ bsdx1 = bs * bsdx1
+ bsdx2 = bs * bsdx2
+
+ val1 = acdx1 + asdy1
+ val2 = acdx2 + asdy1
+ val3 = acdx2 + asdy2
+ val4 = acdx1 + asdy2
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= a || maxval <= 1.)
+ next
+ xm[1] = max (1., min (a, val1))
+ xm[2] = max (1., min (a, val2))
+ xm[3] = max (1., min (a, val3))
+ xm[4] = max (1., min (a, val4))
+ xm[5] = xm[1]
+
+ val1 = bcdy1 - bsdx1
+ val2 = bcdy1 - bsdx2
+ val3 = bcdy2 - bsdx2
+ val4 = bcdy2 - bsdx1
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= b || maxval <= 1.)
+ next
+ ym[1] = max (1., min (b, val1))
+ ym[2] = max (1., min (b, val2))
+ ym[3] = max (1., min (b, val3))
+ ym[4] = max (1., min (b, val4))
+ ym[5] = ym[1]
+
+# The following is put in to work around a bug in msigrl in V2.8. When
+# V2.8 is long gone we can take this stuff out since msigrl will do the
+# rotating of the coordinates itself.
+
+ minval = max (1., minval)
+ y2 = 1
+ do x2 = 2, 4
+ if (ym[x2] < ym[y2])
+ y2 = x2
+ switch (y2) {
+ case 2:
+ xm[1] = xm[2]; ym[1] = ym[2]
+ xm[2] = xm[3]; ym[2] = ym[3]
+ xm[3] = xm[4]; ym[3] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 3:
+ xm[1] = xm[3]; ym[1] = ym[3]
+ xm[3] = xm[5]; ym[3] = ym[5]
+ xm[5] = xm[2]; ym[5] = ym[2]
+ xm[2] = xm[4]; ym[2] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 4:
+ xm[5] = xm[4]; ym[5] = ym[4]
+ xm[4] = xm[3]; ym[4] = ym[3]
+ xm[3] = xm[2]; ym[3] = ym[2]
+ xm[2] = xm[1]; ym[2] = ym[1]
+ xm[1] = xm[5]; ym[1] = ym[5]
+ }
+
+ sum = msigrl (model, xm, ym, 5)
+ }
+ call mkt_gstar (see, see1, nxs, nys, x1, y1, 1.)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + sum * Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ }
+ }
+ ptr = ptr + 1
+ }
+ }
+ sum = asumr (Memr[data], n)
+ }
+
+ # If no flux is accumulated or radius is very small return star image.
+ # Otherwise scale to requested flux.
+
+ if (sum == 0.) {
+ call mkt_gstar (star, see1, nxs, nys, xc, yc, flux)
+ x = nx/2
+ y = ny/2
+ nxs2 = nxs / 2
+ nys2 = nys / 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ ptr1 = data-nys2*nx-nxs2+(y*nx+x+xs1)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ } else
+ call amulkr (Memr[data], flux/sum, Memr[data], n)
+end
diff --git a/noao/artdata/mktemplates.xNEW b/noao/artdata/mktemplates.xNEW
new file mode 100644
index 00000000..048abf21
--- /dev/null
+++ b/noao/artdata/mktemplates.xNEW
@@ -0,0 +1,1399 @@
+include <error.h>
+include <imhdr.h>
+include <math.h>
+include <math/iminterp.h>
+
+# Template data structure
+define LEN_MKT 18
+define MKT_PROF Memi[$1] # Pointer to profile
+define MKT_MSI Memi[$1+1] # MSI interpolation pointer
+define MKT_NXM Memi[$1+2] # Number of X points in model
+define MKT_NYM Memi[$1+3] # Number of Y points in model
+define MKT_F Memr[P2R($1+4)] # Fraction of total flux in profile
+define MKT_SCALE Memr[P2R($1+5)] # Radius scale
+
+define MKT_NALLOC Memi[$1+6] # Allocated space for saved templates
+define MKT_N Memi[$1+7] # Number of saved templates
+
+define MKT_DATA Memi[$1+8] # Data pointer
+define MKT_PTRS Memi[$1+9] # Data pointers
+define MKT_NX Memi[$1+10] # Number of X pixels
+define MKT_NY Memi[$1+11] # Number of Y pixels
+define MKT_XC Memi[$1+12] # Subpixel X center
+define MKT_YC Memi[$1+13] # Subpixel Y center
+define MKT_FLUX Memi[$1+14] # Flux
+define MKT_R Memi[$1+15] # Radius
+define MKT_AR Memi[$1+16] # Axial ratio
+define MKT_PA Memi[$1+17] # Position angle
+
+define NALLOC 25 # Allocation block for saved templates
+define NPROF 5001 # Profile length
+define NY 11 # BINPROF binning parameter
+
+
+# MKT_INIT -- Initialize template memory.
+# MKT_FREE -- Free template memory.
+# MKT_SAVE -- Save a template
+# MKT_GET -- Get a template
+# MKT_STAR -- Set star and seeing templates.
+# MKT_OBJECT -- Set object profiles.
+# MKT_GOBJECT -- Get image raster.
+# MKT_BINPROF -- Bin intensity profile
+# MKT_GSTAR -- Get the precomputed template with center nearest (x,y).
+# MKT_PROFILE -- Make template from profile.
+# MKT_MSI -- Make template from image interpolation function.
+
+
+# MKT_INIT -- Initialize template memory.
+# The symbol table is used as a simple way to store the object types by name.
+
+procedure mkt_init ()
+
+int clgeti()
+real clgetr()
+
+pointer stopen()
+include "mktemplates.com"
+
+begin
+ nxc = clgeti ("nxc")
+ nyc = clgeti ("nyc")
+ nxssub = clgeti ("nxsub")
+ nyssub = clgeti ("nysub")
+ nxgsub = clgeti ("nxgsub")
+ nygsub = clgeti ("nygsub")
+ dynrange = clgetr ("dynrange")
+ psfrange = clgetr ("psfrange")
+ stp = stopen ("mkt", 10, 10, 10*SZ_FNAME)
+end
+
+
+# MKT_FREE -- Free template memory.
+
+procedure mkt_free ()
+
+int i
+pointer sym, mkt, sthead(), stnext()
+include "mktemplates.com"
+
+begin
+ # For each object type free the profile and image interpolator data,
+ # the last unsaved data buffer, all saved templates, and the object
+ # structure. Finally free the symbol table.
+
+ for (sym = sthead (stp); sym != NULL; sym = stnext (stp, sym)) {
+ mkt = Memi[sym]
+ if (mkt != NULL) {
+ call mfree (MKT_PROF(mkt), TY_REAL)
+ if (MKT_MSI(mkt) != NULL)
+ call msifree (MKT_MSI(mkt))
+ call mfree (MKT_DATA(mkt), TY_REAL)
+ if (MKT_NALLOC(mkt) > 0) {
+ do i = 0, MKT_N(mkt)-1
+ call mfree (Memi[MKT_PTRS(mkt)+i], TY_REAL)
+ call mfree (MKT_PTRS(mkt), TY_POINTER)
+ call mfree (MKT_NX(mkt), TY_INT)
+ call mfree (MKT_NY(mkt), TY_INT)
+ call mfree (MKT_XC(mkt), TY_REAL)
+ call mfree (MKT_YC(mkt), TY_REAL)
+ call mfree (MKT_FLUX(mkt), TY_REAL)
+ call mfree (MKT_R(mkt), TY_REAL)
+ call mfree (MKT_AR(mkt), TY_REAL)
+ call mfree (MKT_PA(mkt), TY_REAL)
+ }
+ call mfree (mkt, TY_STRUCT)
+ }
+ }
+ call stclose (stp)
+end
+
+
+# MKT_SAVE -- Save a template
+# If a template may be used more than once it may be saved upon direction of
+# the user in the object list. Otherwise the last unsaved template is
+# freed for the next object.
+
+procedure mkt_save (mkt, data, nx, ny, xc, yc, flux, r, ar, pa, save)
+
+pointer mkt #I MKT pointer
+pointer data #I Pointer to template data
+int nx, ny #I Size of template
+real xc, yc #I Subpixel center
+real flux #I Flux
+real r #I Effective radius
+real ar #I Axial ratio
+real pa #I Position angle
+int save #I Save data?
+
+int i
+
+begin
+ if (save == NO) {
+ MKT_DATA(mkt) = data
+ return
+ }
+
+ if (MKT_NALLOC(mkt) == 0) {
+ i = NALLOC
+ call malloc (MKT_PTRS(mkt), i, TY_POINTER)
+ call malloc (MKT_NX(mkt), i, TY_INT)
+ call malloc (MKT_NY(mkt), i, TY_INT)
+ call malloc (MKT_XC(mkt), i, TY_REAL)
+ call malloc (MKT_YC(mkt), i, TY_REAL)
+ call malloc (MKT_FLUX(mkt), i, TY_REAL)
+ call malloc (MKT_R(mkt), i, TY_REAL)
+ call malloc (MKT_AR(mkt), i, TY_REAL)
+ call malloc (MKT_PA(mkt), i, TY_REAL)
+ MKT_NALLOC(mkt) = i
+ } else if (MKT_N(mkt) == MKT_NALLOC(mkt)) {
+ i = MKT_NALLOC(mkt) + NALLOC
+ call realloc (MKT_PTRS(mkt), i, TY_POINTER)
+ call realloc (MKT_NX(mkt), i, TY_INT)
+ call realloc (MKT_NY(mkt), i, TY_INT)
+ call realloc (MKT_XC(mkt), i, TY_REAL)
+ call realloc (MKT_YC(mkt), i, TY_REAL)
+ call realloc (MKT_FLUX(mkt), i, TY_REAL)
+ call realloc (MKT_R(mkt), i, TY_REAL)
+ call realloc (MKT_AR(mkt), i, TY_REAL)
+ call realloc (MKT_PA(mkt), i, TY_REAL)
+ MKT_NALLOC(mkt) = i
+ }
+ i = MKT_N(mkt)
+ Memi[MKT_PTRS(mkt)+i] = data
+ Memi[MKT_NX(mkt)+i] = nx
+ Memi[MKT_NY(mkt)+i] = ny
+ Memr[MKT_XC(mkt)+i] = xc
+ Memr[MKT_YC(mkt)+i] = yc
+ Memr[MKT_FLUX(mkt)+i] = flux
+ Memr[MKT_R(mkt)+i] = r
+ Memr[MKT_AR(mkt)+i] = ar
+ Memr[MKT_PA(mkt)+i] = pa
+ MKT_N(mkt) = i + 1
+end
+
+
+# MKT_GET -- Get a template
+# If not a saved template just free last unsaved template.
+# If saved search for match with position, size, axial ratio, and pa.
+# Return null if not found.
+
+procedure mkt_get (mkt, data, nx, ny, xc, yc, flux, r, ar, pa, save)
+
+pointer mkt #I MKT pointer
+pointer data #O Pointer to template data
+int nx, ny #O Size of template
+real xc, yc #I Subpixel center
+real flux #I Flux
+real r #I Effective radius
+real ar #I Axial ratio
+real pa #I Position angle
+int save #I Get saved template?
+
+int i
+real f
+
+begin
+ data = NULL
+ call mfree (MKT_DATA(mkt), TY_REAL)
+ if (save == NO)
+ return
+
+ for (i=0; i<MKT_N(mkt); i=i+1) {
+ if (xc != Memr[MKT_XC(mkt)+i])
+ next
+ if (yc != Memr[MKT_YC(mkt)+i])
+ next
+ if (r != Memr[MKT_R(mkt)+i])
+ next
+ if (ar != Memr[MKT_AR(mkt)+i])
+ next
+ if (pa != Memr[MKT_PA(mkt)+i])
+ next
+ data = Memi[MKT_PTRS(mkt)+i]
+ nx = Memi[MKT_NX(mkt)+i]
+ ny = Memi[MKT_NY(mkt)+i]
+ f = Memr[MKT_FLUX(mkt)+i]
+ if (f != flux) {
+ call amulkr (Memr[data], flux/f, Memr[data], nx*ny)
+ Memr[MKT_FLUX(mkt)+i] = flux
+ }
+ return
+ }
+end
+
+
+# MKT_STAR -- Define star and seeing templates.
+# The seeing template has a smaller range for efficiency.
+# THe star templates are determined once over a grid of centers and
+# then not evaluated again.
+
+pointer procedure mkt_star (name)
+
+char name[ARB] # Profile name or file
+
+# Star and seeing parameters obatined through CLIO.
+real r # Major axis sigma (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle (radians)
+
+int i, j, nxm, nym, nx, ny, fd
+real dr, flux, radius, seeing, beta, xc, yc, dxc, dyc, der[2]
+pointer sym, mkt1, mkt2, prof, prof1, asi, msi, data, im
+
+bool streq()
+real clgetr(), asieval()
+int open(), fscan(), nscan()
+pointer immap(), imgs2r(), stfind(), stenter()
+errchk immap, open, imgs2r, asifit, asieval, asider
+
+include "mktemplates.com"
+
+begin
+ # Check if previously defined.
+ sym = stfind (stp, "star")
+ if (sym != NULL)
+ return (Memi[sym])
+
+ # Select type of star profile and set intensity profile array.
+ # Compute the fraction of the total flux in the profile.
+ # Insure that the profile subsamples a subpixel.
+
+ star = NULL
+ see = NULL
+ prof = NULL
+ msi = NULL
+ if (streq (name, "gaussian")) {
+ r = clgetr ("radius") / sqrt (log (2.))
+ radius = sqrt (log (dynrange))
+ seeing = sqrt (log (psfrange))
+ nxm = max (NPROF, 1 + nint (radius*r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1
+ Memr[prof+i] = exp (-(i * dr) ** 2)
+ flux = 1 - Memr[prof+nxm-1]
+
+ r = sqrt (log (2.))
+ radius = radius / r
+ seeing = seeing / r
+ } else if (streq (name, "moffat")) {
+ beta = clgetr ("beta")
+ r = clgetr ("radius") / sqrt (2. ** (1/beta) - 1.)
+ radius = sqrt ((dynrange) ** (1/beta) - 1)
+ seeing = sqrt ((psfrange) ** (1/beta) - 1)
+ nxm = max (NPROF, 1 + nint (radius*r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+
+ dr = radius / (nxm - 1)
+ flux = 0
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = 1. / ((1 + r**2) ** beta)
+ flux = flux + r * Memr[prof+i]
+ }
+
+ # Compute the fraction of the total flux in the profile.
+ # The last part of the total flux below is computed by expanding
+ # (1+r**2) --> r**2 under the approximation that r >> 1.
+ # Note that it is possible to explicitly compute the total
+ # flux F(total) = beta / (2 * beta - 2) (CRC 53rd edition)
+ # I found errors in other versions of CRC for this integral!
+
+ r = r + dr / 2
+ xc = 2 * beta - 2
+ flux = flux / (flux + 1. / (xc * r ** xc))
+
+ r = sqrt (2. ** (1/beta) - 1.)
+ radius = radius / r
+ seeing = seeing / r
+ } else ifnoerr (im = immap (name, READ_ONLY, 0)) {
+ iferr {
+ nxm = IM_LEN(im,1)
+ nym = IM_LEN(im,2)
+ data = imgs2r (im, 1, nxm, 1, nym)
+ call msiinit (msi, II_BILINEAR)
+ call msifit (msi, Memr[data], nxm, nym, nxm)
+ } then
+ call erract (EA_WARN)
+ call imunmap (im)
+
+ flux = 1.
+ radius = 1
+ seeing = 0.8
+ } else ifnoerr (fd = open (name, READ_ONLY, TEXT_FILE)) {
+ nxm = NPROF
+ call malloc (prof1, nxm, TY_REAL)
+
+ j = 0
+ while (fscan (fd) != EOF) {
+ call gargr (flux)
+ if (nscan() < 1)
+ next
+ if (j == nxm) {
+ nxm = nxm + NPROF
+ call realloc (prof1, nxm, TY_REAL)
+ }
+ Memr[prof1+j] = flux
+ j = j + 1
+ }
+ call close (fd)
+ if (j == 0) {
+ call mfree (prof1, TY_REAL)
+ call error (1, "PSF template not found")
+ }
+
+ r = clgetr ("radius")
+ nxm = max (NPROF, 1 + nint (r*nxssub*nyssub))
+ call malloc (prof, nxm, TY_REAL)
+ dr = 1. / (nxm - 1)
+ j = j - 1
+
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[prof1], j)
+ xc = Memr[prof1]
+ call mfree (prof1, TY_REAL)
+
+ if (xc == 0.) {
+ flux = 0.
+ do i = 1, nxm - 1 {
+ r = i * dr
+ call asider (asi, 1+j*r, der, 2)
+ Memr[prof+i] = max (0., der[2] / r)
+ flux = flux + r * Memr[prof+i]
+ }
+ Memr[prof] = max (0., 2 * Memr[prof+1] - Memr[prof+2])
+ } else {
+ flux = 0.
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = asieval (asi, 1+j*r)
+ flux = flux + r * Memr[prof+i]
+ }
+ }
+ call asifree (asi)
+
+ xc = 0.9 * flux
+ flux = 0.
+ for (i=1; i<nxm && flux<xc; i=i+1)
+ flux = flux + i * dr * Memr[prof+i]
+
+ flux = 1.
+ radius = 1.
+ seeing = (i - 1.) * dr
+ } else
+ call error (1, "PSF template not found")
+
+ # Set size and orientation parameters.
+ r = clgetr ("radius")
+ ar = clgetr ("ar")
+ pa = DEGTORAD (clgetr ("pa"))
+ radius = r * radius
+ seeing = r * seeing
+
+ # Compute templates with subsampling over a grid of centers.
+ # Do this for the full star profile and a smaller region for
+ # convolving extended objects.
+
+ # Seeing kernel.
+ sym = stenter (stp, "seeing", 1)
+ call calloc (Memi[sym], LEN_MKT, TY_STRUCT)
+ mkt1 = Memi[sym]
+
+ nx = 2 * nint (seeing) + 1
+ ny = nx
+ dxc = 1. / nxc
+ dyc = 1. / nyc
+ if (prof != NULL) {
+ nym = 1 + nint (radius * nxssub * nyssub)
+ call malloc (prof1, nym, TY_REAL)
+ call mkt_binprof (Memr[prof], nxm, Memr[prof1], nym, radius, nxssub)
+ for (yc = -0.5+dyc/2; yc < 0.5; yc = yc+dyc) {
+ for (xc = -0.5+dxc/2; xc < 0.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_profile (data, nx, ny, xc, yc, 1., Memr[prof1],
+ nym, radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt1, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ } else {
+ for (yc = -0.5+dyc/2; yc < 0.5; yc = yc+dyc) {
+ for (xc = -0.5+dxc/2; xc < 0.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_msi (data, nx, ny, xc, yc, 1., msi, nxm, nym,
+ radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt1, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ }
+
+ # Full star templates.
+ sym = stenter (stp, "star", 1)
+ call calloc (Memi[sym], LEN_MKT, TY_STRUCT)
+ mkt2 = Memi[sym]
+
+ nx = 2 * nint (radius) + 1
+ ny = nx
+ dxc = 1. / nxc
+ dyc = 1. / nyc
+ if (prof != NULL) {
+ for (yc = 0.5+dyc/2; yc < 1.5; yc = yc+dyc) {
+ for (xc = 0.5+dxc/2; xc < 1.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_profile (data, nx, ny, xc, yc, flux, Memr[prof1],
+ nym, radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt2, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ call mfree (prof, TY_REAL)
+ call mfree (prof1, TY_REAL)
+ } else {
+ for (yc = 0.5+dyc/2; yc < 1.5; yc = yc+dyc) {
+ for (xc = 0.5+dxc/2; xc < 1.5; xc = xc+dxc) {
+ call malloc (data, nx*ny, TY_REAL)
+ call mkt_msi (data, nx, ny, xc, yc, flux, msi, nxm, nym,
+ radius, ar, pa, nxssub, nyssub)
+ call mkt_save (mkt2, data, nx, ny, xc, yc, 1., 0., 0., 0.,
+ YES)
+ }
+ }
+ call msifree (msi)
+ }
+
+ see = mkt1
+ star = mkt2
+ return (star)
+end
+
+
+# MKT_OBJECT -- Set object profiles.
+
+pointer procedure mkt_object (name)
+
+char name[ARB] # Profile name or file
+
+int i, j, n, nxm, nym, fd
+real radius, r, dr, s, b, flux, der[2]
+pointer sym, mkt, prof, asi, msi, buf, im
+
+real asieval(), uigamma()
+int open(), fscan(), nscan(), strncmp(), ctor()
+pointer immap(), imgs2r(), stfind(), stenter()
+bool streq()
+errchk open, immap, asifit, asieval, asider
+
+include "mktemplates.com"
+
+begin
+ # Check if previously defined.
+ sym = stfind (stp, name)
+ if (sym != NULL)
+ return (Memi[sym])
+
+ # Select type of profile and set intensity profile array.
+ # Compute the fraction of the total flux in the profile.
+
+ prof = NULL
+ msi = NULL
+ if (strncmp (name, "sersic", 6) == 0) {
+ i = 7
+ if (ctor (name, i, s) == 0) {
+ call eprintf ("WARNING: Bad sersic profile syntax (%s).\n")
+ call pargstr (name)
+ return (NULL)
+ }
+ n = nint (2 * (s + 0.01))
+ s = n / 2
+call eprintf ("n = %.1f, 2n = %d\n")
+call pargr (s)
+call pargi (n)
+ if (n < 1 || n > 20) {
+ call eprintf ("WARNING: Sersic index out of allowed range (%f).\n")
+ call pargi (s)
+ return (NULL)
+ }
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+call eprintf ("A\n")
+ radius = log (dynrange) ** s
+call eprintf ("B\n")
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** (1/s)
+ Memr[prof+i] = exp (-r)
+ }
+
+call eprintf ("C\n")
+ flux = 1 - uigamma (n, r)
+call eprintf ("D\n")
+ r = n - 1./3. + 4./(405.*s) + 46./(25515.*s*s)
+call eprintf ("E\n")
+ radius = radius / r ** s
+call eprintf ("F\n")
+ } else if (streq (name, "expdisk")) {
+ s = 1.
+ n = nint (2 * s)
+
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange) ** s
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** (1/s)
+ Memr[prof+i] = exp (-r)
+ }
+
+ flux = 1 - uigamma (n, r)
+ r = n - 1./3. + 4./(405.*s) + 46./(25515.*s*s)
+ radius = radius / r ** s
+ } else if (streq (name, "devauc")) {
+ s = 4.
+ n = nint (2 * s)
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ radius = log (dynrange) ** s
+ dr = radius / (nxm - 1)
+ do i = 0, nxm - 1 {
+ r = (i * dr) ** (1/s)
+ Memr[prof+i] = exp (-r)
+ }
+
+ flux = 1 - uigamma (n, r)
+ r = n - 1./3. + 4./(405.*s) + 46./(25515.*s*s)
+ radius = radius / r ** s
+ } else ifnoerr (im = immap (name, READ_ONLY, 0)) {
+ iferr {
+ nxm = IM_LEN(im,1)
+ nym = IM_LEN(im,2)
+ buf = imgs2r (im, 1, nxm, 1, nym)
+ call msiinit (msi, II_BILINEAR)
+ call msifit (msi, Memr[buf], nxm, nym, nxm)
+ } then
+ call erract (EA_WARN)
+ call imunmap (im)
+
+ flux = 1.
+ radius = 1.
+ } else ifnoerr (fd = open (name, READ_ONLY, TEXT_FILE)) {
+ nxm = NPROF
+ call malloc (buf, nxm, TY_REAL)
+
+ j = 0
+ while (fscan (fd) != EOF) {
+ call gargr (flux)
+ if (nscan() < 1)
+ next
+ if (j == nxm) {
+ nxm = nxm + NPROF
+ call realloc (buf, nxm, TY_REAL)
+ }
+ Memr[buf+j] = flux
+ j = j + 1
+ }
+ call close (fd)
+ if (j == 0) {
+ call mfree (buf, TY_REAL)
+ nxm = 0
+ call error (1, "PSF template not found")
+ }
+
+ nxm = NPROF
+ call malloc (prof, nxm, TY_REAL)
+ dr = 1. / (nxm - 1)
+ j = j - 1
+
+ call asiinit (asi, II_SPLINE3)
+ call asifit (asi, Memr[buf], j)
+ s = Memr[buf]
+ call mfree (buf, TY_REAL)
+
+ if (s == 0.) {
+ do i = 1, nxm - 1 {
+ r = i * dr
+ call asider (asi, 1+j*r, der, 2)
+ Memr[prof+i] = max (0., der[2] / r)
+ }
+ Memr[prof] = max (0., 2 * Memr[prof+1] - Memr[prof+2])
+ } else {
+ do i = 0, nxm - 1 {
+ r = i * dr
+ Memr[prof+i] = asieval (asi, 1+j*r)
+ }
+ }
+ call asifree (asi)
+
+ flux = 1.
+ radius = 1.
+ } else {
+ call eprintf ("WARNING: Object template %s not found.\n")
+ call pargstr (name)
+ return (NULL)
+ }
+call eprintf ("G\n")
+
+ # Create the template structure if a model is defined..
+ if (prof == NULL && msi == NULL)
+ mkt = NULL
+ else {
+ call calloc (mkt, LEN_MKT, TY_STRUCT)
+ MKT_PROF(mkt) = prof
+ MKT_MSI(mkt) = msi
+ MKT_NXM(mkt) = nxm
+ MKT_NYM(mkt) = nym
+ MKT_F(mkt) = flux
+ MKT_SCALE(mkt) = radius
+call eprintf ("flux = %g, radius = %g\n")
+call pargr (MKT_F(mkt))
+call pargr (MKT_SCALE(mkt))
+do i = 0, nxm {
+call eprintf ("%d: %g\n")
+call pargi (i)
+call pargr (Memr[prof+i])
+}
+ }
+call eprintf ("H\n")
+
+ # Enter object model name in symbol table.
+ sym = stenter (stp, name, 1)
+ Memi[sym] = mkt
+ return (mkt)
+end
+
+
+# MKT_GOBJECT -- Get image raster.
+# The profile type is set by the template structure.
+
+procedure mkt_gobject (mkt, data, nx, ny, x, y, z, r, ar, pa, save)
+
+pointer mkt # Object template
+pointer data # Data
+int nx, ny # Size of returned data
+real x, y # Position of object
+real z # Flux of object
+real r # Major axis scale (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle (radians)
+int save # Use/save template?
+
+real xc, yc, radius
+int nprof
+pointer prof
+
+include "mktemplates.com"
+
+begin
+ data = NULL
+ if (mkt == NULL)
+ return
+
+ # Stars are predefined. Return the nearest template center.
+ # Other objects are computed with or without seeing.
+
+ if (mkt == star)
+ call mkt_gstar (star, data, nx, ny, x, y, z)
+ else {
+ xc = x + 0.5 - int (x + 0.5)
+ yc = y + 0.5 - int (y + 0.5)
+ call mkt_get (mkt, data, nx, ny, xc, yc, z, r, ar, pa, save)
+ if (data != NULL)
+ return
+
+ radius = r * MKT_SCALE(mkt)
+ nx = max (3, 2 * nint (radius) + 1)
+ ny = nx
+ if (see != NULL) {
+ nx = nx + Memi[MKT_NX(see)] / 2
+ ny = ny + Memi[MKT_NY(see)] / 2
+ }
+ call malloc (data, nx * ny, TY_REAL)
+
+ if (MKT_PROF(mkt) != 0) {
+ nprof = 1 + nint (radius * nxgsub * nxgsub)
+ call malloc (prof, nprof, TY_REAL)
+ call mkt_binprof (Memr[MKT_PROF(mkt)], MKT_NXM(mkt),
+ Memr[prof], nprof, radius, nxgsub)
+ call mkt_profile (data, nx, ny, x, y, z*MKT_F(mkt), Memr[prof],
+ nprof, radius, ar, pa, nxgsub, nygsub)
+ call mfree (prof, TY_REAL)
+ } else {
+ call mkt_msi (data, nx, ny, x, y, z, MKT_MSI(mkt),
+ MKT_NXM(mkt), MKT_NYM(mkt), radius, ar, pa,
+ nxgsub, nygsub)
+ }
+
+ call mkt_save (mkt, data, nx, ny, xc, yc, z, r, ar, pa, save)
+ }
+end
+
+
+# MKT_BINPROF -- Bin intensity profile into subpixels
+
+procedure mkt_binprof (prof, nprof, prof1, nprof1, radius, nsub)
+
+real prof[nprof] # Input intensity profile
+int nprof # Number of input points
+real prof1[nprof] # Output binned intensity profile
+int nprof1 # Number of output points
+real radius # Radius of profile
+int nsub # Maximum subsampling
+
+int i, j, k, k1, k2, l, dx
+real scale, dy, val
+
+int debug, open()
+data debug/0/
+
+begin
+ if (radius < 0.1) {
+ call amovkr (1., prof1, nprof1)
+ return
+ } else
+ call aclrr (prof1, nprof1)
+
+ # Set binning parameters
+ scale = (nprof - 1.) / (nprof1 - 1.)
+ dx = nint ((nprof1 - 1.) / nsub / radius / 2.)
+ dy = dx / (NY - 1.)
+
+ # Bin central pixels
+ do i = -dx, 2*dx {
+ k = abs (i)
+ k1 = max (1, i - dx + 1)
+ k2 = i + dx + 1
+ do j = 0, NY-1 {
+ if (j == 0)
+ val = k
+ else if (k == 0)
+ val = dy * j
+ else
+ val = k * sqrt (1. + (dy * j / k) ** 2)
+ l = nint (scale * val + 1)
+ if (l > nprof)
+ next
+ val = prof[l] / NY
+ do l = k1, k2
+ prof1[l] = prof1[l] + val
+ }
+ }
+
+ # Now bin remainder of pixels more crudely
+ do i = 2*dx+1, nprof1-1 {
+ k1 = i - dx + 1
+ k2 = min (nprof1, i + dx + 1)
+ val = prof[nint (scale * i + 1)]
+ do k = k1, k2
+ prof1[k] = prof1[k] + val
+ }
+
+ if (debug == YES) {
+ j = open ("debug1.dat", APPEND, TEXT_FILE)
+ do i = 1, nprof {
+ call fprintf (j, "%d %g\n")
+ call pargi (i)
+ call pargr (prof[i])
+ }
+ call close (j)
+
+ j = open ("debug2.dat", APPEND, TEXT_FILE)
+ do i = 1, nprof1 {
+ call fprintf (j, "%d %g\n")
+ call pargi (i)
+ call pargr (prof1[i])
+ }
+ call close (j)
+ }
+end
+
+
+# MKT_GSTAR -- Get the precomputed template with center nearest (x,y).
+
+procedure mkt_gstar (mkt, data, nx, ny, x, y, z)
+
+pointer mkt # Template
+pointer data # Data
+int nx, ny # Size of data
+real x, y # Position of object
+real z # Flux of object
+
+int i, j
+real f
+
+include "mktemplates.com"
+
+begin
+ i = (x + 0.5 - int (x + 0.5)) * nxc
+ j = (y + 0.5 - int (y + 0.5)) * nyc
+ i = j * nxc + i
+ data = Memi[MKT_PTRS(mkt)+i]
+ nx = Memi[MKT_NX(mkt)+i]
+ ny = Memi[MKT_NY(mkt)+i]
+ f = Memr[MKT_FLUX(mkt)+i]
+ if (f != z) {
+ call amulkr (Memr[data], z/f, Memr[data], nx*ny)
+ Memr[MKT_FLUX(mkt)+i] = z
+ }
+end
+
+
+# MKT_PROFILE -- Make template from profile.
+
+procedure mkt_profile (data, nx, ny, xc, yc, flux, prof, npts, radius,
+ ar, pa, nxsub, nysub)
+
+pointer data # Pointer to data array
+int nx, ny # Size of template
+real xc, yc # Model center
+real flux # Model flux
+real prof[npts] # 1D profile
+int npts # Number of points in profile
+real radius # Major axis radius of profile (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle relative to major axis (radians)
+int nxsub, nysub # Number of subpixel samples
+
+int i, n, nxs, nys, nxs2, nys2, xs1, xs2, ys1, ys2
+int x, y, x2, y2
+real a, b, c, r, s, t, z, sum, sum1, asumr()
+real dx, dy, dsub, dsub2
+real x1, y1, xc1, yc1
+pointer ptr, ptr1, ptr2, see1, see2
+
+include "mktemplates.com"
+
+define see_ 99
+
+begin
+ # Switch on the size of the seeing templates.
+ if (see != NULL)
+ if (Memi[MKT_NX(see)] * Memi[MKT_NY(see)] > 1)
+ goto see_
+
+# NO SEEING:
+
+ # If the radius is very small return delta function.
+ if (radius < 0.1) {
+ call aclrr (Memr[data], nx*ny)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ return
+ }
+
+ # Compute elliptical scale factors for entry into profile array.
+ r = ((npts - 1) / radius) ** 2
+ t = ((npts - 1) / (ar * radius)) ** 2
+ c = cos (pa)
+ s = sin (pa)
+ a = r * c * c + t * s * s
+ b = 2 * (r - t) * c * s
+ c = r * s * s + t * c * c
+
+ # Subsample the profile and sum into each pixel.
+ n = nx * ny
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+
+ sum1 = 0.
+ ptr = data
+ do y = 0, ny-1 {
+ t = (y - yc1) ** 2
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + t)
+ dsub = 1. / max (1, nxsub - i)
+ sum = 0.
+ for (y1 = y-0.5+dsub/2; y1 < y+0.5; y1 = y1+dsub) {
+ dy = (y1 - yc1)
+ s = c * dy**2
+ for (x1=x-0.5+dsub/2; x1<x+0.5; x1=x1+dsub) {
+ dx = (x1-xc1)
+ i = sqrt (a * dx**2 + b * dx * dy + s) + 1.5
+ if (i <= npts)
+ sum = sum + prof[i]
+ }
+ }
+ sum = sum * dsub ** 2
+ sum1 = sum1 + sum
+ Memr[ptr] = sum
+ ptr = ptr + 1
+ }
+ }
+
+ # If the subsamples completely miss and signal is zero then return
+ # delta function otherwise scale to requested flux.
+
+ if (sum1 == 0.)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ else
+ call amulkr (Memr[data], flux/sum1, Memr[data], n)
+ return
+
+# WITH SEEING:
+
+see_ n = nx * ny
+ call aclrr (Memr[data], n)
+ sum = 0.
+
+ nxs = Memi[MKT_NX(see)]
+ nys = Memi[MKT_NY(see)]
+ nxs2 = nxs/2
+ nys2 = nys/2
+
+ # If the profile is very small return full star image rather than
+ # convolution with truncated seeing template.
+
+ if (radius > 0.01) {
+ r = ((npts - 1) / radius) ** 2
+ t = ((npts - 1) / (ar * radius)) ** 2
+ c = cos (pa)
+ s = sin (pa)
+ a = r * c * c + t * s * s
+ b = 2 * (r - t) * c * s
+ c = r * s * s + t * c * c
+
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+ ptr = data-nys2*nx-nxs2
+ do y = 0, ny-1 {
+ t = (y - yc1) ** 2
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + t)
+ dsub = 1. / max (1, nxsub - i)
+ dsub2 = dsub ** 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ptr1 = ptr + xs1
+ for (y1=y-0.5+dsub/2; y1<y+0.5; y1=y1+dsub) {
+ dy = (y1 - yc1)
+ s = c * dy**2
+ for (x1=x-0.5+dsub/2; x1<x+0.5; x1=x1+dsub) {
+ dx = (x1-xc1)
+ i = sqrt (a * dx**2 + b * dx * dy + s) + 1.5
+ if (i <= npts) {
+ z = prof[i] * dsub2
+ call mkt_gstar (see, see1, nxs, nys, x1, y1, 1.)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] +
+ z * Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ }
+ }
+ }
+ ptr = ptr + 1
+ }
+ }
+ sum = asumr (Memr[data], n)
+ }
+
+ # If no flux is accumulated or radius is very small return star image.
+ # Otherwise scale to requested flux.
+
+ if (sum == 0.) {
+ call mkt_gstar (star, see1, nxs, nys, xc, yc, flux)
+ x = nx/2
+ y = ny/2
+ nxs2 = nxs / 2
+ nys2 = nys / 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ ptr1 = data-nys2*nx-nxs2+(y*nx+x+xs1)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ } else
+ call amulkr (Memr[data], flux/sum, Memr[data], n)
+end
+
+
+# MKT_MSI -- Make template from image interpolation function.
+
+procedure mkt_msi (data, nx, ny, xc, yc, flux, model, nxm, nym, radius,
+ ar, pa, nxsub, nysub)
+
+pointer data # Pointer to data array
+int nx, ny # Size of template
+real xc, yc # Model center
+real flux # Model flux
+pointer model # Surface interpolation pointer for image template
+int nxm, nym # Number of points in image template
+real radius # Major axis radius of profile (pixels)
+real ar # Axial ratio (minor / major)
+real pa # Position angle relative to major axis (radians)
+int nxsub, nysub # Number of subpixel samples
+
+int i, n, nxs, nys, nxs2, nys2, xs1, xs2, ys1, ys2, x, y, x2, y2
+real a, b, c, s, xcm, ycm, x1, y1, xc1, yc1, dsub, sum, sum1
+real ac, as, bc, bs, acdx1, acdx2, bsdx1, bsdx2, bcdy1, bcdy2, asdy1, asdy2
+real val1, val2, val3, val4, minval, maxval, xm[5], ym[5]
+real asumr(), msigrl(), msisqgrl()
+pointer ptr, ptr1, ptr2, see1, see2
+
+include "mktemplates.com"
+
+define see_ 99
+
+begin
+ # Switch on the size of the seeing templates.
+ if (see != NULL)
+ if (Memi[MKT_NX(see)] * Memi[MKT_NY(see)] > 1)
+ goto see_
+
+# NO SEEING:
+
+ # If the radius is very small return delta function.
+ if (radius < 0.1) {
+ call aclrr (Memr[data], nx*ny)
+ Memr[data+(ny/2)*nx+(nx/2)] = flux
+ return
+ }
+
+ a = (nxm / 2.) / radius
+ b = (nym / 2.) / (ar * radius)
+ c = cos (pa)
+ s = sin (pa)
+ ac = a * c
+ as = a * s
+ bc = b * c
+ bs = b * s
+ a = nxm
+ b = nym
+ xcm = nxm / 2 + 1.
+ ycm = nym / 2 + 1.
+
+ # Subsample the profile and sum into each pixel.
+ n = nx * ny
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+
+ sum1 = 0.
+ ptr = data
+ do y = 0, ny-1 {
+ c = (y - yc1) ** 2
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + c)
+ dsub = 1. / max (1, nxsub - i)
+ sum = 0.
+ for (y1 = y-0.5; y1 < y+0.5-dsub/2; y1 = y1+dsub) {
+ asdy1 = (y1 - yc1)
+ asdy2 = asdy1 + dsub
+ bcdy1 = bc * asdy1 + ycm
+ bcdy2 = bc * asdy2 + ycm
+ if (pa == 0.) {
+ val3 = max (1., bcdy1)
+ if (val3 >= nym)
+ next
+ val4 = min (b, bcdy2)
+ if (val3 >= val4)
+ next
+ }
+ asdy1 = as * asdy1
+ asdy2 = as * asdy2
+ for (x1=x-0.5; x1<x+0.5-dsub/2; x1=x1+dsub) {
+ bsdx1 = (x1-xc1)
+ bsdx2 = bsdx1 + dsub
+ acdx1 = ac * bsdx1 + xcm
+ acdx2 = ac * bsdx2 + xcm
+ if (pa == 0.) {
+ val1 = max (1., acdx1)
+ if (val1 >= nxm)
+ next
+ val2 = min (a, acdx2)
+ if (val1 >= val2)
+ next
+ s = msisqgrl (model, val1, val2, val3, val4)
+ } else {
+ bsdx1 = bs * bsdx1
+ bsdx2 = bs * bsdx2
+
+ val1 = acdx1 + asdy1
+ val2 = acdx2 + asdy1
+ val3 = acdx2 + asdy2
+ val4 = acdx1 + asdy2
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= a || maxval <= 1.)
+ next
+ xm[1] = max (1., min (a, val1))
+ xm[2] = max (1., min (a, val2))
+ xm[3] = max (1., min (a, val3))
+ xm[4] = max (1., min (a, val4))
+ xm[5] = xm[1]
+
+ val1 = bcdy1 - bsdx1
+ val2 = bcdy1 - bsdx2
+ val3 = bcdy2 - bsdx2
+ val4 = bcdy2 - bsdx1
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= b || maxval <= 1.)
+ next
+ ym[1] = max (1., min (b, val1))
+ ym[2] = max (1., min (b, val2))
+ ym[3] = max (1., min (b, val3))
+ ym[4] = max (1., min (b, val4))
+ ym[5] = ym[1]
+ y2 = 1
+ do x2 = 2, 4
+ if (ym[x2] < ym[y2])
+ y2 = x2
+ switch (y2) {
+ case 2:
+ xm[1] = xm[2]; ym[1] = ym[2]
+ xm[2] = xm[3]; ym[2] = ym[3]
+ xm[3] = xm[4]; ym[3] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 3:
+ xm[1] = xm[3]; ym[1] = ym[3]
+ xm[3] = xm[5]; ym[3] = ym[5]
+ xm[5] = xm[2]; ym[5] = ym[2]
+ xm[2] = xm[4]; ym[2] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 4:
+ xm[5] = xm[4]; ym[5] = ym[4]
+ xm[4] = xm[3]; ym[4] = ym[3]
+ xm[3] = xm[2]; ym[3] = ym[2]
+ xm[2] = xm[1]; ym[2] = ym[1]
+ xm[1] = xm[5]; ym[1] = ym[5]
+ }
+
+ s = msigrl (model, xm, ym, 5)
+ }
+ sum = sum + s
+ }
+ }
+ sum1 = sum1 + sum
+ Memr[ptr] = sum
+ ptr = ptr + 1
+ }
+ }
+
+ call amulkr (Memr[data], flux/sum1, Memr[data], n)
+ return
+
+# SEEING:
+
+see_ n = nx * ny
+ call aclrr (Memr[data], n)
+ sum = 0.
+
+ nxs = Memi[MKT_NX(see)]
+ nys = Memi[MKT_NY(see)]
+ nxs2 = nxs/2
+ nys2 = nys/2
+
+ # If the profile is very small return full star image rather than
+ # convolution with truncated seeing template.
+
+ if (radius > 0.01) {
+ a = (nxm / 2.) / radius
+ b = (nym / 2.) / (ar * radius)
+ c = cos (pa)
+ s = sin (pa)
+ ac = a * c
+ as = a * s
+ bc = b * c
+ bs = b * s
+ a = nxm
+ b = nym
+ xcm = nxm / 2 + 1.
+ ycm = nym / 2 + 1.
+
+ xc1 = xc - int (xc + 0.5) + nx/2
+ yc1 = yc - int (yc + 0.5) + ny/2
+ ptr = data-nys2*nx-nxs2
+ do y = 0, ny-1 {
+ c = (y - yc1) ** 2
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ do x = 0, nx-1 {
+ i = sqrt ((x - xc1) ** 2 + c)
+ dsub = 1. / max (1, nxsub - i)
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ptr1 = ptr + xs1
+ for (y1=y-0.5; y1<y+0.5-dsub/2; y1=y1+dsub) {
+ asdy1 = (y1 - yc1)
+ asdy2 = asdy1 + dsub
+ bcdy1 = bc * asdy1 + ycm
+ bcdy2 = bc * asdy2 + ycm
+ if (pa == 0.) {
+ val3 = max (1., bcdy1)
+ if (val3 >= nym)
+ next
+ val4 = min (b, bcdy2)
+ if (val3 >= val4)
+ next
+ }
+ asdy1 = as * asdy1
+ asdy2 = as * asdy2
+ for (x1=x-0.5; x1<x+0.5-dsub/2; x1=x1+dsub) {
+ bsdx1 = (x1-xc1)
+ bsdx2 = bsdx1 + dsub
+ acdx1 = ac * bsdx1 + xcm
+ acdx2 = ac * bsdx2 + xcm
+ if (pa == 0.) {
+ val1 = max (1., acdx1)
+ if (val1 >= nxm)
+ next
+ val2 = min (a, acdx2)
+ if (val1 >= val2)
+ next
+ sum = msisqgrl (model, val1, val2, val3, val4)
+ } else {
+ bsdx1 = bs * bsdx1
+ bsdx2 = bs * bsdx2
+
+ val1 = acdx1 + asdy1
+ val2 = acdx2 + asdy1
+ val3 = acdx2 + asdy2
+ val4 = acdx1 + asdy2
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= a || maxval <= 1.)
+ next
+ xm[1] = max (1., min (a, val1))
+ xm[2] = max (1., min (a, val2))
+ xm[3] = max (1., min (a, val3))
+ xm[4] = max (1., min (a, val4))
+ xm[5] = xm[1]
+
+ val1 = bcdy1 - bsdx1
+ val2 = bcdy1 - bsdx2
+ val3 = bcdy2 - bsdx2
+ val4 = bcdy2 - bsdx1
+ minval = min (val1, val2, val3, val4)
+ maxval = max (val1, val2, val3, val4)
+ if (minval >= b || maxval <= 1.)
+ next
+ ym[1] = max (1., min (b, val1))
+ ym[2] = max (1., min (b, val2))
+ ym[3] = max (1., min (b, val3))
+ ym[4] = max (1., min (b, val4))
+ ym[5] = ym[1]
+
+# The following is put in to work around a bug in msigrl in V2.8. When
+# V2.8 is long gone we can take this stuff out since msigrl will do the
+# rotating of the coordinates itself.
+
+ minval = max (1., minval)
+ y2 = 1
+ do x2 = 2, 4
+ if (ym[x2] < ym[y2])
+ y2 = x2
+ switch (y2) {
+ case 2:
+ xm[1] = xm[2]; ym[1] = ym[2]
+ xm[2] = xm[3]; ym[2] = ym[3]
+ xm[3] = xm[4]; ym[3] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 3:
+ xm[1] = xm[3]; ym[1] = ym[3]
+ xm[3] = xm[5]; ym[3] = ym[5]
+ xm[5] = xm[2]; ym[5] = ym[2]
+ xm[2] = xm[4]; ym[2] = ym[4]
+ xm[4] = xm[5]; ym[4] = ym[5]
+ xm[5] = xm[1]; ym[5] = ym[1]
+ case 4:
+ xm[5] = xm[4]; ym[5] = ym[4]
+ xm[4] = xm[3]; ym[4] = ym[3]
+ xm[3] = xm[2]; ym[3] = ym[2]
+ xm[2] = xm[1]; ym[2] = ym[1]
+ xm[1] = xm[5]; ym[1] = ym[5]
+ }
+
+ sum = msigrl (model, xm, ym, 5)
+ }
+ call mkt_gstar (see, see1, nxs, nys, x1, y1, 1.)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + sum * Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ }
+ }
+ ptr = ptr + 1
+ }
+ }
+ sum = asumr (Memr[data], n)
+ }
+
+ # If no flux is accumulated or radius is very small return star image.
+ # Otherwise scale to requested flux.
+
+ if (sum == 0.) {
+ call mkt_gstar (star, see1, nxs, nys, xc, yc, flux)
+ x = nx/2
+ y = ny/2
+ nxs2 = nxs / 2
+ nys2 = nys / 2
+ xs1 = max (0, nxs2 - x)
+ xs2 = min (nxs-1, nx - x + nxs2 - 1)
+ ys1 = max (0, nys2 - y)
+ ys2 = min (nys-1, ny - y + nys2 - 1)
+ ptr1 = data-nys2*nx-nxs2+(y*nx+x+xs1)
+ see1 = see1 + xs1
+ do y2 = ys1, ys2 {
+ see2 = see1+y2*nxs
+ ptr2 = ptr1+y2*nx
+ do x2 = xs1, xs2 {
+ Memr[ptr2] = Memr[ptr2] + Memr[see2]
+ ptr2 = ptr2 + 1
+ see2 = see2 + 1
+ }
+ }
+ } else
+ call amulkr (Memr[data], flux/sum, Memr[data], n)
+end
+
+
+# UIGAMMA -- Upper Incomplete Gamma Function
+#
+# uigamma(n,x) = (n-1)!e^(x) sum (x^k/k!) for k=0, n-1
+
+real procedure uigamma (n, x)
+
+int n #I argument
+real x #I argument
+
+int i
+real uigamma
+double sum, numerator, denominator
+
+begin
+ numerator = exp(-x)
+ denominator = 1
+ sum = numerator / denominator
+ do i = 1, n {
+ numerator = numerator * x
+ denominator = denominator * i
+ sum = sum + numerator / denominator
+ }
+ uigamma = denominator * sum
+ return (uigamma)
+end
diff --git a/noao/artdata/numrecipes.x b/noao/artdata/numrecipes.x
new file mode 100644
index 00000000..fa0f56b6
--- /dev/null
+++ b/noao/artdata/numrecipes.x
@@ -0,0 +1,121 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <math.h>
+
+# GAMMLN -- Return natural log of gamma function.
+# POIDEV -- Returns Poisson deviates for a given mean.
+# GASDEV -- Return a normally distributed deviate of zero mean and unit var.
+
+
+# GAMMLN -- Return natural log of gamma function.
+# Argument must greater than 0. Full accuracy is obtained for values
+# greater than 1. For 0<xx<1, the reflection formula can be used first.
+#
+# Based on Numerical Recipes by Press, Flannery, Teukolsky, and Vetterling.
+# Used by permission of the authors.
+# Copyright(c) 1986 Numerical Recipes Software.
+
+real procedure gammln (xx)
+
+real xx # Value to be evaluated
+
+int j
+double cof[6], stp, x, tmp, ser
+data cof, stp / 76.18009173D0, -86.50532033D0, 24.01409822D0,
+ -1.231739516D0,.120858003D-2,-.536382D-5,2.50662827465D0/
+
+begin
+ x = xx - 1.0D0
+ tmp = x + 5.5D0
+ tmp = (x + 0.5D0) * log (tmp) - tmp
+ ser = 1.0D0
+ do j = 1, 6 {
+ x = x + 1.0D0
+ ser = ser + cof[j] / x
+ }
+ return (tmp + log (stp * ser))
+end
+
+
+# POIDEV -- Returns Poisson deviates for a given mean.
+# The real value returned is an integer.
+#
+# Based on Numerical Recipes by Press, Flannery, Teukolsky, and Vetterling.
+# Used by permission of the authors.
+# Copyright(c) 1986 Numerical Recipes Software.
+#
+# Modified to return zero for input values less than or equal to zero.
+
+real procedure poidev (xm, seed)
+
+real xm # Poisson mean
+long seed # Random number seed
+
+real oldm, g, em, t, y, ymin, ymax, sq, alxm, gammln(), urand(), gasdev()
+data oldm /-1./
+
+begin
+ if (xm <= 0)
+ em = 0
+ else if (xm < 12) {
+ if (xm != oldm) {
+ oldm = xm
+ g = exp (-xm)
+ }
+ em = 0
+ for (t = urand (seed); t > g; t = t * urand (seed))
+ em = em + 1
+ } else if (xm < 100) {
+ if (xm != oldm) {
+ oldm = xm
+ sq = sqrt (2. * xm)
+ ymin = -xm / sq
+ ymax = (1000 - xm) / sq
+ alxm = log (xm)
+ g = xm * alxm - gammln (xm+1.)
+ }
+ repeat {
+ repeat {
+ y = tan (PI * urand(seed))
+ } until (y >= ymin)
+ em = int (sq * min (y, ymax) + xm)
+ t = 0.9 * (1 + y**2) * exp (em * alxm - gammln (em+1) - g)
+ } until (urand(seed) <= t)
+ } else
+ em = xm + sqrt (xm) * gasdev (seed)
+ return (em)
+end
+
+
+# GASDEV -- Return a normally distributed deviate with zero mean and unit
+# variance. The method computes two deviates simultaneously.
+#
+# Based on Numerical Recipes by Press, Flannery, Teukolsky, and Vetterling.
+# Used by permission of the authors.
+# Copyright(c) 1986 Numerical Recipes Software.
+
+real procedure gasdev (seed)
+
+long seed # Seed for random numbers
+
+real urand()
+double v1, v2, r, fac
+int iset
+data iset/0/
+
+begin
+ if (iset == 0) {
+ repeat {
+ v1 = 2 * urand (seed) - 1.
+ v2 = 2 * urand (seed) - 1.
+ r = v1 ** 2 + v2 ** 2
+ } until ((r > 0) && (r < 1))
+ fac = sqrt (-2. * log (r) / r)
+
+ iset = 1
+ return (v1 * fac)
+ } else {
+ iset = 0
+ return (v2 * fac)
+ }
+end
diff --git a/noao/artdata/starlist.par b/noao/artdata/starlist.par
new file mode 100644
index 00000000..8416df1f
--- /dev/null
+++ b/noao/artdata/starlist.par
@@ -0,0 +1,43 @@
+# STARLIST Parameter File
+
+starlist,f,a,,,,"Output star list file"
+nstars,i,a,5000,,,"Number of stars in the list"
+interactive,b,h,no,,,"Interactive mode?
+
+SPATIAL DISTRIBUTION"
+spatial,s,h,"uniform","uniform|hubble|file",,"Spatial density function (uniform|hubble|file)"
+xmin,r,h,1.0,,,"Minimum x coordinate value"
+xmax,r,h,512.0,,,"Maximum x coordinate value"
+ymin,r,h,1.0,,,"Minimum y coordinate value"
+ymax,r,h,512.0,,,"Maximum y coordinate value"
+xcenter,r,h,INDEF,,,"X coordinate of center of Hubble distribution"
+ycenter,r,h,INDEF,,,"Y coordinate of center of Hubble distribution"
+core_radius,r,h,30.0,,,"Core radius of Hubble distribution"
+base,r,h,0.0,,,"Relative background density of the Hubble distribution"
+sseed,i,h,1,,,"Seed for sampling the spatial density probability function
+
+MAGNITUDE DISTRIBUTION"
+luminosity,s,h,"powlaw","uniform|salpeter|bands|powlaw|file",,"Luminosity function (uniform|salpeter|bands|powlaw|file)"
+minmag,r,h,-7.0,,,"Minimum magnitude"
+maxmag,r,h,0.0,,,"Maximum magnitude"
+mzero,r,h,-4.,,,"Absolute magnitude zero point for salpeter and bands"
+power,r,h,0.6,,,"Power law magnitude distribution coefficient"
+alpha,r,h,0.74,,,"Alpha parameter for bands function"
+beta,r,h,0.04,,,"Beta parameter for bands function"
+delta,r,h,0.294,,,"Delta parameter for bands function"
+mstar,r,h,1.28,,,"Mstar parameter for bands function"
+lseed,i,h,1,,,"Seed for sampling the luminsosity probability function
+
+USER FUNCTIONS"
+sfile,f,a,"",,,"File containing spatial density function"
+nssample,i,h,100,,,"Number of spatial density function sampling points "
+sorder,i,h,10,,,"Number of spline pieces for spatial probability function"
+lfile,f,a,"",,,"File containing luminosity function"
+nlsample,i,h,100,,,"Number of luminosity function sampling points"
+lorder,i,h,10,,,"Number of spline pieces for luminosity probability function
+
+INTERACTIVE PARAMETERS"
+rbinsize,r,h,10.,,,"Bin size of radial density function histogram in pixels"
+mbinsize,r,h,0.5,,,"Bin size of luminosity function in magnitudes"
+graphics,s,h,stdgraph,,,"Standard graphics device"
+cursor,*gcur,h,"",,,"Graphics cursor"
diff --git a/noao/artdata/stdheader.dat b/noao/artdata/stdheader.dat
new file mode 100644
index 00000000..da6152c3
--- /dev/null
+++ b/noao/artdata/stdheader.dat
@@ -0,0 +1,10 @@
+DATE-OBS= '1990-01-01T00:00:00'
+RA = '00:00:00'
+DEC = '00:00:00'
+EPOCH = 1990.
+UT = '00:00:00'
+ST = '00:00:00'
+ZT = 0.
+AIRMASS = 1.
+EXPTIME = 1.
+INSTRUME= 'IRAF/ARTDATA'
diff --git a/noao/artdata/t_mk1dspec.x b/noao/artdata/t_mk1dspec.x
new file mode 100644
index 00000000..5fe5b928
--- /dev/null
+++ b/noao/artdata/t_mk1dspec.x
@@ -0,0 +1,443 @@
+include <error.h>
+include <imhdr.h>
+
+define LEN_UA 20000 # Maximum user header
+define LEN_COMMENT 70 # Maximum comment length
+
+# Profile types.
+define PTYPES "|gaussian|lorentzian|voigt|"
+define GAUSS 1 # Gaussian profile
+define LORENTZ 2 # Lorentzian profile
+define VOIGT 3 # Voigt profile
+
+
+# T_MK1DSPEC -- Make one dimensional spectra. New images may be created
+# or existing images modified. The continuum may be a slope and/or
+# a blackbody. A line list may be given or random lines generated.
+# The lines may be emission or absorption and may have varying
+# widths and strengths. Subsampling is used.
+
+procedure t_mk1dspec()
+
+int ilist # List of input spectra (input param)
+int olist # List of output spectra (output param)
+
+int line # Line number
+int ap # Aperture
+int beam # Beam
+int nw # Number of pixels (ncols param or imlen)
+double w0 # Starting wavelength (wstart param)
+double wpc # Wavelength per pix (wstart/wend params)
+double z # Redshift
+
+double cont # Continuum at first pixel
+double slope # Continuum slope per pixel
+double temp # Blackbody temperture (Kelvin)
+int fnu # F-nu flux?
+
+int llist # List of files containing lines (lines param)
+pointer profile # Profile type
+double peak # Peak/continuum
+double gfwhm # Sigma of Gaussian (Angstroms)
+double lfwhm # FWHM of Lorentzian (Angstroms)
+int nlines # Number of lines
+double subsample # Subsampling (nxsub param)
+double ngfwhm # Dynamic range of gaussian (dynrange param)
+double nlfwhm # Dynamic range of lorentzian (dynrange param)
+long seed # Random number seed
+
+bool new, ranlist
+int i, j, dtype, ptype
+long seed1
+double w, x, x1, x2, x3, z1
+real v, u, aplow[2], aphigh[2]
+pointer sp, input, output, lines, comment, coeff
+pointer in, out, mw, ptypes, waves, peaks, gfwhms, lfwhms, spec, buf
+
+long clgetl(), clktime()
+int clgeti(), clgwrd(), imtopenp(), imtgetim()
+int nowhite(), access(), open(), fscan(), nscan(), strdic()
+real urand()
+double clgetd()
+pointer immap(), mw_open(), smw_openim(), imgl2d(), impl2d()
+bool clgetb(), streq()
+errchk open()
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (lines, SZ_FNAME, TY_CHAR)
+ call salloc (comment, LEN_COMMENT, TY_CHAR)
+ call salloc (profile, SZ_FNAME, TY_CHAR)
+ coeff = NULL
+
+ # Get file lists and fixed parameters.
+ ilist = imtopenp ("input")
+ olist = imtopenp ("output")
+ llist = imtopenp ("lines")
+ subsample = 1. / clgeti ("nxsub")
+ x1 = clgetd ("dynrange")
+ ngfwhm = 0.424661 * sqrt (2. * log (x1))
+ nlfwhm = sqrt (0.5 * (x1 - 1))
+ z = clgetd ("rv")
+ if (clgetb ("z"))
+ z = 1 + z
+ else {
+ z = z / 299792.5
+ z = sqrt ((1 + z) / (1 - z))
+ }
+
+ # Loop through input images. Missing output images take input
+ # image name. Line list files may be missing.
+
+ Memc[lines] = EOS
+ while (imtgetim (ilist, Memc[input], SZ_FNAME) != EOF) {
+ if (imtgetim (olist, Memc[output], SZ_FNAME) == EOF)
+ call strcpy (Memc[input], Memc[output], SZ_FNAME)
+ i = imtgetim (llist, Memc[lines], SZ_FNAME)
+
+ # Map images. Check for new, existing, and in-place images.
+ if (streq (Memc[input], Memc[output])) {
+ ifnoerr (in = immap (Memc[input], READ_WRITE, 0)) {
+ iferr (mw = smw_openim (in)) {
+ call imunmap (in)
+ call erract (EA_WARN)
+ next
+ }
+ out = in
+ new = false
+ } else {
+ iferr (out = immap (Memc[output], NEW_IMAGE, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+ in = out
+ new = true
+
+ call clgstr ("header", Memc[comment], LEN_COMMENT)
+ iferr (call mkh_header (out, Memc[comment], true, false))
+ call erract (EA_WARN)
+
+ IM_LEN(out,1) = clgeti ("ncols")
+ IM_LEN(out,2) = clgeti ("naps")
+ if (IM_LEN(out,2) == 1)
+ IM_NDIM(out) = 1
+ else
+ IM_NDIM(out) = 2
+ IM_PIXTYPE(out) = TY_REAL
+ call clgstr ("title", IM_TITLE(out), SZ_IMTITLE)
+
+ i = IM_NDIM(out)
+ mw = mw_open (NULL, i)
+ call mw_newsystem (mw, "equispec", i)
+ call mw_swtype (mw, 1, 1, "linear", "")
+ if (i > 1)
+ call mw_swtype (mw, 2, 1, "linear", "")
+ call mw_swattrs (mw, 1, "label", "Wavelength")
+ call mw_swattrs (mw, 1, "units", "Angstroms")
+ call smw_open (mw, NULL, out)
+
+ dtype = -1
+ nw = IM_LEN(out,1)
+ w0 = 1.
+ wpc = 1.
+ aplow[1] = INDEF
+ aplow[2] = INDEF
+ aphigh[1] = INDEF
+ aphigh[2] = INDEF
+ do i = 1, IM_LEN(out,2)
+ call smw_swattrs (mw, i, 1, i, i, dtype, w0, wpc, nw,
+ 0D0, aplow, aphigh, "")
+ }
+ } else {
+ iferr (in = immap (Memc[input], READ_ONLY, 0)) {
+ call erract (EA_WARN)
+ next
+ }
+ iferr (out = immap (Memc[output], NEW_COPY, in)) {
+ call erract (EA_WARN)
+ call imunmap (in)
+ next
+ }
+ iferr (mw = smw_openim (in)) {
+ call imunmap (in)
+ call imunmap (out)
+ call erract (EA_WARN)
+ next
+ }
+ new = false
+ }
+
+ line = max (1, min (clgeti ("ap"), IM_LEN(out,2)))
+ call smw_gwattrs (mw, line, 1, ap, beam, dtype, w0, wpc, nw,
+ z1, aplow, aphigh, coeff)
+
+ if (dtype < 0) {
+ dtype = 0
+ nw = min (clgeti ("ncols"), IM_LEN(out,1))
+ w0 = clgetd ("wstart")
+ wpc = (clgetd ("wend") - w0) / (nw - 1)
+ call smw_swattrs (mw, line, 1, ap, beam, dtype, w0, wpc, nw,
+ 0D0, aplow, aphigh, "")
+ }
+
+ # Get the line list if given or create random lines.
+ ranlist = false
+ i = nowhite (Memc[lines], Memc[lines], SZ_FNAME)
+ if (access (Memc[lines], 0, 0) == YES) {
+ i = open (Memc[lines], READ_ONLY, TEXT_FILE)
+ nlines = 0
+ dtype = clgwrd ("profile", Memc[profile], SZ_FNAME, PTYPES)
+ x1 = clgetd ("peak")
+ x2 = clgetd ("gfwhm")
+ x3 = clgetd ("lfwhm")
+ seed = clgetl ("seed")
+ if (IS_INDEFL(seed))
+ seed1 = seed1 + clktime (long (0))
+ else
+ seed1 = seed
+ while (fscan (i) != EOF) {
+ call gargd (w)
+ call gargd (peak)
+ call gargwrd (Memc[profile], SZ_FNAME)
+ call gargd (gfwhm)
+ call gargd (lfwhm)
+ ptype = strdic (Memc[profile], Memc[profile], SZ_FNAME,
+ PTYPES)
+ if (ptype == 0)
+ ptype = dtype
+ switch (nscan()) {
+ case 0:
+ next
+ case 1:
+ peak = x1 * urand (seed1)
+ ptype = dtype
+ gfwhm = x2
+ lfwhm = x3
+ case 2:
+ ptype = dtype
+ gfwhm = x2
+ lfwhm = x3
+ case 3:
+ gfwhm = x2
+ lfwhm = x3
+ case 4:
+ switch (ptype) {
+ case GAUSS:
+ lfwhm = x3
+ case LORENTZ:
+ lfwhm = gfwhm
+ gfwhm = x2
+ case VOIGT:
+ lfwhm = x3
+ }
+ }
+
+ if (nlines == 0) {
+ j = 50
+ call malloc (ptypes, j, TY_INT)
+ call malloc (waves, j, TY_DOUBLE)
+ call malloc (peaks, j, TY_DOUBLE)
+ call malloc (gfwhms, j, TY_DOUBLE)
+ call malloc (lfwhms, j, TY_DOUBLE)
+ } else if (nlines == j) {
+ j = j + 10
+ call realloc (ptypes, j, TY_INT)
+ call realloc (waves, j, TY_DOUBLE)
+ call realloc (peaks, j, TY_DOUBLE)
+ call realloc (gfwhms, j, TY_DOUBLE)
+ call realloc (lfwhms, j, TY_DOUBLE)
+ }
+ Memi[ptypes+nlines] = ptype
+ Memd[waves+nlines] = z * w
+ Memd[peaks+nlines] = peak / z
+ Memd[gfwhms+nlines] = z * gfwhm
+ Memd[lfwhms+nlines] = z * lfwhm
+ nlines = nlines + 1
+ }
+ call close (i)
+ } else {
+ nlines = clgeti ("nlines")
+ ptype = clgwrd ("profile", Memc[profile], SZ_FNAME, PTYPES)
+ peak = clgetd ("peak")
+ gfwhm = clgetd ("gfwhm")
+ lfwhm = clgetd ("lfwhm")
+ seed = clgetl ("seed")
+ if (IS_INDEFL(seed))
+ seed1 = seed1 + clktime (long (0))
+ else
+ seed1 = seed
+ call malloc (ptypes, nlines, TY_INT)
+ call malloc (waves, nlines, TY_DOUBLE)
+ call malloc (peaks, nlines, TY_DOUBLE)
+ call malloc (gfwhms, nlines, TY_DOUBLE)
+ call malloc (lfwhms, nlines, TY_DOUBLE)
+ do i = 0, nlines-1 {
+ w = z * (w0 + wpc * (nw - 1) * urand (seed1))
+ x = (w - w0) / wpc / (nw - 1)
+ if (x < 0)
+ x = x - int (x - 1)
+ else
+ x = x - int (x)
+ w = w0 + wpc * (nw - 1) * x
+ Memi[ptypes+i] = ptype
+ Memd[waves+i] = w
+ Memd[peaks+i] = peak / z * urand (seed1)
+ Memd[gfwhms+i] = z * gfwhm
+ Memd[lfwhms+i] = z * lfwhm
+ }
+ if (nlines > 0 && Memc[lines] != EOS) {
+ i = open (Memc[lines], NEW_FILE, TEXT_FILE)
+ do j = 0, nlines-1 {
+ switch (Memi[ptypes+j]) {
+ case GAUSS:
+ call fprintf (i, "%g %g %10s %g\n")
+ call pargd (Memd[waves+j] / z)
+ call pargd (Memd[peaks+j] * z)
+ call pargstr ("gaussian")
+ call pargd (Memd[gfwhms+j] / z)
+ case LORENTZ:
+ call fprintf (i, "%g %g %10s %g\n")
+ call pargd (Memd[waves+j] / z)
+ call pargd (Memd[peaks+j] * z)
+ call pargstr ("lorentzian")
+ call pargd (Memd[lfwhms+j] / z)
+ case VOIGT:
+ call fprintf (i, "%g %g %10s %g %g\n")
+ call pargd (Memd[waves+j] / z)
+ call pargd (Memd[peaks+j] * z)
+ call pargstr ("voigt")
+ call pargd (Memd[gfwhms+j] / z)
+ call pargd (Memd[lfwhms+j] / z)
+ }
+ }
+ call close (i)
+ }
+ }
+
+ # Make the spectrum.
+ spec = impl2d (out, line)
+ if (new)
+ call aclrd (Memd[spec], IM_LEN(in,1))
+ else
+ call amovd (Memd[imgl2d(in, line)], Memd[spec], IM_LEN(in,1))
+
+ # Make the lines.
+ call calloc (buf, nw, TY_DOUBLE)
+ do i = 0, nlines-1 {
+ ptype = Memi[ptypes+i]
+ w = (Memd[waves+i] - w0) / wpc + 1.
+ peak = Memd[peaks+i] * subsample
+ gfwhm = Memd[gfwhms+i] / abs(wpc)
+ lfwhm = Memd[lfwhms+i] / abs(wpc)
+ x3 = max (ngfwhm*gfwhm, min (20D0, nlfwhm)*lfwhm)
+ x1 = max (1.0D0, w - x3)
+ x2 = min (double (nw), w + x3)
+ switch (ptype) {
+ case GAUSS:
+ x3 = -0.360674 * gfwhm**2
+ for (x = x1; x <= x2; x = x + subsample) {
+ j = buf + int (x - 0.5)
+ Memd[j] = Memd[j] + peak * exp ((x-w)**2 / x3)
+ }
+ case LORENTZ:
+ x3 = 0.25 * lfwhm**2
+ for (x = x1; x <= x2; x = x + subsample) {
+ j = buf + int (x - 0.5)
+ Memd[j] = Memd[j] + peak / (1 + (x-w)**2 / x3)
+ }
+ case VOIGT:
+ x3 = 1.66511 / gfwhm
+ cont = (lfwhm / 2 ) * x3
+ call voigt (0., real(cont), v, u)
+ peak = peak / v
+ for (x = x1; x <= x2; x = x + subsample) {
+ j = buf + int (x - 0.5)
+ call voigt (real((x-w)*x3), real(cont), v, u)
+ Memd[j] = Memd[j] + peak * v
+ }
+ }
+ }
+
+ # Make the continuum.
+ cont = clgetd ("continuum")
+ slope = clgetd ("slope")
+ temp = clgetd ("temperature")
+ if (clgetb ("fnu"))
+ fnu = 3
+ else
+ fnu = 5
+ if (temp > 0.) {
+ w = w0 * 1.0e-8
+ x1 = exp (1.4388 / (w * temp))
+ x2 = w**fnu * (x1-1.0)
+
+ w = w / z
+ wpc = wpc * 1.0e-8 / z
+ }
+ do i = 0, nw-1 {
+ x = cont + slope / wpc * ((w0 + wpc * i) / z - w0)
+ if (temp > 0.) {
+ x1 = exp (1.4388 / (w * temp))
+ x = x * (x2 / w**fnu / (x1-1.0))
+ w = w + wpc
+ }
+ if (x > 0.)
+ Memd[spec+i] = Memd[spec+i] +
+ max (0.0D0, x * (1. + Memd[buf+i]))
+ else
+ Memd[spec+i] = Memd[spec+i] + Memd[buf+i]
+ }
+
+ call mfree (ptypes, TY_INT)
+ call mfree (waves, TY_DOUBLE)
+ call mfree (peaks, TY_DOUBLE)
+ call mfree (gfwhms, TY_DOUBLE)
+ call mfree (lfwhms, TY_DOUBLE)
+ call mfree (buf, TY_DOUBLE)
+
+ # 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 (out, Memc[comment])
+ call mkh_comment (out, "begin mk1dspec")
+ call mkh_comment1 (out, "ap", 'i')
+ call mkh_comment1 (out, "rv", 'd')
+ call mkh_comment1 (out, "z", 'b')
+ call mkh_comment1 (out, "wstart", 'd')
+ call mkh_comment1 (out, "wend", 'd')
+ call mkh_comment1 (out, "continuum", 'd')
+ call mkh_comment1 (out, "slope", 'd')
+ call mkh_comment1 (out, "temperature", 'd')
+ call mkh_comment1 (out, "fnu", 'b')
+ if (nlines > 0) {
+ if (Memc[lines] != EOS)
+ call mkh_comment1 (out, "lines", 's')
+ call sprintf (Memc[comment], LEN_COMMENT, "%9tnlines%24t%d")
+ call pargi (nlines)
+ call mkh_comment (out, Memc[comment])
+ if (ranlist) {
+ call mkh_comment1 (out, "profile", 's')
+ call mkh_comment1 (out, "peak", 'd')
+ call mkh_comment1 (out, "gfwhm", 'd')
+ call mkh_comment1 (out, "lfwhm", 'd')
+ call mkh_comment1 (out, "seed", 'i')
+ }
+ }
+ }
+
+ call smw_saveim (mw, out)
+ call smw_close (mw)
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+ }
+
+ call mfree (coeff, TY_CHAR)
+ call imtclose (ilist)
+ call imtclose (olist)
+ call imtclose (llist)
+ call sfree (sp)
+end
diff --git a/noao/artdata/t_mk2dspec.x b/noao/artdata/t_mk2dspec.x
new file mode 100644
index 00000000..8f13d8e2
--- /dev/null
+++ b/noao/artdata/t_mk2dspec.x
@@ -0,0 +1,297 @@
+include <error.h>
+include <imhdr.h>
+include <math/iminterp.h>
+
+define LEN_UA 20000 # Maximum user header
+define LEN_COMMENT 70 # Maximum comment length
+
+define NALLOC 10 # Alloc block size
+define NPROF 201 # Length of profile
+
+# Each spectrum is described by a 1D spectrum and shape and position info.
+define LEN_MOD 7 # Length of model spectrum structure
+define SPEC Memi[$1] # Pointer to spectrum
+define NPTS Memi[$1+1] # Number of points in spectrum
+define PTYPE Memi[$1+2] # Profile type
+define WIDTH Memr[P2R($1+3)] # Profile width (FWHM at center line)
+define DWIDTH Memr[P2R($1+4)] # Derivative of width
+define POS Memr[P2R($1+5)] # Profile position (at center line)
+define DPOS Memr[P2R($1+6)] # Derivative of position
+
+define PTYPES "|gaussian|slit|"
+define GAUSS 1 # Gaussian (pexp = 2)
+define SLIT 2 # Slit (pexp = 10)
+
+
+# T_MK2DSPEC -- Make a 2D spectrum from 1D template and a profile function.
+# The dispersion axis is along the columns and the spectrum is taken from
+# 1D spectrum. The cross dispersion profile is either a Gaussian or a
+# slit with a specified FWHM. The center of the profile along the dispersion
+# axis is a sloped line. The width of the profile may also be variable.
+
+procedure t_mk2dspec ()
+
+pointer input # Input image
+pointer output # Output image
+pointer models # Spectrum models (file)
+int nc # Number of columns
+int nl # Number of lines
+bool cmmts # Add comments?
+
+pointer template # Template spectrum name
+real scale # Intensity scale
+int ptype # Profile type
+real width # Profile width (FWHM at center line)
+real dwidth # Derivative of profile width
+real pos # Profile position (center of image)
+real dpos # Deriviative of position
+
+bool new
+int ilist, olist, mlist
+int i, j, k, k1, k2, fd, npts, nmods, nalloc
+real pcen[2], fwhm[2], flux[2], peak, pstep, pstart, pend, x1, x2, dx
+pointer sp, comment, mod, mods, asi, asis[2], data, in, out, temp, pname
+
+bool streq(), clgetb()
+real asigrl()
+int clgeti(), access(), open(), fscan(), nscan(), strdic()
+int imtopenp(), imtlen(), imtgetim(), clktime()
+pointer immap(), imgl1r(), imgl2r(), impl2r()
+errchk open, immap
+
+begin
+ call smark (sp)
+ call salloc (comment, LEN_COMMENT, TY_CHAR)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (models, SZ_FNAME, TY_CHAR)
+ call salloc (template, SZ_FNAME, TY_CHAR)
+ call salloc (pname, SZ_FNAME, TY_CHAR)
+
+ # Make the profile templates stored as an interpolation function
+ # with the returned center, fwhm, and flux.
+
+ call mkprof (2., asis[1], pcen[1], fwhm[1], flux[1])
+ call mkprof (10., asis[2], pcen[2], fwhm[2], flux[2])
+
+ # Get the file lists and loop through them.
+ ilist = imtopenp ("input")
+ olist = imtopenp ("output")
+ mlist = imtopenp ("models")
+ cmmts = clgetb ("comments")
+
+ if (max (1, imtlen (olist)) != imtlen (ilist))
+ call error (1, "Output image list does not match input image list")
+
+ Memc[models] = EOS
+ while (imtgetim (ilist, Memc[input], SZ_FNAME) != EOF) {
+ if (imtgetim (olist, Memc[output], SZ_FNAME) == EOF)
+ call strcpy (Memc[input], Memc[output], SZ_FNAME)
+ i = imtgetim (mlist, Memc[models], SZ_FNAME)
+ if (access (Memc[models], 0, 0) == NO) {
+ call eprintf ("WARNING: Can't access model file (%s)\n")
+ call pargstr (Memc[models])
+ next
+ }
+
+ # Map images. Check for new, existing, and in-place images.
+ if (streq (Memc[input], Memc[output])) {
+ ifnoerr (in = immap (Memc[input], READ_WRITE, 0)) {
+ out = in
+ new = false
+ } else {
+ iferr (out = immap (Memc[output], NEW_IMAGE, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+ in = out
+ new = true
+
+ call clgstr ("header", Memc[comment], LEN_COMMENT)
+ iferr (call mkh_header (out, Memc[comment], true, false))
+ call erract (EA_WARN)
+
+ IM_NDIM(out) = 2
+ IM_LEN(out,1) = clgeti ("ncols")
+ IM_LEN(out,2) = clgeti ("nlines")
+ IM_PIXTYPE(out) = TY_REAL
+ call clgstr ("title", IM_TITLE(out), SZ_IMTITLE)
+ call imaddi (out, "dispaxis", 2)
+ }
+ } else {
+ iferr (in = immap (Memc[input], READ_ONLY, 0)) {
+ call erract (EA_WARN)
+ next
+ }
+ iferr (out = immap (Memc[output], NEW_COPY, in)) {
+ call erract (EA_WARN)
+ call imunmap (in)
+ next
+ }
+ new = false
+ }
+ nc = IM_LEN(out,1)
+ nl = IM_LEN(out,2)
+
+ # Read the models file.
+ fd = open (Memc[models], READ_ONLY, TEXT_FILE)
+ nmods = 0
+ while (fscan (fd) != EOF) {
+ call gargwrd (Memc[template], SZ_FNAME)
+ call gargr (scale)
+ call gargwrd (Memc[pname], SZ_FNAME)
+ call gargr (width)
+ call gargr (dwidth)
+ call gargr (pos)
+ call gargr (dpos)
+ if (nscan() != 7)
+ next
+
+ temp = immap (Memc[template], READ_ONLY, 0)
+ npts = IM_LEN(temp,1)
+ call malloc (data, npts, TY_REAL)
+ call amulkr (Memr[imgl1r(temp)], scale, Memr[data], npts)
+ call imunmap (temp)
+
+ call malloc (mod, LEN_MOD, TY_STRUCT)
+ SPEC(mod) = data
+ NPTS(mod) = npts
+ PTYPE(mod) = strdic (Memc[pname], Memc[pname], SZ_FNAME, PTYPES)
+ WIDTH(mod) = width - nl / 2. * dwidth
+ DWIDTH(mod) = dwidth
+ POS(mod) = pos - 1 - nl / 2. * dpos
+ DPOS(mod) = dpos
+
+ if (nmods == 0) {
+ nalloc = NALLOC
+ call malloc (mods, nalloc, TY_POINTER)
+ } else if (nmods == nalloc) {
+ nalloc = nalloc + NALLOC
+ call realloc (mods, nalloc, TY_POINTER)
+ }
+ Memi[mods+nmods] = mod
+ nmods = nmods + 1
+ }
+ call close (fd)
+ if (nmods == 0) {
+ call imunmap (out)
+ call sfree (sp)
+ call error (1, "No model spectra defined")
+ }
+
+ # Now expand the 1D spectra into 2D profiles.
+
+ do i = 1, nl {
+ data = impl2r (out, i)
+ if (new)
+ call aclrr (Memr[data], nc)
+ else
+ call amovr (Memr[imgl2r(in,i)], Memr[data], nc)
+ do j = 1, nmods {
+ mod = Memi[mods+j-1]
+ if (NPTS(mod) < i)
+ next
+ ptype = PTYPE(mod)
+ asi = asis[ptype]
+ peak = Memr[SPEC(mod)+i-1] / flux[ptype]
+ width = WIDTH(mod) + i * DWIDTH(mod)
+ pos = POS(mod) + i * DPOS(mod)
+ pstep = width / fwhm[ptype]
+ pstart = max (-0.5, pos - pcen[ptype] * pstep)
+ pend = min (nc - 0.51, pos + pcen[ptype] * pstep)
+ if (pstart >= pend)
+ next
+
+ k1 = pstart + 0.5
+ k2 = pend + 0.5
+ x1 = (pstart - pos) / pstep + pcen[ptype] + 1
+ x2 = (k1 + 0.5 - pos) / pstep + pcen[ptype] + 1
+ x1 = max (1., x1)
+ x2 = max (1., x2)
+ Memr[data+k1] = Memr[data+k1] + peak * asigrl (asi, x1, x2)
+
+ dx = 1 / pstep
+ do k = k1+1, k2-1 {
+ x1 = x2
+ x2 = x1 + dx
+ Memr[data+k] = Memr[data+k] + peak * asigrl (asi, x1, x2)
+ }
+ x1 = x2
+ x2 = (pend - pos) / pstep + pcen[ptype] + 1
+ Memr[data+k2] = Memr[data+k2] + peak * asigrl (asi, x1, x2)
+ }
+ }
+
+ # Add comment history of task parameters.
+ if (cmmts) {
+ call strcpy ("# ", Memc[comment], LEN_COMMENT)
+ call cnvtime (clktime (0), Memc[comment+2], LEN_COMMENT-2)
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment (out, "begin mk2dspec")
+ call mkh_comment1 (out, "models", 's')
+
+ fd = open (Memc[models], READ_ONLY, TEXT_FILE)
+ while (fscan (fd) != EOF) {
+ call gargstr (Memc[comment], LEN_COMMENT)
+ call mkh_comment (out, Memc[comment])
+ }
+ call close (fd)
+ }
+
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+ do i = 0, nmods-1
+ call mfree (SPEC(Memi[mods+i]), TY_REAL)
+ call mfree (mods, TY_POINTER)
+ }
+
+ call asifree (asis[1])
+ call asifree (asis[2])
+ call imtclose (ilist)
+ call imtclose (olist)
+ call imtclose (mlist)
+ call sfree (sp)
+end
+
+
+# MKPROF -- Make a well sampled profile and fit it by an interpolation
+# function. Return the interpolation function, the center, the FWHM,
+# and total flux.
+
+procedure mkprof (pexp, asi, center, fwhm, flux)
+
+real pexp # Profile exponent
+pointer asi # IMINTERP pointer
+real center # Profile center
+real fwhm # FWHM of profile
+real flux # Flux of profile
+
+int i
+real scale, x, asigrl()
+pointer sp, prof
+
+begin
+ call smark (sp)
+ call salloc (prof, NPROF, TY_REAL)
+
+ # Put the profile center at the center of the array. Set the
+ # scale so the array extends to the 0.5% level. Compute the
+ # FWHM. Generate the profile values and fit the interpolation
+ # function. Finally, compute the total flux by integrating
+ # the interpolation function.
+
+ center = (NPROF - 1) / 2.
+ scale = center / (log (200.) ** (1/pexp))
+ fwhm = 2 * scale * log(2.) ** (1/pexp)
+ do i = 0, NPROF-1 {
+ x = abs (i - center) / scale
+ Memr[prof+i] = exp (-(x**pexp))
+ }
+ call asiinit (asi, II_LINEAR)
+ call asifit (asi, Memr[prof], NPROF)
+
+ flux = asigrl (asi, 1., real (NPROF))
+
+ call sfree (sp)
+end
diff --git a/noao/artdata/t_mkechelle.x b/noao/artdata/t_mkechelle.x
new file mode 100644
index 00000000..ca9e5e37
--- /dev/null
+++ b/noao/artdata/t_mkechelle.x
@@ -0,0 +1,1248 @@
+include <error.h>
+include <imhdr.h>
+include <math.h>
+
+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 (; m1<mc[1]; m1=m1+1) {
+ w = mw1 / m1
+ 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 < nc)
+ break
+ }
+ for (; m2>mc[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
diff --git a/noao/artdata/t_mkheader.x b/noao/artdata/t_mkheader.x
new file mode 100644
index 00000000..d9074d24
--- /dev/null
+++ b/noao/artdata/t_mkheader.x
@@ -0,0 +1,52 @@
+include <error.h>
+include <imhdr.h>
+include <ctype.h>
+
+define LEN_UA 20000 # Maximum user header
+
+# T_MKHEADER -- Append or substitute new image header from an image or file.
+# Only the legal FITS cards (ignoring leading whitespace) will be copied
+# from a file.
+
+procedure t_mkheader ()
+
+int imlist # List of images
+int flist # List of data files
+bool append # Append to existing keywords?
+bool verbose # Verbose output?
+
+int stat
+pointer im, sp, image, fname
+
+bool clgetb()
+int imtopenp(), clpopnu(), clplen(), imtgetim(), clgfil()
+pointer immap()
+
+begin
+ call smark (sp)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ imlist = imtopenp ("images")
+ flist = clpopnu ("headers")
+ if (clplen (flist) == 0)
+ call error (1, "No header files specified")
+ append = clgetb ("append")
+ verbose = clgetb ("verbose")
+
+ while (imtgetim (imlist, Memc[image], SZ_FNAME) != EOF) {
+ stat = clgfil (flist, Memc[fname], SZ_FNAME)
+
+ iferr (im = immap (Memc[image], READ_WRITE, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+ iferr (call mkh_header (im, Memc[fname], append, verbose))
+ call erract (EA_WARN)
+ call imunmap (im)
+ }
+
+ call imtclose (imlist)
+ call clpcls (flist)
+ call sfree (sp)
+end
diff --git a/noao/artdata/t_mknoise.x b/noao/artdata/t_mknoise.x
new file mode 100644
index 00000000..59e7258a
--- /dev/null
+++ b/noao/artdata/t_mknoise.x
@@ -0,0 +1,577 @@
+include <error.h>
+include <imhdr.h>
+include <mach.h>
+
+define LEN_UA 20000 # Maximum user header
+define LEN_COMMENT 70 # Maximum comment length
+
+# Cosmic ray data structure
+define LEN_MKO 4
+define MKO_X Memi[$1] # X position
+define MKO_Y Memi[$1+1] # Y position
+define MKO_Z Memi[$1+2] # Flux
+define MKO_SORT Memi[$1+3] # Sort index
+
+
+# T_MKNOISE -- Add cosmic rays and possion and readout noise to images.
+# New images may be created or noise added to existing images.
+# The noise is not completely random for reasons of speed.
+
+procedure t_mknoise ()
+
+int ilist # Input image list
+int olist # Output image list
+int objects # List of cosmic ray files
+int nl # Number of lines
+int nc # Number of columns
+real background # Background level
+real gain # Gain (electrons/DN)
+int ranbuf # Random number buffer size
+real rdnoise # Read noise (in electrons)
+bool poisson # Add Poisson noise?
+long seed # Random number seed
+int nobjects # Number of random cosmic rays
+real energy # Maximum random energy (electrons)
+bool cmmts # Add comments?
+
+bool new, fcmmts
+long seed1
+real x, y, z, dmin, dmax
+int i, j, k, l, nx, ny, nlines, c1, c2, c3, c4, l1, l2, l3, l4, irbuf, ipbuf
+pointer sp, input, output, fname, comment, rbuf, pbuf
+pointer in, out, buf, obuf, lines, newlines, obj, ptr1, ptr2
+pointer mko, mkt
+
+long clgetl(), clktime()
+bool clgetb(), streq()
+int imtopenp(), imtlen(), imtgetim()
+int clgeti(), access(), nowhite(), open(), fscan(), nscan()
+real clgetr(), urand()
+pointer immap(), imgl2r(), impl2r()
+pointer mkt_star()
+errchk open, immap, imgl2r, impl2r, malloc, realloc, mkt_gstar
+
+int mko_compare()
+extern mko_compare
+pointer mko_sort
+common /mko_qsort/ mko_sort
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (comment, max (SZ_FNAME,LEN_COMMENT), TY_CHAR)
+
+ # Get parameters which apply to all images.
+ ilist = imtopenp ("input")
+ olist = imtopenp ("output")
+ objects = imtopenp ("cosrays")
+ background = clgetr ("background")
+ gain = clgetr ("gain")
+ ranbuf = clgeti ("ranbuf")
+ if (ranbuf == 0)
+ ranbuf = -1
+ rdnoise = clgetr ("rdnoise") / gain
+ if (rdnoise > 0. && ranbuf > 0)
+ call salloc (rbuf, ranbuf, TY_REAL)
+ poisson = clgetb ("poisson")
+ if (poisson && ranbuf > 0)
+ call salloc (pbuf, ranbuf, TY_REAL)
+ seed = clgetl ("seed")
+ if (IS_INDEFL(seed))
+ seed1 = seed1 + clktime (long (0))
+ else
+ seed1 = seed
+ cmmts = clgetb ("comments")
+
+ if (imtlen (ilist) == 0)
+ call error (1, "No input image list")
+
+ # Loop through input, output, and cosmic ray lists.
+ # Missing output images take the input image name.
+ # The cosmic ray list will repeat if shorter than input list.
+
+ Memc[fname] = EOS
+ while (imtgetim (ilist, Memc[input], SZ_FNAME) != EOF) {
+ if (imtgetim (olist, Memc[output], SZ_FNAME) == EOF)
+ call strcpy (Memc[input], Memc[output], SZ_FNAME)
+ i = imtgetim (objects, Memc[fname], SZ_FNAME)
+
+ # Map images. Check for new, existing, and in-place images.
+ if (streq (Memc[input], Memc[output])) {
+ ifnoerr (in = immap (Memc[input], READ_WRITE, LEN_UA)) {
+ out = in
+ new = false
+ } else {
+ iferr (out = immap (Memc[output], NEW_IMAGE, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+ in = out
+
+ call clgstr ("header", Memc[comment], SZ_FNAME)
+ iferr (call mkh_header (out, Memc[comment], true, false))
+ call erract (EA_WARN)
+
+ IM_NDIM(in) = 2
+ IM_LEN(in,1) = clgeti ("ncols")
+ IM_LEN(in,2) = clgeti ("nlines")
+ IM_PIXTYPE(in) = TY_REAL
+ call clgstr ("title", IM_TITLE(out), SZ_IMTITLE)
+ new = true
+ }
+ } else {
+ iferr (in = immap (Memc[input], READ_ONLY, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+ iferr (out = immap (Memc[output], NEW_COPY, in)) {
+ call erract (EA_WARN)
+ call imunmap (in)
+ next
+ }
+ new = false
+ }
+ nc = IM_LEN(in,1)
+ nl = IM_LEN(in,2)
+ IM_MIN(out) = MAX_REAL
+ IM_MAX(out) = -MAX_REAL
+
+ call imaddr (out, "gain", gain)
+ call imaddr (out, "rdnoise", rdnoise * gain)
+
+ # Read the object list.
+ call malloc (mko, LEN_MKO, TY_STRUCT)
+ call mkt_init ()
+
+ # Set cosmic ray templates.
+ mkt = mkt_star ("gaussian")
+
+ # Read the object list. If none or a nonexistent list is given
+ # and a number of random events is specified then generate them.
+ # If a nonexistent object list is given then write the random
+ # events out.
+
+ fcmmts = false
+ energy = INDEF
+ nobjects = 0
+ i = nowhite (Memc[fname], Memc[fname], SZ_FNAME)
+ if (access (Memc[fname], 0, 0) == YES) {
+ i = open (Memc[fname], READ_ONLY, TEXT_FILE)
+ while (fscan (i) != EOF) {
+ call gargr (x)
+ call gargr (y)
+ call gargr (z)
+ if (nscan() < 3) {
+ fcmmts = true
+ next
+ }
+ if (x < 1 || x > nc || y < 1 || y > nl)
+ next
+ if (nobjects == 0) {
+ j = 100
+ call malloc (MKO_X(mko), j, TY_REAL)
+ call malloc (MKO_Y(mko), j, TY_REAL)
+ call malloc (MKO_Z(mko), j, TY_REAL)
+ call malloc (MKO_SORT(mko), j, TY_INT)
+ } else if (nobjects == j) {
+ j = j + 100
+ call realloc (MKO_X(mko), j, TY_REAL)
+ call realloc (MKO_Y(mko), j, TY_REAL)
+ call realloc (MKO_Z(mko), j, TY_REAL)
+ call realloc (MKO_SORT(mko), j, TY_INT)
+ }
+
+ Memr[MKO_X(mko)+nobjects] = x
+ Memr[MKO_Y(mko)+nobjects] = y
+ Memr[MKO_Z(mko)+nobjects] = z / gain
+ Memi[MKO_SORT(mko)+nobjects] = nobjects
+ nobjects = nobjects + 1
+ }
+ call close (i)
+ } else {
+ nobjects = clgeti ("ncosrays")
+ if (nobjects > 0) {
+ energy = clgetr ("energy") / gain
+ call malloc (MKO_X(mko), nobjects, TY_REAL)
+ call malloc (MKO_Y(mko), nobjects, TY_REAL)
+ call malloc (MKO_Z(mko), nobjects, TY_REAL)
+ call malloc (MKO_SORT(mko), nobjects, TY_INT)
+ do i = 0, nobjects-1 {
+ Memr[MKO_X(mko)+i] = 1 + (nc-1) * urand (seed1)
+ Memr[MKO_Y(mko)+i] = 1 + (nl-1) * urand (seed1)
+ Memr[MKO_Z(mko)+i] = energy * urand (seed1)
+ Memi[MKO_SORT(mko)+i] = i
+ }
+ if (Memc[fname] != EOS) {
+ i = open (Memc[fname], NEW_FILE, TEXT_FILE)
+ do j = 0, nobjects-1 {
+ call fprintf (i, "%g %g %g\n")
+ call pargr (Memr[MKO_X(mko)+j])
+ call pargr (Memr[MKO_Y(mko)+j])
+ call pargr (gain * Memr[MKO_Z(mko)+j])
+ }
+ call close (i)
+ }
+ }
+ }
+
+ # If no objects are requested then do the image I/O
+ # line by line to add requested background and noise
+ # and then go on to the next image.
+
+ irbuf = 0
+ ipbuf = 0
+ if (nobjects == 0) {
+ call mkt_free ()
+ call mfree (mko, TY_STRUCT)
+
+ if (new) {
+ do i = 1, nl {
+ obuf = impl2r (out, i)
+ if (background == 0.)
+ call aclrr (Memr[obuf], nc)
+ else
+ call amovkr (background, Memr[obuf], nc)
+ if (poisson)
+ call mkpnoise (Memr[obuf], Memr[obuf], nc, 0.,
+ gain, pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[obuf], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ } else {
+ do i = 1, nl {
+ obuf = impl2r (out, i)
+ ptr1 = imgl2r (in, i)
+ if (background == 0.)
+ call amovr (Memr[ptr1], Memr[obuf], nc)
+ else
+ call aaddkr (Memr[ptr1], background,
+ Memr[obuf], nc)
+ if (poisson)
+ call mkpnoise (Memr[obuf], Memr[obuf], nc, 0.,
+ gain, pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[obuf], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ }
+
+ # Add comment history of task parameters.
+ if (cmmts) {
+ call strcpy ("# ", Memc[comment], LEN_COMMENT)
+ call cnvtime (clktime (0), Memc[comment+2], LEN_COMMENT-2)
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment (out, "begin mknoise")
+ call mkh_comment1 (out, "background", 'r')
+ call mkh_comment1 (out, "gain", 'r')
+ call mkh_comment1 (out, "rdnoise", 'r')
+ call mkh_comment1 (out, "poisson", 'b')
+ call mkh_comment1 (out, "seed", 'i')
+ }
+
+ IM_LIMTIME(out) = IM_MTIME(out) + 1
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+ next
+ }
+
+ # Add the cosmic rays.
+ #
+ # The object list is first sorted in Y for efficiency.
+ # Get buffer of as many lines as possible to minimize random
+ # access and speed up adding the objects. Ideally the whole
+ # image should be in memory but if not we scroll a buffer
+ # using the fact that the objects are ordered in Y.
+ # Use error checking to determine how much memory is available.
+
+ mko_sort = MKO_Y(mko)
+ call qsort (Memi[MKO_SORT(mko)], nobjects, mko_compare)
+
+ for (nlines=nl;; nlines = 0.8 * nlines)
+ ifnoerr (call malloc (buf, nlines * nc, TY_REAL))
+ break
+ call malloc (lines, nlines, TY_INT)
+ call malloc (lines, nlines, TY_INT)
+ call malloc (newlines, nl, TY_INT)
+ call amovki (YES, Memi[newlines], nl)
+
+ # Fill the line buffer.
+ do l = 1, nlines {
+ j = mod (l, nlines)
+ ptr2 = buf + j * nc
+ Memi[lines+j] = l
+ if (new)
+ call aclrr (Memr[ptr2], nc)
+ else
+ call amovr (Memr[imgl2r(in,l)], Memr[ptr2], nc)
+ Memi[newlines+l-1] = NO
+ }
+
+ do i = 0, nobjects-1 {
+ j = Memi[MKO_SORT(mko)+i]
+ x = Memr[MKO_X(mko)+j]
+ y = Memr[MKO_Y(mko)+j]
+ z = Memr[MKO_Z(mko)+j]
+
+ call mkt_gstar (mkt, obj, nx, ny, x, y, z)
+
+ c1 = x - nx/2 + 0.5
+ c2 = c1 + nx - 1
+ c3 = max (1, c1)
+ c4 = min (nc, c2)
+ l1 = y - ny/2 + 0.5
+ l2 = l1 + ny - 1
+ l3 = max (1, l1)
+ l4 = min (nl, l2)
+ k = c4 - c3 + 1
+ ptr1 = obj + (l3 - l1) * nx + c3 - c1
+ c3 = c3 - 1
+ do l = l3, l4 {
+ j = mod (l, nlines)
+ if (l != Memi[lines+j]) {
+ ptr2 = buf + j * nc
+ obuf = impl2r (out, Memi[lines+j])
+ call amovr (Memr[ptr2], Memr[obuf], nc)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ Memi[lines+j] = l
+ if (Memi[newlines+l-1] == NO)
+ call amovr (Memr[imgl2r(out,l)], Memr[ptr2], nc)
+ else if (new)
+ call aclrr (Memr[ptr2], nc)
+ else
+ call amovr (Memr[imgl2r(in,l)], Memr[ptr2], nc)
+ Memi[newlines+l-1] = NO
+ }
+ ptr2 = buf + j * nc + c3
+ call aaddr (Memr[ptr1], Memr[ptr2], Memr[ptr2], k)
+ ptr1 = ptr1 + nx
+ }
+ }
+
+ # Flush out the line buffer. If the whole image is in memory then
+ # we can add the background and noise before flushing the data.
+ # Otherwise, we need a second pass reading the image in line
+ # by line and adding the background and noise.
+
+ if (nlines == nl) {
+ do i = 1, nlines {
+ j = mod (i, nlines)
+ ptr2 = buf + j * nc
+ l = Memi[lines+j]
+ if (background != 0.)
+ call aaddkr (Memr[ptr2], background, Memr[ptr2], nc)
+ if (poisson)
+ call mkpnoise (Memr[ptr2], Memr[ptr2], nc, 0., gain,
+ pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[ptr2], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ obuf = impl2r (out, l)
+ call amovr (Memr[ptr2], Memr[obuf], nc)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ } else {
+ do i = 1, nlines {
+ j = mod (i, nlines)
+ ptr2 = buf + j * nc
+ l = Memi[lines+j]
+ obuf = impl2r (out, l)
+ call amovr (Memr[ptr2], Memr[obuf], nc)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+
+ call imflush (out)
+ do i = 1, nl {
+ obuf = impl2r (out, i)
+ ptr1 = imgl2r (out, i)
+ if (background == 0.)
+ call amovr (Memr[ptr1], Memr[obuf], nc)
+ else
+ call aaddkr (Memr[ptr1], background, Memr[obuf], nc)
+ if (poisson)
+ call mkpnoise (Memr[obuf], Memr[obuf], nc, 0., gain,
+ pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[obuf], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ }
+
+ # Since each image is different and the object lists may be
+ # different we free most of the memory within the image list
+ # loop.
+
+ call mfree (buf, TY_REAL)
+ call mfree (lines, TY_INT)
+ call mkt_free ()
+ call mfree (MKO_X(mko), TY_REAL)
+ call mfree (MKO_Y(mko), TY_REAL)
+ call mfree (MKO_Z(mko), TY_REAL)
+ call mfree (MKO_SORT(mko), TY_INT)
+ call mfree (mko, TY_STRUCT)
+
+ # Add comment history of task parameters.
+ if (cmmts) {
+ call strcpy ("# ", Memc[comment], LEN_COMMENT)
+ call cnvtime (clktime (0), Memc[comment+2], LEN_COMMENT-2)
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment (out, "begin mknoise")
+ call mkh_comment1 (out, "background", 'r')
+ call mkh_comment1 (out, "gain", 'r')
+ call mkh_comment1 (out, "rdnoise", 'r')
+ call mkh_comment1 (out, "poisson", 'b')
+ call mkh_comment1 (out, "seed", 'i')
+ if (fcmmts && Memc[fname] != EOS) {
+ call mkh_comment1 (out, "cosrays", 's')
+ i = open (Memc[fname], READ_ONLY, TEXT_FILE)
+ while (fscan (i) != EOF) {
+ call gargr (x)
+ call gargr (y)
+ call gargr (z)
+ if (nscan() < 3) {
+ call reset_scan ()
+ call gargstr (Memc[comment], LEN_COMMENT)
+ call mkh_comment (out, Memc[comment])
+ }
+ }
+ call close (i)
+ }
+ call sprintf (Memc[comment], LEN_COMMENT,
+ "%9tncosrays%24t%d")
+ call pargi (nobjects)
+ call mkh_comment (out, Memc[comment])
+ if (!IS_INDEF (energy))
+ call mkh_comment1 (out, "energy", 'r')
+ call mkh_comment1 (out, "radius", 'r')
+ call mkh_comment1 (out, "ar", 'r')
+ call mkh_comment1 (out, "pa", 'r')
+ }
+
+ IM_LIMTIME(out) = IM_MTIME(out) + 1
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+
+ }
+
+ call imtclose (ilist)
+ call imtclose (olist)
+ call sfree (sp)
+end
+
+
+# MKRNOISE -- Make gaussian read noise. A buffer of saved noise values may be
+# used to greatly speed up the noise. In this case new noise values
+# are randomly chosen from the buffer.
+
+procedure mkrnoise (data, ndata, rdnoise, buf, nbuf, ibuf, seed)
+
+real data[ndata] # Output data
+int ndata # Number of data points
+real rdnoise # Read noise (in data units)
+pointer buf # Random value buffer
+int nbuf # Size of random value buffer (may be zero)
+int ibuf # Number of random numbers saved
+ # ibuf < nbuf Save new values
+ # ibuf = nbuf Use saved values
+ # ibuf > nbuf Use new values
+long seed # Random number seed
+
+int i
+real val, urand(), gasdev()
+
+begin
+ if (ibuf == nbuf)
+ do i = 1, ndata
+ data[i] = data[i] + Memr[buf+int(nbuf*urand (seed))]
+ else if (ibuf > nbuf)
+ do i = 1, ndata
+ data[i] = data[i] + rdnoise * gasdev (seed)
+ else {
+ do i = 1, ndata {
+ if (ibuf < nbuf) {
+ val = rdnoise * gasdev (seed)
+ Memr[buf+ibuf] = val
+ ibuf = ibuf + 1
+ } else
+ val = Memr[buf+int(nbuf*urand (seed))]
+ data[i] = data[i] + val
+ }
+ }
+end
+
+
+# MKPNOISE -- Make poisson noise. For speed, values greater than 20
+# use a gaussian approximation with the square root of the value as
+# the sigma. The normalized gaussian values may be saved and reused
+# by random selection to speed things up.
+
+procedure mkpnoise (in, data, ndata, b, g, buf, nbuf, ibuf, seed)
+
+real in[ndata] # Data to add noise
+real data[ndata] # Output data
+int ndata # Number of data points
+real b # Background (in data units)
+real g # Gain
+pointer buf # Random value buffer
+int nbuf # Size of random value buffer (may be zero)
+int ibuf # Number of random numbers saved
+ # ibuf < nbuf Save new values
+ # ibuf = nbuf Use saved values
+ # ibuf > nbuf Use new values
+long seed # Random number seed
+
+int i
+real v, gv, urand(), poidev(), gasdev()
+
+begin
+ if (ibuf == nbuf)
+ do i = 1, ndata {
+ v = g * (in[i] + b)
+ if (v < 20.)
+ data[i] = data[i] + (poidev (v, seed) - v) / g
+ else
+ data[i] = data[i] +
+ sqrt (v) * Memr[buf+int(nbuf*urand(seed))] / g
+ }
+ else if (ibuf > nbuf)
+ do i = 1, ndata {
+ v = g * (in[i] + b)
+ data[i] = data[i] + (poidev (v, seed) - v) / g
+ }
+ else {
+ do i = 1, ndata {
+ v = g * (in[i] + b)
+ if (v < 20.)
+ data[i] = data[i] + (poidev (v, seed) - v) / g
+ else {
+ if (ibuf < nbuf) {
+ gv = gasdev (seed)
+ Memr[buf+ibuf] = gv
+ ibuf = ibuf + 1
+ } else
+ gv = Memr[buf+int(nbuf*urand (seed))]
+ data[i] = data[i] + sqrt (v) * gv / g
+ }
+ }
+ }
+end
diff --git a/noao/artdata/t_mkobjects.x b/noao/artdata/t_mkobjects.x
new file mode 100644
index 00000000..c9ad0ce3
--- /dev/null
+++ b/noao/artdata/t_mkobjects.x
@@ -0,0 +1,552 @@
+include <error.h>
+include <imhdr.h>
+include <math.h>
+include <mach.h>
+
+define LEN_UA 20000 # Maximum user header
+define LEN_COMMENT 70 # Maximum comment length
+
+# Object data structure
+define LEN_MKO 9
+define MKO_MKT Memi[$1] # Template
+define MKO_X Memi[$1+1] # X position
+define MKO_Y Memi[$1+2] # Y position
+define MKO_Z Memi[$1+3] # Flux
+define MKO_R Memi[$1+4] # Scale size
+define MKO_AR Memi[$1+5] # Axial ratio
+define MKO_PA Memi[$1+6] # Position angle
+define MKO_SAVE Memi[$1+7] # Save template?
+define MKO_SORT Memi[$1+8] # Sort index
+
+
+# T_MKOBJECTS -- Add stars and galaxies to images.
+# New images may be created with a background and noise.
+
+procedure t_mkobjects ()
+
+int ilist # Input image list
+int olist # Output image list
+int objects # List of model files
+real xo # X offset
+real yo # Y offset
+int nl # Number of lines
+int nc # Number of columns
+real background # Background level
+real gain # Gain (electrons/DN)
+int ranbuf # Random number buffer size
+real rdnoise # Read noise (in electrons)
+bool poisson # Add Poisson noise?
+real exptime # Exposure time
+real distance # Relative distance
+real m0 # Magnitude zero point
+long seed # Random number seed
+
+int nobjects, save
+real x, y, z, r, ar, pa, dmin, dmax
+
+bool new, bsave, cmmts, fcmmts
+int i, j, k, l, nx, ny, nlines, c1, c2, c3, c4, l1, l2, l3, l4, irbuf, ipbuf
+long seed1
+pointer sp, input, output, fname, type, star, comment, rbuf, pbuf
+pointer in, out, buf, obuf, lines, newlines, obj, ptr1, ptr2
+pointer mko, mkt
+
+long clgetl(), clktime()
+bool clgetb(), streq()
+int imtopenp(), imtlen(), imtgetim(), btoi()
+int clgeti(), open(), fscan(), nscan()
+real clgetr()
+pointer immap(), imgl2r(), impl2r()
+pointer mkt_star(), mkt_object()
+errchk open, immap, imgl2r, impl2r, malloc, realloc, mkt_star, mkt_object
+
+int mko_compare()
+extern mko_compare
+pointer mko_sort
+common /mko_qsort/ mko_sort
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (type, SZ_FNAME, TY_CHAR)
+ call salloc (star, SZ_FNAME, TY_CHAR)
+ call salloc (comment, LEN_COMMENT, TY_CHAR)
+
+ # Get parameters which apply to all images.
+ ilist = imtopenp ("input")
+ olist = imtopenp ("output")
+ objects = imtopenp ("objects")
+ xo = clgetr ("xoffset")
+ yo = clgetr ("yoffset")
+ call clgstr ("star", Memc[star], SZ_FNAME)
+ distance = clgetr ("distance")
+ background = clgetr ("background")
+ gain = clgetr ("gain")
+ ranbuf = clgeti ("ranbuf")
+ if (ranbuf == 0)
+ ranbuf = -1
+ rdnoise = clgetr ("rdnoise") / gain
+ if (rdnoise > 0. && ranbuf > 0)
+ call salloc (rbuf, ranbuf, TY_REAL)
+ poisson = clgetb ("poisson")
+ if (poisson && ranbuf > 0)
+ call salloc (pbuf, ranbuf, TY_REAL)
+ exptime = clgetr ("exptime")
+ m0 = clgetr ("magzero")
+ seed = clgetl ("seed")
+ if (IS_INDEFL(seed))
+ seed1 = seed1 + clktime (long (0))
+ else
+ seed1 = seed
+ cmmts = clgetb ("comments")
+
+ background = exptime * background
+
+ if (imtlen (objects) != imtlen (ilist))
+ call error (1, "Input and objects lists don't match")
+
+ # Initialize the template library.
+ call mkt_init ()
+
+ # Loop through input, output, and object lists.
+ # Missing output images take the input image name.
+ # The object list will repeat if shorter than input list.
+
+ Memc[fname] = EOS
+ while (imtgetim (ilist, Memc[input], SZ_FNAME) != EOF) {
+ if (imtgetim (olist, Memc[output], SZ_FNAME) == EOF)
+ call strcpy (Memc[input], Memc[output], SZ_FNAME)
+
+ # Get and check object list.
+ i = imtgetim (objects, Memc[fname], SZ_FNAME)
+ iferr (i = open (Memc[fname], READ_ONLY, TEXT_FILE)) {
+ call erract (EA_WARN)
+ next
+ }
+
+ # Map images. Check for new, existing, and in-place images.
+ if (streq (Memc[input], Memc[output])) {
+ ifnoerr (out = immap (Memc[output], READ_WRITE, LEN_UA)) {
+ in = out
+ new = false
+ } else {
+ iferr (out = immap (Memc[output], NEW_IMAGE, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+
+ call clgstr ("header", Memc[comment], LEN_COMMENT)
+ iferr (call mkh_header (out, Memc[comment], false, false))
+ call erract (EA_WARN)
+
+ IM_NDIM(out) = 2
+ IM_LEN(out,1) = clgeti ("ncols")
+ IM_LEN(out,2) = clgeti ("nlines")
+ IM_PIXTYPE(out) = TY_REAL
+ call clgstr ("title", IM_TITLE(out), SZ_IMTITLE)
+ call imaddr (out, "exptime", exptime)
+ call imaddr (out, "gain", gain)
+ call imaddr (out, "rdnoise", rdnoise * gain)
+
+ in = out
+ new = true
+ }
+ } else {
+ iferr (in = immap (Memc[input], READ_ONLY, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+ iferr (out = immap (Memc[output], NEW_COPY, in)) {
+ call erract (EA_WARN)
+ call imunmap (in)
+ next
+ }
+ new = false
+ }
+ nc = IM_LEN(in,1)
+ nl = IM_LEN(in,2)
+ IM_MIN(out) = MAX_REAL
+ IM_MAX(out) = -MAX_REAL
+
+ # Set star and seeing templates.
+ mkt = mkt_star (Memc[star])
+
+ # Read the object list.
+ call malloc (mko, LEN_MKO, TY_STRUCT)
+ fcmmts = false
+ nobjects = 0
+ while (fscan (i) != EOF) {
+ call gargr (x)
+ call gargr (y)
+ call gargr (z)
+ if (nscan() < 3) {
+ fcmmts = true
+ next
+ }
+ call gargwrd (Memc[type], SZ_FNAME)
+ call gargr (r)
+ call gargr (ar)
+ call gargr (pa)
+ call gargb (bsave)
+ x = xo + x / distance
+ y = yo + y / distance
+ if (x < 1 || x > nc || y < 1 || y > nl)
+ next
+ if (nobjects == 0) {
+ j = 100
+ call malloc (MKO_MKT(mko), j, TY_POINTER)
+ call malloc (MKO_X(mko), j, TY_REAL)
+ call malloc (MKO_Y(mko), j, TY_REAL)
+ call malloc (MKO_Z(mko), j, TY_REAL)
+ call malloc (MKO_R(mko), j, TY_REAL)
+ call malloc (MKO_AR(mko), j, TY_REAL)
+ call malloc (MKO_PA(mko), j, TY_REAL)
+ call malloc (MKO_SAVE(mko), j, TY_INT)
+ call malloc (MKO_SORT(mko), j, TY_INT)
+ } else if (nobjects == j) {
+ j = j + 100
+ call realloc (MKO_MKT(mko), j, TY_POINTER)
+ call realloc (MKO_X(mko), j, TY_REAL)
+ call realloc (MKO_Y(mko), j, TY_REAL)
+ call realloc (MKO_Z(mko), j, TY_REAL)
+ call realloc (MKO_R(mko), j, TY_REAL)
+ call realloc (MKO_AR(mko), j, TY_REAL)
+ call realloc (MKO_PA(mko), j, TY_REAL)
+ call realloc (MKO_SAVE(mko), j, TY_INT)
+ call realloc (MKO_SORT(mko), j, TY_INT)
+ }
+
+ Memr[MKO_X(mko)+nobjects] = x
+ Memr[MKO_Y(mko)+nobjects] = y
+ Memr[MKO_Z(mko)+nobjects] =
+ exptime / (distance * distance) * 10. ** (-0.4*(z-m0))
+ if (nscan() < 7)
+ Memi[MKO_MKT(mko)+nobjects] = mkt_star (Memc[star])
+ else {
+ Memi[MKO_MKT(mko)+nobjects] = mkt_object (Memc[type])
+ Memr[MKO_R(mko)+nobjects] = r / distance
+ Memr[MKO_AR(mko)+nobjects] = ar
+ Memr[MKO_PA(mko)+nobjects] = DEGTORAD (pa)
+ if (nscan() == 8)
+ Memi[MKO_SAVE(mko)+nobjects] = btoi (bsave)
+ else
+ Memi[MKO_SAVE(mko)+nobjects] = NO
+ }
+ Memi[MKO_SORT(mko)+nobjects] = nobjects
+ nobjects = nobjects + 1
+ }
+ call close (i)
+
+ # If no objects are requested then do the image I/O
+ # line by line. Add noise if creating a new image or
+ # copy the input image if a new output image is desired.
+ # Then go on to the next image.
+
+ irbuf = 0
+ ipbuf = 0
+ if (nobjects == 0) {
+ call mfree (mko, TY_STRUCT)
+
+ if (new) {
+ do i = 1, nl {
+ obuf = impl2r (out, i)
+ if (background == 0.)
+ call aclrr (Memr[obuf], nc)
+ else
+ call amovkr (background, Memr[obuf], nc)
+ if (poisson)
+ call mkpnoise (Memr[obuf], Memr[obuf], nc, 0., gain,
+ pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[obuf], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ } else if (in != out) {
+ do i = 1, nl {
+ obuf = impl2r (out, i)
+ call amovr (Memr[imgl2r(in,i)], Memr[obuf],
+ IM_LEN(in,1))
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ }
+
+ # Add comment history of task parameters.
+ if (cmmts) {
+ call strcpy ("# ", Memc[comment], LEN_COMMENT)
+ call cnvtime (clktime (0), Memc[comment+2], LEN_COMMENT-2)
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment (out, "begin mkobjects")
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment1 (out, "background", 'r')
+ call mkh_comment1 (out, "xoffset", 'r')
+ call mkh_comment1 (out, "yoffset", 'r')
+ call mkh_comment1 (out, "distance", 'r')
+ call mkh_comment1 (out, "exptime", 'r')
+ call mkh_comment1 (out, "magzero", 'r')
+ call mkh_comment1 (out, "gain", 'r')
+ call mkh_comment1 (out, "rdnoise", 'r')
+ call mkh_comment1 (out, "poisson", 'b')
+ call mkh_comment1 (out, "seed", 'i')
+ }
+
+ IM_LIMTIME(out) = IM_MTIME(out) + 1
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+ next
+ }
+
+ # Add the objects.
+ #
+ # The object list is first sorted in Y for efficiency.
+ # Get buffer of as many lines as possible to minimize random
+ # access and speed up adding the objects. Ideally the whole
+ # image should be in memory but if not we scroll a buffer
+ # using the fact that the objects are ordered in Y.
+ # Use error checking to determine how much memory is available.
+
+ mko_sort = MKO_Y(mko)
+ call qsort (Memi[MKO_SORT(mko)], nobjects, mko_compare)
+
+ for (nlines=nl;; nlines = 0.8 * nlines)
+ ifnoerr (call malloc (buf, nlines * nc, TY_REAL))
+ break
+ call malloc (lines, nlines, TY_INT)
+ call malloc (newlines, nl, TY_INT)
+ call amovki (YES, Memi[newlines], nl)
+
+ # Fill the line buffer.
+ do l = 1, nlines {
+ j = mod (l, nlines)
+ ptr2 = buf + j * nc
+ Memi[lines+j] = l
+ if (new)
+ call aclrr (Memr[ptr2], nc)
+ else
+ call amovr (Memr[imgl2r(in,l)], Memr[ptr2], nc)
+ Memi[newlines+l-1] = NO
+ }
+
+ # Generate the object subrasters, add noise if needed, and
+ # add the data to the line buffer. Check for parts of the
+ # object off the image (the object center is guarenteed to
+ # be on the image). Do image I/O if needed.
+
+ do i = 0, nobjects-1 {
+ j = Memi[MKO_SORT(mko)+i]
+ mkt = Memi[MKO_MKT(mko)+j]
+ if (mkt == NULL)
+ next
+ x = Memr[MKO_X(mko)+j]
+ y = Memr[MKO_Y(mko)+j]
+ z = Memr[MKO_Z(mko)+j]
+ r = Memr[MKO_R(mko)+j]
+ ar = Memr[MKO_AR(mko)+j]
+ pa = Memr[MKO_PA(mko)+j]
+ save = Memi[MKO_SAVE(mko)+j]
+
+ call mkt_gobject (mkt, obj, nx, ny, x, y, z, r, ar, pa, save)
+
+ c1 = nint (x) - nx/2
+ c2 = c1 + nx - 1
+ c3 = max (1, c1)
+ c4 = min (nc, c2)
+ l1 = nint (y) - ny/2
+ l2 = l1 + ny - 1
+ l3 = max (1, l1)
+ l4 = min (nl, l2)
+ k = c4 - c3 + 1
+ ptr1 = obj + (l3 - l1) * nx + c3 - c1
+ c3 = c3 - 1
+ do l = l3, l4 {
+ j = mod (l, nlines)
+ if (l != Memi[lines+j]) {
+ ptr2 = buf + j * nc
+ obuf = impl2r (out, Memi[lines+j])
+ call amovr (Memr[ptr2], Memr[obuf], nc)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ Memi[lines+j] = l
+ if (Memi[newlines+l-1] == NO)
+ call amovr (Memr[imgl2r(out,l)], Memr[ptr2], nc)
+ else if (new)
+ call aclrr (Memr[ptr2], nc)
+ else
+ call amovr (Memr[imgl2r(in,l)], Memr[ptr2], nc)
+ Memi[newlines+l-1] = NO
+ }
+ ptr2 = buf + j * nc + c3
+ call aaddr (Memr[ptr1], Memr[ptr2], Memr[ptr2], k)
+ if (!new) {
+ if (poisson)
+ call mkpnoise (Memr[ptr1], Memr[ptr2], k,
+ background, gain, pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[ptr2], k,
+ rdnoise, rbuf, ranbuf, irbuf, seed1)
+ }
+ ptr1 = ptr1 + nx
+ }
+ }
+
+ # Flush out the line buffer. A new image requires addition of
+ # background and noise. If the whole image is in memory then
+ # we can add the background and noise before flushing the data.
+ # Otherwise, we need a second pass reading the image in line
+ # by line and adding the background and noise. Note that if
+ # the image was not new then noise was added only to the
+ # objects.
+
+ if (nlines == nl) {
+ do i = 1, nlines {
+ j = mod (i, nlines)
+ ptr2 = buf + j * nc
+ l = Memi[lines+j]
+ if (new) {
+ if (background != 0.)
+ call aaddkr (Memr[ptr2], background, Memr[ptr2], nc)
+ if (poisson)
+ call mkpnoise (Memr[ptr2], Memr[ptr2], nc, 0., gain,
+ pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[ptr2], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ }
+ obuf = impl2r (out, l)
+ call amovr (Memr[ptr2], Memr[obuf], nc)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ } else {
+ do i = 1, nlines {
+ j = mod (i, nlines)
+ ptr2 = buf + j * nc
+ l = Memi[lines+j]
+ obuf = impl2r (out, l)
+ call amovr (Memr[ptr2], Memr[obuf], nc)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+
+ if (new) {
+ call imflush (out)
+ do i = 1, nl {
+ obuf = impl2r (out, i)
+ ptr1 = imgl2r (out, i)
+ if (background == 0.)
+ call amovr (Memr[ptr1], Memr[obuf], nc)
+ else
+ call aaddkr (Memr[ptr1], background, Memr[obuf], nc)
+ if (poisson)
+ call mkpnoise (Memr[obuf], Memr[obuf], nc, 0., gain,
+ pbuf, ranbuf, ipbuf, seed1)
+ if (rdnoise > 0.)
+ call mkrnoise (Memr[obuf], nc, rdnoise,
+ rbuf, ranbuf, irbuf, seed1)
+ call alimr (Memr[obuf], nc, dmin, dmax)
+ IM_MIN(out) = min (IM_MIN(out), dmin)
+ IM_MAX(out) = max (IM_MAX(out), dmax)
+ }
+ }
+ }
+
+ # Since each image is different and the object lists may be
+ # different we free most of the memory within the image list
+ # loop.
+
+ call mfree (buf, TY_REAL)
+ call mfree (lines, TY_INT)
+ call mfree (newlines, TY_INT)
+ call mfree (MKO_MKT(mko), TY_POINTER)
+ call mfree (MKO_X(mko), TY_REAL)
+ call mfree (MKO_Y(mko), TY_REAL)
+ call mfree (MKO_Z(mko), TY_REAL)
+ call mfree (MKO_R(mko), TY_REAL)
+ call mfree (MKO_AR(mko), TY_REAL)
+ call mfree (MKO_PA(mko), TY_REAL)
+ call mfree (MKO_SAVE(mko), TY_INT)
+ call mfree (MKO_SORT(mko), TY_INT)
+ call mfree (mko, TY_STRUCT)
+
+ # Add comment history of task parameters.
+ if (cmmts) {
+ call strcpy ("# ", Memc[comment], LEN_COMMENT)
+ call cnvtime (clktime (0), Memc[comment+2], LEN_COMMENT-2)
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment (out, "begin mkobjects")
+ call sprintf (Memc[comment], LEN_COMMENT, "%9t%s%24t%s")
+ call pargstr ("objects")
+ call pargstr (Memc[fname])
+ call mkh_comment (out, Memc[comment])
+ call mkh_comment1 (out, "background", 'r')
+ call mkh_comment1 (out, "xoffset", 'r')
+ call mkh_comment1 (out, "yoffset", 'r')
+ call mkh_comment1 (out, "star", 's')
+ call mkh_comment1 (out, "radius", 'r')
+ call mkh_comment1 (out, "beta", 'r')
+ call mkh_comment1 (out, "ar", 'r')
+ call mkh_comment1 (out, "pa", 'r')
+ call mkh_comment1 (out, "distance", 'r')
+ call mkh_comment1 (out, "exptime", 'r')
+ call mkh_comment1 (out, "magzero", 'r')
+ call mkh_comment1 (out, "gain", 'r')
+ call mkh_comment1 (out, "rdnoise", 'r')
+ call mkh_comment1 (out, "poisson", 'b')
+ call mkh_comment1 (out, "seed", 'i')
+ if (fcmmts) {
+ i = open (Memc[fname], READ_ONLY, TEXT_FILE)
+ while (fscan (i) != EOF) {
+ call gargr (x)
+ call gargr (y)
+ call gargr (z)
+ if (nscan() < 3) {
+ call reset_scan ()
+ call gargstr (Memc[comment], LEN_COMMENT)
+ call mkh_comment (out, Memc[comment])
+ }
+ }
+ call close (i)
+ }
+ }
+
+ IM_LIMTIME(out) = IM_MTIME(out) + 1
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+
+ }
+
+ call mkt_free ()
+ call imtclose (ilist)
+ call imtclose (olist)
+ call sfree (sp)
+end
+
+
+# MKO_COMPARE -- Compare two values in the mko_sort array.
+
+int procedure mko_compare (i, j)
+
+int i, j # Array indices to be compared.
+
+pointer mko_sort
+common /mko_qsort/ mko_sort
+
+begin
+ if (Memr[mko_sort+i] < Memr[mko_sort+j])
+ return (-1)
+ else if (Memr[mko_sort+i] > Memr[mko_sort+j])
+ return (1)
+ else
+ return (0)
+end
diff --git a/noao/artdata/t_mkpattern.x b/noao/artdata/t_mkpattern.x
new file mode 100644
index 00000000..08b1973b
--- /dev/null
+++ b/noao/artdata/t_mkpattern.x
@@ -0,0 +1,318 @@
+include <error.h>
+include <imhdr.h>
+
+define LEN_UA 20000
+
+# Editing options
+define OPTIONS "|replace|add|multiply|"
+define REPLACE 1 # Replace pixels
+define ADD 2 # Add to pixels
+define MULTIPLY 3 # Multiply pixels
+
+# Patterns
+define PATTERNS "|constant|grid|checker|coordinates|slope|square"
+define CONST 1 # Constant = v1
+define GRID 2 # Grid lines of v2 with given spacing
+define CHECK 3 # Checkboard of given size
+define COORD 4 # Coordinates
+define SLOPE 5 # Slope
+define SQUARE 6 # Square root checkerboard
+
+# T_MKPATTERN -- Create or modify images using simple patterns.
+# Images may be created of a specified size, dimensionality, and pixel
+# datatype. The images may be modified to replace, add, or multiply
+# by specified values. The patterns include a constant value,
+# a grid, a checkerboard or fixed size or increasing size, the
+# 1D pixel coordinate, and a slope. For dimensions greater than
+# 2 the 2D pattern is repeated.
+
+procedure t_mkpattern ()
+
+
+int ilist # Input image list
+int olist # Output image list
+int op # Operation option
+int pat # Pattern
+real v1 # Pattern value 1
+real v2 # Pattern value 2
+int size # Pattern size
+int nl # Number of lines
+int nc # Number of columns
+
+bool new
+int i
+long vin[IM_MAXDIM], vout[IM_MAXDIM]
+pointer sp, input, output, header, in, out, indata, outdata, pat1, pat2
+
+char clgetc()
+bool streq()
+int clgwrd(), clgeti()
+int imtopenp(), imtlen(), imtgetim(), imgnlr(), impnlr()
+real clgetr()
+pointer immap()
+errchk immap
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (header, SZ_FNAME, TY_CHAR)
+
+ # Set the task parameters which apply to all images.
+ ilist = imtopenp ("input")
+ olist = imtopenp ("output")
+ pat = clgwrd ("pattern", Memc[input], SZ_FNAME, PATTERNS)
+ op = clgwrd ("option", Memc[input], SZ_FNAME, OPTIONS)
+ v1 = clgetr ("v1")
+ v2 = clgetr ("v2")
+ size = max (1, clgeti ("size"))
+
+ if (max (1, imtlen (olist)) != imtlen (ilist))
+ call error (1, "Output image list does not match input image list")
+
+ # Loop over the input image lists. If no output list is given
+ # then create or modify the input image.
+
+ while (imtgetim (ilist, Memc[input], SZ_FNAME) != EOF) {
+ if (imtgetim (olist, Memc[output], SZ_FNAME) == EOF)
+ call strcpy (Memc[input], Memc[output], SZ_FNAME)
+
+ # Map images. Check for new, existing, and inplace images.
+ if (streq (Memc[input], Memc[output])) {
+ ifnoerr (out = immap (Memc[output], READ_WRITE, 0)) {
+ in = out
+ new = false
+ } else {
+ iferr (out = immap (Memc[output], NEW_IMAGE, LEN_UA)) {
+ call erract (EA_WARN)
+ next
+ }
+
+ call clgstr ("header", Memc[header], SZ_FNAME)
+ iferr (call mkh_header (out, Memc[header], false, false))
+ call erract (EA_WARN)
+
+ IM_NDIM(out) = clgeti ("ndim")
+ IM_LEN(out,1) = clgeti ("ncols")
+ IM_LEN(out,2) = clgeti ("nlines")
+ IM_LEN(out,3) = clgeti ("n3")
+ IM_LEN(out,4) = clgeti ("n4")
+ IM_LEN(out,5) = clgeti ("n5")
+ IM_LEN(out,6) = clgeti ("n6")
+ IM_LEN(out,7) = clgeti ("n7")
+ switch (clgetc ("pixtype")) {
+ case 'u':
+ IM_PIXTYPE(out) = TY_USHORT
+ case 's':
+ IM_PIXTYPE(out) = TY_SHORT
+ case 'i':
+ IM_PIXTYPE(out) = TY_INT
+ case 'l':
+ IM_PIXTYPE(out) = TY_LONG
+ case 'r':
+ IM_PIXTYPE(out) = TY_REAL
+ case 'd':
+ IM_PIXTYPE(out) = TY_DOUBLE
+ case 'c':
+ IM_PIXTYPE(out) = TY_COMPLEX
+ default:
+ call error (0, "Bad pixel type")
+ }
+ call clgstr ("title", IM_TITLE(out), SZ_IMTITLE)
+ in = out
+ new = true
+ }
+ } else {
+ iferr (in = immap (Memc[input], READ_ONLY, 0)) {
+ call erract (EA_WARN)
+ next
+ }
+ iferr (out = immap (Memc[output], NEW_COPY, in)) {
+ call erract (EA_WARN)
+ call imunmap (in)
+ next
+ }
+ new = false
+ }
+ nc = IM_LEN(out,1)
+ nl = IM_LEN(out,2)
+
+ call amovkl (long (1), vin, IM_MAXDIM)
+ call amovkl (long (1), vout, IM_MAXDIM)
+
+ # Initialize the pattern; two pointers are returned.
+ call mkpatinit (pat, size, v1, v2, pat1, pat2, nc, nl)
+
+ # Create or modify the image with the specified pattern.
+ # A new image is always the same as replace.
+
+ if (new) {
+ while (impnlr (out, outdata, vout) != EOF)
+ call mkpattern (pat, size, v1, v2, pat1, pat2,
+ vout[2]-1, outdata, nc, nl)
+ } else {
+ switch (op) {
+ case REPLACE:
+ while (impnlr (out, outdata, vout) != EOF)
+ call mkpattern (pat, size, v1, v2, pat1, pat2,
+ vout[2]-1, outdata, nc, nl)
+ case ADD:
+ while (impnlr (out, outdata, vout) != EOF) {
+ i = imgnlr (in, indata, vin)
+ call mkpattern (pat, size, v1, v2, pat1, pat2,
+ vout[2]-1, outdata, nc, nl)
+ call aaddr (Memr[indata], Memr[outdata], Memr[outdata],
+ nc)
+ }
+ case MULTIPLY:
+ while (impnlr (out, outdata, vout) != EOF) {
+ i = imgnlr (in, indata, vin)
+ call mkpattern (pat, size, v1, v2, pat1, pat2,
+ vout[2]-1, outdata, nc, nl)
+ call amulr (Memr[indata], Memr[outdata], Memr[outdata],
+ nc)
+ }
+ }
+ }
+
+ call mkpatfree (pat1, pat2)
+ if (in != out)
+ call imunmap (in)
+ call imunmap (out)
+ }
+
+ call imtclose (ilist)
+ call imtclose (olist)
+ call sfree (sp)
+end
+
+
+# MKPATINIT -- Initialize the pattern. For speed one or two lines of the
+# pattern are created and then used over the image by simple array
+# operations.
+
+procedure mkpatinit (pat, size, v1, v2, pat1, pat2, nc, nl)
+
+int pat # Pattern
+int size # Pattern size
+real v1 # Value 1 for pattern
+real v2 # Value 2 for pattern
+pointer pat1 # Pattern 1 buffer
+pointer pat2 # Pattern 2 buffer
+int nc # Number of columns
+int nl # Number of lines
+
+int i
+
+begin
+ pat1 = NULL
+ pat2 = NULL
+
+ switch (pat) {
+ case CONST:
+ call malloc (pat1, nc, TY_REAL)
+ call amovkr (v1, Memr[pat1], nc)
+ case GRID:
+ call malloc (pat1, nc, TY_REAL)
+ call malloc (pat2, nc, TY_REAL)
+ call amovkr (v1, Memr[pat1], nc)
+ call amovkr (v2, Memr[pat2], nc)
+ size = max (size, 2)
+ do i = 1, nc-1, size
+ Memr[pat1+i] = v2
+ case CHECK:
+ call malloc (pat1, nc, TY_REAL)
+ call malloc (pat2, nc, TY_REAL)
+ do i = 0, nc-1 {
+ if (mod (i/size, 2) == 0) {
+ Memr[pat1+i] = v1
+ Memr[pat2+i] = v2
+ } else {
+ Memr[pat1+i] = v2
+ Memr[pat2+i] = v1
+ }
+ }
+ case COORD:
+ call malloc (pat1, nc, TY_REAL)
+ do i = 0, nc-1
+ Memr[pat1+i] = i / size + 1
+ case SLOPE:
+ call malloc (pat1, nc, TY_REAL)
+ call malloc (pat2, 1, TY_REAL)
+ Memr[pat2] = (v2 - v1) / ((nc + nl - 2) / size)
+ do i = 0, nc - 1
+ Memr[pat1+i] = v1 + Memr[pat2] * i / size
+ case SQUARE:
+ call malloc (pat1, nc, TY_REAL)
+ call malloc (pat2, nc, TY_REAL)
+ do i = 0, nc-1 {
+ if (mod (int (sqrt (real (i/size))), 2) == 0) {
+ Memr[pat1+i] = v1
+ Memr[pat2+i] = v2
+ } else {
+ Memr[pat1+i] = v2
+ Memr[pat2+i] = v1
+ }
+ }
+ }
+end
+
+
+# MKPATFREE -- Free memory used in the pattern buffers.
+
+procedure mkpatfree (pat1, pat2)
+
+pointer pat1, pat2 # Pattern buffers
+
+begin
+ call mfree (pat1, TY_REAL)
+ call mfree (pat2, TY_REAL)
+end
+
+
+# MKPATTERN -- Make a line of data.
+
+procedure mkpattern (pat, size, v1, v2, pat1, pat2, line, data, nc, nl)
+
+int pat # Pattern
+int size # Pattern size
+real v1 # Pattern value
+real v2 # Pattern value
+pointer pat1 # Pattern 1
+pointer pat2 # Pattern 2
+int line # Line
+pointer data # Data
+int nc # Number of columns
+int nl # Number of lines
+
+int i
+
+begin
+ i = max (0, line-1) / size
+
+ switch (pat) {
+ case CONST:
+ call amovr (Memr[pat1], Memr[data], nc)
+ case GRID:
+ if (mod (line, size) == 1)
+ call amovr (Memr[pat2], Memr[data], nc)
+ else
+ call amovr (Memr[pat1], Memr[data], nc)
+ case CHECK:
+ if (mod (i, 2) == 0)
+ call amovr (Memr[pat1], Memr[data], nc)
+ else
+ call amovr (Memr[pat2], Memr[data], nc)
+ case COORD:
+ call amovr (Memr[pat1], Memr[data], nc)
+ call aaddkr (Memr[data], real(i*nc/size), Memr[data], nc)
+ case SLOPE:
+ call amovr (Memr[pat1], Memr[data], nc)
+ call aaddkr (Memr[data], i*Memr[pat2], Memr[data], nc)
+ case SQUARE:
+ if (mod (int (sqrt (real (i))), 2) == 0)
+ call amovr (Memr[pat1], Memr[data], nc)
+ else
+ call amovr (Memr[pat2], Memr[data], nc)
+ }
+end
diff --git a/noao/artdata/voigt.x b/noao/artdata/voigt.x
new file mode 100644
index 00000000..08a44c78
--- /dev/null
+++ b/noao/artdata/voigt.x
@@ -0,0 +1,71 @@
+# VOIGT -- Compute the real (Voigt function) and imaginary parts of the
+# complex function w(z)=exp(-z**2)*erfc(-i*z) in the upper half-plane
+# z=x+iy. The maximum relative error of the real part is 2E-6 and the
+# imaginary part is 5E-6.
+#
+# From: Humlicek, J. Quant. Spectrosc. Radiat. Transfer, V21, p309, 1979.
+
+procedure voigt (xarg, yarg, wr, wi)
+
+real xarg #I Real part of argument
+real yarg #I Imaginary part of argument
+real wr #O Real part of function
+real wi #O Imaginary part of function
+
+int i
+real x, y, y1, y2, y3, d, d1, d2, d3, d4, r, r2
+real t[6], c[6], s[6]
+
+data t/.314240376,.947788391,1.59768264,2.27950708,3.02063703,3.8897249/
+data c/1.01172805,-.75197147,1.2557727e-2,1.00220082e-2,-2.42068135e-4,
+ 5.00848061e-7/
+data s/1.393237,.231152406,-.155351466,6.21836624e-3,9.19082986e-5,
+ -6.27525958e-7/
+
+begin
+ x = xarg
+ y = abs (yarg)
+ wr = 0.
+ wi = 0.
+ y1 = y + 1.5
+ y2 = y1 * y1
+
+ # Region II
+ if (y < 0.85 && abs(x) > 18.1*y+1.65) {
+ if (abs(x) < 12)
+ wr = exp (-x * x)
+ y3 = y + 3
+ do i = 1, 6 {
+ r = x - t[i]
+ r2 = r * r
+ d = 1 / (r2 + y2)
+ d1 = y1 * d
+ d2 = r * d
+ wr = wr + y * (c[i] * (r * d2 - 1.5 * d1) + s[i] * y3 * d2) /
+ (r2 + 2.25)
+ r = x + t[i]
+ r2 = r * r
+ d = 1 / (r2 + y2)
+ d3 = y1 * d
+ d4 = r * d
+ wr = wr + y * (c[i] * (r * d4 - 1.5 * d3) - s[i] * y3 * d4) /
+ (r2 + 2.25)
+ wi = wi + c[i] * (d2 + d4) + s[i] * (d1 - d3)
+ }
+
+ # Region I
+ } else {
+ do i = 1, 6 {
+ r = x - t[i]
+ d = 1 / (r * r + y2)
+ d1 = y1 * d
+ d2 = r * d
+ r = x + t[i]
+ d = 1 / (r * r + y2)
+ d3 = y1 * d
+ d4 = r * d
+ wr = wr + c[i] * (d1 + d3) - s[i] * (d2 - d4)
+ wi = wi + c[i] * (d2 + d4) + s[i] * (d1 - d3)
+ }
+ }
+end
diff --git a/noao/artdata/x_artdata.x b/noao/artdata/x_artdata.x
new file mode 100644
index 00000000..2429722a
--- /dev/null
+++ b/noao/artdata/x_artdata.x
@@ -0,0 +1,9 @@
+task mk1dspec = t_mk1dspec,
+ mk2dspec = t_mk2dspec,
+ mkechelle = t_mkechelle,
+ mkheader = t_mkheader,
+ mknoise = t_mknoise,
+ mkobjects = t_mkobjects,
+ mkpattern = t_mkpattern,
+ starlist = t_starlist,
+ gallist = t_gallist