aboutsummaryrefslogtreecommitdiff
path: root/noao/twodspec/multispec
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/twodspec/multispec
downloadiraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'noao/twodspec/multispec')
-rw-r--r--noao/twodspec/multispec/Revisions28
-rw-r--r--noao/twodspec/multispec/_msfindspec1.cl41
-rw-r--r--noao/twodspec/multispec/_msfindspec1.par15
-rw-r--r--noao/twodspec/multispec/_msfindspec2.cl28
-rw-r--r--noao/twodspec/multispec/_msfindspec2.par8
-rw-r--r--noao/twodspec/multispec/_msfindspec3.cl22
-rw-r--r--noao/twodspec/multispec/_msfindspec3.par6
-rw-r--r--noao/twodspec/multispec/armsr.x44
-rw-r--r--noao/twodspec/multispec/clinput.x28
-rw-r--r--noao/twodspec/multispec/dbio/dbio.h24
-rw-r--r--noao/twodspec/multispec/dbio/dbio.x564
-rw-r--r--noao/twodspec/multispec/dbio/mkpkg9
-rw-r--r--noao/twodspec/multispec/doc/MSalgo.ms1032
-rw-r--r--noao/twodspec/multispec/doc/MSalgo_c.doc522
-rw-r--r--noao/twodspec/multispec/doc/MSalgo_c.hlp449
-rw-r--r--noao/twodspec/multispec/doc/MSspecs.doc698
-rw-r--r--noao/twodspec/multispec/doc/MSspecs.hlp659
-rw-r--r--noao/twodspec/multispec/doc/MSspecs_c.hlp243
-rw-r--r--noao/twodspec/multispec/doc/findpeaks.hlp88
-rw-r--r--noao/twodspec/multispec/doc/fitfunc.hlp73
-rw-r--r--noao/twodspec/multispec/doc/fitgauss5.hlp148
-rw-r--r--noao/twodspec/multispec/doc/modellist.hlp52
-rw-r--r--noao/twodspec/multispec/doc/msextract.hlp172
-rw-r--r--noao/twodspec/multispec/doc/mslist.hlp77
-rw-r--r--noao/twodspec/multispec/doc/msplot.hlp44
-rw-r--r--noao/twodspec/multispec/doc/msset.hlp104
-rw-r--r--noao/twodspec/multispec/doc/multispec.ms532
-rw-r--r--noao/twodspec/multispec/doc/newextract.hlp61
-rw-r--r--noao/twodspec/multispec/doc/newimage.hlp130
-rw-r--r--noao/twodspec/multispec/exgauss5.x100
-rw-r--r--noao/twodspec/multispec/exsmooth.x107
-rw-r--r--noao/twodspec/multispec/exstrip.x203
-rw-r--r--noao/twodspec/multispec/findpeaks.par13
-rw-r--r--noao/twodspec/multispec/fitclean.x257
-rw-r--r--noao/twodspec/multispec/fitfunction.par8
-rw-r--r--noao/twodspec/multispec/fitgauss5.com9
-rw-r--r--noao/twodspec/multispec/fitgauss5.par23
-rw-r--r--noao/twodspec/multispec/fitgauss5.x460
-rw-r--r--noao/twodspec/multispec/fitsmooth.x168
-rw-r--r--noao/twodspec/multispec/history.x29
-rw-r--r--noao/twodspec/multispec/intgauss5.x140
-rw-r--r--noao/twodspec/multispec/mkpkg66
-rw-r--r--noao/twodspec/multispec/modellist.par9
-rw-r--r--noao/twodspec/multispec/modgauss5.x164
-rw-r--r--noao/twodspec/multispec/ms.h77
-rw-r--r--noao/twodspec/multispec/msextract.par20
-rw-r--r--noao/twodspec/multispec/msextract.x154
-rw-r--r--noao/twodspec/multispec/msget.x208
-rw-r--r--noao/twodspec/multispec/msio.x194
-rw-r--r--noao/twodspec/multispec/mslist.par7
-rw-r--r--noao/twodspec/multispec/msnames.x140
-rw-r--r--noao/twodspec/multispec/msplot.par9
-rw-r--r--noao/twodspec/multispec/msplot.x104
-rw-r--r--noao/twodspec/multispec/msput.x123
-rw-r--r--noao/twodspec/multispec/msset.par9
-rw-r--r--noao/twodspec/multispec/mssmooth.x81
-rw-r--r--noao/twodspec/multispec/multispec.cl21
-rw-r--r--noao/twodspec/multispec/multispec.hd14
-rw-r--r--noao/twodspec/multispec/multispec.hlp14
-rw-r--r--noao/twodspec/multispec/multispec.men10
-rw-r--r--noao/twodspec/multispec/multispec.par3
-rw-r--r--noao/twodspec/multispec/newextraction.par5
-rw-r--r--noao/twodspec/multispec/newimage.par17
-rw-r--r--noao/twodspec/multispec/peaks.x397
-rw-r--r--noao/twodspec/multispec/profinterp.x186
-rw-r--r--noao/twodspec/multispec/ranges.x385
-rw-r--r--noao/twodspec/multispec/response.par11
-rw-r--r--noao/twodspec/multispec/sampline.x73
-rw-r--r--noao/twodspec/multispec/setfitparams.x27
-rw-r--r--noao/twodspec/multispec/setmodel.x86
-rw-r--r--noao/twodspec/multispec/setranges.x23
-rw-r--r--noao/twodspec/multispec/setsmooth.x250
-rw-r--r--noao/twodspec/multispec/solve.x312
-rw-r--r--noao/twodspec/multispec/t_findpeaks.x137
-rw-r--r--noao/twodspec/multispec/t_fitfunc.x158
-rw-r--r--noao/twodspec/multispec/t_fitgauss5.x209
-rw-r--r--noao/twodspec/multispec/t_modellist.x126
-rw-r--r--noao/twodspec/multispec/t_msextract.x112
-rw-r--r--noao/twodspec/multispec/t_mslist.x312
-rw-r--r--noao/twodspec/multispec/t_msset.x189
-rw-r--r--noao/twodspec/multispec/t_newextract.x99
-rw-r--r--noao/twodspec/multispec/t_newimage.x97
-rw-r--r--noao/twodspec/multispec/unblend.x38
-rw-r--r--noao/twodspec/multispec/x_multispec.x10
84 files changed, 12104 insertions, 0 deletions
diff --git a/noao/twodspec/multispec/Revisions b/noao/twodspec/multispec/Revisions
new file mode 100644
index 00000000..1e62477c
--- /dev/null
+++ b/noao/twodspec/multispec/Revisions
@@ -0,0 +1,28 @@
+.help revisions Jun88 noao.twodspec.multispec
+.nf
+The multispec package is no longer compiled and defined. The source code
+will someday be revised and the capabilities of tracing and deblending
+will be returned. (8/23/90, Valdes)
+
+====
+V2.9
+====
+
+noao$twodspec/multispec/t_fitgauss5.x
+ Valdes, Oct. 3, 1986
+ 1. Missing third argument to msmap. Found in the AOS port.
+
+====================================
+Version 2.3 Release, August 18, 1986
+====================================
+
+From Valdes Oct. 23, 1985:
+
+1. Recoded msio.x and dbio.x to remove entry statements and instead use
+separate procedures with a common block.
+------
+From Valdes Oct. 11, 1985:
+
+1. The MSPLOT script using PLOT.GRAPH has been removed and an interactive
+task based on GIO has replaced it.
+.endhelp
diff --git a/noao/twodspec/multispec/_msfindspec1.cl b/noao/twodspec/multispec/_msfindspec1.cl
new file mode 100644
index 00000000..1d9ae624
--- /dev/null
+++ b/noao/twodspec/multispec/_msfindspec1.cl
@@ -0,0 +1,41 @@
+#{ _MSFINDSPEC1 -- Create a new database, find the peaks, trace, and fit a
+# function.
+
+#image,f,a,,,,Image
+#sample_lines,s,a,"10x50",,,Sample image lines
+#start,i,a,1,,,Starting image line
+#min_nspectra,i,a,1,,,Minimum number of spectra to be found
+#max_nspectra,i,a,100,,,Maximum number of spectra to be found
+#separation,i,a,20,,,Minimum separation between spectra
+#threshold,r,a,0.,,,Minimum peak threshold for selecting spectra
+#contrast,r,a,0.1,,,Maximum contrast between peaks
+#width,r,a,10,,,Width of spectra
+#naverage,i,a,20,1,,Number of lines to average
+#verbose,b,a,no,,,Verbose output?
+
+{
+ # Verbose message.
+ if (verbose) {
+ time
+ print (" Find the spectra in ", image, ".")
+ }
+
+ # Create a new database.
+ newextraction (image, "", sample_lines=sample_lines)
+
+ # Find the peaks.
+ findpeaks (image, start, contrast, separation=separation,
+ threshold=threshold, min_npeaks=min_nspectra, edge=width/3,
+ max_npeaks=max_nspectra, naverage=naverage)
+
+ # Initialize the model parameters and fit the model with tracking.
+ msset (image, "s0", 1., lines=start)
+ msset (image, "s1", 0., lines=start)
+ msset (image, "s2", 0., lines=start)
+ fitgauss5 (image, start, lower=-width/2, upper=width/2,
+ lines="*", spectra="*", naverage=naverage, track=yes,
+ algorithm=2)
+
+ # Fit the default interpolation function to the positions.
+ fitfunction (image, parameter="x0", lines="*", spectra="*")
+}
diff --git a/noao/twodspec/multispec/_msfindspec1.par b/noao/twodspec/multispec/_msfindspec1.par
new file mode 100644
index 00000000..5263bedb
--- /dev/null
+++ b/noao/twodspec/multispec/_msfindspec1.par
@@ -0,0 +1,15 @@
+
+# _MSFINDSPEC1 -- Create a new database, find the peaks, trace, and fit a
+# function.
+
+image,f,a,,,,Image
+sample_lines,s,a,"10x50",,,Sample image lines
+start,i,a,1,,,Starting image line
+min_nspectra,i,a,1,,,Minimum number of spectra to be found
+max_nspectra,i,a,100,,,Maximum number of spectra to be found
+separation,i,a,20,,,Minimum separation between spectra
+threshold,r,a,0.,,,Minimum peak threshold for selecting spectra
+contrast,r,a,0.1,,,Maximum contrast between peaks
+width,r,a,10,,,Width of spectra
+naverage,i,a,20,1,,Number of lines to average
+verbose,b,a,no,,,Verbose output?
diff --git a/noao/twodspec/multispec/_msfindspec2.cl b/noao/twodspec/multispec/_msfindspec2.cl
new file mode 100644
index 00000000..d09212fc
--- /dev/null
+++ b/noao/twodspec/multispec/_msfindspec2.cl
@@ -0,0 +1,28 @@
+#{ _MSFINDSPEC2 -- Create a new database, initialize with a template
+# image, refine the positions, and fit a position function.
+
+#image,f,a,,,,Image
+#template,f,a,,,,Template image
+#width,r,a,10,,,Width of spectra
+#naverage,i,a,20,1,,Number of lines to average
+#verbose,b,a,no,,,Verbose output?
+
+{
+ # Verbose message.
+ if (verbose) {
+ time
+ print (" Find the spectra in ", image, " using template image ",
+ template, ".")
+ }
+
+ # Create a new database and initialize with a template image.
+ newextraction (image, template)
+
+ # Refit the model.
+ fitgauss5 (image, 1, lower=-width/2, upper=width/2,
+ lines="*", spectra="*", naverage=naverage, track=no,
+ algorithm=2)
+
+ # Fit the default interpolation function to the positions.
+ fitfunction (image, parameter="x0", lines="*", spectra="*")
+}
diff --git a/noao/twodspec/multispec/_msfindspec2.par b/noao/twodspec/multispec/_msfindspec2.par
new file mode 100644
index 00000000..d9e10b5d
--- /dev/null
+++ b/noao/twodspec/multispec/_msfindspec2.par
@@ -0,0 +1,8 @@
+# _MSFINDSPEC2 -- Create a new database, initialize with a template
+# image, refine the positions, and fit a position function.
+
+image,f,a,,,,Image
+template,f,a,,,,Template image
+width,r,a,10,,,Width of spectra
+naverage,i,a,20,1,,Number of lines to average
+verbose,b,a,no,,,Verbose output?
diff --git a/noao/twodspec/multispec/_msfindspec3.cl b/noao/twodspec/multispec/_msfindspec3.cl
new file mode 100644
index 00000000..d8150a90
--- /dev/null
+++ b/noao/twodspec/multispec/_msfindspec3.cl
@@ -0,0 +1,22 @@
+#{ _MSFINDSPEC3 -- Refine the model and fit a position function.
+
+#image,f,a,,,,Image
+#width,r,a,10,,,Width of spectra
+#naverage,i,a,20,1,,Number of lines to average
+#verbose,b,a,no,,,Verbose output?
+
+{
+ # Verbose message.
+ if (verbose) {
+ time
+ print (" Refit the spectra in ", image, ".")
+ }
+
+ # Refit the model.
+ fitgauss5 (image, 1, lower=-width/2, upper=width/2,
+ lines="*", spectra="*", naverage=naverage, track=no,
+ algorithm=2)
+
+ # Fit the default interpolation function to the positions.
+ fitfunction (image, parameter="x0", lines="*", spectra="*")
+}
diff --git a/noao/twodspec/multispec/_msfindspec3.par b/noao/twodspec/multispec/_msfindspec3.par
new file mode 100644
index 00000000..fccb3d23
--- /dev/null
+++ b/noao/twodspec/multispec/_msfindspec3.par
@@ -0,0 +1,6 @@
+# _MSFINDSPEC3 -- Refine the model and fit a position function.
+
+image,f,a,,,,Image
+width,r,a,10,,,Width of spectra
+naverage,i,a,20,1,,Number of lines to average
+verbose,b,a,no,,,Verbose output?
diff --git a/noao/twodspec/multispec/armsr.x b/noao/twodspec/multispec/armsr.x
new file mode 100644
index 00000000..2f9ce657
--- /dev/null
+++ b/noao/twodspec/multispec/armsr.x
@@ -0,0 +1,44 @@
+# ARMSR -- Compute the rms of an array.
+
+real procedure armsr (a, npoints)
+
+real a[ARB] # Return rms of this array
+int npoints # Number of points in the array
+
+int i
+real avg, rms
+
+begin
+ avg = 0.
+ rms = 0.
+ do i = 1, npoints {
+ avg = avg + a[i]
+ rms = rms + a[i] * a[i]
+ }
+ rms = sqrt ((npoints * rms - avg * avg) / (npoints * (npoints - 1)))
+
+ return (rms)
+end
+
+
+# ARMSRR -- Compute the vector rms between two real arrays.
+
+real procedure armsrr (a, b, npoints)
+
+real a[ARB] # First array
+real b[ARB] # Second array
+int npoints # Number of points
+
+int i
+real residual, rms
+
+begin
+ rms = 0.
+ do i = 1, npoints {
+ residual = a[i] - b[i]
+ rms = rms + residual ** 2
+ }
+ rms = sqrt (rms / npoints)
+
+ return (rms)
+end
diff --git a/noao/twodspec/multispec/clinput.x b/noao/twodspec/multispec/clinput.x
new file mode 100644
index 00000000..163c8354
--- /dev/null
+++ b/noao/twodspec/multispec/clinput.x
@@ -0,0 +1,28 @@
+# Specialized CL get routines.
+
+
+# CLGRANGES -- Get a range. A range string is input and the string is
+# decoded into a range array. The number of values in the range array is
+# returned by the function.
+
+int procedure clgranges (param, min_value, max_value, ranges, max_ranges)
+
+char param[ARB]
+int min_value
+int max_value
+int ranges[ARB]
+int max_ranges
+
+char str[SZ_LINE]
+int n
+
+int decode_ranges()
+
+begin
+ call clgstr (param, str, SZ_LINE)
+
+ if (decode_ranges (str,ranges,max_ranges,min_value,max_value,n) == ERR)
+ call error (0, "Error in range string")
+
+ return (n)
+end
diff --git a/noao/twodspec/multispec/dbio/dbio.h b/noao/twodspec/multispec/dbio/dbio.h
new file mode 100644
index 00000000..dd9f65f1
--- /dev/null
+++ b/noao/twodspec/multispec/dbio/dbio.h
@@ -0,0 +1,24 @@
+
+# Definitions for subset DBIO
+
+define SZ_DB_KEY 79 # Size of database reference keys
+define MAX_DB_DES 10 # Maximum number of DBIO descriptors
+define DB_ERRCODE 1000 # Start of DBIO error codes
+
+# DBIO descriptor
+
+define LEN_DB_DES 3
+
+define DB_FD Memi[$1] # The database FIO descriptor
+define DB_DIC Memi[$1+1] # Pointer to the dictionary memory
+define DB_UPDATE Memi[$1+2] # Has dictionary been change [y/n]
+
+# DBIO dictionary entry. Each entry is referenced with the pointer to the
+# dictionary memory ($1) and the entry number ($2).
+
+define LEN_DB_ENTRY 43
+
+define DB_KEY Memi[$1+($2-1)*LEN_DB_ENTRY] # Key
+define DB_OFFSET Meml[$1+($2-1)*LEN_DB_ENTRY+40] # File Offset
+define DB_SZ_ELEM Memi[$1+($2-1)*LEN_DB_ENTRY+41] # Element size
+define DB_DIM Memi[$1+($2-1)*LEN_DB_ENTRY+42] # Number of elements
diff --git a/noao/twodspec/multispec/dbio/dbio.x b/noao/twodspec/multispec/dbio/dbio.x
new file mode 100644
index 00000000..faa21cef
--- /dev/null
+++ b/noao/twodspec/multispec/dbio/dbio.x
@@ -0,0 +1,564 @@
+
+include <fset.h>
+include <error.h>
+include "dbio.h"
+
+.help dbio 2 "Subset Database I/O Procedures"
+.sh
+1. Introduction
+
+ These DBIO procedures are a subset of the general
+DBIO design described in "Specifications of the IRAF DBIO Interface" by
+Doug Tody (Oct 1983). It is designed to allow programs written using
+the subset DBIO to be easily converted to the full DBIO. It's features
+are:
+.ls 4 1.
+Database open and close.
+.le
+.ls 4 2.
+Reference to entries by a (possibly) subscripted record name string.
+.le
+.ls 4 3.
+Ability to add new record types as desired.
+.le
+.ls 4 4.
+Error recovery procedure to cleanup after an uncaught error.
+.le
+
+The primary limitations are:
+.ls 4 1.
+No aliases.
+.le
+.ls 4 2.
+No datatyping and no self-describing structures.
+.le
+.ls 4 3.
+No deletions of entries.
+.le
+.sh
+2. Procedures
+
+.nf
+ db = dbopen (file_name, mode, max_entries)
+ db = dbreopen (db)
+ dbclose (db)
+ dbenter (db, record_name, sz_elem, nreserve)
+ y/n = dbaccess (db, record_name)
+ nread = dbread (db, reference, buf, maxelems)
+ dbwrite (db, reference, buf, nelems)
+.fi
+
+ A new, empty database is created by opening with access modes NEW_FILE
+or TEMP_FILE. The dictionary will be intialized to allow max_entries
+number of dictionary entries. The other legal access modes are READ_ONLY and
+READ_WRITE. The max_entries argument is not used with these modes. To create
+a new entry in the database the name of the record, the size of a record
+element, and the maximum number of such records to be stored are specified.
+This differs from the full DBIO specification in that a record is described
+only by a size instead of a datatype. Also it is not possible to increase
+the number of elements once it has been entered. The database read and
+write procedures do no type conversion. They read procedure returns
+the number of elements read. If a reference is not found in the
+dictionary in either reading or writing an error condition occurs.
+Also an attempt to read or write an element exceeding the dimension
+entered in the dictionary will create an error condition.
+.endhelp
+
+
+# DBOPEN, DBREOPEN -- Open a database file.
+
+pointer procedure dbopen (file_name, ac_mode, db_nentries)
+
+# Procedure dbopen parameters:
+char file_name[SZ_FNAME] # Database filename
+int ac_mode # Access mode (new,temp,r,rw)
+int db_nentries # Creation dictionary size
+
+# Entry dbreopen parameters:
+pointer dbreopen # Function type
+pointer db_old # Old database descriptor
+
+int mode
+pointer fd, db # FIO descriptor and DBIO descriptor
+pointer dic
+int nelem, nentries
+
+bool strne()
+int open(), dbread(), reopen()
+errchk db_getdes, calloc, dbenter, dbread, db_init
+
+begin
+ # Check for valid access mode. Valid modes require read permission.
+ # If a valid access mode open database with FIO.
+ mode = ac_mode
+ if ((mode == WRITE_ONLY) || (mode == APPEND))
+ call error (DB_ERRCODE + 0, "Invalid database access mode")
+ fd = open (file_name, mode, BINARY_FILE)
+ goto 10
+
+entry dbreopen (db_old)
+
+ fd = reopen (DB_FD(db_old), mode)
+
+ # Get DBIO descriptor
+10 call db_getdes (db)
+ DB_FD(db) = fd
+
+ # If the database is being created enter the dictionary in the file.
+ # If the database already exists read the current dictionary and
+ # check to see if the file is a database.
+ switch (mode) {
+ case NEW_FILE, TEMP_FILE:
+ # Allocate dictionary space and enter it in the database.
+ # The request entries is increased by one for the dictionary
+ # database entry itself.
+ nentries = db_nentries + 1
+ call calloc (dic, nentries * LEN_DB_ENTRY, TY_STRUCT)
+ DB_DIC(db) = dic
+ call dbenter (db, "db_dictionary", LEN_DB_ENTRY * SZ_STRUCT,
+ nentries)
+ case READ_ONLY, READ_WRITE:
+ # Read dictionary.
+ call db_init (db, 1)
+ dic = DB_DIC(db)
+ nelem = dbread (db, "db_dictionary", Memi[dic], 1)
+ if (nelem != 1)
+ call error (DB_ERRCODE + 1, "Error reading database dictionary")
+ if (strne (DB_KEY(dic, 1), "db_dictionary"))
+ call error (DB_ERRCODE + 2, "File is not a database")
+
+ nentries = DB_DIM(dic, 1)
+ call db_init (db, nentries)
+ dic = DB_DIC(db)
+ nelem = dbread (db, "db_dictionary", Memi[dic], nentries)
+ if (nelem != nentries)
+ call error (DB_ERRCODE + 3, "Error reading database dictionary")
+ }
+
+ return (db)
+end
+
+
+# DB_INIT -- Initialize the program dictionary space
+
+procedure db_init (db, db_nentries)
+
+pointer db
+int db_nentries
+
+pointer dic
+
+long note()
+errchk mfree, calloc, seek
+
+begin
+ # Allocate dictionary memory
+ dic = DB_DIC(db)
+ if (dic != NULL)
+ call mfree (dic, TY_STRUCT)
+ call calloc (dic, db_nentries * LEN_DB_ENTRY, TY_STRUCT)
+ DB_DIC(db) = dic
+
+ # Fill in dictionary entry
+ call strcpy ("db_dictionary", DB_KEY(dic, 1), SZ_DB_KEY)
+ DB_SZ_ELEM(dic, 1) = LEN_DB_ENTRY * SZ_STRUCT
+ DB_DIM(dic, 1) = db_nentries
+ call seek (DB_FD(db), BOF)
+ DB_OFFSET(dic, 1) = note (DB_FD(db))
+end
+
+# DBENTER -- Make a new entry in the database dictionary and reserve
+# file space in the database.
+
+procedure dbenter (db, record_name, sz_elem, nreserve)
+
+pointer db # DBIO descriptor
+char record_name[SZ_DB_KEY] # Record name string
+int sz_elem # Size of record element in CHARS
+int nreserve # Number of record elements to reserve
+
+int i
+int sz_reserve, sz_buf
+pointer dic, buf
+
+bool streq()
+int fstati()
+long note()
+
+errchk calloc, dbclose, write, seek
+
+begin
+ # Check access mode
+ if (fstati(DB_FD(db), F_WRITE) == NO)
+ call error (DB_ERRCODE + 4, "Database is read only")
+
+ # Find the last entry. Check for attempts to redefine an
+ # entry and to overflow the dictionary.
+ dic = DB_DIC(db)
+ for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
+ if (DB_DIM(dic, i) == 0)
+ break
+ if (streq (record_name, DB_KEY(dic, i)))
+ call error (DB_ERRCODE + 5, "Attempt to redefine dictionary entry")
+ }
+ if ((i > 1) && (i > DB_DIM(dic, 1)))
+ call error (DB_ERRCODE + 6, "Database dictionary is full")
+
+ # Make dictionary entry
+ call strcpy (record_name, DB_KEY(dic, i), SZ_DB_KEY)
+ DB_SZ_ELEM(dic, i) = sz_elem
+ DB_DIM(dic, i) = nreserve
+ call seek (DB_FD(db), EOF)
+ DB_OFFSET(dic, i) = note (DB_FD(db))
+ DB_UPDATE(db) = YES
+
+ # Initialize file space to zero. Zero file blocks for efficiency.
+ sz_reserve = sz_elem * nreserve
+ sz_buf = min (fstati (DB_FD(db), F_BLKSIZE), sz_reserve)
+ call calloc (buf, sz_buf, TY_CHAR)
+
+ while (sz_reserve > 0) {
+ call write (DB_FD(db), Memc[buf], sz_buf)
+ sz_reserve = sz_reserve - sz_buf
+ sz_buf = min (sz_buf, sz_reserve)
+ }
+ call mfree (buf, TY_CHAR)
+end
+
+# DBACCESS -- Is data reference in the database?
+
+bool procedure dbaccess (db, record_name)
+
+pointer db # DBIO descriptor
+char record_name[SZ_DB_KEY] # Record name string
+
+int i
+pointer dic
+
+bool streq()
+
+begin
+ dic = DB_DIC(db)
+ for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
+ if (DB_DIM(dic, i) == 0)
+ return (FALSE)
+ if (streq (record_name, DB_KEY(dic, i)))
+ return (TRUE)
+ }
+ return (FALSE)
+end
+
+
+# DBNEXTNAME -- Return name of the next dictionary entry.
+
+int procedure dbnextname (db, previous, outstr, maxch)
+
+pointer db # DBIO descriptor
+char previous[ARB]
+char outstr[ARB]
+int maxch
+
+int i
+pointer dic
+
+bool streq(), strne()
+
+begin
+ dic = DB_DIC(db)
+ i = 1
+ if (strne (previous, "")) {
+ for (; i <= DB_DIM(dic, 1); i = i + 1) {
+ if (DB_DIM(dic, i) == 0)
+ return (EOF)
+ if (streq (previous, DB_KEY(dic, i)))
+ break
+ }
+ }
+ i = i + 1
+ if ((i > DB_DIM(dic, 1)) || (DB_DIM(dic, i) == 0))
+ return (EOF)
+ else
+ call strcpy (DB_KEY(dic, i), outstr, maxch)
+
+ return (OK)
+end
+
+
+#DBREAD - Read data from the database.
+# The number of data elements read is returned.
+
+int procedure dbread (db, ref, buf, maxelems)
+
+pointer db # Database file descriptor
+char ref[ARB] # Data reference
+char buf[ARB] # Data buffer
+int maxelems # Number of elements to be read
+
+int i, j
+int stat, sz_elem, index, nread
+long offset
+pointer dic
+
+int strncmp(), strlen(), stridxs(), ctoi()
+bool streq()
+int read()
+errchk read, dbclose
+
+begin
+ dic = DB_DIC(db)
+
+ # Decode the data reference and set the file offset and the size
+ # of the data element. If a valid data reference is not found
+ # then a read status of 0 is returned.
+
+ j = stridxs ("[", ref)
+ for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
+ if (DB_DIM(dic, i) == 0)
+ call error (DB_ERRCODE + 7, "Database request not found")
+ if (j == 0) {
+ if (streq (ref, DB_KEY(dic, i)))
+ break
+ } else {
+ if (strlen (DB_KEY(dic, i)) == j - 1)
+ if (strncmp (ref, DB_KEY(dic, i), j - 1) == 0)
+ break
+ }
+ }
+
+ offset = DB_OFFSET(dic, i)
+ sz_elem = DB_SZ_ELEM(dic, i)
+ nread = maxelems
+ if (j > 0) {
+ j = ctoi (ref, j + 1, index)
+ if (j > 0) {
+ if (maxelems > DB_DIM(dic, i) - index + 1) {
+ call error (DB_ERRCODE + 8, "Database request out of bounds")
+ }
+ offset = offset + (index - 1) * sz_elem
+ }
+ }
+
+ # Seek and read the data
+ call seek (DB_FD(db), offset)
+ stat = read (DB_FD(db), buf, sz_elem * nread) / sz_elem
+ return (stat)
+end
+
+
+# DBWRITE - Write data to the database.
+
+procedure dbwrite (db, ref, buf, nelems)
+
+pointer db # DBIO descriptor
+char ref[ARB] # Data reference
+char buf[ARB] # Data buffer
+int nelems # Number of elements to written
+
+int i, j
+int sz_elem, index, nwritten
+long offset
+pointer dic
+
+int strncmp(), strlen(), stridxs(), ctoi()
+bool streq()
+errchk write, dbclose
+
+begin
+ dic = DB_DIC(db)
+
+ # Decode the data reference and set the file offset and the size
+ # of the data element. If a valid data reference is not found
+ # then the data is not written and a write status of 0 is returned.
+
+ j = stridxs ("[", ref)
+ for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
+ if (DB_DIM(dic, i) == 0)
+ call error (DB_ERRCODE + 9, "Database request not found")
+ if (j == 0) {
+ if (streq (ref, DB_KEY(dic, i)))
+ break
+ } else {
+ if (strlen (DB_KEY(dic, i)) == j - 1)
+ if (strncmp (ref, DB_KEY(dic, i), j - 1) == 0)
+ break
+ }
+ }
+
+ offset = DB_OFFSET(dic, i)
+ sz_elem = DB_SZ_ELEM(dic, i)
+ nwritten = nelems
+ if (j > 0) {
+ j = ctoi (ref, j + 1, index)
+ if (j > 0) {
+ if (nelems > DB_DIM(dic, i) - index + 1) {
+ call error (DB_ERRCODE + 10, "Database request out of bounds")
+ }
+ offset = offset + (index - 1) * sz_elem
+ }
+ }
+
+ # Seek and write the data
+ call seek (DB_FD(db), offset)
+ call write (DB_FD(db), buf, sz_elem * nwritten)
+ return
+end
+
+
+# DBCLOSE -- Update the dictionary in the database, close the database
+# and free DBIO descriptor.
+
+procedure dbclose (db)
+
+pointer db
+
+begin
+ # Update dictionary in database
+ if (DB_UPDATE(db) == YES)
+ call dbwrite (db, "db_dictionary", Memi[DB_DIC(db)],
+ DB_DIM(DB_DIC(db), 1))
+
+ call close (DB_FD(db))
+ call db_freedes (db)
+end
+
+
+# Procedures accessing the DBIO descriptor list.
+#
+# DB_GETDES -- Allocate and return a DBIO descriptor. Post error recovery.
+# DB_FREEDES -- Close a database and free allocated memory.
+# DB_ERROR -- Take error recovery action by closing all open databases.
+
+procedure db_getdes (db)
+
+pointer db # Allocated DBIO descriptor
+
+extern db_error()
+errchk malloc()
+
+int ndes # Number of allocated DBIO descriptors
+pointer dbdes[MAX_DB_DES] # DBIO descriptor list
+
+common /dbiocom/ ndes, dbdes
+
+int init
+data init/YES/
+
+begin
+ if (init == YES) {
+ ndes = 0
+ init = NO
+ }
+
+ # Check to see if the requested descriptor would overflow the descriptor
+ # list. If not allocate memory for the descriptor otherwise
+ # start error handling. On the first call post the error handler.
+
+ if (ndes == MAX_DB_DES)
+ call error (DB_ERRCODE + 11, "Attempt to open too many database files")
+
+ ndes = ndes + 1
+ call malloc (dbdes[ndes], LEN_DB_DES, TY_STRUCT)
+ db = dbdes[ndes]
+ DB_FD(db) = NULL
+ DB_DIC(db) = NULL
+ DB_UPDATE(db) = NO
+
+ if (ndes == 1)
+ call onerror (db_error)
+end
+
+
+# DB_FREEDES -- Close a database and free allocated memory.
+
+procedure db_freedes (db)
+
+pointer db # DBIO descriptor to be freed
+
+int i
+
+int ndes # Number of allocated DBIO descriptors
+pointer dbdes[MAX_DB_DES] # DBIO descriptor list
+
+common /dbiocom/ ndes, dbdes
+
+begin
+
+ # Locate the specified descriptor in the descriptor list.
+ # If the descriptor is not in the list do nothing.
+ # If the descriptor is in the list free allocated
+ # memory and remove the entry from the list.
+
+ for (i = 1; (i <= ndes) && (db != dbdes[i]); i = i + 1)
+ ;
+ if (i > ndes)
+ return
+
+ if (DB_DIC(db) != NULL)
+ call mfree (DB_DIC(db), TY_STRUCT)
+ call mfree (db, TY_STRUCT)
+
+ if (i < ndes)
+ dbdes[i] = dbdes[ndes]
+ ndes = ndes - 1
+end
+
+
+# DB_ERROR -- Take error recovery action by closing all open databases.
+
+procedure db_error (error_code)
+
+int error_code # Error code
+
+int i
+
+int ndes # Number of allocated DBIO descriptors
+pointer dbdes[MAX_DB_DES] # DBIO descriptor list
+
+common /dbiocom/ ndes, dbdes
+
+begin
+ # Let fio_cleanup deal with the open files and the system
+ # restart deal with freeing the stack. This procedure
+ # cleans up the dbio descriptors and updates the database
+ # dictionary.
+
+ do i = 1, ndes
+ # Update dictionary in database. Catch errors.
+ if (DB_UPDATE(dbdes[i]) == YES)
+ iferr (call dbwrite (dbdes[i], "db_dictionary",
+ Memi[DB_DIC(dbdes[i])], DB_DIM(DB_DIC(dbdes[i]), 1)))
+ call erract (EA_WARN)
+
+ call db_freedes (dbdes[i])
+end
+
+
+int procedure dbgeti (db, key, type)
+
+pointer db
+char key[ARB]
+char type[ARB]
+
+int i
+pointer dic
+
+bool streq()
+
+begin
+ dic = DB_DIC(db)
+ for (i = 1; i <= DB_DIM(dic, 1); i = i + 1) {
+ if (DB_DIM(dic, i) == 0)
+ call error (0, "Key not in database")
+ if (streq (key, DB_KEY(dic, i)))
+ break
+ }
+ if (i > DB_DIM(dic, 1))
+ call error (0, "Key not in database")
+
+ if (streq (type, "r_len"))
+ return (DB_DIM(dic, i))
+ else if (streq (type, "r_size"))
+ return (DB_SZ_ELEM(dic, i))
+ else
+ call error (0, "Unknown database key attribute")
+end
diff --git a/noao/twodspec/multispec/dbio/mkpkg b/noao/twodspec/multispec/dbio/mkpkg
new file mode 100644
index 00000000..f1aee503
--- /dev/null
+++ b/noao/twodspec/multispec/dbio/mkpkg
@@ -0,0 +1,9 @@
+# Multispec/dbio library.
+
+$checkout libpkg.a ../
+$update libpkg.a
+$checkin libpkg.a ../
+
+libpkg.a:
+ dbio.x dbio.h
+ ;
diff --git a/noao/twodspec/multispec/doc/MSalgo.ms b/noao/twodspec/multispec/doc/MSalgo.ms
new file mode 100644
index 00000000..0c64e2b3
--- /dev/null
+++ b/noao/twodspec/multispec/doc/MSalgo.ms
@@ -0,0 +1,1032 @@
+.de FX
+.nf
+.ps -2
+.ss 25
+.cs R 25
+..
+.de EX
+.ps +2
+.ss
+.cs R
+.fi
+..
+.EQ
+delim $$
+.EN
+.RP
+.TL
+Algorithms for the Multi-Spectra Extraction Package
+.AU
+Francisco Valdes
+.K2
+.TU
+.AB
+The algorithms for the Multi-Spectra Extraction Package (\fBmultispec\fR)
+in the Image Reduction and Analysis Facility (\fBIRAF\fR) is described.
+The basic aspects of the general two dimensional aperture spectra extraction
+problem are first discussed.
+The specific algorithms for extraction of multi-aperture plate and
+Echelle digital data are presented. Results of the authors experiments
+with this type of data are included.
+The detailed specification of the package is given in a second document,
+\fIDetailed Specifications for the Multi-Spectra Extraction Package\fB.
+.AE
+.NH
+Introduction
+.PP
+There are an increasing number of astronomical instruments which produce
+multiple spectra on a two dimensional detector.
+The basic concept is to use one dimension for wavelength,
+the dispersion dimension, and the other, the cross dimension, for
+packing additional information during a single exposure.
+For example, the cross dimension can be different objects or
+different spectral orders. The classic multi-spectra instrument is
+the Echelle spectrograph. New instruments are the aperture plate and
+Medusa spectrographs.
+.PP
+There is an additional aspect of the multi-spectra format; namely,
+the individual spectra can contain spatial data. An example of
+this would be multiple slit spectra in which each slit spectra contains
+sky signal and object signal. In the following
+discussion we limit the spectra to be simple aperture spectra in
+which we only desire to sum the intensities at each wavelength to form
+a one dimensional spectrum.
+.PP
+The analysis of multi-spectra aperture data consists of two steps; the
+separation and extraction into individual aperture spectra
+and the calibration and measurement of the spectra. These steps can
+either be incorporated into one analysis package or two separate
+packages. There are advantages to the first approach since some
+aspects of the individual spectra are directly related by the physical
+geometry of the multi-spectra format. However, because packages for
+the analysis of individual spectra exist we begin by dividing the
+reduction into separate extraction and analysis tasks. It is
+important to realize, however, that the existing analysis tools are not well
+suited to reducing the larger number of spectra and treating sets of
+spectra together.
+.PP
+The latter part of this paper describes the algorithms for the
+extraction of two types of data; the multi-aperture plate (MAP)
+and Echelle used with digital detectors. However,
+it is important to keep the more general problem in mind
+and the remainder of this introduction considers the different
+conceptual aspects of the multi-spectra extraction task.
+Table 1 lists many of the general properties of multi-spectra aperture data.
+The other two columns give possible alternatives that each property may take.
+
+.TS
+center box;
+c s s
+c s s
+c c s
+= = =
+c || c | c.
+Table 1: Aspects of Multi-Spectral Data
+
+Property Alternatives
+detector digital photographic
+alignment aligned skewed
+blending blended unblended
+aperture holes slits
+spectral resolution low high
+.TE
+
+.PP
+The detector determines what kind of calibration procedures are
+required to produce intensity from the measurements.
+A digital detector requires sensitivity calibrations on all scales.
+This is the "flat field" problem. There are also corrections for
+bias and dark current. Photographic detectors require
+intensity calibration. Data which are not aligned with the natural
+dimensions of the digital image require extra procedures. Two types
+of non-alignment are a rotation of the dispersion dimension relative
+to the pixel dimension and a "wiggling" or "snaking" of the dispersion
+dimension. Blending refers to the degree of contamination along the
+cross dimension. Blended data requires extra effort to correct for
+the overlap between different spectra and to determine the background.
+The aperture defines the extent of the spectra in the cross dimension.
+The two most relevant choices are holes and slits. In some
+instruments, like the Echelle, the size of the aperture can be varied
+at the time of the observations. Finally, the spectral resolution is
+important in conjunction with digital detectors. If the resolution is
+high then quartz flat field calibrations are relatively easy because
+the spectral
+signature need not be considered. Otherwise, the flat field problem
+is more difficult because the gain variations of the detector
+must be separated from the natural spectral intensity variation of the
+quartz.
+.PP
+There is always some confusion of terms when talking about multi-spectra
+data; in particular, the terms x, y, line, and band.
+The image pixel dimensions are refered to as x and y. We assume
+for the moment that the alignment of the multi-spectra format is such
+that x corresponds to the cross dimension and y to the dispersion
+dimension. If this is not the case a rotation or interpolation
+program can be used to approximately orient the data in this way.
+A line is the set of intensity values as a function of x at constant y.
+In other words, a line is a cut across the dispersion dimension.
+A band is the average of more than one line.
+The image residing on disk will generally be organized
+such that x varies more rapidly and a line of the image is easily
+obtained. In this form a display of the image will have the spectra
+running vertically. In the Cyber extraction package the data is
+organized with x corresponding to the dispersion dimension.
+.NH
+Multi-Spectra Image Formats
+.PP
+The remainder of this paper will refer to two specfic and very
+different multi-spectra formats; the Kitt Peak Multi-Aperture Plate
+System and the Kitt Peak Echelle Spectrograph.
+.NH 2
+Kitt Peak Multi-Aperture Plate System
+.PP
+The reduction of data from multi-aperture plate observations is the
+driving force for the development of a multi-spectra extraction
+package. This application turns out to have most of the worse aspects
+of the properties listed in Table 1. The multi-aperture plate spectrograph uses
+digital dectectors with low resolution, the spectra are blended and
+change alignment along the pixel dimension. Furthermore, the camera
+has a variable point-spread function and focus,
+suffers from flexture problems, has a different illumination for
+the quartz than object exposures, and unexplained background level
+variations (in the CRYOCAM). There are two detectors which have been
+used with the multi-aperture plate system, the Cryogenic Camera
+(CRYOCAM) and the High Gain Video Spectrometer (HGVS).
+.NH 2
+Echelle
+.PP
+As some of the algorithms were developed the Echelle data was brought
+to my attention. It is considerably simpler than the MAP data because
+it is unblended and of high spectral resolution.
+Furthermore, the quartz exposure
+can be made wider than the object exposures and flexture is not a
+problem. The principle problem in this data was the
+prevalence of cosmic rays. It pointed to the need to maintain generality
+in dealing with both the MAP data and other types of
+multi-spectra data which have different profiles, may or may not be
+merged, and may or may not have different widths in quartz and object.
+Dealing with the cosmic ray problem lead to a very effective solution
+usable in both the Echelle and multi-aperture plate data.
+.NH
+User Level Extraction Logic
+.PP
+The user should generally only be concerned with the logical steps of
+extracting the individual spectra from the multi-spectra image. This
+means that apart from specifying the detector system and the format
+he should not deal with details of the detector and the format.
+In the paper,
+\fIDetailed Specifications for the Multi-Spectra Extraction Package\fB,
+the \fBIRAF\fR extraction package design and program specifications
+are described.
+.NH
+Flat Fields
+.PP
+There are two types of flat field situations depending on the spectral
+resolution. When the resolution is high then the spectral signature of
+the continum calibration source, a quartz exposure, will be unimportant
+and variations in the signal will be due to detector sensitivity variations.
+In this case the quartz frame, or average of several frames, is the flat
+field and division of the object frames by the quartz frame is all that
+is required. However, a special
+image division program is desirable to handle the region of low or absent
+signal between the the spectra. This is described in section 4.2.
+.PP
+In the alternate case of lower resolution the quartz spectral signature is
+larger than the detector response variations. A flat
+field in which the intrinsic quartz spectrum is removed is produced by
+assuming that the true value of a pixel is given by the smoothed average
+of the pixels near that point in position and wavelength and taking
+the ratio of the data value to the smoothed value.
+This requires a special smoothing program described in section 4.1.
+After the flat field is generated then the same image division
+program used for the Echelle data is applied.
+The image division and smoothing programs are general image operators and
+not specific to the Multi-Spectra Extraction Package.
+.NH 2
+MULTISPEC_FLAT
+.PP
+The multi-aperture plate data varies in both dimensions. Thus, any averaging
+to smooth the image must take this variation into account. In the Cyber
+a flat field for the multi-aperture plate data smooths across the dispersion
+by modeling the spectra. This is a difficult task to do accurately because
+the true shape of the spectra is not known and the counts vary greatly
+and rapidly in this dimension. This approach has the further difficulty
+that it is not possible to average several quartz exposures directly.
+.PP
+The alternate approach to modeling is statistical averaging.
+Averaging across the dispersion requires very high order polynomials
+because of the rapid variations;
+the spectra are typically spaced about 8 pixels apart and there are on
+the order of 50 spectra. On the other hand, variations along the dispersion
+are much slower even if the spectra are slightly skewed; a bad case would
+have two peaks in 800 pixels along the y dimension. This kind
+of variation is tractable with relatively simple averaging polynomials
+and is the one adopted for the multi-aperture plate data.
+.PP
+The flat fields are produced by a quadratic moving average along the
+y direction. This means that the region centered at a given pixel
+is fitted by a least-squares quadratic polynomial and the value of the
+polynomial at that point is the appropriate statistical average.
+The width of the moving average is an adjustable parameter.
+At the edges of the frame where it is not possible to center a region of
+the specified width about the desired pixel the polynomial fit is used to
+extrapolate the average value to the edge.
+.PP
+Because the quadratic fit will
+be influenced by bad pixels an attempt is made to detect and smooth over
+the bad pixels. This is accomplished by comparing the smoothed values to
+the observed values and ignoring pixels with a value of
+
+.EQ (1)
+ chi = | observed - smoothed | / sqrt smoothed
+.EN
+
+greater than a specified value. Then the smoothing is recalculated and tested
+again for bad pixels. This iteration continues until no further pixels
+are rejected.
+.PP
+Following the smoothing the flat field is produced by ratioing the raw quartz
+to the smoothed quartz. Pixels of low signal (specified by the
+parameter \fIconstant\fR )
+are treated by the equation
+
+.EQ
+ r = (data + (constant - smoothed) ) / constant .
+.EN
+
+The resultant flat field image is then divided into the object frames in
+the manner described in the next section.
+.PP
+Experience with data from the Cryogenic Camera has proved very good.
+The flat field which is produced can be examined on a display. It
+shows fringing at red wavelengths and is not too strongly affected
+by bad pixels. Some further effort, however, could go into smoothing
+over the bad pixels.
+.PP
+The smoothing operation on data from the Cryogenic Camera actually
+consists of four steps. The quartz exposures are first averaged.
+The average quartz is rotated so that the dispersion
+direction is the most rapidly varying or x dimension. Then the
+smoothing is performed along x followed by another rotation to return
+the flat field image to its original orientation. The reason for the
+rotations is that they can be done quickly and efficiently whereas
+smoothing along the y dimension is very slow and coding an efficient
+version is much more complicated than doing a single line at a time.
+.NH 2
+FLAT_DIVIDE
+.PP
+The Echelle data has quartz frames which can be used directly as flat fields.
+One just has to divide the object frames by the quartz or average of several
+quartz. However, in image division consideration has to be given the
+problem of division by zero or very small numbers. In direct imaging this
+may not be much of a problem but in multi-spectra data the region between
+the spectra and near the edges of the spectra will have very low counts.
+Another aspect of image division for making flat field corrections is the
+scaling of the result. The flat field integer image data must be large
+to give accurate relative response values. However, one wants to divide
+an object frame by values near unity.
+This section describes a special image division operator allowing the user
+to specify how to handle these cases.
+.PP
+The parameters are a \fIdivision threshold\fR
+(default of zero) and a \fIthreshold violation value\fR. Values of the
+denominator above the \fIthreshold\fR are treated separatedly from those
+below the \fIthreshold\fR. The denominator image is scaled to have an
+average of one for pixels above the \fIthreshold\fR. The pixel by pixel
+division is then performed for those points for which the denominator
+is above the \fIthreshold\fR. Pixels for which the denominator is below the
+\fIthreshold\fR are set to the \fIthreshold violation value\fR in the resultant
+image if the \fIviolation value\fR is specified. If the value is not
+specified then the numerator value is taken as the resultant value.
+The divisions can be done in place or the result put into a new image file.
+.PP
+For the multi-spectra situation where the object spectra have a
+smaller width than the quartz, as in the Echelle, one can either
+set the \fIthreshold
+violation value\fR to zero or not set it at all resulting in either
+exactly zero or background values between the spectra while still flattening
+the spectra. This allows looking at the flattened spectra without the
+annoying "grass" between the spectra caused by dividing by small
+values.
+.NH
+Extraction
+.NH 2
+MULTIAP_EXTRACT
+.PP
+The extraction of spectra from multi-aperture plate images consists of
+a series of steps. The steps are executed from a script.
+The command
+
+.FX
+ms> multiap_extract "ap165.*", "", 165, 50
+.EX
+
+will take the flattened images, ap165.*, from aperture plate 165 with 50
+spectra and automatically locate the spectra, model the profiles, and
+extract the one dimensional spectra. The script consists of
+a number of steps as described below.
+.PP
+\fBFind_spectra\fR (section 6) initializes the \fBmultispec\fR data file
+and does a peak search to determine the initial positions of the
+spectra.
+\fBFind_bckgrnd\fR fits a polynomial of order 1 (or more) for the pixels which
+are not near the spectra as defined by \fBfind_spectra\fR.
+.PP
+The spectra are then modeled in bands of 32 lines by the model profiles
+described in section 8.1. The first \fBmodel_fit\fR uses three Gaussian
+parameters for
+each spectra measuring the peak intensity, peak position, and width.
+The second \fBmodel_fit\fR adds a fourth parameter to modify the wings of the
+profile.
+.PP
+The \fBmodel_extract\fR program extracts the spectra line by line and also
+detects and removes cosmic ray events which do not fit the model
+profiles (see section 9).
+In outline, the extraction of blended spectral data uses the
+model profiles to determine the fraction of light
+from each of the neighboring spectra at the pixel in question. The
+appropriate fraction of the
+.ul
+observed
+pixel intensity (minus the background) is
+assigned to the luminosities of the spectra. There are two versions
+of the \fBmodel_extract\fR extraction. The first simultaneously fits the
+peak intensity of all the spectra and the second uses the
+data value at the peak of each spectra to normalize the model. The
+first method is slow and accurate and the second is fast and approximate.
+Because the models are used in extraction only to define the relative
+contributions of neighboring spectra to the total observed pixel luminosity
+the speed of the approximate method far outweighs the need for
+accuracy. However, cleaning the spectra of cosmic rays is a different
+matter and is discussed further in section 12.
+.PP
+After the extraction the spectra are correlated with the aperture plate
+description using \fBap_plate\fR (see Section 10) to determine the
+relative wavelength offsets and assign identification information to
+the spectra.
+.PP
+For successive frames it is not necessary to resort to the initial
+steps of finding the spectra and fitting from scratch. The \fBcopy_params\fR
+routine makes a new copy of the fitting database. Small shifts in positions
+of the spectra and the peak intensities are determined by doing a two
+parameter fit for the peak and position using the previously determined
+shape parameters.
+Changes in the shape of the spectra are then determined by the three
+and four parmater fits. Because the solution is likely to be close to
+the previously determined shape the transfering of one solution from a
+previously solved image is faster than starting from scratch.
+Note that the shapes as well as the positions and peak intensities
+of all frames including the object exposures are allowed to change.
+.PP
+The spectra are then extracted from the image by \fBmodel_extract\fR and the
+process repeats for the succeeding images.
+.PP
+One useful feature is the ability to specify the bands or lines to be
+modeled or extracted.
+This feature is useful for diagnosising the programs quickly.
+The default is all bands or lines.
+.NH 2
+ECHELLE_EXTRACT
+.PP
+The extraction of the unblended Echelle spectra is performed
+begins in a similar way with \fBfind_spectra\fR and \fBfind_bckgrnd\fR.
+The extraction and cleaning, however, uses \fBstrip_extract\fR which
+adds up the instrumental counts for each unblended spectra at each
+wavelength to get the total luminosity.
+.NH
+FIND_SPECTRA -- Finding the Spectra
+.PP
+The first step in the extraction and processing of multi-spectra data is
+to locate the spectra. This can be done interactively by
+the user but it is far preferable to automate the process;
+particularly since multi-spectra data can have a large number of
+spectra and frames. The approach is to find the peaks in a line, or
+average of lines, sort the peaks found in some manner, such as by
+strength, and select the expected number of peaks from the top of the
+list.
+Beyond this simple outline are several algorithmic details such as how
+to define and locate valid peaks and how to sort the list of peaks.
+Peak finding is a general problem and a subroutine for peak finding is
+described below. The \fBfind_spectra\fR program provides an
+interface between the \fBmultispec\fR data file and the
+general peak finding algorithm.
+.PP
+The \fBpeaks\fR function takes arrays of x (position) and y (value) points
+and the number of
+points in the arrays and returns the number of peaks found. It also
+returns the estimated positions of the peaks in the x array and the
+extimated peak values in the y array in order of peak likelihood.
+There is one user parameter, the smoothing \fIwidth\fR.
+The choice of the \fIwidth\fR parameter is dicatated by how closely and how
+wide the peaks are to be expected.
+The algorithm takes a region of \fIwidth\fR points
+centered on each x point and fits a quadratic;
+
+.EQ
+y sub fit = a + b x + c x sup 2~.
+.EN
+
+A peak is defined
+when the slopes, $b sub 1$ and $b sub 2$, of two neighboring points
+$x sub 1$ and $x sub 2$ change
+sign from positive to negative and the curvatures, $c sub 1$ and $c
+sub 2$, are less than -0.001 for both points.
+The quadratic polynomials define two estimated peak positions
+
+.EQ
+x sub 1 sub peak = x sub 1 - b sub 1 / (2 * c sub 1 ),~~
+x sub 2 sub peak = x sub 2 - b sub 2 / (2 * c sub 2 )~.
+.EN
+
+The offsets are then normalized to give a linear interpolation
+fraction
+$f = ( x sub 1 sub peak - x sub 1 ) / ( x sub 2 sub peak - x sub 1 sub
+peak )$ in the interval between the two points.
+The estimated position of the peak is then
+
+.EQ
+x sub peak = f * ( x sub 1 - x sub 2 )
+.EN
+
+and the estimated peak value is the average value of the two quadratic
+polynomials at $x sub peak$. The curvature at the peak is
+estimated by $c sub peak = c sub 1 + f * (c sub 1 - c sub 2 )$.
+Finally, the peaks are sorted by the magnitude of the peak curvature.
+.PP
+This peak finding algorithm works quite well. I have also used it to
+automatically locate peaks in the extracted one dimensional spectra
+and then do peak correlations between spectra to find a relative
+wavelength solution. Some such use of this program may be implemented
+in either future additions to the Multi-Spectra Extraction Package or
+the Spectral Reduction Package.
+.PP
+In \fBfind_spectra\fR the number of spectra to be found is specified by
+the user. The user should have previously looked at an image
+on a display or done a profile plot across the
+dispersion to count the observed spectra.
+Additional parameters specify the columns in which the spectra
+are to be found and the minimum separation and width of the spectra.
+The column specification allows the elimination of problems with defective
+areas of the detector such as the LED in the Cryogenic Camera. The minimum
+width and separation provide algorithmic constraints to the spectra finding
+procedure.
+.PP
+The peaks are found at two or more points in the
+multi-spectra image for a band of 32 lines using a
+\fBpeaks\fR \fIwidth\fR parameter of 5. After the peaks are found
+at a number of bands in the image a linear fit is made to determine any small
+slope of the spectra relative to the columns.
+The reason for specifying only a few bands is that the process of
+finding the peaks is moderately slow and only two bands are needed for
+determining the initial position angle of the spectra to the y
+dimension. Furthermore, some bands do not give a satisfactory result
+because of extraneous data such as the LED in the CRYOCAM or bad
+focus. Another possibility is that a spectrum may go off the edge
+and in order to "find" the spectrum for partial extraction bands that
+include the on image part of the spectrum must be specified.
+.NH
+FIND_BCKGRND -- Background
+.PP
+The background on a multi-spectra image is the result of very broad
+scattering as opposed to the narrower scattering which produces
+distinguishable wings on individual spectra.
+Modeling of the background in a Cryogenic Camera multi-aperture plate
+image shows that the background is well explained by a broad
+scattering function.
+It is not reasonable, however, to model the scattering to this detail
+in actual extractions.
+Instead a smooth polynomial is fitted to the pixels not covered by
+spectra. The order of the polynomial is a specified parameter.
+For the CRYOCAM MAP data a quadratic is appropriate.
+.PP
+The algorithm is the same for all multi-spectra data except for the
+choice of parameters. First, the location of the spectra must be
+determined. This is done by the \fBfind_spectra\fR program. There
+are two principle parameters, a buffer distance and the order of the
+fitting polynomial. Each line, or average of several lines, is fitted
+by least-squares for the points lying farther than the buffer
+distance from any spectra. If there are no points which completely
+stradle the spectra, i.e. located on each side of the spectra, then
+the order of the fitting polynomial is ignored and a constant, or
+first order polynomial, is determined.
+A hidden parameter specifying the columns allowed for searching for
+background points is available so that bad parts of the image can be
+ignored.
+.PP
+A difference in philosophy with the Cyber programs is that the
+determined background is not subtracted from the image data. It is
+instead kept in the database for the image. Generally, it is better to
+modify the basic image data as little as possible. This is the approach
+used in the Multi-Spectra Extraction Package.
+.NH
+Spectra Profiles
+.NH 2
+MODEL_FIT -- Models for Multi-Spectra Images
+.PP
+The object of modeling is to separate blended spectra for extraction
+and to remove artifacts, such as cosmic rays, which do not fit
+the model. The models should have the minimum number of parameters
+which give residuals approaching the detector statistics, they
+should incorporate constraints based on the physics of the
+detector/camera system, and the models must be ammenable to a
+statistical fitting algorithm which is stable.
+There are a large number of possibilities.
+.PP
+An important point to bear in mind during the following discussion is
+the necessary accuracy of the model fitting. In the design proposed
+here the model fitting is not used for determining the smooth quartz.
+Use of a model for making a flat field would require a very accurate
+model and using an average quartz is not possible. However, for
+extraction the model is used only to indicate the
+relative fraction of light for each spectrum when the spectra are
+blended. The cleaning application is more critical but not nearly so
+much as in the flat field modeling. Thus, though we do a good job of
+model fitting (better the the Cyber modeling) some additional features
+such as smoothing along the spectra are not included.
+Also, though some improvement can be gained by the additional shape
+parameters in the fit, they are not necessary for the required purpose
+and can be left out leading to a faster extraction.
+.PP
+During the course of my investigation I tried more than one hundred
+models and combinations of constraints. Some general results of this
+study follow.
+The model which I find gives the best results has six parameters not
+counting the background. The model is defined by the following
+equations where x is the cross dimension.
+
+.EQ (1)
+I = I sub 0 exp (-s * ( DELTA x sup 2 ))
+.EN
+.EQ
+DELTA x = (x - x sub 0 )
+.EN
+.EQ
+s = s sub 0 + s sub 1 y sup 2 + s sub 2 y sup 3
+.EN
+.EQ
+y = DELTA x / sqrt { DELTA x sup 2 + x sub 1 sup 2 }
+.EN
+
+The model consists of a intensity scale parameter, $I sub 0$,
+and a profile which is
+written in a Gaussian form. The center of the profile is given by
+the parameter $x sub 0$. The profile is not exactly Gaussian because the
+scale, $s$, is not a constant but depends on $DELTA x$. The scale
+function has three terms; a constant term, $s sub 0$, which is the scale
+near the center of the profile, and even and odd terms, $s sub 1$
+and $s sub 2$,
+which change the scale in the wings of the profile.
+.PP
+The characteristic of the profile which must be satisfied is that at
+large distances from the profile center the scale is positive. This
+requirement means that the profile will be monotonically decreasing at
+large distances and will have a finite luminosity. This point was
+crucial in determining the form of the scale function. A straight
+power series in $DELTA x$ does not work because power series diverge.
+Instead, the scale function is defined in terms of a separation
+variable $y$ which is bounded by -1 and 1 at infinite separation and is
+zero at zero separation. The parameter $x sub 1$ defines a characteristic
+distance where the character of $y$ changes from going as $DELTA x$ to
+asymptotic to 1. The parameters are, thus, $I sub 0$, $x sub 0$, $s sub 0$,
+$s sub 1$, $s sub 2$, $x sub 1$.
+.PP
+An important property of this model is that the terms have a physical
+interpretation. The profile scale and profile center are obvious and
+any model must include them. It is the remaining terms, $s sub 0$,
+$s sub 1$, $s sub 2$,
+and $x sub 1$, which are called the shape parameters, which are interesting.
+In an ideal aperture plate system the shape of a profile would be
+given by the projection of the circular aperture into the cross dimension:
+
+.EQ
+P( DELTA x ) = sqrt {1 - a DELTA x sup 2}
+.EN
+
+where the constant a is related to the size of the hole by
+
+.EQ
+a = 1 / r sup 2
+.EN
+
+For small $DELTA x$ the profile can be expressed in the Gaussian form with
+a scale
+
+.EQ
+s = a( 1/2 + a DELTA x sup 2 + ...)
+.EN
+
+Thus, even in a perfect aperture plate system a Gaussian form shows the
+scale increasing from a central value determined by the size of the hole.
+In other words, the profile decreases more sharply than a Gaussian.
+.PP
+However, no aperture plate system is ideal because the thickness of
+the aperture plate is finite and there is scattering and changes in
+the focus of the system. One must
+convolve the profile above with a scattering/focus function. One can show
+that for reasonable functions, exponential and Gaussian,
+with some scale b the final profile is a function of the ratio b/a.
+If the ratio is less than 1 then the profile will be more like that of
+the hole and the profile will be sharper than a Gaussian in the wings.
+If the ratio is much greater than 1 then the profile will become the
+scattering profile at large separations. Simulations using Gaussian
+and exponential scattering profiles show behaviors very much like the
+profile (1) with $s sub 1$ greater than zero when b/a < 1
+meaning the profile becomes sharper (than a Gaussian) in the wings
+and $s sub 1$ < 0 when b/a > 1.
+Thus, $s sub 1$ defines the scale of the scattering profile relative
+to the hole size.
+The size of the hole is incorporated into the parameter $x sub 1$.
+The parameter $s sub 2$ allows an asymmetry in the profile.
+.PP
+An interesting property of the scale function is that it is all right
+for it to be negative at small distances from the profile center. This
+occurs when $s sub 0$ is negative. The effect of this, provided $s$
+becomes positive at large distances, is to give a two horned profile.
+This, in fact, is observed when the focus of the system becomes very
+poor.
+.PP
+The best fits (least chi-square or rms residual) are
+obtained when each spectrum at each wavelength has independent
+parameters. However, this sometimes gives rise to unphysical results.
+If left entirely unconstrained the parameter fitting algorithm can
+make one line broad and dominant and a neighboring line weak and
+sharp.
+This is not, of course, a property of the camera or detector.
+Thus, constraints based on the physics of the
+camera/detector are used. This means that the shape
+parameters $s sub 0$, $s sub 1$, $s sub 2$, and $x sub 1$
+are coupled locally by making them vary as a polynomial of position
+across the dispersion. One might also
+constrain the variation of the shape along the spectrum as is done in
+the Cyber. This is not needed because there are no drastic differences
+between the fitted parameters at neighboring points along the spectra.
+.PP
+My experience with the Cyrogenic Camera system has shown the
+following. The focus ripples twice across the CCD with the
+propagation angle being approximately 30 degrees from the long dimension.
+The change in focus is partly just a scale change. This is seen in
+the correlation of $s sub 0$ with the image scale found by \fBap_plate\fR.
+The shape parameter $s sub 1$ changes sign from positive to
+negative indicating that when the focus is good the profile
+decreases faster than a Gaussian and when the focus is bad it decreases
+slower. Occassionally the focus is very bad and $s sub
+0$ is negative and $s sub 1$ is small and positive causing a broad two
+horned profile. The
+assymetry parameter, $s sub 2$, is useful only when the signal is strong near
+the peak of a quartz exposure. It is not really necessary to include
+it in the model fits. The assymetry parameter was dominant, however,
+in some Echelle data which were clearly asymmetric. The value of
+$x sub 1$ is
+not highly sensitive and can be fixed for a given hole size. Large
+changes in the hole size would require resetting $x sub 1$.
+The use of the four parameters, $I sub 0$, $x sub 0$, $s sub 0$,
+and $s sub 1$, allow good fits
+to all the data I've examined including those in which the peak/valley
+intensity ratio across the spectra is about 1.1. It is the importance
+of the parameter $s sub 1$ which improves the fitting dramatically over the
+Cyber three parameter fitting (in addition to a different fitting
+algorithm).
+.PP
+The heart of profile fitting is the solution of the multi-parameter
+least-squares problem. In a blended multi-spectra image the profile
+parameters of one spectra are affected by its neighbors which are,
+in turn, affected by their neighbors and so forth. The key to this
+type of problem is to realize that only nearest neighbors affect the
+profile parameters and this leads to a "banded" least-squares matrix.
+A banded matrix is one in which cross terms far from the diagonal are
+zero. Solution of banded matrices is much more efficient than solving
+the entire matrix. This allows solution for more than 100 parameters
+simultaneously in a short time.
+.PP
+Use of the banded multi-parameter solution has the restriction, however,
+that there can be no parameters in the model which are not local to
+the profiles. This affects the way
+global constraints are applied to the parameters. In particular,
+the way the shape parameters are constrained to vary smoothly across the
+detector.
+The shape parameters are first found as independent parameters by the
+banded matrix solution and then smoothed by a polynomial in x.
+.PP
+An area which was extensively investigated was the appropriate
+weighting to use for the model fitting. The most likely choices are
+weighting by $1 / sqrt data$ and unit weight corresponding to
+$chi sup 2$
+and least squares fitting. It was found that the two methods
+agreed fairly closely but that the least squares fitting was more
+appropriate because the blending correction depends largely on the
+value of the peak intensity and less on the exact fit of the wings.
+With $chi sup 2$ the peak is fit with less accuracy in order to improve
+the fit in the wings of the profile. In some cases this gave clear
+errors in estimating the peak intensity and, hence, the proper contributions
+between the blended spectra were not made.
+.PP
+Now follows the details of the fitting algorithm.
+The algorithm is a series of script steps in \fBmultiap_extract\fR
+which call the model fitting program \fBmodel_fit\fR with different
+parameters. In the script all bands are fit, $x sub 1$ is fixed,
+and the asymmetry shape parameter $s sub 2$ is ignored.
+The four parameter fit is applied to bands of 32 lines. The band
+solutions are linearly interpolated to the full image and then only
+the intensity scale parameter is calculated for each line during the
+extraction of the spectra with \fBmodel_extract\fR.
+.PP
+The general fitting scheme proceeds as follows:
+.LP
+1. Fit the three parameters $I sub 0$, $x sub 0$, $s sub 0$ with
+$x sub 1$ fixed and $s sub 1$ and $s sub 2$
+zero. This is precisely a Gaussian fit. The three parameters are
+determined simultaneously for all the lines at once using the banded
+matrix method. Thus for 50 lines the solution has 150 variables.
+After each fit the scale
+parameter $s sub 0$ is smoothed by a polynomial in x. The polynomial is
+taken with seven terms.
+.LP
+2. Once the improvement in each iteration becomes less than a
+specified amount (2% in rms residual) the next parameter $s sub 1$ is added.
+The solution has two steps: fit for $s sub 0$ and $s sub 1$ with $I sub 0$
+and $x sub 0$ fixed and
+then fit $I sub 0$ and $x sub 0$ with $s sub 0$ and $s sub 1$ fixed. As before the scale terms
+are smoothed by a seventh order polynomial. Attempts to solve for all
+four parameters a once gave unstable results for reasons I don't
+understand.
+.LP
+3. If desired, the last shape parameter $s sub 2$ can be added by solving
+for $s sub 0$, $s sub 1$, and $s sub 2$ while holding $I sub 0$ and
+$x sub 0$ fixed and then solving for
+$I sub 0$ and $x sub 0$. This provides some improvement when the signal is very
+strong but is generally not needed in the multi-aperture plate data.
+It can be an important term in the Echelle data.
+.LP
+4. It is possible to then adjust $x sub 1$ followed by steps 2 or 3.
+However, this gives very little improvement and $x sub 1$ should be fixed for
+each type of data.
+.LP
+5. During the final extraction when individual lines are evaluated a one
+parameter fit is used to find $I sub 0$ for each spectra. This is
+rather slow, however, on the order of 3 hours per frame. By using
+the pixel value near $x sub 0$ as the value for $I sub 0$ the extraction is reduced
+to 13 minutes per frame (see section 12).
+.PP
+In addition to the preceeding steps the fitting algorithm applies some
+heuristic constraints. These constraints limit how far the peak positions can
+shift in each iteration, require the peak intensity to remain positive, and
+limit the scale function to be positive at large values of y.
+.NH 2
+STRIP_EXTRACT -- Unblended Profiles
+.PP
+For unblended multi-spectra data the profiles can be anything. The profiles
+are obtained by averaging a number of lines (say 32) and normalizing
+at some point like the peak value. These profiles are then used for
+detecting bad pixels, such as cosmic rays, and correcting for them as
+discussed in the section on cleaning. Modeling using the \fBmodel_fit\fR
+program is only used on Echelle data to find peak positions
+accurately in order to follow any curvature of the spectra.
+.NH
+Extraction and Cleaning
+.PP
+The extraction of spectra are done separately from the modeling. It is
+possible to extract spectra without any modeling at all using
+\fBstrip_extract\fR. The extraction step also allows the user to specify
+if cleaning of the spectra for cosmic rays is desired. Also modifying
+the image is an option.
+.NH 2
+MODEL_EXTRACT
+.PP
+Extraction and cleaning using a model fit is described here.
+First the $I sub 0$ values for the model profiles are determined for
+all the spectra in a line either by multi-parameter fitting or by
+taking the peak value. The pixel values are then compared to the
+model in a chi-squared way:
+
+.EQ
+r = (data - model) / sqrt model
+.EN
+
+If the value of r is larger than a set amount, say 5, then the pixel
+value is set to that of the model. Since the "bad" pixel may affect
+the intensity scale $I sub 0$ the cleaning is iterated until no further
+pixels are changed.
+.PP
+The fitting of the data from an individual line of data to the model profiles
+is the key element in this scheme. The best method is to use all the
+data in a multi-parameter least square fit. This minimizes the effect
+of bad pixels on the estimated profile which is the essence of this
+cleaning method. However, while the time required to do this for one
+line is not great, it adds up to nearly three hours for the 800 lines
+in a CRYOCAM frame. A quick alternative is to scale the model profile
+by the data value at the peak position. This is
+quite fast. However, if the peak has a cosmic ray event or is
+otherwise bad then the estimated profile will not correspond closely
+to the data profile and the cleaning procedure will make gross errors.
+The limited experience I've had with the Echelle and MAP data
+has worked well with using the peak estimate. However, the possible
+problems make me nervous and some compromise based on using more than
+the peak to estimate the intensity scale of the profile to the data
+needs to be found. This is important because much of the feedback on
+the \fBmultispec\fR package from Paul Hintzen and Caty Pilachowski
+have dealt with
+the particular usefulness of a good cosmic ray cleaning algorithm in
+extracting multi-spectra data.
+.NH 2
+STRIP_EXTRACT
+.PP
+Removing cosmic rays is the major part of Echelle extraction.
+Because these are unblended spectra of arbitrary shape a strip
+extraction is all that is needed.
+The cleaning is done by the same algorithm used for the multi-aperture
+plates except that the profiles are found, as described earlier, by
+averaging a number of lines.
+The intensity scaling is determined from either a least-square fit
+or the peak value.
+The same question about the appropriate way to
+determine the fit of the profiles to the data discussed previously
+applies here except since the spectra are not blended the spectra
+can be treated separately in any least square fitting.
+.NH
+AP_PLATE -- Aperture Plate Correlation
+.PP
+The final step in the extraction of a multi-aperture plate image is to
+correlate the spectra with the on-line database description of the
+drilled hole positions. This allows for estimates of relative wavelength
+offsets and the identification of the spectra with the ID, RA, and DEC
+parameters.
+.PP
+The spectra are fitted to the known aperture plate drilled positions, given in
+millimeters, to find an \fIangle\fR for the aperture plate relative to the
+detector x dimension and the image \fIscale\fR in pixels / millimeter,
+
+.EQ
+x sub fit = a + scale (x sub drill cos (angle) + y sub drill sin (angle))~.
+.EN
+
+If the number of spectra is less than that given by the aperture plate drilled
+positions then a correlation is done leaving out sequences of
+consecutive holes until the fit residual is minimized. If the number of
+spectra is greater than that supposedly drilled then sequences of
+consecutive peaks are left out of the fit to minimize the residual.
+The missing holes or extra peaks are printed out and, if allowed, the aperture
+plate description file is modified, otherwise the program terminates.
+In all cases if the final fit residual is greater than 1
+pixel the program will terminate.
+The program prints out the \fIangle\fR of the aperture plate and the \fIscale\fR
+which is also stored in the database.
+.PP
+An indication that a large part of the focus variation is purely a
+scale change is that the derived image \fIscale\fR correlates very well with
+the width of the spectra as derived from the profile fitting. I
+estimate that at least 50% of the focus variation is a scale
+variation. This is good news in the sense that a scale variation will
+be taken out in the dispersion solution and lines in different parts
+of the detector will become more similiar after the solution.
+However, the scale variation does not account for all the profile
+shape changes and there is indeed a change in the point spread function
+across the detector.
+.NH
+Problems
+.PP
+There a few problems which I have not been able to resolve or have not
+had the time to consider. The problems which are largely intractable
+without a great deal of effort are the unexplained background
+variations and deconvolving the spectra for the variation in the
+point-spread-function. The background variations are abrupt increases
+in the background in parts of the CRYOCAM detector. The step edge sometimes
+occurs under the spectra and so any smooth polynomial fit to the
+background will not be very good. The modeling of the multi-aperture
+plate profiles provides information about the point-spread function
+but a deconvolution of the variation in the PSF is a difficult problem
+and not warrented at this time.
+.PP
+I had expected that the large scale response of the CRYOCAM could be
+corrected by determining an overall average quartz spectrum from all the
+extracted quartz spectra and then dividing the object spectra in each
+hole by the ratio of the average quartz spectra from that hole to the
+overall average quartz spectrum. This was attempted and it was found
+to work only partially. Specifically, while there might be a 20%
+difference between a spectrum on the edge and one near the center of
+the detector the quartz correction left a 10% difference in the object
+spectra. This is apparently due to a poor illumination by the quartz
+light source which does not correspond to the telescope illumination.
+This quartz correction technique may be made available to users if
+desired.
+.NH
+Comparison with the Cyber Extraction Package
+.PP
+The discussion of this section must be qualified by the fact that I
+have not used the Cyber Extraction Package and I base my understanding on the
+algorithms from the Multi-Aperture Plate Data Reduction Manual and
+conversations with knowledgable people. There are many differences
+both major and minor and this section only seeks to mention the
+some of the important differences. In the Cyber package:
+
+The background is subtracted from the images as a preliminary process.
+
+The background is either constant or linear across the spectra.
+
+The flat fields are produced by modeling the quartz and data from
+several quartz exposures cannot be easily combined.
+
+The initial peak finding and aperture plate correlation algorithm is less
+automated in determining missing or additional holes.
+
+The model fitting uses only a three parameter Gaussian model
+and the algorithms do not yield results when the focus becomes poor.
+
+The fitting algorithm is neighbor subtraction rather than full
+simultaneous solution for all the profiles.
+
+The model fitting is applied only to a quartz and the model is transfered to
+object exposures. This does not allow the shape of the profiles to
+change with time as the telescope moves.
+
+The modelling does not couple solutions for neighboring spectra
+across the dispersion as is suggested in this design and it does smooth
+along the spectra which is not done in this proposal.
+
+The extraction is only to some specified sigma in the model profile and
+there is no attempt to correct for blending.
+
+There is no cleaning of the spectra.
+.NH
+Discussion
+.PP
+The only data which has passed beyond the extraction phase using the
+algorithms described here was that of Paul Hintzen.
+Comparison of data reduced by the TV package for
+spectra extracted by both the Cyber package and the techniques of the
+suggested \fBmultispec\fR package were quite comparable. To the level he
+examined
+the spectra there was no clear increase in accuracy though the \fBmultispec\fR
+extractions generally had higher counts due to the full extraction of
+the blended spectra. The big advantages found were
+the ability to extract all the data even when the focus
+became very poor and the success of the cosmic ray cleaning
+algorithm. Thus, Hintzen feels that the need for speed in the extraction
+(primarily dependent on the cleaning algorithm)
+is modified significantly by the difficulty of dealing with cosmic
+rays in the TV spectral analysis programs. More exploration
+of techniques for determining the profile intensity scale from the
+model without the full multi-parameter solution is warrented for this
+reason.
+.PP
+I have extracted some Echelle data including field flattening. The
+data had a considerable number of cosmic rays which were removed
+quite well. The extracted spectra were put into a CAMERA format
+for further analysis.
+.PP
+The programs were recently applied to a long slit analysis problem
+being studied by Vesa Junkkarinen. The image was already flat fielded.
+The data had two closely spaced and very faint diffuse objects and scattered
+light from a nearby QSO.
+The three spectra were so weak and closely spaced
+that the automatic finding was not used. However, the rest of the modeling
+and extraction were applied directly.
+The \fBfind_bckgrnd\fR program, whose original purpose was to correct for
+scattered light, worked well to extrapolate the sky across the
+image. The model fitting accurately followed
+the peaks of the spectra but the shape fitting was only moderately accurate
+since the model shape parameters are not suited to modeling galaxies.
+It successfully extracted spectra with a minimum of effort on my part.
+Analysis of the extracted spectra and comparison with other techniques
+must still be done. The conclusions to be drawn from this experiment are
+that with sufficiently general multi-spectra tools multiple objects in
+long slit spectroscopy can be handled.
+.PP
+One area in which I do not have practical experience is
+the extraction of HGVS data. I believe
+the proposed design will work on this type of data.
+.PP
+A point which needs to be considered in the final design are the
+formats of the data files. The currently used one dimensional spectra
+formats are an IIDS format and a CAMERA image format.
+The formating of data files for the current spectral analysis packages by
+\fBto_iids\fR starts from the \fBmultispec\fR database and throws away a lot
+of information about the spectra.
+Some refinement of this database should focus on the format
+to be used by a new \fBIRAF\fR spectral analysis package.
+.PP
+It should be pointed out that many of the operations can have
+alternate algorithms substituted. In particular, the smoothing
+algorithm for the multi-aperture plate flat fields can be replaced by
+some other scheme. The links between the multi-parameter fitting
+program and the model have been made very general for investigating
+a broad range of models. Thus, it is also possible to substitute
+additional model profiles with relative ease.
+.PP
+Estimates of excution time are taken from the experimental C programs
+implementing the algorithms of this design and they are only
+approximate estimates. The steps corresponding
+to \fBdebias\fR, \fBmultispec_flat\fR, and \fBflat_divide\fR for
+the multi-aperture data from the CRYOCAM take
+about 1 hour for a typical set of frames, say 5 to 15. This includes
+debiasing, triming, computing a flat field from several quartz frames
+and dividing the quartz into the object frames.
+.PP
+The CRYOCAM \fBmultiap_extract\fR phase takes about 40 minutes for the modeling of a frame using 32 lines per band and either 3 hours for an extraction
+using the profile fitting
+method or 14 minutes for extraction using the peak profile scaling
+method.
+.PP
+Finally, the \fBto_iids\fR takes about 3 minutes per frame. It takes
+this long because it has to convert the \fBmultispec\fR database organized across
+the dispersion into formats in which the data is stored as consecutive
+spectra; i.e. a type of rotation operation.
diff --git a/noao/twodspec/multispec/doc/MSalgo_c.doc b/noao/twodspec/multispec/doc/MSalgo_c.doc
new file mode 100644
index 00000000..b3322dff
--- /dev/null
+++ b/noao/twodspec/multispec/doc/MSalgo_c.doc
@@ -0,0 +1,522 @@
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+ Algorithms for the Multi-Spectra Extraction Package
+ Analysis and Discussion
+ December 2, 1983
+
+
+
+1. Disclaimer
+
+ This should not be taken as a statement of how the algorithms of
+the final package should function; this is merely an analysis and
+discussion of the algorithms, and should be followed by further
+discussion before we decide what course to follow in the final
+package. We may very well decide that the level of effort required to
+implement rigorously correct nonlinear fitting algorithms is not
+justified by the expected scientific usage of the package. Before we
+can decide that, though, we need an accurate estimate of the level of
+effort required.
+
+In attacking nonlinear surface fitting problems it is important to
+recognize that almost any techniques can be made to yield a result
+without the program crashing. Production of a result (extraction of a
+spectrum) does not mean that the algorithm converged, that the
+solution is unique, that the model is accurate, or that the
+uncertainties in the computed coefficients have been minimized.
+
+
+
+2. Multispec Flat (pg. 4)
+
+ This sounds like a classical high pass filter and might be best
+implemented via convolution. Using a convolution operator with a
+numerical kernel has the advantage that the filter can be easily
+modifed by resampling the kernel or by changing the size of the
+kernel. It is also quite efficient. The boundary extension feature
+of IMIO makes it easy to deal with the problem of the kernel
+overlapping the edge of the image in a convolution. Since the
+convolution is one-dimensional (the image is only filtered in Y), it
+will always be desirable to transpose the image.
+
+The method used to detect and reject bad pixels (eqn 1) is not correct.
+The rejection criteria should be invariant with respect to a scaling
+of the pixel values. If the data has gone through very much
+processing (i.e., dtoi on photographic data), the relation between
+photon counts and pixel value may be linear, but the scale is
+unknown. Rejection by comparison of a data value to a "smoothed"
+value is more commonly done as follows:
+
+ reject if: abs (observed - smoothed) > (K * sigma)
+
+where sigma is the noise sigma of the data, generally a function of
+the signal.
+
+It is often desirable in rejection algorithms to be able to specify,
+
+
+ -1-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+as an option, that all pixels within a specified radius of a bad pixel
+be rejected, rather than just the pixel which was detected. This is
+only unnecessary if the bad pixels are single pixel events (no
+wings). Region rejection makes an iterative rejection scheme converge
+faster, as well as rejecting the faint wings of the contaminated
+region.
+
+
+
+2.1 Dividing by the Flat (pg. 5)
+
+ There is no mention of any need for registering the flat with the
+data field. Is it safe to assume that the quartz and the object
+frames are precisely registered? What if the user does in fact
+average several quartz frames taken over a period of time? (Image
+registration is a general problem that is probably best left until
+solved in IMAGES).
+
+
+
+3. Multiap Extraction (pg. 5-6, 8-13)
+
+ The thing that bothers me most about the modeling and extraction
+process is that the high signal to noize quartz information is not
+used to full advantage, and the background is not fitted very
+accurately. The present algorithms will work well for high signal to
+noise data, but will result in large (percentage) errors for faint
+spectra.
+
+Basically, it seems to me that the high signal to noise quartz spectra
+should, in many cases, be used to determine the position and shape of
+the spectral lines. This is especially attractive since the quartz
+and spectra appear to be closely registered. Furthermore, if the
+position-shape solution and extraction procedures are separate
+procedures, there is nothing to prevent one from applying both to the
+object spectum if necessary for some reason (i.e., poor registration,
+better signal to noise in the object spectrum in the region of
+interest, signal dependent distortions, lack of a quartz image, etc.,
+would all justify use of the object frame). It should be possible to
+model either the quartz or the object frame, and to reuse a model for
+more than one extraction.
+
+Let us divide the process up into two steps, "modeling", and
+"extraction" (as it is now). The "calibration frame" may be the
+quartz, an averaged quartz, or the object frame. Ideally it will have
+a high signal to noise ratio and any errors in the background should
+be negligible compared to the signal.
+
+We do not solve for the background while modeling the calibration
+frame; we assume that the background has been fitted by any of a
+variety of techniques and a background frame written before the
+calibration frame is modeled. A "swath" is the average of several
+image lines, where an image line runs across the dispersion, and a
+
+
+ -2-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+column along the dispersion.
+
+
+
+3.1 Modeling
+
+ I would set the thing up to start fitting at any arbitrary swath,
+rather than the first swath, because it not much harder, and there is
+no guarantee that the calibration frame will have adequate signal to
+noise in the first swath (indeed often the lowest signal to noise will
+be found there). We define the "center" swath as the first swath to
+be fitted, corresponding to the highest signal to noise region of the
+calibration frame. By default the center swath should be the swath
+used by find_spectra, especially if there is significant curvature in
+the spectra.
+
+algorithm model_calibration_frame
+
+begin
+ extract center swath
+ initialize coeff using centers from find_spectra
+ model center swath (nonlinear)
+
+ for (successive swaths upward to top of frame) {
+ extract swath
+ initialize coeff to values from last fit
+ model swath (nonlinear)
+ save coeff in datafile
+ }
+
+ set last-fit coeff to values for center swath
+ for (successive swaths downward to bottom of frame) {
+ extract swath
+ initialize coeff to values from last fit
+ model swath (nonlinear)
+ save coeff in datafile
+ }
+
+ smooth model coeff (excluding intensity) along the dispersion
+ [high freq variations in spectra center and shape from line]
+ [to line are nonphysical]
+ variance of a coeff at line-Y from the smoothed model value is
+ a measure of the uncertainty in that coeff.
+end
+
+
+I would have the background fitting routine write as output a
+background frame, the name of which would be saved in the datafile,
+rather than saving the coeff of the bkg fit in the datafile. The
+background frame may then be produced by any of a number of
+techniques; storing the coefficients of the bkg fit in the datafile
+limits the technique used to a particular model. For similar reasons,
+the standard bkg fitting routine should be broken up into a module
+
+
+ -3-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+which determines the region to be fitted, and a module which fits the
+bkg pixels and writes the bkg image.
+
+For example, if the default background fitting routine is a line by
+line routine, the output frame could be smoothed to remove the
+(nonphysical) fluctuations in the background from line to line. A
+true two dimensional background fitting routine may be added later
+without requiring modifications to the datafile. Second order
+corrections could be made to the background by repeating the solution
+using the background fitted by the extraction procedure.
+
+
+procedure extract_swath
+
+begin
+ extract raw swath from calibration frame
+ extract raw swath from background frame
+ return (calib swath minus bkg swath)
+end
+
+
+The algorithm used to simultaneously model all spectra in a swath from
+across the dispersion is probably the most difficult and time consuming
+part of the problem. The problem is nonlinear in all but one of the
+four or more parameters for each spectra. You have spent a lot of
+time on this and we are probably not going to be able to improve on
+your algorithms significantly, though the generation of the matrix in
+each step can probably be optimized significantly.
+
+The analytic line-profile model is the most general and should work
+for most instruments with small circular apertures, even in the
+presence of severe distortions. It should be possible, however, to
+fit a simpler model given by a lookup table, solving only for the
+position and height of each spectra. This model may be adequate for
+many instruments, should be must faster to fit, and may produce more
+accurate results since there are fewer parameters in the fit. The
+disadvantage of an empirical model is that it must be accurately
+interpolated (including the derivatives), requiring use of spline
+interpolation or a similar technique (I have tried linear and it is
+not good enough). Vesa has implemented procedures for fitting splines
+and evaluating their derivatives.
+
+Fitting the empirical model simultaneously to any number of spectra
+should be straightforward provided the signal to noise is reasonable,
+since there are few parameters in the fit and the matrix is banded
+(the Marquardt algorithm would work fine). However, if you ever have
+to deal with data where a very faint or nonexistent spectra is next to
+a bright one, it may be difficult to constrain the fit. I doubt if
+the present approach of smoothing the coeff across the dispersion and
+iterating would work in such a case. The best approach might be to
+fix the center of the faint spectra relative to the bright one once
+the signal drops below a certain level, or to drop it from the fit
+entirely. This requires that the matrix be able to change size during
+
+
+ -4-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+the fit.
+
+algorithm fit_empirical_model
+
+begin
+ [upon entry, we already have an initial estimate of the coeff]
+
+ # Marquardt (gradient expansion) algorithm. Make 2nd order
+ # Taylor's expansion to chisquare near minimum and solve for
+ # correction vector which puts us at minimum (subject to
+ # Taylor's approx). Taylor's approximation rapidly becomes
+ # better as we near the minimum of the multidimensional
+ # chisquare, hence convergence is extremely rapid given a good
+ # starting estimate.
+
+ repeat {
+ evaluate curvature matrix using current coeff.
+ solve banded curvature matrix
+
+ compute error matrix
+ for (each spectra)
+ if (uncertainty in center coeff > tol) {
+ fix center by estimation given relative spacing
+ in higher signal region
+ remove spectra center from solution
+ }
+
+ if (no center coeff were rejected)
+ tweak correction vector to accelerate convergence
+ new coeff vector = old coeff vector + correction vector
+ compute norm of correction vector
+ } until (no more center coeff rejected and norm < tolerance)
+
+ compute final uncertainties
+end
+
+
+The following is close to what is currently done to fit the analytic
+model, as far as I can remember (I have modified it slightly to
+stimulate discussion). The solution is broken up into two parts to
+reduce the number of free parameters and increase stability. If the
+uncertainty in a free parameter becomes large it is best to fix the
+parameter (it is particularly easy for this data to estimate all but
+the intensity parameter). A fixed parameter is used in the model and
+affects the solution but is not solved for (i.e., like the background).
+
+The analytic fit will be rather slow, even if the outer loop is
+constrained to one iteration. If it takes (very rough estimates) .5
+sec to set up the banded matrix and .3 sec to solve it, 3 iterations
+to convergence, we have 5 sec per swath. If we have an 800 lines
+broken into swaths of 32 lines, the total is 125 sec per image (to
+within a factor of 5).
+
+
+
+ -5-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+algorithm fit_analytic_model
+
+begin
+ [upon entry, we already have an initial estimate of the coeff]
+
+ repeat {
+ save coeff
+ solve for center,height,width of each line with second
+ order terms fixed (but not necessarily zero)
+ apply constraints on line centers and widths
+ repeat solution adding second order coeff (shape terms)
+
+ compute error matrix
+ for (each coeff)
+ if (uncertainty in coeff > tol) {
+ fix coeff value to reasonable estimate
+ remove coeff from solution
+ }
+
+ compute total correction vector given saved coeff
+ if (no coeff were rejected)
+ tweak correction vector to accelerate convergence
+ compute norm of correction vector
+ } until (no additional coeff rejected and norm < tolerance)
+
+ compute final uncertainties
+end
+
+
+
+3.2 Extraction
+
+ The purpose of extraction is to compute the integral of the spectra
+across the dispersion, producing I(y) for each spectra. An estimate of
+the uncertainty U(y) should also be produced. The basic extraction
+techniques are summarized below. The number of spectra, spectra
+centers, spectra width and shape parameters are taken from the model
+fitted to the calibration frame as outlined above. We make a
+simultaneous solution for the profile heights and the background (a
+linear problem), repeating the solution independently for each line in
+the image. For a faint spectrum, it is essential to determine the
+background accurately, and we can do that safely here since the matrix
+will be very well behaved.
+
+ (1) Aperture sum. All of the pixels within a specified radius of
+ the spectra are summed to produce the raw integral. The
+ background image is also summed and subtracted to yield the
+ final integral. The radius may be a constant or a function of
+ the width of the profile. Fractional pixel techniques should
+ be used to minimize sampling effects at the boundaries of the
+ aperture. Pixel rejection is not possible since there is no
+ fitted surface. The model is used only to get the spectra
+ center and width. This technique is fastest and may be best
+
+
+ -6-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+ if the profile is difficult to model, provided the spectra are
+ not crowded.
+
+ (2) Weighted aperture sum. Like (1), except that a weighting
+ function is applied to correct for the effects of crowding.
+ The model is fitted to each object line, solving for I
+ (height) and B (background) with all other parameters fixed.
+ This is a linear solution of a banded matrix and should be
+ quite fast provided the model can be sampled efficiently to
+ produce the matrix. It is possible to iterate to reject bad
+ pixels. The weight for a spectra at a data pixel is the
+ fractional contribution of that spectra to the integral of the
+ contributions of all spectra.
+
+ (3) Fit and integrate the model. The model is fitted as in (2) to
+ the data pixels but the final integral is produced by
+ integrating the model. This technique should be more
+ resistant to noise in the data than is (2), because we are
+ using the high signal to noise information in the model to
+ constrain the integral. More accurate photometric results
+ should therefore be possible.
+
+
+Method (2) has the advantage that the integral is invariant with
+respect to scale errors in the fitted models, provided the same error
+is made in each model. Of course, the same error is unlikely to be
+made in all models contributing to a point; it is more likely that an
+error will put more energy into one spectra at the expense of its
+neighbors. In the limit as the spectra become less crowded, however,
+the effects of errors in neighboring spectra become small and the
+weighted average technique looks good; it becomes quite insensitive to
+errors in the model and in the fit. For crowded spectra there seems
+no alternative to a good multiparameter fit. For faint spectra method
+(3) is probably best, and fitting the background accurately becomes
+crucial.
+
+In both (2) and (3), subtraction of the scaled models yields a residual
+image which can be used to evaluate at a glance the quality of the fit.
+Since most all of the effort in (2) and (3) is in the least squares
+solution and the pixel rejection, it might be desirable to produce two
+integrals (output spectra), one for each algorithm, as well as the
+uncertainty vector (computed from the covariance matrix, not the
+residual).
+
+
+
+3.3 Smoothing Coefficient Arrays
+
+ In several places we have seen the need for smoothing coefficient
+arrays. The use of polynomials for smoothing is questionable unless
+the order of the polynomial is low (3 or less). High order
+polynomials are notoriously bad near the endpoints of the fitted
+array, unless the data curve happens to be a noisy low order
+
+
+ -7-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+polynomial (rare, to say the least). Convolution or piecewise
+polynomial functions (i.e., the natural cubic smoothing spline) should
+be considered if there is any reason to believe that the coefficient
+array being smoothed may have high frequency components which are
+physical and must be followed (i.e., a bend or kink).
+
+
+
+3.4 Weighting (pg. 11)
+
+ The first weighting scheme (1 / sqrt (data)) seems inverted to me.
+The noise goes up as with the signal, to be sure, but the signal to
+noise usually goes up faster. Seems to me the weight estimate should
+be sqrt(data). It also make more sense to weight the least blended
+(peak) areas most.
+
+
+
+3.5 Rejection criteria (pg. 13)
+
+ The same comments apply to this rejection criterium as in section
+2. I assume that "(data - model)" is supposed to be "abs (data -
+model").
+
+
+
+3.6 Uncertainties and Convergence Criteria
+
+ I got the impression that you were using the residual of the data
+minus the fitted surface both as the convergence criterium and as a
+measure of the errors in the fit. It is neither; assuming a perfect
+model, the residual gives only a measure of the noise in the data.
+
+Using the residual to establish a convergence criterium seems
+reasonable except that it is hard to reliably say what the criterium
+should be. Assuming that the algorithm converges, the value of the
+residual when convergence is acheived is in general hard to predict,
+so it seems to me to be difficult to establish a convergence
+criterium. The conventional way to establish when a nonlinear fit
+converges is by measuring the norm of the correction vector. When the
+norm becomes less than some small number the algorithm is said to have
+converged. The multidimensional chisquare surface is parabolic near
+the minimum and a good nonlinear algorithm will converge very rapidly
+once it gets near the minimum.
+
+The residual is a measure of the overall goodness of fit, but tells us
+nothing about the uncertainties in the individual coefficients of the
+model. The uncertainties in the coefficients are given by the
+covariance or error matrix (see Bevington pg. 242). It is ok to push
+forward and produce an extraction if the algorithm fails to converge,
+but ONLY provided the code gives a reliable estimate of the
+uncertainty.
+
+
+
+ -8-
+ MULTISPEC (Dec83) Multispec Algorithms MULTISPEC (Dec83)
+
+
+
+3.6 Evaluating the Curvature Matrix Efficiently
+
+ The most expensive part of the reduction process is probably
+evaluating the model to form the curvature matrix at each iteration in
+the nonlinear solution. The most efficient way to do this is to use
+lookup tables. If the profile shape does not change, the profile can
+be sampled, fitted with a spline, and the spline evaluated to get the
+zero through second derivatives for the curvature matrix. This can be
+done even if the width of the profile changes by adding a linear
+term. If the shape of the profile has to change, it is still possible
+to sample either the gaussian or the exponential function with major
+savings in computation time.
+
+
+
+3.7 Efficient Extraction (pg. 12)
+
+ The reported time of 3 cpu hours to extract the spectra from an
+800 line image is excessive for a linear solution. I would estimate
+the time required for the 800 linear banded matrix solutions at 4-8
+minutes, with a comparable time required for matrix setup if it is
+done efficiently. I suspect that the present code is not setting up
+the linear banded matrix efficiently (not sampling the model
+efficiently). Pixel rejection should not seriously affect the timings
+assuming that bad pixels are not detected in most image lines.
+
+
+
+4. Correcting for Variations in the PSF
+
+ For all low signal to noise data it is desirable to correct for
+variations in the point spread function, caused by variable focus,
+scattering, or whatever. This does not seem such a difficult problem
+since the width of the line profile is directly correlated with the
+width of the PSF and the information is provided by the current model
+at each point in each extracted spectrum. The extracted spectra can
+be corrected for the variation in the PSF by convolution with a spread
+function the width of which varies along the spectrum.
diff --git a/noao/twodspec/multispec/doc/MSalgo_c.hlp b/noao/twodspec/multispec/doc/MSalgo_c.hlp
new file mode 100644
index 00000000..4b9c3356
--- /dev/null
+++ b/noao/twodspec/multispec/doc/MSalgo_c.hlp
@@ -0,0 +1,449 @@
+.help multispec Dec83 "Multispec Algorithms"
+.ce
+Algorithms for the Multi-Spectra Extraction Package
+.ce
+Analysis and Discussion
+.ce
+December 2, 1983
+
+.sh
+1. Disclaimer
+
+ This should not be taken as a statement of how the algorithms of the
+final package should function; this is merely an analysis and discussion
+of the algorithms, and should be followed by further discussion before we
+decide what course to follow in the final package. We may very well decide
+that the level of effort required to implement rigorously correct nonlinear
+fitting algorithms is not justified by the expected scientific usage of
+the package. Before we can decide that, though, we need an accurate estimate
+of the level of effort required.
+
+In attacking nonlinear surface fitting problems it is important to recognize
+that almost any techniques can be made to yield a result without the program
+crashing. Production of a result (extraction of a spectrum) does not mean
+that the algorithm converged, that the solution is unique, that the model
+is accurate, or that the uncertainties in the computed coefficients have
+been minimized.
+
+.sh
+2. Multispec Flat (pg. 4)
+
+ This sounds like a classical high pass filter and might be best implemented
+via convolution. Using a convolution operator with a numerical kernel has
+the advantage that the filter can be easily modifed by resampling the kernel
+or by changing the size of the kernel. It is also quite efficient. The
+boundary extension feature of IMIO makes it easy to deal with the problem of
+the kernel overlapping the edge of the image in a convolution. Since the
+convolution is one-dimensional (the image is only filtered in Y), it will
+always be desirable to transpose the image.
+
+The method used to detect and reject bad pixels (eqn 1) is not correct.
+The rejection criteria should be invariant with respect to a scaling of the
+pixel values. If the data has gone through very much processing (i.e.,
+dtoi on photographic data), the relation between photon counts and pixel value
+may be linear, but the scale is unknown. Rejection by comparison of a data
+value to a "smoothed" value is more commonly done as follows:
+
+ reject if: abs (observed - smoothed) > (K * sigma)
+
+where sigma is the noise sigma of the data, generally a function of the signal.
+
+It is often desirable in rejection algorithms to be able to specify,
+as an option, that all pixels within a specified radius of a bad pixel
+be rejected, rather than just the pixel which was detected. This is only
+unnecessary if the bad pixels are single pixel events (no wings). Region
+rejection makes an iterative rejection scheme converge faster, as well as
+rejecting the faint wings of the contaminated region.
+
+.sh
+2.1 Dividing by the Flat (pg. 5)
+
+ There is no mention of any need for registering the flat with the data
+field. Is it safe to assume that the quartz and the object frames are
+precisely registered? What if the user does in fact average several quartz
+frames taken over a period of time? (Image registration is a general
+problem that is probably best left until solved in IMAGES).
+
+.sh
+3. Multiap Extraction (pg. 5-6, 8-13)
+
+ The thing that bothers me most about the modeling and extraction
+process is that the high signal to noize quartz information is not used to
+full advantage, and the background is not fitted very accurately. The
+present algorithms will work well for high signal to noise data, but
+will result in large (percentage) errors for faint spectra.
+
+Basically, it seems to me that the high signal to noise quartz spectra
+should, in many cases, be used to determine the position and shape of the
+spectral lines. This is especially attractive since the quartz and spectra
+appear to be closely registered. Furthermore, if the position-shape solution
+and extraction procedures are separate procedures, there is nothing to prevent
+one from applying both to the object spectum if necessary for some reason
+(i.e., poor registration, better signal to noise in the object spectrum in
+the region of interest, signal dependent distortions, lack of a quartz image,
+etc., would all justify use of the object frame). It should be possible to
+model either the quartz or the object frame, and to reuse a model for more
+than one extraction.
+
+Let us divide the process up into two steps, "modeling", and "extraction"
+(as it is now). The "calibration frame" may be the quartz, an averaged
+quartz, or the object frame. Ideally it will have a high signal to noise
+ratio and any errors in the background should be negligible compared to
+the signal.
+
+We do not solve for the background while modeling the calibration frame;
+we assume that the background has been fitted by any of a variety of
+techniques and a background frame written before the calibration frame
+is modeled. A "swath" is the average of several image lines, where an
+image line runs across the dispersion, and a column along the dispersion.
+
+.sh
+3.1 Modeling
+
+ I would set the thing up to start fitting at any arbitrary swath, rather
+than the first swath, because it not much harder, and there is no guarantee
+that the calibration frame will have adequate signal to noise in the first
+swath (indeed often the lowest signal to noise will be found there).
+We define the "center" swath as the first swath to be fitted, corresponding
+to the highest signal to noise region of the calibration frame. By default
+the center swath should be the swath used by find_spectra, especially if
+there is significant curvature in the spectra.
+
+.ks
+.nf
+algorithm model_calibration_frame
+
+begin
+ extract center swath
+ initialize coeff using centers from find_spectra
+ model center swath (nonlinear)
+
+ for (successive swaths upward to top of frame) {
+ extract swath
+ initialize coeff to values from last fit
+ model swath (nonlinear)
+ save coeff in datafile
+ }
+
+ set last-fit coeff to values for center swath
+ for (successive swaths downward to bottom of frame) {
+ extract swath
+ initialize coeff to values from last fit
+ model swath (nonlinear)
+ save coeff in datafile
+ }
+
+ smooth model coeff (excluding intensity) along the dispersion
+ [high freq variations in spectra center and shape from line]
+ [to line are nonphysical]
+ variance of a coeff at line-Y from the smoothed model value is
+ a measure of the uncertainty in that coeff.
+end
+.fi
+.ke
+
+
+I would have the background fitting routine write as output a background
+frame, the name of which would be saved in the datafile, rather than saving
+the coeff of the bkg fit in the datafile. The background frame may then
+be produced by any of a number of techniques; storing the coefficients of
+the bkg fit in the datafile limits the technique used to a particular model.
+For similar reasons, the standard bkg fitting routine should be broken up
+into a module which determines the region to be fitted, and a module which
+fits the bkg pixels and writes the bkg image.
+
+For example, if the default background fitting routine is a line by line
+routine, the output frame could be smoothed to remove the (nonphysical)
+fluctuations in the background from line to line. A true two dimensional
+background fitting routine may be added later without requiring modifications
+to the datafile. Second order corrections could be made to the background
+by repeating the solution using the background fitted by the extraction
+procedure.
+
+
+.ks
+.nf
+procedure extract_swath
+
+begin
+ extract raw swath from calibration frame
+ extract raw swath from background frame
+ return (calib swath minus bkg swath)
+end
+.fi
+.ke
+
+
+The algorithm used to simultaneously model all spectra in a swath from
+across the dispersion is probably the most difficult and time consuming
+part of the problem. The problem is nonlinear in all but one of the four
+or more parameters for each spectra. You have spent a lot of time on this
+and we are probably not going to be able to improve on your algorithms
+significantly, though the generation of the matrix in each step can
+probably be optimized significantly.
+
+The analytic line-profile model is the most general and should work for most
+instruments with small circular apertures, even in the presence of severe
+distortions. It should be possible, however, to fit a simpler model given
+by a lookup table, solving only for the position and height of each spectra.
+This model may be adequate for many instruments, should be must faster to
+fit, and may produce more accurate results since there are fewer parameters
+in the fit. The disadvantage of an empirical model is that it must be
+accurately interpolated (including the derivatives), requiring use of spline
+interpolation or a similar technique (I have tried linear and it is not good
+enough). Vesa has implemented procedures for fitting splines and evaluating
+their derivatives.
+
+Fitting the empirical model simultaneously to any number of spectra should
+be straightforward provided the signal to noise is reasonable, since there
+are few parameters in the fit and the matrix is banded (the Marquardt
+algorithm would work fine). However, if you ever have to deal with data
+where a very faint or nonexistent spectra is next to a bright one, it may
+be difficult to constrain the fit. I doubt if the present approach of
+smoothing the coeff across the dispersion and iterating would work in such
+a case. The best approach might be to fix the center of the faint spectra
+relative to the bright one once the signal drops below a certain level,
+or to drop it from the fit entirely. This requires that the matrix be able
+to change size during the fit.
+
+.ks
+.nf
+algorithm fit_empirical_model
+
+begin
+ [upon entry, we already have an initial estimate of the coeff]
+
+ # Marquardt (gradient expansion) algorithm. Make 2nd order
+ # Taylor's expansion to chisquare near minimum and solve for
+ # correction vector which puts us at minimum (subject to
+ # Taylor's approx). Taylor's approximation rapidly becomes
+ # better as we near the minimum of the multidimensional
+ # chisquare, hence convergence is extremely rapid given a good
+ # starting estimate.
+
+ repeat {
+ evaluate curvature matrix using current coeff.
+ solve banded curvature matrix
+
+ compute error matrix
+ for (each spectra)
+ if (uncertainty in center coeff > tol) {
+ fix center by estimation given relative spacing
+ in higher signal region
+ remove spectra center from solution
+ }
+
+ if (no center coeff were rejected)
+ tweak correction vector to accelerate convergence
+ new coeff vector = old coeff vector + correction vector
+ compute norm of correction vector
+ } until (no more center coeff rejected and norm < tolerance)
+
+ compute final uncertainties
+end
+.fi
+.ke
+
+
+The following is close to what is currently done to fit the analytic
+model, as far as I can remember (I have modified it slightly to stimulate
+discussion). The solution is broken up into two parts to reduce the number
+of free parameters and increase stability. If the uncertainty in a free
+parameter becomes large it is best to fix the parameter (it is particularly
+easy for this data to estimate all but the intensity parameter). A fixed
+parameter is used in the model and affects the solution but is not solved
+for (i.e., like the background).
+
+The analytic fit will be rather slow, even if the outer loop is constrained
+to one iteration. If it takes (very rough estimates) .5 sec to set up the
+banded matrix and .3 sec to solve it, 3 iterations to convergence, we have
+5 sec per swath. If we have an 800 lines broken into swaths of 32 lines,
+the total is 125 sec per image (to within a factor of 5).
+
+
+.ks
+.nf
+algorithm fit_analytic_model
+
+begin
+ [upon entry, we already have an initial estimate of the coeff]
+
+ repeat {
+ save coeff
+ solve for center,height,width of each line with second
+ order terms fixed (but not necessarily zero)
+ apply constraints on line centers and widths
+ repeat solution adding second order coeff (shape terms)
+
+ compute error matrix
+ for (each coeff)
+ if (uncertainty in coeff > tol) {
+ fix coeff value to reasonable estimate
+ remove coeff from solution
+ }
+
+ compute total correction vector given saved coeff
+ if (no coeff were rejected)
+ tweak correction vector to accelerate convergence
+ compute norm of correction vector
+ } until (no additional coeff rejected and norm < tolerance)
+
+ compute final uncertainties
+end
+.fi
+.ke
+
+.sh
+3.2 Extraction
+
+ The purpose of extraction is to compute the integral of the spectra
+across the dispersion, producing I(y) for each spectra. An estimate of
+the uncertainty U(y) should also be produced. The basic extraction techniques
+are summarized below. The number of spectra, spectra centers, spectra width
+and shape parameters are taken from the model fitted to the calibration
+frame as outlined above. We make a simultaneous solution for the profile
+heights and the background (a linear problem), repeating the solution
+independently for each line in the image. For a faint spectrum, it is
+essential to determine the background accurately, and we can do that safely
+here since the matrix will be very well behaved.
+.ls 4
+.ls (1)
+Aperture sum. All of the pixels within a specified radius of the spectra
+are summed to produce the raw integral. The background image is also summed
+and subtracted to yield the final integral. The radius may be a constant or a
+function of the width of the profile. Fractional pixel techniques should
+be used to minimize sampling effects at the boundaries of the aperture.
+Pixel rejection is not possible since there is no fitted surface. The model
+is used only to get the spectra center and width. This technique is fastest
+and may be best if the profile is difficult to model, provided the spectra
+are not crowded.
+.le
+.ls (2)
+Weighted aperture sum. Like (1), except that a weighting function is
+applied to correct for the effects of crowding. The model is fitted
+to each object line, solving for I (height) and B (background) with all
+other parameters fixed. This is a linear solution of a banded matrix and
+should be quite fast provided the model can be sampled efficiently to
+produce the matrix. It is possible to iterate to reject bad pixels.
+The weight for a spectra at a data pixel is the fractional contribution
+of that spectra to the integral of the contributions of all spectra.
+.le
+.ls (3)
+Fit and integrate the model. The model is fitted as in (2) to the data
+pixels but the final integral is produced by integrating the model.
+This technique should be more resistant to noise in the data than is (2),
+because we are using the high signal to noise information in the model to
+constrain the integral. More accurate photometric results should therefore
+be possible.
+.le
+.le
+
+
+Method (2) has the advantage that the integral is invariant with respect
+to scale errors in the fitted models, provided the same error is made in
+each model. Of course, the same error is unlikely to be made in all
+models contributing to a point; it is more likely that an error will put
+more energy into one spectra at the expense of its neighbors. In the limit
+as the spectra become less crowded, however, the effects of errors in
+neighboring spectra become small and the weighted average technique looks
+good; it becomes quite insensitive to errors in the model and in the fit.
+For crowded spectra there seems no alternative to a good multiparameter
+fit. For faint spectra method (3) is probably best, and fitting the
+background accurately becomes crucial.
+
+In both (2) and (3), subtraction of the scaled models yields a residual
+image which can be used to evaluate at a glance the quality of the fit.
+Since most all of the effort in (2) and (3) is in the least squares solution
+and the pixel rejection, it might be desirable to produce two integrals
+(output spectra), one for each algorithm, as well as the uncertainty vector
+(computed from the covariance matrix, not the residual).
+
+.sh
+3.3 Smoothing Coefficient Arrays
+
+ In several places we have seen the need for smoothing coefficient arrays.
+The use of polynomials for smoothing is questionable unless the order of
+the polynomial is low (3 or less). High order polynomials are notoriously
+bad near the endpoints of the fitted array, unless the data curve happens
+to be a noisy low order polynomial (rare, to say the least). Convolution or
+piecewise polynomial functions (i.e., the natural cubic smoothing spline)
+should be considered if there is any reason to believe that the coefficient
+array being smoothed may have high frequency components which are physical and
+must be followed (i.e., a bend or kink).
+
+.sh
+3.4 Weighting (pg. 11)
+
+ The first weighting scheme (1 / sqrt (data)) seems inverted to me.
+The noise goes up as with the signal, to be sure, but the signal to noise
+usually goes up faster. Seems to me the weight estimate should be sqrt(data).
+It also make more sense to weight the least blended (peak) areas most.
+
+.sh
+3.5 Rejection criteria (pg. 13)
+
+ The same comments apply to this rejection criterium as in section 2.
+I assume that "(data - model)" is supposed to be "abs (data - model").
+
+.sh
+3.6 Uncertainties and Convergence Criteria
+
+ I got the impression that you were using the residual of the data minus
+the fitted surface both as the convergence criterium and as a measure of the
+errors in the fit. It is neither; assuming a perfect model, the residual gives
+only a measure of the noise in the data.
+
+Using the residual to establish a convergence criterium seems reasonable
+except that it is hard to reliably say what the criterium should be.
+Assuming that the algorithm converges, the value of the residual when
+convergence is achieved is in general hard to predict, so it seems to me to
+be difficult to establish a convergence criterium. The conventional way
+to establish when a nonlinear fit converges is by measuring the norm of
+the correction vector. When the norm becomes less than some small number
+the algorithm is said to have converged. The multidimensional chisquare
+surface is parabolic near the minimum and a good nonlinear algorithm will
+converge very rapidly once it gets near the minimum.
+
+The residual is a measure of the overall goodness of fit, but tells us
+nothing about the uncertainties in the individual coefficients of the model.
+The uncertainties in the coefficients are given by the covariance or error
+matrix (see Bevington pg. 242). It is ok to push forward and produce an
+extraction if the algorithm fails to converge, but ONLY provided the code
+gives a reliable estimate of the uncertainty.
+
+.sh
+3.6 Evaluating the Curvature Matrix Efficiently
+
+ The most expensive part of the reduction process is probably evaluating
+the model to form the curvature matrix at each iteration in the nonlinear
+solution. The most efficient way to do this is to use lookup tables.
+If the profile shape does not change, the profile can be sampled, fitted
+with a spline, and the spline evaluated to get the zero through second
+derivatives for the curvature matrix. This can be done even if the width
+of the profile changes by adding a linear term. If the shape of the profile
+has to change, it is still possible to sample either the gaussian or the
+exponential function with major savings in computation time.
+
+.sh
+3.7 Efficient Extraction (pg. 12)
+
+ The reported time of 3 cpu hours to extract the spectra from an 800 line
+image is excessive for a linear solution. I would estimate the time required
+for the 800 linear banded matrix solutions at 4-8 minutes, with a comparable
+time required for matrix setup if it is done efficiently. I suspect that the
+present code is not setting up the linear banded matrix efficiently (not
+sampling the model efficiently). Pixel rejection should not seriously affect
+the timings assuming that bad pixels are not detected in most image lines.
+
+.sh
+4. Correcting for Variations in the PSF
+
+ For all low signal to noise data it is desirable to correct for variations
+in the point spread function, caused by variable focus, scattering, or
+whatever. This does not seem such a difficult problem since the width of
+the line profile is directly correlated with the width of the PSF and the
+information is provided by the current model at each point in each extracted
+spectrum. The extracted spectra can be corrected for the variation in the
+PSF by convolution with a spread function the width of which varies along
+the spectrum.
+.endhelp
diff --git a/noao/twodspec/multispec/doc/MSspecs.doc b/noao/twodspec/multispec/doc/MSspecs.doc
new file mode 100644
index 00000000..09955e9c
--- /dev/null
+++ b/noao/twodspec/multispec/doc/MSspecs.doc
@@ -0,0 +1,698 @@
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+ Detailed Specifications for the Multi-Spectra Extraction Package
+ F. Valdes
+ December 8, 1983
+
+
+1. Introduction
+
+ The multi-spectra extraction package (MULTISPEC) provides the
+basic tools for modeling, cleaning, and extracting spectra from images
+containing multiple aperture spectra running roughly parallel. These
+tools will generally be combined in reduction script tasks but may
+also be used directly for non-standard analysis.
+
+ This design presents the requirements and specifications for the
+MULTISPEC package. Details concerning the algorithms are given in a
+separate document, Algorithms for the Multi-Spectra Extraction Package.
+
+
+2. Input Data Requirements
+
+ The input data for the MULTISPEC package consists of image files
+containing one or more aperture spectra. The spectra are required to
+run roughly parallel to each other and parallel to the second
+digitization axis. The latter requirement may require a general
+rotation and interpolation image operator. The images are assumed to
+be corrected to linear relative intensity. Thus, the steps of
+correcting digital detector images for dark current, bias, and
+pixel-to-pixel sensitivity variations must be performed before using
+the MULTISPEC tasks.
+
+ Because the the MULTISPEC package is being developed concurrently
+with the IRAF standard image processing tools this document specifies
+the requirements for the preliminary image processing needed to
+prepare digital detector images for the MULTISPEC package.
+
+
+2.1 Basic Digital Detector Reduction Tasks
+
+ The prelimary reduction of multi-spectra images uses CL scripts
+based on general image operators. Some of the scripts are for
+specific instruments or specific reduction applications and some are
+generally useful image processing tasks. The scripts allow the
+specification of many images for which the operations will be
+repetitively applied.
+
+ The following CL scripts are required to reduce multi-spectra
+images from digital detectors.
+
+
+ debias multispec_flat flat_divide
+
+
+
+
+
+ -1-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+debias
+ The files in a list of filenames are automatically debiased and
+ trimmed. This routine will be instrument specific but used by
+ other reduction tasks beyond MULTISPEC.
+
+multispec_flat
+ The files in a list of quartz multi-spectra filenames are added,
+ the result is smoothed along the dispersion dimension, and then
+ the original image is divided by the smoothed image to produce a
+ flat field image. The unsmoothed to smoothed ratio is computed
+ only if the value of the smoothed pixel is greater than a
+ specified amount. Otherwise, the ratio is set to unity. This
+ routine is not instrument specific but is used only for MULTISPEC
+ reductions.
+
+flat_divide
+ The files in a list of filenames are each divided by a specified
+ flat field image. This routine is not instrument or application
+ specific.
+
+ The required general image processing programs needed to implement
+these scripts are specified below.
+
+
+(1) A routine to compute the average value from a specified area of the
+ image. Used to determine the average bias value from a bias strip.
+
+(2) A routine to trim a specified portion of an image. Used to trim
+ the bias strip.
+
+(3) Routines to multiply and subtract images by a constant. Used to
+ scale images such as dark exposures and to remove the average bias
+ value as obtained by (1) above.
+
+(4) Routines to subtract, add, and divide images. Used to subtract
+ dark current and bias exposures, to add several exposures to
+ increase the signal-to-noise, and to divide by a flat field image.
+ The divide routine must give the user the option to substitute a
+ constant or ignore any divisions in which the denominator is less
+ than a specified value.
+
+(5) A routine to rotate or transpose an image. Used to align the
+ spectra along lines or columns.
+
+(6) A routine to apply a filter to lines of the image. For
+ multi-spectra images a smooth quartz is produced by using a
+ running quadratic filter along each line of the dispersion
+ dimension. The filter must be able to recognize bad pixels
+ (specified by a user defined threshold) and remove them from the
+ filtering operation.
+
+
+
+
+
+ -2-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+3. Requirements for the MULTSPEC Package
+
+ The MULTISPEC package shall satisfy the following requirements.
+
+(1) The component programs shall be CL callable.
+
+(2) The programs shall interact only through image files and MULTISPEC
+ data files.
+
+(3) It shall be possible to extract spectra without modeling.
+
+(4) The entire image shall be extracted and not limited by failures in
+ the algorithms.
+
+(5) It shall be possible to specify specific lines or swaths in the
+ image on which to operate.
+
+(6) CL scripts shall be provided for the common data sources. These
+ scripts will work automatically.
+
+The follow functions shall be provided:
+
+o Make an initial rough but automated identification of the spectra
+ locations.
+
+o Provide for a user identification list for the spectra locations.
+ This list shall be of the standard image cursor type to allow
+ generation of the list with the standard image cursor programs.
+
+o Determine and correct for a slowly varying background.
+
+o Reliably and accurately trace spectra in the presence of geometric
+ distortions (pincushion, s, shear, etc.).
+
+o Extract spectra by one of:
+
+ a. Strips of constant width about the located spectra. The width
+ may be specified to fractions of a pixel and the extraction
+ will use fractional pixel interpolation. l
+
+ b. Strips of width proportional to a Gaussian width parameter.
+
+ c. Modeling to obtain estimates of the total luminosity. The
+ estimate will be the integral of the model.
+
+ d. Summation of the data pixel values with fractional
+ contributions of the pixel value to the spectra based on
+ modeling.
+
+o An option shall be available to specify whether to ignore blank
+ pixels or use interpolated values.
+
+ o Programs shall be provided to produce data files which can be
+
+
+ -3-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+ accessed by one dimensional spectroscopic reduction routines.
+ At a minimum these formats shall include:
+
+ a. Reduction to an image file consisting of one line per
+ extracted spectrum
+
+ b. The standard IIDS format available with the CYBER
+ Multi-Aperture Plate programs
+
+
+3.2 Modeling Requirements
+
+ The modeling of multi-spectra images, particularly in the case of
+blended spectra, shall:
+
+(1) Model blended spectra with sufficient reliability and robustness
+ that a reasonable solution is always obtained, though of possibly
+ limited usefulness.
+
+(2) The modeling shall provide estimates for the uncertainties in the
+ fitted parameters as a function of position along the spectrum.
+
+(3) Remove cosmic rays and other defective pixels by reference to the
+ model.
+
+(4) Allow the transfer of a model solution for one image to another
+ image.
+
+(5) Display numerically and graphically the data, the fitted model, and
+ the residuals.
+
+
+4. Program Specifications
+
+
+4.1 Basic Programs
+
+ The basic programs of the package are general purpose tools which
+initialize a MULTISPEC data file and perform a single fundamental
+operation on the data in the MULTISPEC data file. There is one data
+file associated with each image. The data file is hidden from the
+user and so the user need not be aware of the data file. The data
+files are referenced only the image filename specified in the program
+parameters. The data files contain such information as a processing
+history, the spectra positions and extracted luminosities, the model
+parameters (one set for each spectra for each modelled image line (or
+swath), etc. The programs generally are allowed to specify specific
+lines, columns, and/or spectra on which to operate. The line, column
+and spectra specifications are given as strings which contain numbers
+separated by whitespace, commas, and the range indicator "-". The
+script tasks of section 4.2 will combine these basic programs to
+perform a general multi-spectra extraction.
+
+
+
+ -4-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+ ap_plate copy_params find_spectra convolve
+ fit_bckgrnd find_bckgrnd line_list model_extrac
+ model_fit model_image model_list sigma_extract
+ strip_extract to_iids to_image to_onedspec
+
+ap_plate
+ The information from an on-line data file containing descriptions
+ of all the aperture plates prepared at Kitt Peak is read to find a
+ specified aperture plate. The drilled aperture positions are
+ correlated with the spectra in the image to deduce relative
+ wavelength offsets. The identifications for the spectra as well
+ as other auxiliary information is recorded in the data file. If
+ no image file is specified then only the aperture plate
+ information is printed. This program is used in the
+ MULTIAP_EXTRACT program. This program is not essential to the
+ operation of the MULTISPEC package.
+
+ Multi-Spectra image image =
+ Aperture plate plate =
+ (mode = ql)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -5-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+The Background
+ The are two possibilities for dealing with the background. In the
+ first case, FIT_BCKGRND, the background will be fitted by
+ polynomials and the coefficients stored in the MULTISPEC data
+ file. These coefficients are then used by the other programs to
+ estimate the background at the spectra. The second option,
+ FIND_BCKGRND, generates a background image in which the spectra
+ and other selected areas are set to blank pixels. Then a general
+ image interpolator is used fill in the blank pixels with background
+ estimates. The other MULTISPEC programs will then access this
+ background frame. The background frame image name will be stored
+ in the MULTISPEC data file and the image header.
+
+
+ fit_bckgrnd
+ Fit a background in a MULTISPEC image by a polynomial using
+ pixels not near the spectra and in the user specified swaths
+ and columns. The buffer distance is in pixels and refers to a
+ minimum distance from the center of any spectrum beyond which
+ the background pixels are found. Blank pixels are ignored in
+ the background fit. Deviant pixels will be rejected.
+
+ Multi-Spectra image image =
+ Buffer from spectra buffer = 12
+ Polynomial order order = 3
+ Lines per swath (lines_per_swath = 32)
+ Swaths to fit (swaths = 1-1000)
+ Columns to fit (columns = 1-1000)
+ Rejection threshold (threshold = 5)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+
+ find_bckgrnd
+ The spectra within a buffer distance and specified areas are
+ set to blank pixels and the remaining pixels copied to a
+ background image file.
+
+ Multi-Spectra image image =
+ Background image background =
+ Buffer from spectra buffer = 12
+ Lines to ignore (lines = )
+ Columns to ignore (columns = )
+ (mode = ql)
+
+convolve
+ A program will be provided to reduce either the extracted spectrum
+ or the modeled image to a common point-spread function.
+
+
+
+
+
+
+
+
+ -6-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+copy_params
+ Create a MULTISPEC data file for a new image using appropriate
+ MULTISPEC parameters from an old image. The old image must have
+ been processed to find the spectra using FIND_SPECTRA and possibly
+ model fit.
+
+ Old Multi-Spectra image old_image =
+ New Multi-Spectra image new_image =
+ (mode = ql)
+
+find_spectra
+ Initially locate the spectra in a MULTISPEC image. The positions
+ of the spectra within the range of columns are determined for the
+ starting line and then the spectra are tracked within the range of
+ lines. The minimum separation and minimum width would generally
+ be set for a particular instrument. If the automatic search is
+ not used then a list of cursor positions is read from the standard
+ input.
+
+ Multi-Spectra image image =
+ Automatic search auto = yes
+ Starting line start_line =
+ Minimum separation (min_sep = 1)
+ Minimum width (min_width = 1)
+ Averaging width (average = 32)
+ Lines to search (lines = 1-1000)
+ Columns to search (columns = 1-1000)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+
+line_list
+ For the specified lines in the image print the image column
+ number, data value (possibly as a swath average), the model value
+ at that point (i.e. the sum of the model contributions from all
+ the spectra), the background value, and the residual. Plotting
+ scripts may be written using this routine to show the quality of a
+ model fit.
+
+ Multi-Spectra image image =
+ Lines to list (lines = 1-1000)
+ (mode = ql)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -7-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+model_extract
+ A previously fitted model is used to extract the spectra total
+ luminosity by apportioning the data values to spectra in the ratio
+ indicated by the model. If the clean option is specified then the
+ model is used to detect pixels which deviate from the model by a
+ specified amount. The model value replaces the deviant pixel in
+ the extraction and, if specified, also in the image file.
+
+ Multi-Spectra image image =
+ Lines to extract (lines = 1-1000)
+ Clean spectra (clean = yes)
+ Cleaning threshold (threshold = 5)
+ Modify image (modify = yes)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+
+model_fit
+ A specified model is iteratively fitted to the data in each of the
+ specified lines (or swaths) until the RMS residual fails to
+ decrease. The models are selected by a string. The possible
+ values are
+
+ (null string) - initialize the model
+ i - fit only the intensity scale
+ ip - fit the intensity scale and the position
+ ips1 - fit the intensity scale, position, and one parameter shape
+ ips2 - fit the intensity scale, position, and two parameter shape
+ ips3 - fit the intensity scale, position, and three parameter shape
+ ips4 - fit the intensity scale, position, and four parameter shape
+ These models will be combined in a script to search for the best
+ fit.
+
+ The initial shape parameters will generally be set by scripts for a
+ particular data reduction.
+
+ Multi-Spectra image image =
+ Model type model =
+ Lines per swath (lines_per_swath = 32)
+ Swaths to model (swaths = 1-1000)
+ Initial shape1 (shape1 = .1 )
+ Initial shape2 (shape2 = 0 )
+ Initial shape3 (shape3 = 0 )
+ Initial shape4 (shape4 = 5 )
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+
+
+
+
+
+
+
+
+
+
+ -8-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+model_image
+ An image file of the fitted model is created. This image may then
+ be displayed or a residual image may be calculated and displayed.
+
+ Multi-Spectra image image =
+ Model image model =
+ (mode = ql)
+ .nf
+ .le
+ .ls model_list
+ For the specified lines and spectra the model is listed.
+ The listing gives, for each spectra,
+ the spectrum number, the line number, the fitted position,
+ the estimated wavelength, the
+ extracted luminosity, the intensity scale, model width parameters, and
+ the background polynomial coefficients. This routine can be used in scripts
+ to plot the extracted spectra, the trend of width with wavelength, and so
+ forth.
+
+ .nf
+ Multi-Spectra image image =
+ Lines to list (lines = 1-1000)
+ Spectra to list (spectra = 1-1000)
+ (mode = ql)
+
+sigma_extract
+ A previously fitted model is used to extract the spectra luminosity
+ within a specified sigma of the peak. Because the model is not
+ necessarily a Gaussian the sigma is used to compute the intensity
+ ratio of the cutoff to the peak assumining a Gaussian profile and
+ then the data is extracted to the point the model intensity falls
+ below that cutoff. If the clean option is specified then the
+ model is used to detect pixels which deviate from the model by a
+ specified amount. The model value replaces the deviant pixel in
+ the extraction and, if specified, also in the image file.
+
+ Multi-Spectra image image =
+ Sigma extraction width width = 1.
+ Lines to extract (lines = 1-1000)
+ Clean spectra (clean = yes)
+ Cleaning threshold (threshold = 5)
+ Modify image (modify = yes)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+
+
+
+
+
+
+
+
+
+
+
+ -9-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+strip_extract
+ A strip of constant width about the spectra positions is extracted.
+ If cleanning is desired a smoothed estimate of the profile is
+ obtained by averaging a number of lines about the line to be
+ cleaned. After fitting for the intensity scale pixels are found
+ which deviate from the profile by a specified amount. The profile
+ value replaces the deviant pixel in the extraction and, if
+ specified, also in the image file. No prior modeling is required
+ to use this extraction routine.
+
+ Multi-Spectra image image =
+ Strip extraction width width = 1.
+ Lines to extract (lines = 1-1000)
+ Clean spectra (clean = yes)
+ Cleaning threshold (threshold = 5)
+ Lines per profile average (averge_lines = 32)
+ Modify image (modify = yes)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+
+to_iids
+ For a specified prefix, files of the form prefix.nn, where nn is a
+ specified spectra number, are created containing the extracted
+ spectra for all the specified image files. The format of the
+ files is the IIDS format developed for the CYBER Multi-Aperture
+ Plate Extractions.
+
+ Multi-Spectra image images =
+ IIDS filename prefix iids_file =
+ Spectra to format (spectra = 1-1000)
+ (mode = ql)
+
+to_image
+ An image file containing one line of the extracted luminosities
+ for each specified spectra in the specified MULTISPEC image.
+
+ Multi-Spectra image in_image =
+ Extracted spectra image out_image =
+ Spectra (spectra = 1-1000)
+ (mode = ql)
+
+to_onedspec
+ The extractions are converted to an as yet to be specified format
+ for use in the ONEDSPEC reduction package.
+
+ Multi-Spectra images images =
+ ONEDSPEC data file onedspec_file =
+ Spectra (spectra = 1-1000)
+ (mode = ql)
+
+
+
+
+
+
+ -10-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+4.2 General MULTISPEC CL Scripts
+
+ The general MULTISPEC CL scripts perform a series of steps needed
+to extract the spectra from a specified list of image files. These
+steps have been found to generally perform the desired extraction task
+fully.
+
+
+ multiap_extract echelle_extract
+
+multiap_extract
+ The specified multi-aperture plate images are extracted. If no
+ starting solution image, one which has previously been extracted,
+ is specified then the script performs an automatic search for the
+ specified number of spectra. Otherwise the solution from the
+ starting image is used as the initial model. The background is
+ then determined. This is followed by a series of fitting steps on
+ swaths of data. (For further details on the fitting steps see the
+ Algorithms paper). A MODEL_EXTRACT and cleaning follows.
+ Finally, the extraction is correlated with the specified aperture
+ plate using AP_PLATE. If there was no starting image then this
+ extraction becomes the initial solution image. Subsequent images
+ are extracted starting from the initial solution image.
+
+ Multi-Aperture images images =
+ Initial solution image initial =
+ Aperture plate number plate =
+ Number of spectra nspectra =
+ (mode = ql)
+
+echelle_extract
+ The specified echelle images are extracted. If no starting
+ solution image, one which has previously been extracted, is
+ specified then the script performs an automatic search for the
+ specified number of orders. Otherwise the solution from the
+ starting image is used as the initial starting point. The
+ background is then determined. Finally a STRIP_EXTRACT and
+ cleaning is performed. If there was no starting image then this
+ extraction becomes the initial solution image. Subsequent images
+ are extracted starting from the initial solution image.
+
+ Echelle images images =
+ Initial solution image initial =
+ Number of orders norders =
+ Extraction width width =
+ (mode = ql)
+
+
+5. Outline of a MULTISPEC Reduction
+
+ The following outline is for the reduction of a cryogenic camera
+multi-aperture plate. All the programmer supplied default values are
+used.
+
+
+ -11-
+ MULTISPEC (Oct83) Multi-Spectra Extraction Package MULTISPEC (Oct83)
+
+
+
+ (1) rcamera mtb, "ap165.", "s", "3-9"
+ (2) debias "ap165.*"
+ (3) multispec_flat "ap165.[36]", "ap165.flat"
+ (4) flat_divide "ap165.*", "ap165.flat"
+ (5) multiap_extract "ap165.*", "", 165, 50
+ (6) to_onedspec "ap165.*", oned165
+
+
+(1) The data is read from the observing tape(s) using RCAMERA. The
+ image files created are ap165.3, ap165.4, ..., ap165.9. This is
+ easily accomplished by using the filename prefix "ap165." in the
+ RCAMERA program. The raw images may be examined at this point on
+ a display.
+
+(2) The images are debiased using DEBIAS with all the "ap165." files
+ specified. The debias program knows about the location of the
+ bias strip for the cryogenic camera.
+
+(3) A a flat field is created using MULTISPEC_FLAT in which the
+ desired quartz frames are specified and a flat field image
+ filename is defined. The created flat field image may be examined
+ on an image display if desired.
+
+(4) All the debiased images are divided by the flat field using
+ FLAT_DIVIDE.
+
+(5) The script MULTIAP_EXTRACT is run in which the aperture plate
+ number, the number of spectra, and the image files to be extracted
+ are specified. The number of spectra is found by examining an
+ image on an image display or by plotting a cut across the spectra
+ using a general image profile program.
+
+(6) Finally, the extracted spectra are formatted for the ONEDSPEC
+ package using TO_ONEDSPEC with the extracted images specified.
diff --git a/noao/twodspec/multispec/doc/MSspecs.hlp b/noao/twodspec/multispec/doc/MSspecs.hlp
new file mode 100644
index 00000000..92f285ed
--- /dev/null
+++ b/noao/twodspec/multispec/doc/MSspecs.hlp
@@ -0,0 +1,659 @@
+.help multispec Oct83 "Multi-Spectra Extraction Package"
+.sp 3
+.ce
+Detailed Specifications for the Multi-Spectra Extraction Package
+.ce
+F. Valdes
+.ce
+December 8, 1983
+.sh
+1. Introduction
+
+ The multi-spectra extraction package (MULTISPEC) provides the basic tools
+for modeling, cleaning, and extracting spectra from images
+containing multiple aperture spectra running roughly parallel.
+These tools will generally be combined in reduction script tasks
+but may also be used directly for non-standard analysis.
+
+ This design presents the requirements and specifications
+for the MULTISPEC package. Details concerning the
+algorithms are given in a separate document, Algorithms for the
+Multi-Spectra Extraction Package.
+.sh
+2. Input Data Requirements
+
+ The input data for the MULTISPEC package consists of image
+files containing one or more aperture spectra. The spectra are
+required to run roughly parallel to each other and parallel to the
+second digitization axis. The latter requirement may require a
+general rotation and interpolation image operator. The images are
+assumed to be corrected to linear relative intensity. Thus, the
+steps of correcting digital detector images for dark current, bias, and
+pixel-to-pixel sensitivity variations must be performed before using
+the MULTISPEC tasks.
+
+ Because the MULTISPEC package is being developed
+concurrently with the IRAF standard image processing
+tools this document specifies the requirements for the preliminary
+image processing needed to prepare digital detector images for the MULTISPEC
+package.
+.sh
+2.1 Basic Digital Detector Reduction Tasks
+
+ The prelimary reduction of multi-spectra images uses CL scripts
+based on general image operators.
+Some of the scripts are for specific instruments or specific
+reduction applications and some are generally useful image processing
+tasks. The scripts allow the specification of many images for which
+the operations will be repetitively applied.
+
+ The following CL scripts are required to reduce multi-spectra images
+from digital detectors.
+
+
+.nf
+ debias multispec_flat flat_divide
+.fi
+.ke
+.ks
+.ls 4 debias
+The files in a list of filenames are automatically debiased and trimmed.
+This routine will be instrument specific but used by other reduction
+tasks beyond MULTISPEC.
+.le
+.ke
+.ks
+.ls multispec_flat
+The files in a list of quartz multi-spectra filenames are added,
+the result is smoothed
+along the dispersion dimension, and then the original image is divided
+by the smoothed image to produce a flat field image. The unsmoothed
+to smoothed ratio is computed only if the value of the smoothed
+pixel is greater than a specified amount. Otherwise, the ratio is set
+to unity. This routine is not instrument specific but is used only
+for MULTISPEC reductions.
+.le
+.ke
+.ks
+.ls flat_divide
+The files in a list of filenames are each divided by a specified flat
+field image. This routine is not instrument or application specific.
+.le
+.ke
+
+ The required general image processing programs needed to implement these
+scripts are specified below.
+
+.ls (1)
+A routine to compute the average value from a specified area of the
+image. Used to determine the average bias value from a bias strip.
+.le
+.ls (2)
+A routine to trim a specified portion of an image. Used to trim the
+bias strip.
+.le
+.ls (3)
+Routines to multiply and subtract images by a constant. Used to scale
+images such as dark exposures and to remove the average bias value as
+obtained by (1) above.
+.le
+.ls (4)
+Routines to subtract, add, and divide images. Used to subtract dark
+current and bias exposures, to add several exposures to increase the
+signal-to-noise, and to divide by a flat field image.
+The divide routine must give the user the option to substitute a constant or
+ignore any divisions in which the denominator is less than a specified value.
+.le
+.ls (5)
+A routine to rotate or transpose an image. Used to align the spectra
+along lines or columns.
+.le
+.ls (6)
+A routine to apply a filter to lines of the image. For multi-spectra images
+a smooth quartz is produced by using a running quadratic filter along each
+line of the dispersion dimension. The filter must be able to recognize
+bad pixels (specified by a user defined threshold) and remove them from the
+filtering operation.
+.le
+.sh
+3. Requirements for the MULTSPEC Package
+
+ The MULTISPEC package shall satisfy the following requirements.
+.ls (1)
+The component programs shall be CL callable.
+.le
+.ls (2)
+The programs shall interact only through image files and MULTISPEC data files.
+.le
+.ls (3)
+It shall be possible to extract spectra without modeling.
+.le
+.ls (4)
+The entire image shall be extracted and not limited by failures in the
+algorithms.
+.le
+.ls (5)
+It shall be possible to specify specific lines or swaths in the image
+on which to operate.
+.le
+.ls (6)
+CL scripts shall be provided for the common data sources. These scripts
+will work automatically.
+.le
+
+The follow functions shall be provided:
+.ls o
+Make an initial rough but automated identification of the spectra
+locations.
+.le
+.ls o
+Provide for a user identification list for the spectra locations.
+This list shall be of the standard image cursor type to allow generation
+of the list with the standard image cursor programs.
+.le
+.ls o
+Determine and correct for a slowly varying background.
+.le
+.ls o
+Reliably and accurately trace spectra in the presence of geometric
+distortions (pincushion, s, shear, etc.).
+.le
+
+Extract spectra by one of:
+.ls a.
+Strips of constant width about the located spectra. The width may be specified
+to fractions of a pixel and the extraction will use fractional pixel
+interpolation.
+l
+.le
+.ls b.
+Strips of width proportional to a Gaussian width parameter.
+.le
+.ls c.
+Modeling to obtain estimates of the total luminosity. The estimate will
+be the integral of the model.
+.le
+.ls d.
+Summation of the data pixel values with fractional contributions of the
+pixel value to the spectra based on modeling.
+.le
+.le
+.ls o
+An option shall be available to specify whether to ignore blank pixels
+or use interpolated values.
+.ls o
+Programs shall be provided to produce data files which can be accessed
+by one dimensional spectroscopic reduction routines. At a minimum
+these formats shall include:
+.ls a.
+Reduction to an image file consisting of one line per extracted
+spectrum
+.le
+.ls b.
+The standard IIDS format available with the CYBER Multi-Aperture Plate
+programs
+.le
+.le
+.sh
+3.2 Modeling Requirements
+
+ The modeling of multi-spectra images, particularly in the case of
+blended spectra, shall:
+.ls (1)
+Model blended spectra with sufficient reliability and robustness that
+a reasonable solution is always obtained, though of possibly limited
+usefulness.
+.le
+.ls (2)
+The modeling shall provide estimates for the uncertainties in the fitted
+parameters as a function of position along the spectrum.
+.le
+.ls (3)
+Remove cosmic rays and other defective pixels by reference to the model.
+.le
+.ls (4)
+Allow the transfer of a model solution for one image to another image.
+.le
+.ls (5)
+Display numerically and graphically the data, the fitted model, and
+the residuals.
+.le
+.sh
+4. Program Specifications
+.sh
+4.1 Basic Programs
+
+ The basic programs of the package are general purpose tools which
+initialize a MULTISPEC data file and perform a single fundamental operation
+on the data in the MULTISPEC data file. There is one data file associated
+with each image. The data file is hidden from the user and so the user
+need not be aware of the data file.
+The data files are referenced only the image filename specified in the
+program parameters.
+The data files contain such information as a processing history, the
+spectra positions and extracted luminosities, the model parameters (one
+set for each spectra for each modelled image line (or swath), etc.
+The programs generally are allowed to specify specific
+lines, columns, and/or spectra on which to operate.
+The line, column and spectra specifications are given as strings which
+contain numbers separated by whitespace, commas, and the range indicator
+"-". The script tasks
+of section 4.2 will combine these basic programs to perform a general
+multi-spectra extraction.
+
+.ks
+.nf
+ ap_plate copy_params find_spectra convolve
+ fit_bckgrnd find_bckgrnd line_list model_extrac
+ model_fit model_image model_list sigma_extract
+ strip_extract to_iids to_image to_onedspec
+.fi
+.ke
+.ks
+.ls ap_plate
+The information from an on-line data file containing descriptions of all
+the aperture plates prepared at Kitt Peak is read to find a specified
+aperture plate. The drilled aperture positions are correlated with the
+spectra in the image to deduce relative wavelength offsets. The
+identifications for the spectra as well as other auxiliary information
+is recorded in the data file.
+If no image file is specified then only the aperture
+plate information is printed. This program is used in the MULTIAP_EXTRACT
+program. This program is not essential to the operation of the MULTISPEC
+package.
+
+.nf
+ Multi-Spectra image image =
+ Aperture plate plate =
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls The Background
+The are two possibilities for dealing with the background. In the first
+case, FIT_BCKGRND, the background will be fitted by polynomials and
+the coefficients stored in the MULTISPEC data file. These coefficients
+are then used by the other programs to estimate the background at the
+spectra. The second option, FIND_BCKGRND, generates a background image in which
+the spectra and other selected areas are set to blank pixels. Then a
+general image interpolator is used fill in the blank pixels with background
+estimates. The other MULTISPEC programs will then access this background
+frame. The background frame image name will be stored in the MULTISPEC
+data file and the image header.
+
+.ls fit_bckgrnd
+Fit a background in a MULTISPEC image by a polynomial using pixels
+not near the spectra and in the user specified swaths and columns.
+The buffer distance is in pixels and refers to a minimum distance from
+the center of any spectrum beyond which the background pixels are found.
+Blank pixels are ignored in the background fit. Deviant pixels will be
+rejected.
+
+.nf
+ Multi-Spectra image image =
+ Buffer from spectra buffer = 12
+ Polynomial order order = 3
+ Lines per swath (lines_per_swath = 32)
+ Swaths to fit (swaths = 1-1000)
+ Columns to fit (columns = 1-1000)
+ Rejection threshold (threshold = 5)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+.fi
+.le
+.ls find_bckgrnd
+The spectra within a buffer distance and specified areas are set to blank
+pixels and the remaining pixels copied to a background image file.
+
+.nf
+ Multi-Spectra image image =
+ Background image background =
+ Buffer from spectra buffer = 12
+ Lines to ignore (lines = )
+ Columns to ignore (columns = )
+ (mode = ql)
+.fi
+.le
+.le
+.ke
+.ks
+.ls convolve
+A program will be provided to reduce either the extracted spectrum or
+the modeled image to a common point-spread function.
+.le
+.ke
+.ks
+.ls copy_params
+Create a MULTISPEC data file for a new image using
+appropriate MULTISPEC parameters from an old image.
+The old image must have been processed to find the spectra using FIND_SPECTRA
+and possibly model fit.
+
+.nf
+ Old Multi-Spectra image old_image =
+ New Multi-Spectra image new_image =
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls find_spectra
+Initially locate the spectra in a MULTISPEC image.
+The positions of the spectra within the range of columns are determined
+for the starting line and then the spectra are tracked within the
+range of lines. The minimum separation
+and minimum width would generally be set for a particular instrument.
+If the automatic search is not used then a list of cursor positions is
+read from the standard input.
+
+.nf
+ Multi-Spectra image image =
+ Automatic search auto = yes
+ Starting line start_line =
+ Minimum separation (min_sep = 1)
+ Minimum width (min_width = 1)
+ Averaging width (average = 32)
+ Lines to search (lines = 1-1000)
+ Columns to search (columns = 1-1000)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls line_list
+For the specified lines in the image print the image column
+number, data value (possibly as a swath average), the model value at that
+point (i.e. the sum of the model contributions from all the spectra),
+the background value, and the residual.
+Plotting scripts may be written using this routine to
+show the quality of a model fit.
+
+.nf
+ Multi-Spectra image image =
+ Lines to list (lines = 1-1000)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls model_extract
+A previously fitted model is used to extract the spectra total luminosity
+by apportioning the data values to spectra in the ratio indicated by the
+model. If the clean option is specified then the model is used to detect
+pixels which deviate from the model by a specified amount.
+The model value replaces the deviant pixel in the extraction and, if specified,
+also in the image file.
+
+.nf
+ Multi-Spectra image image =
+ Lines to extract (lines = 1-1000)
+ Clean spectra (clean = yes)
+ Cleaning threshold (threshold = 5)
+ Modify image (modify = yes)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls model_fit
+A specified model is iteratively fitted to the data in each of the specified
+lines (or swaths) until the RMS residual fails to decrease. The models
+are selected by a string. The possible values are
+
+.nf
+ (null string) - initialize the model
+ i - fit only the intensity scale
+ ip - fit the intensity scale and the position
+ ips1 - fit the intensity scale, position, and one parameter shape
+ ips2 - fit the intensity scale, position, and two parameter shape
+ ips3 - fit the intensity scale, position, and three parameter shape
+ ips4 - fit the intensity scale, position, and four parameter shape
+.fi
+These models will be combined in a script to search for the best fit.
+
+The initial shape parameters will generally be set by scripts for a
+particular data reduction.
+
+.nf
+ Multi-Spectra image image =
+ Model type model =
+ Lines per swath (lines_per_swath = 32)
+ Swaths to model (swaths = 1-1000)
+ Initial shape1 (shape1 = .1 )
+ Initial shape2 (shape2 = 0 )
+ Initial shape3 (shape3 = 0 )
+ Initial shape4 (shape4 = 5 )
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls model_image
+An image file of the fitted model is created. This image may then be displayed
+or a residual image may be calculated and displayed.
+
+.nf
+ Multi-Spectra image image =
+ Model image model =
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls model_list
+For the specified lines and spectra the model is listed.
+The listing gives, for each spectra,
+the spectrum number, the line number, the fitted position,
+the estimated wavelength, the
+extracted luminosity, the intensity scale, model width parameters, and
+the background polynomial coefficients. This routine can be used in scripts
+to plot the extracted spectra, the trend of width with wavelength, and so
+forth.
+
+.nf
+ Multi-Spectra image image =
+ Lines to list (lines = 1-1000)
+ Spectra to list (spectra = 1-1000)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls sigma_extract
+A previously fitted model is used to extract the spectra luminosity
+within a specified sigma of the peak. Because the model is not necessarily
+a Gaussian the sigma is used to compute
+the intensity ratio of the cutoff to the peak assuming a Gaussian profile
+and then the data is extracted to the point the model intensity falls below that
+cutoff. If the clean option is specified then the model is used to detect
+pixels which deviate from the model by a specified amount.
+The model value replaces the deviant pixel in the extraction and, if specified,
+also in the image file.
+
+.nf
+ Multi-Spectra image image =
+ Sigma extraction width width = 1.
+ Lines to extract (lines = 1-1000)
+ Clean spectra (clean = yes)
+ Cleaning threshold (threshold = 5)
+ Modify image (modify = yes)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls strip_extract
+A strip of constant width about the spectra positions is extracted.
+If cleanning is desired a smoothed estimate of the profile is obtained
+by averaging a number of lines about the line to be cleaned. After fitting
+for the intensity scale pixels are found which deviate from the profile by
+a specified amount.
+The profile value replaces the deviant pixel in the extraction and,
+if specified, also in the image file. No prior modeling is required
+to use this extraction routine.
+
+.nf
+ Multi-Spectra image image =
+ Strip extraction width width = 1.
+ Lines to extract (lines = 1-1000)
+ Clean spectra (clean = yes)
+ Cleaning threshold (threshold = 5)
+ Lines per profile average (averge_lines = 32)
+ Modify image (modify = yes)
+ Print general diagnostics (verbose = no)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls to_iids
+For a specified prefix, files of the form prefix.nn, where nn is a specified
+spectra number, are created containing the extracted spectra for all
+the specified image files. The format of the files is the IIDS format
+developed for the CYBER Multi-Aperture Plate Extractions.
+
+.nf
+ Multi-Spectra image images =
+ IIDS filename prefix iids_file =
+ Spectra to format (spectra = 1-1000)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls to_image
+An image file containing one line of the extracted luminosities for each
+specified spectra in the specified MULTISPEC image.
+
+.nf
+ Multi-Spectra image in_image =
+ Extracted spectra image out_image =
+ Spectra (spectra = 1-1000)
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls to_onedspec
+The extractions are converted to an as yet to be specified format for
+use in the ONEDSPEC reduction package.
+
+.nf
+ Multi-Spectra images images =
+ ONEDSPEC data file onedspec_file =
+ Spectra (spectra = 1-1000)
+ (mode = ql)
+.fi
+.le
+.ke
+.sh
+4.2 General MULTISPEC CL Scripts
+
+ The general MULTISPEC CL scripts perform a series of steps needed to
+extract the spectra from a specified list of image files. These steps have
+been found to generally perform the desired extraction task fully.
+
+
+.nf
+ multiap_extract echelle_extract
+.fi
+.ks
+.ls multiap_extract
+The specified multi-aperture plate images are extracted.
+If no starting solution image, one which has previously been extracted,
+is specified then the script performs an automatic search for the
+specified number of spectra.
+Otherwise the solution from the starting image is used as the initial
+model. The background is then determined.
+This is followed by a series of fitting steps on swaths of data.
+(For further details on the fitting steps see the Algorithms paper).
+A MODEL_EXTRACT and cleaning follows.
+Finally, the extraction is correlated with the specified aperture plate
+using AP_PLATE.
+If there was no starting image then this extraction becomes the
+initial solution image.
+Subsequent images are extracted starting from the initial solution image.
+
+.nf
+ Multi-Aperture images images =
+ Initial solution image initial =
+ Aperture plate number plate =
+ Number of spectra nspectra =
+ (mode = ql)
+.fi
+.le
+.ke
+.ks
+.ls echelle_extract
+The specified echelle images are extracted.
+If no starting solution image, one which has previously been extracted,
+is specified then the script performs an automatic search for the
+specified number of orders.
+Otherwise the solution from the starting image is used as the initial
+starting point. The background is then determined.
+Finally a STRIP_EXTRACT and cleaning is performed.
+If there was no starting image then this extraction becomes the
+initial solution image.
+Subsequent images are extracted starting from the initial solution image.
+
+.nf
+ Echelle images images =
+ Initial solution image initial =
+ Number of orders norders =
+ Extraction width width =
+ (mode = ql)
+.fi
+.le
+.sh
+5. Outline of a MULTISPEC Reduction
+
+ The following outline is for the reduction of a cryogenic camera
+multi-aperture plate. All the programmer supplied default values are
+used.
+
+.nf
+ (1) rcamera mtb, "ap165.", "s", "3-9"
+ (2) debias "ap165.*"
+ (3) multispec_flat "ap165.[36]", "ap165.flat"
+ (4) flat_divide "ap165.*", "ap165.flat"
+ (5) multiap_extract "ap165.*", "", 165, 50
+ (6) to_onedspec "ap165.*", oned165
+.fi
+
+.ls (1)
+The data is read from the observing tape(s) using RCAMERA.
+The image files created are ap165.3, ap165.4, ..., ap165.9. This is
+easily accomplished by using the filename prefix "ap165." in the RCAMERA
+program. The raw images may be examined at this point on a display.
+.le
+.ls (2)
+The images are debiased using DEBIAS with all the "ap165." files specified.
+The debias program knows about the location of the bias strip for the
+cryogenic camera.
+.le
+.ls (3)
+A a flat field is created
+using MULTISPEC_FLAT in which the desired quartz frames are specified
+and a flat field image filename is defined. The created flat field
+image may be examined on an image display if desired.
+.le
+.ls (4)
+All the debiased images are divided by the flat field using FLAT_DIVIDE.
+.le
+.ls (5)
+The script MULTIAP_EXTRACT is run in which the aperture plate number,
+the number of spectra, and the image files to be extracted are specified.
+The number of spectra is found by examining an image on an image display
+or by plotting a cut across the spectra using a general image profile
+program.
+.le
+.ls (6)
+Finally, the extracted spectra are formatted for the ONEDSPEC package
+using TO_ONEDSPEC with the extracted images specified.
+.le
+.endhelp
diff --git a/noao/twodspec/multispec/doc/MSspecs_c.hlp b/noao/twodspec/multispec/doc/MSspecs_c.hlp
new file mode 100644
index 00000000..848d589d
--- /dev/null
+++ b/noao/twodspec/multispec/doc/MSspecs_c.hlp
@@ -0,0 +1,243 @@
+
+.help multispec Nov82 "Multispec Specifications"
+.ce
+Comments on Multispec Package Specifications
+.ce
+November 8, 1983
+
+
+
+ The basic package structure and the decomposition of the package into
+tasks looks good. The requirements for both general operators and canned
+procedures are addressed well. I got the impression that you have a pretty
+clear idea of what you want to do (which is the thing I am most looking for
+when I read a specs document), but I confess to having to reread the document
+several times to figure out what you have in mind. Your writing style is
+very terse and leaves much up to the reader!
+
+Most of my comments have to do with details. These are presented in the
+order in which they occurred while reading the document. These comments
+apply only to the specs document. I have started going over the algorithms
+paper, mostly when I could not understand a section of the specs document,
+but I have not finished it yet.
+
+.sh
+General Comments
+.ls 4
+.ls (1)
+When eventually we write the user documentation, the nomenclature
+should be carefully explained up front. Users will tend to confuse
+image lines and spectral lines, but there is little we can do about
+that other than to make the distinction clear. The term "band" is
+confusing because it normally refers to the third dimension of an
+image and that is not how it is used here. A better term might be
+"swath". In what follows I will continue to use the term band, but
+it is definitely not too late to change.
+.le
+.ls (2)
+It seems to me that the concept of a band or swath is a detail of how
+the algorithm works and should not have such a prominent place in the
+user interface to the package. Several of the routines require that
+image coordinates be entered in units of band number and column.
+This introduces an unnecessary coupling between two input parameters
+and forces the user to convert from line number to band number. The
+result will be that the user will be reluctant to change the number
+of lines per band (I'll bet that you have kept this a constant in
+using the prototype). My inclination would be to have the user enter
+all coordinates in units of lines and columns, and have the program
+select the nearest band depending on the band width parameter.
+The band width could then be easily changed depending on the data,
+without need to respecify the region of the image to be processed.
+.le
+.ls (3)
+Routines all over the system will have an option for printing extra
+information, i.e., a verbose mode of execution. I think we should
+standardize on the name of this parameter. "Verbose" seems to me
+more descriptive than "print", and is consistent with UNIX terminology.
+.le
+.le
+
+.sh
+Pages 3,4
+.ls
+.ls (1)
+Functions for extracting spectra. I assume "strips of constant
+width" means aperture sum out to a specified x-radius from the
+center of a spectra. Can the radius be specified in fractional
+pixels, and if so, does the routine do fractional pixel interpolation.
+What happens if there are blank pixels in the aperture?
+
+If extraction is based on the model, I gather that you are still
+summing data pixel values, using a weight for each spectra based
+on the modeled contribution of each spectra to the data pixel. In
+other words we are still taking an aperture sum, but with allowances
+for crowding. This has the disadvantage that if we sum way out into
+the wings, we will be adding noise to the aperture sum, degrading signal
+to noise.
+
+Extraction based on integration of the model rather than
+the data should be available as another extraction procedure; this may
+yield better photometric results. I would eventually like to compare
+the two approaches with artificial data. Also by integrating the model
+there is no need to "clean" (I assume that deviant pixels are detected
+and rejected when the model is fitted, or the model will not be
+accurate). Blank pixels should be recognized and ignored when fitting
+the model.
+.le
+
+.ls (2)
+I gather that all extracted spectra for an image are put into a single
+imagefile. This is fine, even desirable, as long as it is ok if all
+spectra share the same header, and as long as all we want to output
+is intensity versus wavelength. If it is desired to also output the
+signal to noise or whatever than another scheme may be needed.
+.le
+.ls (3)
+The text file output form ('c'pg.4) should be engineered with the idea
+that the user will take the data away in cardimage form. From the
+description it sounds like there is one pixel (wavelength bin) per
+line in the text file. This has its advantages, but is not what one
+wants for a cardimage file, which always writes 80 chars per line.
+Also, the detailed technical specs should give some details about
+such a format; it is a major part of the user interface and people
+will want to know what this format is going to look like. In a way
+it is more important to specify formats like this than the calling
+sequences of the tasks, because it is harder to change after the
+package is released, and other program are written to read the
+text format spectra.
+.le
+.ls (4)
+To item 3.2 (2) (on uncertainty estimates) I would add "as a function
+of position along the spectrum".
+.le
+.le
+
+.sh
+4.1 Basic Programs
+.ls
+.ls (1)
+Evidently there is a datafile associated with each image. What is
+the function of the datafile? Is it transparent to the user? How
+much is stored in the image header and how much in the datafile?
+.le
+.ls (2)
+The distinction between "line_list" and "model_list" is confusing.
+Does "line_list" print the sum of the models for all the spectra
+a each column? Please specify the form of the output for this
+procedure in more detail. The line_list and model_list procedures
+are natural candidates for use with the "lists" utilities for
+extracting columns, plotting one column against another, etc. I
+could not tell whether or not this would work well from the info
+given.
+.le
+.ls (3)
+"ap_plate": "The identifications for the spectra ... is recorded."
+Is recorded where? In the datafile? Is this information essential
+to the operation of multispec, or is it merely passed on through
+multispec?
+.le
+.ls (4)
+"find_background": Might be more aptly named "fit_background".
+I would expect "find" to mean find which regions of the image are
+background and which are spectra. Find is spatial, fit is grayscale.
+
+We need to decide whether we want to specify polynomials in IRAF by
+the order (0,1,2, etc.) or by the number of coefficients or terms.
+It seems to me that people are most used to talking about second,
+third, fifth etc. order polynomials and that we might better specify
+polynomials with an "order" parameter rather than a "terms" param.
+
+Buffer radius or diameter? I would assume radius, but it is not
+clear from the docs. What is being "searched"? Shouldn't that read
+"bands to be fitted". The "colummns" parameter should permit a list
+of ranges of columns; I couldn't tell whether this was the case
+from the specs. Cursor input may be desirable here.
+
+Blank pixels should be detected and ignored when fitting the
+background. Are deviant pixels detected and rejected? This is
+generally a desirable option in a bkg fit. You may be able to
+decompose this routine (internally) into a find_background and
+a fit_background, making use of the Images background fitting
+routines, though these generate an image as output rather than the
+coeff of the fitted functions. I wuld guess that you are storing
+the bkg coeff for each band in the datafile from the description,
+and that the fit is strictly one-dimensional.
+
+If only a limited number of bands are fitted, what do you do about
+the other bands if the bkg fit is one-dimensional? Is the user
+req'd to use the same bands range when they do the extraction?
+.le
+
+.ls (5)
+"find_spectra". It is not clear how this routine uses cursor input.
+Perhaps you should have a gcur type parameter. Reading cursor
+coordinates from the standard input may be the way to go, but you
+should explain how this is going to work.
+.le
+.ls (6)
+"line_list". One output line per image line? One or more spectra
+per output line? Output should be suitable for further processing
+with the LISTS package utilities (i.e., getcol, and the graphics
+utility which will plot or overplot lists). The specs should
+specify the form of the output.
+.le
+.ls (7)
+I assume that the extraction procedures extract spectra which
+are put somewhere. Where, in the datafile? If the image is
+to be cleaned, it would be safer to write a new output image,
+or at least to rename the original. It is strange to have these
+two quite different functions in the same module.
+.le
+.ls (8)
+"model_fit". The range of modeling options is impressive, good
+stuff. However, there must be something better than magic integer
+numbers for specifying the model to be fitted. Perhaps the
+strings "i, ip, ipw, ipw2, ipw3, ipw4", where 'i' is for intensity,
+'p' for position, and 'w' for width.
+
+How are the "initial parameters" specified?
+.le
+.ls (9)
+"model_list". Again, I can only guess from the description what the
+output will look like. It sounds like it might be best to have
+this routine print data for only one spectra at a time, particularly
+if the lists package is to be used for analysis. It might be good
+to have the line number in the output somewhere, especially if the
+wavelength information is not available.
+.le
+.le
+
+.sh
+4.2 Scripts
+.ls
+.ls (1)
+It sounds like there is no easy alternative to an automatic search
+for the line centers. This is best as long as it works, but the
+users will want easy way to use the cursor available as an option.
+A script such as this can easily use the line plot routine Images
+to make a plot and generate a list of line centers, without even
+requiring find_spectra to be able to access the cursor (and perhaps
+it should not if the script can do it). The graphics cursor should
+be used here rather than the image cursor.
+.le
+.le
+
+.sh
+5. Example
+.ls
+.ls (1)
+The rcamera example is in error. Rcamera, as implemented, has only
+three query mode params, while you show four in the example.
+I believe the ranges string should be quoted and should be the second
+argument.
+
+The last command should be "to_onedspec", not "onedspec".
+.le
+.ls (2)
+5.(5): It seems strange to make the user manually count 50 spectra
+by examining a plot. If the program automatically finds centers,
+this should not be necessary; if the user interactively marks centers,
+it is not necessary.
+.le
+.le
+.endhelp
diff --git a/noao/twodspec/multispec/doc/findpeaks.hlp b/noao/twodspec/multispec/doc/findpeaks.hlp
new file mode 100644
index 00000000..f6118281
--- /dev/null
+++ b/noao/twodspec/multispec/doc/findpeaks.hlp
@@ -0,0 +1,88 @@
+.help findpeaks Jul84 noao.twodspec.multispec
+.ih
+NAME
+findpeaks -- Find peaks in a multi-spectra image
+.ih
+USAGE
+findpeaks image lines contrast
+.ih
+PARAMETERS
+.ls image
+Image to be searched.
+.le
+.ls lines
+Sample image lines in which the peaks are to be found.
+.le
+.ls contrast
+Maximum contrast between the highest peak and the lowest peak.
+.le
+.ls separation = 5
+Minimum separation in pixels between acceptable peaks.
+.le
+.ls edge = 0
+Minimum distance in pixels to the edge of the image for acceptable peaks.
+.le
+.ls threshold = 0.
+The minimum acceptable peak pixel value.
+.le
+.ls min_npeaks = 1
+Minimum number of peaks to be found. It is an error for fewer than
+this number of peaks to be found.
+.le
+.ls max_npeaks = 1000
+Maximum number of peaks to be found. If more than this number of peaks
+is found then only the those with the highest peak values are accepted.
+.le
+.ls columns = '*'
+Columns to be searched.
+.le
+.ls naverage = 20
+Number of image lines around the sample line to be averaged before
+finding the peaks.
+.le
+.ls debug = no
+Print detailed information on the progress of the peak finding algorithm.
+.le
+.ih
+DESCRIPTION
+For each specified sample image line the number of peaks and their column
+positions in the image are determined.
+The number of peaks and their positions are assumed to correspond to points
+along the spectra. This information is entered in the MULTISPEC database.
+
+The \fInaverage\fR image lines about the specified sample line are first
+averaged. The local maxima in the average line are then located
+in the specified columns more than the minimum distance from the edge of the
+image. A minimum peak pixel value cutoff is determined as the maximum of
+the specified \fIthreshold\fR and \fIcontrast\fR times the largest peak pixel
+value. All local maxima with pixel values below the cutoff are rejected.
+Next all peaks with separations less than \fIseparation\fR from a stronger
+peak are rejected. Finally, if there are more than \fImax_npeaks\fR remaining
+only the \fImax_npeaks\fR strongest peaks are accepted. If fewer
+than \fImin_npeaks\fR are found then the task quits with an error.
+
+If the number of spectra has been previously determined, such as by an earlier
+use of \fBfindpeaks\fR, then it is an error if a different number of
+peaks is found.
+.ih
+EXAMPLES
+The parameters of this task provide a great deal of flexibility in
+automatically determining the number and positions of the peaks.
+The most automatic method just uses the contrast to limit the acceptable
+peaks:
+
+ cl> findpeaks image.db 1 .1
+
+However, if the number of spectra in the image is known:
+
+ cl> findpeaks image.db 1 0 min=10 max=10
+
+or if a threshold is known:
+
+ cl> findpeaks image.db 1 0 threshold = 1000
+
+For a noisy image the separation parameter can be set to eliminate spurious
+noise peaks near the peaks to be found:
+
+ cl> findpeaks image.db 1 .1 sep=20
+.endhelp
diff --git a/noao/twodspec/multispec/doc/fitfunc.hlp b/noao/twodspec/multispec/doc/fitfunc.hlp
new file mode 100644
index 00000000..09510bb7
--- /dev/null
+++ b/noao/twodspec/multispec/doc/fitfunc.hlp
@@ -0,0 +1,73 @@
+.help fitfunction Jul84 noao.twodspec.multispec
+.ih
+NAME
+fitfunction -- Fit a function to the spectra parameter values
+.ih
+USAGE
+fitfunction image
+.ih
+PARAMETERS
+.ls image
+Image in which the parameter values are to be fitted.
+.le
+.ls parameter = "x0"
+Parameter to be fit. The legal minimum match abbreviated parameters
+are x0, s0, s1, s2.
+.le
+.ls lines = "*"
+Sample image lines to be used in the function fit.
+.le
+.ls spectra = "*"
+Spectra for which the parameters are to be fit.
+.le
+.ls function = "interpolation spline"
+Fitting function to be used. The function is specified as a string
+which may be minimum match abbreviated. The functions currently available
+are:
+.ls interpolation spline
+Interpolation spline of specified order.
+.le
+.ls smoothing spline
+Smoothing spline of specified order and number of polynomial pieces.
+.le
+.le
+.ls spline_order = 4
+Order of the fitting spline. The order must be even.
+The minimum value is 2 and maximum value is determined from the number of
+sample lines in the fit.
+.le
+.ls spline_pieces = 1
+The number of polynomial pieces in a smoothing spline.
+The minimum value is 1 and the maximum value is determined from the number of
+sample lines in the fit.
+.le
+.ih
+DESCRIPTION
+A function is fit to the parameter values previously determined at the sample
+lines for each spectrum. The function coefficients are stored in the
+database and the fitted values replace the original values at all the sample
+lines (not just the sample lines used in the fit). The type of function,
+the parameter to be fitted, the sample lines used in the fit, and the
+spectra to be fitted are all selected by the user. The function is
+extrapolated to cover all image lines.
+
+The values of the function fit at arbitrary image lines may be listed
+with \fBmslist\fR.
+.ih
+EXAMPLES
+The extraction of the spectra requires that a fitting function be
+determined for the spectra positions. This is done by:
+
+ cl> fitfunction image
+
+To smooth the parameter "s0" in model \fIgauss5\fR with a cubic spline
+and leave out a bad point at sample line 7:
+
+.nf
+ cl> fitfunction image parmeter=s0 function=smooth \
+ >>> lines="1-6,8-"
+.fi
+.ih
+SEE ALSO
+mslist
+.endhelp
diff --git a/noao/twodspec/multispec/doc/fitgauss5.hlp b/noao/twodspec/multispec/doc/fitgauss5.hlp
new file mode 100644
index 00000000..bcb37276
--- /dev/null
+++ b/noao/twodspec/multispec/doc/fitgauss5.hlp
@@ -0,0 +1,148 @@
+.help fitgauss5 Jul84 noao.twodspec.multispec
+.ih
+NAME
+fitgauss5 -- Fit spectra profiles with five parameter Gaussian model
+.ih
+USAGE
+fitgauss5 image start
+.ih
+PARAMETERS
+.ls image
+Image to be modeled.
+.le
+.ls start
+Starting sample line containing the initial model parameters.
+.le
+.ls lower = -10
+Lower limit for the profile fit relative to each spectrum position.
+.le
+.ls upper = 10
+Upper limit for the profile fit relative to each spectrum position.
+.le
+.ls lines = "*"
+Sample image lines to be fit.
+.le
+.ls spectra = "*"
+Spectra to be fit.
+.le
+.ls naverage = 20
+Number of data lines to be averaged about each sample image line before
+model fitting.
+.le
+.ls factor = 0.05
+The model fit to each line is iterated until the RMS error between the
+model line and the data line improves by less than this factor.
+.le
+.ls track = yes
+Track the model solution from the starting line to the other sample lines?
+.le
+.ls algorithm = 1
+Parameter fitting algorithm to use. Legal values are 1 and 2.
+.le
+.ls fit_i0 = yes
+Fit the profile scale parameters i0?
+.le
+.ls fit_x0 = yes
+Fit the spectra position parameters x0?
+.le
+.ls fit_s0 = yes
+Fit the spectra shape parameters s0?
+.le
+.ls fit_s1 = no
+Fit the spectra shape parameters s1?
+.le
+.ls fit_s2 = no
+Fit the spectra shape parameters s2?
+.le
+.ls smooth_s0 = yes
+Fit a smoothing spline to the shape parameters s0 after each iteration?
+.le
+.ls smooth_s1 = yes
+Fit a smoothing spline to the shape parameters s1 after each iteration?
+.le
+.ls smooth_s2 = yes
+Fit a smoothing spline to the shape parameters s2 after each iteration?
+.le
+.ls spline_order = 4
+Order of the smoothing spline to be fit to the shape parameters.
+.le
+.ls spline_pieces = 3
+Number of polynomial pieces for the smoothing spline.
+.le
+.ls verbose = no
+Print general information about the progress of the model fitting.
+.le
+.ih
+DESCRIPTION
+The spectra profiles in the interval (\fIlower, upper\fR) about each
+spectrum position are fit with a five parameter Gaussian model for
+the specified sample lines of the image. For a description of
+the model see \fBgauss5\fR. The model fitting is performed using
+simultaneous linearized least squares on the selected model profile
+parameters as determined by the \fIalgorithm\fR for the specified
+\fIspectra\fR. The parameter fitting technique computes correction
+vectors for the parameters until the RMS error of the model image line
+to the data image line, which is an average of \fInaverage\fR lines
+about the sample line, improves by less than \fIfactor\fR.
+A solution which increases the RMS error of the model is not allowed.
+
+If the parameter \fItrack\fR is yes then the initial model parameters are
+those given in the database for the sample line \fIstart_line\fR. From
+this starting point the model parameters are iterated to a best fit at
+each specified sample line and then the best fit is used as the starting
+point at the next line. The tracking sequence is from the starting line
+to the last line and then, starting again from the starting line, to
+the first line. Note that the model parameters, including the starting
+spectra positions, need be set only at the starting line.
+
+If \fItrack\fR is no then each specified sample line is fitted independently
+from the initial model parameters previously set for that line. This option
+is used to add additional parameters to the model after an
+initial solution has been obtained or to refit a new image whose database
+was created as a copy of the database of a previously fit image.
+
+The shape parameters s0, s1, and s2 can be smoothed by fitting a spline of
+specified \fIorder\fR and number of spline pieces, \fInpp\fR to the
+parameters as a function of spectra position.
+The smoothing is performed after each iteration and before
+computing the next RMS error. The smoothing is a form of local constraint
+to keep neighboring spectra from having greatly different shapes.
+The possibility of such erroneous solutions being obtained is present in
+very blended data.
+
+In \fIverbose\fR mode the RMS errors of each iteration are printed on the
+standard output.
+
+The selection of the parameters to be fit and the order in which they are
+fit is determined by \fIalgorithm\fR. These algorithms are:
+
+.ls 4 1
+This algorithm fits the selected parameters (\fIfit_i0, fit_x0,
+fit_s0, fit_s1, fit_s2\fR) for the selected \fIspectra\fR simultaneously.
+.le
+.ls 4 2
+This algorithm begins by fitting the parameters i0, x0, and s0
+simultaneously. Note that the values of s1 and s2 are used but are
+kept fixed. Next the parameters s0 and s1 (the shape) are fit simultaneously
+keeping i0, x0, and s2 fixed followed by fitting i0 and x0 while
+keeping s0, s1, and s2 (the shape) fixed. If either of these fits
+fails to improve the RMS then the algorithm terminates.
+Also, if after the two steps (the fit of s0 and s1 followed by the fit
+of i0 and x0), the RMS of the fit has not improved by more than the
+user specified factor the algorithm also terminates. This algorithm has been
+found to be the best way to fit highly blended spectra.
+.le
+.ih
+EXAMPLES
+The default action is to fit Gaussian profiles to the spectra and trace
+the fit from the starting line. An example of this is:
+
+ cl> fitgauss5 image 1
+
+To fit heavily blended spectra with the four parameter model (i0, x0, s0, s1):
+
+ cl> fitgauss5 image 1 algorithm=2
+.ih
+SEE ALSO
+findspectra
+.endhelp
diff --git a/noao/twodspec/multispec/doc/modellist.hlp b/noao/twodspec/multispec/doc/modellist.hlp
new file mode 100644
index 00000000..70e95ce4
--- /dev/null
+++ b/noao/twodspec/multispec/doc/modellist.hlp
@@ -0,0 +1,52 @@
+.help modellist Jul84 noao.twodspec.multispec
+.ih
+NAME
+modellist -- List data and model pixel values
+.ih
+USAGE
+modellist image lines
+.ih
+PARAMETERS
+.ls image
+Image whose model is to be listed.
+.le
+.ls lines
+Sample lines to be listed.
+.le
+.ls model = "gauss5"
+Profile model to be used to create the model line.
+The only model currently defined is \fIgauss5\fR.
+.le
+.ls columns = "*"
+Image columns to be listed.
+.le
+.ls naverage = 20
+The number of image lines to be averaged to form the data values.
+.le
+.ls lower = -10
+Lower limit of model profiles measured in pixels from the spectra centers.
+.le
+.ls upper = 10
+Upper limit of model profiles measured in pixels from the spectra centers.
+.le
+.ih
+DESCRIPTION
+The model of the image for the selected sample \fIlines\fR
+are used to generate model image lines. Only the model \fIgauss5\fR is
+currently available. The output format is column, sample line, image pixel
+value, and model pixel value. The image pixel data are formed by averaging
+\fInaverage\fR lines about the sample lines.
+.ih
+EXAMPLES
+To list the image and model pixel values for the first sample line after
+fitting the \fIgauss5\fR model with \fBfitgauss5\fR:
+
+ cl> modellist image 1 >outputlist
+
+The list file \fIoutputlist\fR can be used with the \fBlists\fR and
+\fBplot\fR packages to graph the image and model lines or to compute
+and graph residuals.
+.ih
+SEE ALSO
+newimage
+.endhelp
diff --git a/noao/twodspec/multispec/doc/msextract.hlp b/noao/twodspec/multispec/doc/msextract.hlp
new file mode 100644
index 00000000..fa361b38
--- /dev/null
+++ b/noao/twodspec/multispec/doc/msextract.hlp
@@ -0,0 +1,172 @@
+.help msextract Jul84 noao.twodspec.multispec
+.ih
+NAME
+msextract -- Extract spectra from a multi-spectra image
+.ih
+USAGE
+msextract image output
+.ih
+PARAMETERS
+.ls image
+Image to be extracted.
+.le
+.ls output
+Filename for the three dimensional image to be created containing the
+extracted spectra.
+.le
+.ls lower = -10
+Lower limit of the integral for integrated spectra or the first column of the
+strip spectra. It is measured in pixels from the spectrum center
+defined by the position function in the MULTISPEC database.
+.le
+.ls upper = 10
+Upper limit of the integral for integrated spectra or (approximately) the
+last column of the strip spectra. It is measured in pixels from the
+spectrum center defined by the position function in the MULTISPEC database.
+.le
+.ls spectra = "*"
+Spectra to be extracted.
+.le
+.ls lines = "*"
+Image lines to be extracted.
+.le
+.ls ex_model = no
+Extract model spectra fit to the image spectra?
+.le
+.ls integrated = yes
+Extract integrated spectra?
+.le
+.ls unblend = no
+Correct for blending in the extracted spectra?
+.le
+.ls clean = yes
+Replace bad pixels with model values? The following parameters are used:
+.ls nreplace = 1000.
+Maximum number of pixels to be replaced per image line when cleaning with
+model \fIgauss5\fR or maximum number of pixels to be replaced per spectrum when
+cleaning with model \fIsmooth\fR.
+.le
+.ls sigma_cut = 4.
+Cleaning threshold in terms of sigma of the fit.
+.le
+.ls niterate = 1
+Maximum number of cleaning iterations per line when cleaning with model
+\fIgauss5\fR.
+.le
+.le
+.ls model = "smooth"
+Choice of \fIgauss5\fR or \fIsmooth\fR. Minimum match abbreviation is
+allowed. This parameter is required only if \fIex_model\fR = yes
+or \fIclean\fR = yes.
+.le
+.ls naverage = 20
+Number of lines to be averaged in model \fIsmooth\fR.
+.le
+.ls fit_type = 2
+Model fitting algorithm for model \fIgauss5\fR.
+.le
+.ls interpolator = "spline3"
+Type of image interpolation function to be used.
+The choices are "nearest", "linear", "poly3", "poly5", and "spline3".
+Minimum match abbreviation is allowed.
+.le
+.ls verbose = no
+Print verbose output?
+.le
+.ih
+DESCRIPTION
+The MULTISPEC database describing the spectra positions and shapes
+is used to guide the extraction of the spectra in the multi-spectra image.
+The user selects the \fIspectra\fR and image
+\fIlines\fR to be extracted and whether to extract integrated or strip spectra.
+In addition options are available to extract model spectra, replace bad
+pixels by model spectra values, and correct for blending of the spectra.
+The \fIoutput_file\fR three dimensional
+image consists of one band (the third dimension) per extracted spectrum,
+the extracted lines (the second dimension) and either one column for
+the integrated luminosity or the number of columns in the extracted strip.
+
+Integrated spectra (\fIintegrated\fR = yes) are extracted by summing
+the pixel or model values over the specified limits \fIlower\fR and \fIupper\fR
+measured relative to the spectra centers defined by the position functions in
+the database. Partial pixel sums are used at the endpoints.
+
+Strip spectra (\fIintegrated\fR = no) are extracted by image interpolation
+of the image line or model profiles to obtain a line of values for
+each spectrum and for each image line. The length of the strip is the
+smallest integer containing the interval between \fIlower\fR and \fIupper\fR.
+The strips for each spectrum are aligned so that the first column is a distance
+\fIlower\fR from the spectrum center as given by the position function in the
+database.
+
+If \fIex_model\fR = yes, \fIunblend\fR = yes, or \fIclean\fR = yes model
+spectra are fit to the spectra in the image. There are two models:
+a five parameter Gaussian profile called \fIgauss5\fR and profiles obtained
+by averaging \fInaverage\fR image lines surrounding the image line being
+modeled called \fIsmooth\fR. The model is selected either when the parameter
+\fIunblend\fR = yes or with the parameter \fImodel\fR. If \fIunblend\fR = yes
+then the model is \fIgauss5\fR regardless of the value of \fImodel\fR.
+
+When \fIex_model\fR = yes the effect is to substitute model spectra for the
+image spectra in the output extraction image.
+
+When \fIclean\fR = yes pixels with large residuals from the model are
+detected and removed from the model fit. The selected model is
+fit to the pixels which are not in the bad pixel list (not yet implemented)
+and which have not been removed from the model fit. The sigma of the fit
+is computed. Deviant pixels are detected by comparing them to the model
+to determine if they differ by more than \fIsigma_cut\fR times the sigma.
+The model fit is iterated, removing deviant pixels at each iteration, until
+no more pixels are found deviant or \fInreplace\fR pixels have been found.
+The pixels removed or in the bad pixel list are then replaced with
+model values. (To clean an image with this algorithm see \fBnewimage\fR.)
+
+There are some technical differences in the model fitting and cleaning
+algorithms for the two models. In model \fIsmooth\fR
+the fit for the profile scale factors is done independently for each spectrum
+and automatically corrected when a bad pixel is detected. This fitting process
+is fast and rigorous. The parameter \fInreplace\fR in this model refers to
+the maximum number of pixels replaced \fIper spectrum\fR.
+
+In model \fIgauss5\fR, however, the profile scale factors are fit
+to the entire image line (hence its ability to fit blended spectra).
+There are two fitting algorithms; a rigorous simultaneous fit
+and an approximate method. The simultaneous fit is selected when
+\fIfit_type\fR = 1. This step is relatively slow. The
+alternative method of \fIfit_type\fR = 2 sets the scale factor for each
+spectrum by taking the median scale, where scale = data / model profile,
+for the three pixels nearest the center of the profile. The median
+minimizes the chance of a large error due to a single bad pixel. This
+scale may be greatly in error in the case of extreme blending but is also
+quite fast; the extraction time is reduced by at least 40%.
+The steps of profile fitting and deviant pixel detection are alternated
+and the maximum number of iterations through these two steps is
+set by \fIniterate\fR. The default of 1 means that the model fitting is not
+repeated after detecting deviant pixels.
+
+When \fIunblend\fR = yes the \fIgauss5\fR model
+is fitted to the image spectra (including possible cleaning).
+The relative contributions to the total image pixel value from each of the
+blended spectra are determined from the model and applied toward either the
+integrated or strip spectra. If \fIex_model\fR = yes then this option has
+no effect other than to force the selection of model \fIgauss5\fR.
+
+The option \fIverbose\fR is used to print the image lines being extracted
+and the number of pixels replaced by the cleaning process.
+.ih
+EXAMPLES
+To extract all the integrated spectra from all the image lines:
+
+ cl> msextract image image.ms
+
+To extract model strip spectra:
+
+ cl> msextract image image.ms ex_model=yes int=no
+
+To extract integrated spectra without any modeling:
+
+ cl> msextract image image.ms clean=no
+.ih
+SEE ALSO
+newimage
+.endhelp
diff --git a/noao/twodspec/multispec/doc/mslist.hlp b/noao/twodspec/multispec/doc/mslist.hlp
new file mode 100644
index 00000000..461b52b4
--- /dev/null
+++ b/noao/twodspec/multispec/doc/mslist.hlp
@@ -0,0 +1,77 @@
+.help mslist Jul84 noao.twodspec.multispec
+.ih
+NAME
+mslist -- List entries in a MULTISPEC database
+.ih
+USAGE
+mslist image keyword lines spectra
+.ih
+PARAMETERS
+.ls image
+Image whose MULTISPEC database entries are to be listed.
+.le
+.ls keyword
+Keyword for the database entry to be listed. The keywords are:
+.ls header
+List general header information.
+.le
+.ls comments
+List the comments.
+.le
+.ls samples
+List the sample image lines.
+.le
+.ls x0
+List the spectra positions for the specified sample lines and spectra.
+.le
+.ls i0
+List the model profile scales for the specified sample lines and spectra.
+.le
+.ls s0, s1, or s2
+List the gauss5 model shape parameter s0, s1, or s2 for the specified sample
+lines and spectra.
+.le
+.ls gauss5
+List the gauss5 model parameters x0, i0, s0, s1, and s2 for the specified
+sample lines and spectra.
+.le
+.ls x0 spline
+List the spline evaluation of the spectra positions for the specified
+image lines and spectra.
+.le
+.ls s0 spline, s1 spline, or s2 spline
+List the spline evaluation of the gauss5 model shape parameters s0, s1, or s2
+for the specified image lines and spectra.
+.le
+.le
+.ls lines
+Lines to be listed. For the entries x0, i0, s0, s1, s2, and gauss5 the
+lines refer only to the sample image lines. For the spline entries the
+lines refer to the image lines at which the spline is to be evaluated.
+.le
+.ls spectra
+Spectra to be listed.
+.le
+.ls titles = no
+Print additional titles?
+.le
+.ih
+DESCRIPTION
+This task is a general MULTISPEC database listing tool. A keyword is selected
+and the referenced data is listed. Some entries require the specification of
+the desired sample or image lines and the desired spectra.
+.ih
+EXAMPLES
+To list the spectra positions for spectrum 3 at all the sample lines:
+
+ cl> mslist image x0 "*" 3
+
+To list the model profile scale parameter for sample line 1:
+
+ cl> mslist image i0 1 "*"
+
+To list the gauss5 model parameters for spectra 2 and 3 and sample lines 5
+and 7:
+
+ cl> mslist image gauss5 "5,7" "2-3" titles+
+.endhelp
diff --git a/noao/twodspec/multispec/doc/msplot.hlp b/noao/twodspec/multispec/doc/msplot.hlp
new file mode 100644
index 00000000..f08eac1b
--- /dev/null
+++ b/noao/twodspec/multispec/doc/msplot.hlp
@@ -0,0 +1,44 @@
+.help msplot Oct85 noao.twodspec.multispec
+.ih
+NAME
+msplot -- Plot data and model image line
+.ih
+USAGE
+msplot image line
+.ih
+PARAMETERS
+.ls image
+Image to be plotted.
+.le
+.ls line
+The image line to be plotted. Actually the nearest sample line will be
+plotted.
+.le
+.ls naverage = 20
+Number of image lines to average about the specified line.
+.le
+.ls lower = -10., upper = 10.
+Limits of the model profiles relative to the center of each profile.
+.le
+.ls graphics = "stdgraph"
+Graphics output device.
+.le
+.ls cursor = ""
+Graphics cursor input. If a file is given then the cursor input is taken
+from the file. If no file is given then the standard graphics cursor will
+be used.
+.le
+.ih
+DESCRIPTION
+A line of image data and the profile model for the line is graphed.
+The model is graphed with a dashed line. The graph may be then expanded,
+manipulated, and printed with the standard cursor mode commands.
+.ih
+EXAMPLES
+To plot the model fit for image sample for image line 400:
+
+ cl> msplot sample 400
+.ih
+SEE ALSO
+modellist
+.endhelp
diff --git a/noao/twodspec/multispec/doc/msset.hlp b/noao/twodspec/multispec/doc/msset.hlp
new file mode 100644
index 00000000..689e525a
--- /dev/null
+++ b/noao/twodspec/multispec/doc/msset.hlp
@@ -0,0 +1,104 @@
+.help msset Jul84 noao.twodspec.multispec
+.ih
+NAME
+msset -- Set entries in a MULTISPEC database
+.ih
+USAGE
+msset image keyword value
+.ih
+PARAMETERS
+.ls image
+Image in which the MULTISPEC database entries are to be modified or initialized.
+.le
+.ls keyword
+Keyword for the database entry to be set. The keywords are:
+.ls nspectra
+Set the number of spectra in the header.
+.le
+.ls comments
+Add comments lines to the database comment block.
+.le
+.ls x0
+Set the spectra positions for the specified sample lines and spectra.
+.le
+.ls i0
+Set the model profile central intensities for the specified sample lines
+and spectra.
+.le
+.ls s0, s1, or s2
+Set the gauss5 model shape parameter s0, s1, or s2 for the specified sample
+lines and spectra.
+.le
+.le
+.ls value
+Value to be used for value input.
+.le
+.ls lines = "*"
+Sample lines to be affected by value input.
+.le
+.ls spectra = "*"
+Spectra to be affected by value input.
+.le
+.ls read_list = no
+If yes use list input and if no use value input.
+.le
+.ls list = ""
+List for list input. See the description below for the appropriate format.
+.le
+.ih
+DESCRIPTION
+The entries in a MULTISPEC database associated with the image
+are modified or initialized.
+The parameters \fIimage\fR, \fIkeyword\fR, and \fIread_list\fR
+determine the database to be operated upon, the database entry to
+be set, and the input type. There are two forms of input;
+list input and value input.
+The input type is selected by the boolean parameter
+\fIread_list\fR. For list input the parameter \fIlist\fR
+is used and for value input the parameter \fIvalue\fR and
+possibly the parameters \fIlines\fR and \fIspectra\fR are used.
+The required parameters and input formats for the different keywords
+are outlined below.
+.ls nspectra
+For list input the list format is the number of spectra and
+for value input the \fIvalue\fR parameter is the number of spectra.
+.le
+.ls comments
+For list input the list format is lines of comments and for value
+input \fIvalue\fR parameter is a comment string.
+.le
+.ls x0, i0, s0, s1, s2
+For list input the list format is sample line, spectrum number, and
+parameter value
+and for value input \fIlines\fR is a range string selecting the
+sample lines to be affected, \fIspectra\fR is a range string selecting
+the spectra to be affected, and \fIvalue\fR is the value to be set for all
+the selected lines and spectra.
+.le
+.ih
+EXAMPLES
+To add several comments to the database by query:
+
+.nf
+ cl> msset image "comments" read_list+
+ Input list> First comment here.
+ Input list> Second comment here.
+ Input list> <eof>
+.fi
+
+where <eof> is the end of file character terminating the list.
+To set the value of s0 to 1 for all the spectra in sample line 1:
+
+ cl> msset image "s0" 1
+
+To set the spectra positions from a list:
+
+ cl> msset image "x0" read_list+ list=positionlist
+
+To add a single comment such as in a script:
+
+ cl> msset image "comments" "Comment here."
+.ih
+SEE ALSO
+findspectra mslist
+.endhelp
diff --git a/noao/twodspec/multispec/doc/multispec.ms b/noao/twodspec/multispec/doc/multispec.ms
new file mode 100644
index 00000000..cc17352e
--- /dev/null
+++ b/noao/twodspec/multispec/doc/multispec.ms
@@ -0,0 +1,532 @@
+.EQ
+delim $$
+.EN
+.TL
+The Multi-Spectra Extraction Package (multispec)
+.AU
+Francisco Valdes
+.AI
+IRAF Group
+.K2
+October 1984
+.NH
+Introduction
+.PP
+This document provides an introduction and overview of the multi-spectra
+extraction package \fBmultispec\fR. Detailed descriptions and usage
+information for the tasks of the package are available in the manual
+pages. The tasks in the package are:
+
+.TS
+center;
+n.
+findpeaks \&- Find the peaks
+fitfunction \&- Fit a function to the spectra parameter values
+fitgauss5 \&- Fit spectra profiles with five parameter Gaussian model
+modellist \&- List data and model pixel values
+msextract \&- Extract spectra
+mslist \&- List entries in a MULTISPEC database
+msplot \&- Plot a line of image and model data
+msset \&- Set entries in a MULTISPEC database
+newextraction \&- Create a new MULTISPEC extraction database
+newimage \&- Create a new multi-spectra image
+.TE
+
+.PP
+The \fBmultispec\fR package is a subpackage of the \fBtwodspec\fR package.
+It provides tools to locate, model, clean, correct for blending,
+and extract integrated or strip spectra from two dimensional, multi-spectra
+images. These tools may be used directly or combined in scripts to
+extract specific types of spectra or spectra from specific instruments.
+Examples of the latter usage are the tasks in the image reduction package
+\fBcryomap\fR.
+.PP
+The extraction of spectra consists of locating pixels along each
+image line which intersect the spectra and recording either the sum of
+the pixels, \fIintegrated spectra\fR (some times referred to as
+one-dimensional spectra), or the set of pixels,
+\fIstrip spectra\fR, for each line and for each spectrum as output.
+The size and limits of the intersection region are specified by the
+user relative to the centers of the spectra.
+The locations of the spectra in each image line are determined separately
+so that the spectra need not be aligned along the columns of the image nor
+be perfectly straight. However, since the extraction is done by image line,
+if the spectra are not aligned with the columns then the spectral resolution
+will be decreased. If the spectra are aligned with the image lines then
+the image should be rotated or transposed using \fBimtranspose\fR.
+.PP
+The \fBmultispec\fR extraction produces three dimensional images with
+one image band (the third dimension) for each extracted spectrum
+and one line (the second dimension) for each extracted image line.
+For integrated spectra there is only one column
+while for strip spectra, the number of columns is equal to the extraction
+strip width. The strips are aligned to the same positions relative to the
+spectra centers by image interpolation. If desired the output extractions can
+be reformated in a variety of ways.
+.PP
+In addition to direct extraction of the image spectra the \fBmultispec\fR
+package provides for modeling the spectrum profiles. The model
+may be extracted instead of the image spectra as either integrated or
+strip spectra. The model may be used to correct for blending of the spectra
+and to detect and replace bad pixels. The cleaning replaces data pixels which
+are discrepant from the model by the model values.
+.PP
+The modeling and cleaning features of the \fBmultispec\fR package can also
+be used for creating new multi-spectra images. In other words a new
+image is created containing cleaned or model spectra for selected
+lines.
+.PP
+Section 2 gives an overview of the \fBmultispec\fR package and the extraction
+process. The next section briefly describes the tasks in the package.
+This is followed by a description of the extraction database.
+The final section defines the model profiles used in the \fBmultispec\fR
+package.
+.NH
+Overview of the Multispec Package and the Extraction Process
+.PP
+The \fBmultispec\fR package consists of general and flexible tools
+for creating and manipulating databases which describe multi-spectra
+images. The contents of the databases are described in a later section.
+Each database is associated with a particular image and is referenced
+through the image name. The first positional argument in all the
+\fBmultispec\fR tasks is an image. In the current version of the package each
+database exists as a separate binary file with a filename formed by adding
+the extension '.db' to the image name. Note, however, that this
+need not be the case in the future.
+.PP
+The organization of the package as a set of tools operating on a database
+allows room for the package to evolve. Different algorithms may be
+designed for different types of multi-spectra images by using combinations
+of the existing tools and by adding new tools. The discussion below
+points out areas where new tasks might be added as well as citing the
+applicable existing tasks.
+.PP
+The extraction of spectra from a multi-spectra image consists of two
+basic steps; determining the locations of the spectra in the image and
+extracting the spectra. The positions of the spectra in a multi-spectra
+image are determined at a set of "sample" image lines. These positions
+are used to fit an interpolation function defining the spectrum positions
+at all the image lines. This function is then used in the extraction of
+the spectra.
+.PP
+The sample image lines are chosen by the user when the database is first
+created by the task \fBnewextraction\fR. An exception to this is when
+a template image is used (discussed below). However, in this case the
+sample image lines are still those chosen by the user when the template
+image database was created. The sample image lines may consist of
+anywhere from one image line to all the image lines. The purpose
+of the sample lines is to sample the image often enough to follow changes
+in the positions and shapes of the spectra but to still minimize the
+time spent in finding the spectra and the size of the database. The choice
+of sample lines also depends on the algorithm used to determine the
+positions of the spectra; a large number of sample
+lines for a fast, approximate method and a smaller number of lines
+for a complex and accurate method. For example, in order to deal with
+very blended spectra the task \fBfitgauss5\fR provides a sophisticated
+model fitting algorithm. This technique is computationally slow and, so,
+the user should not choose too many sample lines.
+.PP
+After the database has been created the minimum information needed
+for extraction is the spectrum positions at the sample lines. There
+are many ways in which the positions may be determined. Some
+possibilities are listed below.
+
+.IP (1)
+Enter the spectrum positions from a list using \fBmsset\fR. The
+list might be generated from a graphics/cursor task.
+This is method is very time consuming when the number of spectra and
+the number of images are large.
+.IP (2)
+Determine the spectrum positions automatically by finding the peaks in
+each sample image line. The task \fBfindpeaks\fR performs this function.
+.IP (3)
+Determine the spectrum positions at just one sample image line
+using either (1) or (2) and trace the spectra by a fast and refined
+peak finding method. Such a task is desirable but is not a part of the
+current package.
+.IP (4)
+Determine the spectrum positions at just one sample image line
+using either (1) or (2) and trace the spectra by fitting model
+spectrum profiles. The task \fBfitgauss5\fR does this using
+the model gauss5 described in section 5. Additional model fitting
+tasks can be added as needed.
+.IP (5)
+Use the positions determined for a previous image and, if necessary,
+refine the positions. \fBFitgauss5\fR is used to
+refine the spectrum positions at each sample line independently.
+
+.PP
+Several position finding algorithms may be used in stages to achieve
+the degree of accuracy required by the user.
+Thus, the first position determinations may be relatively crude and
+then, if needed, more sophisticated methods may be applied to refine the
+positions. The task \fBfindpeaks\fR is a crude peak finder. The positions
+are only determined to the nearest pixel. The task \fBfitgauss5\fR is
+a sophisticated model fitting techique which is used after \fBfindpeaks\fR
+first determines the approximate positions of the spectra.
+.PP
+The determination of the spectra locations may be performed independently
+at each sample line as in (1) and (2) above or the spectra locations may
+be traced starting from one sample line as in (3) and (4). The second method
+is preferable. Generally, \fBfindpeaks\fR is used at only one sample line
+to initially determine the number and approximate locations of the spectra.
+\fBFitgauss5\fR then fits model gauss5 to the spectrum profiles and
+the model solution is used at the next sample line as the starting
+point for the next model fit. In this manner the positions of
+the spectra are determined at the other sample image lines.
+.PP
+The results of the peak finding and profile fitting are improved
+by using an average of many image lines about the sample image line rather
+than just the sample image line by itself. Both \fBfindpeaks\fR and
+\fBfitgauss5\fR have this ablility.
+.PP
+It is often the case that several multi-spectra images have essentially
+the same format; i.e. the same image size, the same number of spectra,
+and the same positions (either approximately or identically).
+Commonly, one of the images is used for calibrations and has strong,
+high signal-to-noise spectra while the other images have weaker spectra.
+In this case it is not necessary to repeat the position determinations.
+The spectrum positions in one of the images, generally the one with
+the strong calibration spectra, are determined first. This image is
+then used as a "template" to provide the initial position estimates for
+the other images. If the positions are identical no further work is needed,
+otherwise, the positions can be refined to correct for small changes in the
+positions and shapes of the spectra.
+.PP
+The task \fBnewextraction\fR creates new databases. If a template image
+is specified then a copy is made of the template image database. This means
+that the number of spectra and the sample image lines remain the same.
+If the spectrum positions are slightly different from the template image
+then the task \fBfitgauss5\fR is used to determine the new positions.
+.PP
+The spectrum positons and possibly any model parameters are interpolated
+from the sample lines to the remaining image lines by fitting a function
+to values at the sample lines. In addition, the function fits may
+leave out poorly determined points and also smooth the values at the
+sample lines. The task \fBfitfunction\fR fits selected functions of
+specified order to the selected spectra and sample image lines.
+.PP
+The extraction of the spectra from multi-spectra images is performed by
+the task \fBmsextract\fR. The task extracts either integrated or strip
+spectra, either data or model values, with or without blending corrections,
+and with or without replacing bad pixels by model values.
+The user specifies the limits of the extraction
+strip as well as the spectra and image lines to be extracted.
+.PP
+For the simplest type of data extractions (basically strip extraction)
+no modeling is required. Other types of extractions, such as model
+extractions and/or with cleaning and blending corrections require some
+degree of modeling. There are two models which may be used;
+"smooth" and "gauss5". These models are described in section 5.
+The model parameters for model gauss5 must be set by \fBfitgauss5\fR
+before \fBmsextract\fR is used. Additional models may added for
+extraction as well as for the spectrum position determinations.
+.PP
+The model based features of \fBmsextract\fR -- model extractions
+and cleaning -- are available in the related task \fBnewimage\fR.
+This task creates new images which consist of either model spectra
+or cleaned data spectra.
+.PP
+The models in the \fBmultispec\fR package assume that the profiles
+go to zero; i.e. there is no background light. Background light
+may be removed using \fBbackground\fR. In the future a task will
+be provided create a mask defining the locations of the spectra from
+the database which can be used with general surface fitting tasks
+to create a background surface to be subtracted from the image.
+.PP
+The final step in using the \fBmultispec\fR package is to convert the
+extraction output to the desired format. This may include graphs,
+card image formats, and files for the \fBonedspec\fR and \fBlongslit\fR
+packages. Currently, the available formats are images and IIDS
+card images.
+.NH
+The Tasks of the Multispec Package
+.PP
+Use of the \fBmultispec\fR package begins with \fBnewextraction\fR and
+ends, usually, with \fBmsextract\fR. In between there are tasks which
+update, refine or change the database and tasks which provide diagnositic
+information. The informational tasks can be combined with tasks from
+other packages to produce tabular or graphical output. The task
+\fBmsplot\fR is an example. In this section a brief description of
+each task is given. Further information about the tasks, including usage,
+is available in the manual pages.
+.SH
+findpeaks
+.IP
+Selected sample image lines are examined to determine the number and
+column positions of data peaks in the line. An average of a number of image
+lines surrounding the sample lines is formed in which the local maxima
+are located. Various criteria are applied to cull the list of local
+maxima to the desired peaks. These criteria include a peak threshold,
+a maximum peak-to-peak contrast, a minimum peak separation, and a
+maximum number of peaks. This task is used to determine crude, initial
+estimates for the spectrum positions. It could be used alone for
+simple extractions.
+.SH
+fitfunction
+.IP
+This task has two roles. It's primary role is to define the
+interpolation/extrapolation function for the spectra
+positions between the sample lines. The fitting function can be
+either purely interpolative or may also provide smoothing of the
+parameters from the sample lines. The second role is to provide
+smoothing of the model parameters along the dispersion and the
+ability to replace bad values by the function fit to the remaining
+parameters. In this second role the user may iterate between
+smoothing and model fittng. The functions are always defined between
+the first and last image lines.
+.SH
+fitgauss5
+.IP
+The model profiles gauss5, described in section 5, are fit to the
+selected spectra and sample lines. The parameters to be determined
+and the fitting algorithm may also be selected.
+The model parameters are recorded in the database.
+The model may be tracked from a starting line to other sample image
+lines or each sample line may be fitted independently.
+This task is used to accurately determine the spectrum positions
+and provide an extraction model for heavily blended spectra.
+.SH
+modellist
+.IP
+For the selected sample image lines and image columns data
+and model values are listed. This task is used to check how well
+the model fitting tasks (currently just \fBfitgauss5\fR) have fit
+the sample image line. The task \fBmsplot\fR is used to produce
+graphical output.
+.SH
+msextract
+.IP
+This task does the actual extraction of spectra. It requires that
+the spectrum positions are defined by fitting functions in the
+database. If model gauss5 is to be used then the database must
+also contain the model parameters for the sample image lines. It
+extracts integrated or strip spectra, using data or model values,
+with or without blending corrections, and with or without cleaning
+of bad pixels.
+.SH
+mslist
+.IP
+Of the diagnositic or informational tasks \fBmslist\fR is the most
+general. The user selects the type of information from the database
+which is desired and it is then printed. The types of information
+include the database header, the database comments, the spectra
+positions and model parameter values for the sample lines, and the
+interpolation/smoothing function values for any desired set of
+image lines.
+.SH
+msplot
+.IP
+This task extracts data and models values and plots them superposed.
+This task is used as a diagnositic tool to inspect how well model fitting
+represents the image spectra.
+.SH
+msset
+.IP
+This task is a general tool for modifying or setting some of the quantities
+in the database. The quantity to be changed or set is
+selected by a keyword and the values are input in two ways;
+with a list structured parameter (such a file containing the list of
+values or the standard input) or as a parameter value. This task
+is the way a user may enter comments in the database or manually
+set the number and positions of the spectra. It is also used to
+set the initial values for the gauss5 model parameters s0, s1, and s2
+prior to using \fBfitgauss5\fR.
+.SH
+newextraction
+.IP
+This task has three important roles. First it creates the database
+associated with the multi-spectra image. Second, it defines the sample
+image lines to be used. The user can specify as many or as few sample lines
+as desired. It should be kept in mind that the more sample lines used
+the larger the database becomes and the longer the processing time when
+modeling the spectra. Finally, \fBnewextraction\fR allows
+a database from another image (called a template image) to initialize the
+database for the new multi-spectra image. The template image is generally
+a calibration image with strong, well-defined spectra.
+Initializing a database with a template image saves time, reduces problems
+with bad pixels, and is more accurate when an image with weak spectra is
+to be extracted.
+.SH
+newimage
+.IP
+This task is similar to \fBmsextract\fR; it uses the same algorithms
+and parameters. It differs in the type output.
+Rather than producing extracted integrated or strip spectra this task
+produces new image lines. It is particularly useful for extracting
+model images to be compared against the original image or to
+produce images which have been cleaned.
+.NH
+The Multispec Database
+.PP
+The tasks in the \fBmultispec\fR package create and manipulate a database.
+The database contains a description of the multi-spectra image which
+is modified, refined, examined, or otherwise used by the tasks in the package.
+In the current version the database is a separate binary file with a filename
+formed by appending ".db" to the image name described by the database.
+.PP
+The database contains four basic types of data; general information,
+comments and history, position parameters, and model parameters.
+The data in the database is examined with the task \fBmslist\fR.
+The general information section, called the database header, contains the
+the name of the image, the size of the image, and the number of spectra in
+the image. Once the number of spectra in the image has
+been entered in the database it is an error to attempt to change this
+number. The database must be deleted and a new database created in order
+to change the number of spectra.
+.PP
+The comment and history section of the database contains text
+strings. Each task which modifies the contents of the database places
+a dated history line in this section. The user may also add comments
+with \fBmsset\fR. Currently this information is not passed on to
+the extraction output.
+.PP
+There are three types of position information in the database. The
+first is a set of sample image lines. The sample lines are set when
+the database is created by \fBnewextraction\fR. The sample lines select
+which image lines from the multi-spectra image are to be examined and used
+during the extraction. Information from these sample lines, and only
+these sample lines, is entered in the database. The sample lines
+may be listed with \fBmslist\fR.
+.PP
+The second type of position information is the positions of the
+spectra (centers) at each sample line. These positions are initially
+set by either \fBfindpeaks\fR or, manually, by \fBmsset\fR. The
+position information is refined by fitting model profiles.
+.PP
+The third type of position information is a function fit to the
+positions from all the sample lines for each spectrum.
+These function fits are produced by \fBfitfunction\fR.
+The functions define the positions of the spectra at all the image
+lines. The spectra positions at the sample lines or the function
+evaluation for any image line may be listed with \fBmslist\fR.
+.PP
+The finally type of basic data contained in the database are
+model parameter values. A model need not be used in the extraction
+but if one is used then the parameters determining the model profiles
+are recorded in the database. The specific parameters depend on the
+model. Currently the only model is \fIgauss5\fR. The model and its
+parameters are described in section 5.
+.PP
+As with the spectra positions the parameters are stored in the database
+in two forms; as values for each spectrum at each sample image line
+and as function fits to the values at the sample lines which interpolate
+them to any image line. The sample line values are
+set by the model fitting tasks and the function fits are set by
+\fBfitfunction\fR. The parameter values at the sample lines or the
+function evaluations for any image lines may be listed with \fBmslist\fR.
+.NH
+Multispec Spectrum Profile Models
+.PP
+The spectra profiles in the image are modeled for many reasons:
+To provide accurate, subpixel position determinations, to extract model
+spectra or model images, to detect and replace bad pixels, and
+to estimate and correct for blending between the spectra.
+There are currently two models used in the \fBmultispec\fR package, "gauss5"
+and "smooth".
+.NH 2
+Model Gauss5
+.PP
+The gauss5 model profiles are Gaussian but with a scale which varies
+smoothly between the center and the edge of the profile. There
+are five parameters:
+
+.RS
+.IP x0
+The column position in the image line of the center of the profile.
+.IP i0
+The intensity scale of the profile. It corresponds to the intensity
+of the center of the profile.
+.IP s0
+The zeroth order, constant, term in the Gaussian scale.
+.IP s1
+The even first order term in the Gaussian scale.
+.IP s2
+The odd first order term in the Gaussian scale.
+.RE
+
+.PP
+The mathematical form of the the model is shown in equation (1):
+.EQ (1)
+roman profile (x)~=~i0 exp~left { -s( DELTA x )~DELTA x sup 2 right }
+.EN
+where
+.EQ
+DELTA x ~=~x~-~x0~,
+.EN
+.EQ
+s( DELTA x)~=~s0~+~s1~|y| +~s2~y~,
+.EN
+and
+.EQ
+y~=~ DELTA x / ( DELTA x sup 2 + alpha ) sup half ~.
+.EN
+The profile is defined within the user specified limits \fIlower\fR and
+\fIupper\fR measured relative to the the profile center and
+$alpha~=~(upper-lower)/4$. The quantity $y$ lies in the range
+-1 to 1 over the interval in which the profile is defined. The odd
+and even terms, s1 and s2, allow for symmetric and antisymmetric profile
+changes relative to a simple Gaussian profile.
+.PP
+The task \fBfitgauss5\fR fits the gauss5 model to the spectrum profiles in
+the sample image lines to determine one or more of the model parameters for
+each spectrum. The parameter values are stored in the database for the image.
+In \fBmsextract\fR the model profiles for each
+image line are obtained by interpolating the profile shapes from the sample
+lines (with the model parameters in the database determined by
+\fBfitgauss5\fR) and then fitting only the intensity scale "i0".
+There are a number of technical details associated with the model fitting
+in each of these tasks which are discussed in the manual pages.
+.PP
+The gauss5 model is used to accurately determine the positions of the
+spectrum centers at the sample image lines. Fitting simultaneously
+for the model parameters allows the spectra to be blended.
+This is the chief advantage of this model.
+This model is also used during extraction to correct for blending of
+the spectra and to detect and replace bad pixels.
+.NH 2
+Model Smooth
+.PP
+The spectrum profiles from the lines immediately preceeding
+the image line in which the spectrum profile is to be fit are shifted
+to a common center and averaged to form the model profile.
+An intensity scale factor is then determined which best fits the model
+profile to the image profile. This is done for each spectrum in the
+image. The scale factors are determined by least squares with
+possible bad pixel rejection. Rejected pixels are eliminated
+when the image line is later used in forming new average model profiles.
+.PP
+The advantages of this model are that the image spectrum profiles may
+have any shape and the least squares fitting with bad pixel rejection
+is fast and rigorous. By passing through the image lines sequentially
+the image lines need be accessed only once and the profile averages
+can be quickly updated for the next image line.
+.PP
+The disadvantages of this model are that the spectrum profiles cannot
+be blended and the model does not measure profile positions.
+This means that the spectrum profile positions must be
+known. This model is suitable for model extractions and cleaning of
+bad pixels in unblended multi-spectra images. It is available in
+the task \fBmsextract\fR.
+.bp
+.SH
+Glossary
+.LP
+\fBmultispec\fR
+.IP
+Acronym for Multi-Spectra Extraction as in \fBmultispec\fR Package.
+.LP
+integrated spectra
+.IP
+The spectra are extracted by integrating the pixel values across the spectrum
+to produce a single aperture luminosity value.
+.LP
+sample image line
+.IP
+The spectra positions and model profile shapes are determined at a set
+of image lines selected when the database is created.
+.LP
+strip spectra
+.IP
+The spectra are extracted as a strip of fixed with the spectra shifted by
+image interpolation to a common center.
diff --git a/noao/twodspec/multispec/doc/newextract.hlp b/noao/twodspec/multispec/doc/newextract.hlp
new file mode 100644
index 00000000..37123f28
--- /dev/null
+++ b/noao/twodspec/multispec/doc/newextract.hlp
@@ -0,0 +1,61 @@
+.help newextraction Jul84 noao.twodspec.multispec
+.ih
+NAME
+newextraction -- Initialize a new MULTISPEC extraction
+.ih
+USAGE
+newextraction image template
+.ih
+PARAMETERS
+.ls image
+Image to be extracted.
+.le
+.ls template
+The previously created database for the template image is used to initialize
+the new database. If the null string is given then the database is not
+initialized.
+.le
+.ls sample_lines = "10x50"
+Sample image lines in which the spectra positions are to be determined and,
+optionally, modeled. This parameter is not used if a template image is given.
+.le
+.ih
+DESCRIPTION
+To extract the spectra from a multi-spectra image a database must be created
+and associated with the image. This task creates the database with a name
+formed by adding the extension '.db' and initializes some of the database
+entries.
+
+The sample lines are used to track the spectra positions and, if an analytic
+profile model is to be fit to the spectra, to map profile shape changes.
+The image lines only need be sampled enough to track \fInon-linear\fR position
+distortions and significant profile shape changes since interpolation
+is used between the sample lines. Though specifying just one sample
+line is allowed using at least two sample lines is recommended to allow for
+any slope in the position of the spectra. Specifying all the image lines
+will greatly increase the processing time and is never justified.
+
+Using a previous database to initialize the new database is useful if the
+new image is only slightly different in the positions and profiles of the
+spectra. In some cases extraction may proceed immediately without any
+further position determination and modeling. Further modeling
+and spectra position determinations will refine the previously determined
+parameters with an increase in execution time. Using a template image is
+particularly important if the first image extracted has strong spectra
+and subsequent images have much weaker spectra since the automatic spectra
+position location and profile modeling may yield poor results for very weak
+spectra.
+.ih
+EXAMPLES
+To initialize a MULTISPEC database for extracting the spectra in
+the image \fIimage1\fR:
+
+ cl> newextraction image1 ""
+
+To create a new MULTISPEC database for extracting the spectra in
+the image \fIimage2\fR using \fIimage1\fR as a template image:
+
+.nf
+ cl> newextraction image2 image1
+.fi
+.endhelp
diff --git a/noao/twodspec/multispec/doc/newimage.hlp b/noao/twodspec/multispec/doc/newimage.hlp
new file mode 100644
index 00000000..1ef7fbe0
--- /dev/null
+++ b/noao/twodspec/multispec/doc/newimage.hlp
@@ -0,0 +1,130 @@
+.help newimage Jul84 noao.twodspec.multispec
+.ih
+NAME
+newimage -- Create a new multi-spectra image
+.ih
+USAGE
+newimage image output
+.ih
+PARAMETERS
+.ls image
+Image to be used to create the new image.
+.le
+.ls output
+Filename for the new multi-spectra image.
+.le
+.ls lower = -10
+Lower limit for model profiles. It is measured in pixels from the
+spectra centers defined by the position functions in the database.
+.le
+.ls upper = -10
+Upper limit for model profiles. It is measured in pixels from the
+spectra centers defined by the position functions in the database.
+.le
+.ls lines = "*"
+Image lines of the multi-spectra image to be in the new multi-spectra image.
+.le
+.ls ex_model = no
+Create a model image?
+.le
+.ls clean = yes
+Replace bad pixels with model values? The following parameters are used:
+.ls nreplace = 1000.
+Maximum number of pixels to be replaced per image line when cleaning with
+model \fIgauss5\fR or maximum number of pixels to be replaced per spectrum when
+cleaning with model \fIsmooth\fR.
+.le
+.ls sigma_cut = 4.
+The cleaning threshold in terms of the predicted pixel sigma.
+.le
+.ls niterate = 1
+Maximum number of cleaning iterations per line when cleaning with model
+\fIgauss5\fR.
+.le
+.le
+.ls model = "smooth"
+Choice of \fIgauss5\fR or \fIsmooth\fR. Minimum match abbreviation is
+allowed. This parameter is required only if \fIex_model\fR = yes
+or \fIclean\fR = yes.
+.le
+.ls fit_type = 2
+Model fitting algorithm for model \fIgauss5\fR.
+.le
+.ls naverage = 20
+Number of lines to be averaged in model \fIsmooth\fR.
+.le
+.ls interpolator = "spline3"
+Type of image interpolation function to be used.
+The choices are "nearest", "linear", "poly3", "poly5", and "spline3".
+Minimum match abbreviation is allowed.
+.le
+.ls verbose = no
+Print verbose output?
+.le
+.ih
+DESCRIPTION
+A new multi-spectra image is created using the description of the
+multi-spectra image in the MULTISPEC database associated with \fIimage\fR.
+The user selects the image \fIlines\fR from the original image to be in
+the new image. The options allow the creation of model images or images in
+which the bad or deviant pixels are replaced by model profile values.
+
+If \fIex_model\fR = yes or \fIclean\fR = yes model
+spectra are fit to the spectra in the image. There are two models:
+a five parameter Gaussian profile called \fIgauss5\fR and profiles obtained
+by averaging \fInaverage\fR image lines surrounding the image line being
+modeled called \fIsmooth\fR. The model is selected with the parameter
+\fImodel\fR.
+
+When \fIex_model\fR = yes an image containing model spectra is produced.
+
+When \fIclean\fR = yes pixels with large residuals from the model are
+detected and removed from the model fit. The selected model is
+fit to the pixels which are not in the bad pixel list (not yet implemented)
+and which have not been removed from the model fit. The sigma of the fit
+is computed. Deviant pixels are detected by comparing them to the model
+to determine if they differ by more than \fIsigma_cut\fR times the sigma.
+The model fit is iterated, removing deviant pixels at each iteration, until
+no more pixels are found deviant or \fInreplace\fR pixels have been found.
+The pixels removed or in the bad pixel list are then replaced with
+model values. (To clean and extract the spectra with this algorithm see
+\fBmsextract\fR.)
+
+There are some technical differences in the model fitting and cleaning
+algorithms for the two models. In model \fIsmooth\fR
+the fit for the profile scale factors is done independently for each spectrum
+and automatically corrected when a bad pixel is detected. This fitting process
+is fast and rigorous. The parameter \fInreplace\fR in this model refers to
+the maximum number of pixels replaced \fIper spectrum\fR.
+
+In model \fIgauss5\fR, however, the profile scale factors are fit
+to the entire image line (hence its ability to fit blended spectra).
+There are two fitting algorithms; a rigorous simultaneous fit
+and an approximate method. The simultaneous fit is selected when
+\fIfit_type\fR = 1. This step is relatively slow. The
+alternative method of \fIfit_type\fR = 2 sets the scale factor for each
+spectrum by taking the median scale, where scale = data / model profile,
+for the three pixels nearest the center of the profile. The median
+minimizes the chance of a large error due to a single bad pixel. This
+scale may be greatly in error in the case of extreme blending but is also
+quite fast; the extraction time is reduced by at least 40%.
+The steps of profile fitting and deviant pixel detection are alternated
+and the maximum number of iterations through these two steps is
+set by \fIniterate\fR. The default of 1 means that the model fitting is not
+repeated after detecting deviant pixels.
+
+The option \fIverbose\fR can be used to print the image lines being extracted
+and any pixels replaced by the cleaning process.
+.ih
+EXAMPLES
+To create a cleaned version of the image using model \fIsmooth\fR for cleaning:
+
+ cl> newimage image newimage
+
+To create an model image using model \fIgauss5\fR:
+
+ cl> newimage image newimage ex_model=yes model="gauss5"
+.ih
+SEE ALSO
+msextract
+.endhelp
diff --git a/noao/twodspec/multispec/exgauss5.x b/noao/twodspec/multispec/exgauss5.x
new file mode 100644
index 00000000..5c009239
--- /dev/null
+++ b/noao/twodspec/multispec/exgauss5.x
@@ -0,0 +1,100 @@
+include <imhdr.h>
+include "ms.h"
+
+
+# EX_GAUSS5 -- Extract spectra using the GAUSS5 model.
+#
+# This procedure is called either by t_extract to extract spectra (either
+# integrated or strip) or by t_newimage to extract a new image (either
+# model or cleaned data). It is called only if model GAUSS5 must be used
+# for cleaning, blending corrections, or model extraction.
+
+procedure ex_gauss5 (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, ex_integral)
+
+pointer ms # MULTISPEC pointer
+pointer im_in # Input image descriptor
+pointer im_out # Output image descriptor
+int spectra[ARB] # Spectra range list
+int lines[ARB] # Line range list
+real lower # Lower limit of strip
+real upper # Upper limit of strip
+bool ex_spectra # Extract spectra or image line
+bool ex_model # Extract model or data
+bool ex_integral # Extract integrated spectra or strip
+
+int len_line, len_profile, nspectra, nparams
+int line_in, line_out
+pointer data, data_in, data_out
+pointer sp, model, profiles, ranges, data_profiles
+
+int get_next_number()
+pointer imgl2r(), impl2r()
+
+begin
+ # Set array size variables.
+ len_line = MS_LEN(ms, 1)
+ nspectra = MS_NSPECTRA(ms)
+ nparams = MS_NGAUSS5
+ len_profile = nint (upper - lower + 2)
+
+ # Allocate and setup necessary arrays.
+ call smark (sp)
+ call salloc (model, len_line, TY_REAL)
+ call salloc (ranges, nspectra * LEN_RANGES * 3, TY_REAL)
+ call salloc (profiles, len_profile * nspectra * nparams * 3, TY_REAL)
+ call salloc (data_profiles, len_profile * nspectra, TY_REAL)
+
+ # Initialize ranges arrays.
+ Memr[ranges] = INDEFR
+
+ # Loop through the input lines and write an output line for each
+ # input line.
+ line_in = 0
+ line_out = 0
+ while (get_next_number (lines, line_in) != EOF) {
+ line_out = line_out + 1
+ call ex_prnt2 (line_in, line_out)
+
+ # Get the multi-spectra image data.
+ data = imgl2r (im_in, line_in)
+
+ # Get the GAUSS5 model profiles using interpolation between the
+ # sample lines.
+ call int_gauss5 (ms, lower, Memr[profiles], Memr[ranges],
+ len_profile, nspectra, nparams, line_in)
+
+ # Iteratively fit the profile scales to the data and replace
+ # deviant pixels by model values.
+ call fit_and_clean (ms, Memr[data], Memr[model], Memr[ranges],
+ Memr[profiles], len_line, len_profile, nspectra, nparams)
+
+ # Unblend data spectra only if needed.
+ if (ex_spectra && !ex_model)
+ call unblend (Memr[data], Memr[data_profiles], Memr[model],
+ Memr[profiles], Memr[ranges], len_line, len_profile,
+ nspectra)
+
+ if (!ex_spectra) {
+ # Output a new model or data image line.
+ data_out = impl2r (im_out, line_out)
+ if (ex_model)
+ data_in = model
+ else
+ data_in = data
+ call amovr (Memr[data_in], Memr[data_out], len_line)
+ } else {
+ # Output either model or data extracted spectra.
+ if (ex_model)
+ data_in = profiles
+ else
+ data_in = data_profiles
+ call ex_out (im_out, line_out, spectra, lower, upper,
+ Memr[ranges], Memr[data_in], len_profile, nspectra,
+ ex_integral)
+ }
+ }
+
+ # Free allocated memory.
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/exsmooth.x b/noao/twodspec/multispec/exsmooth.x
new file mode 100644
index 00000000..f092529a
--- /dev/null
+++ b/noao/twodspec/multispec/exsmooth.x
@@ -0,0 +1,107 @@
+include <imhdr.h>
+include <math/interp.h>
+include "ms.h"
+
+# EX_SMOOTH -- Extract spectra using the SMOOTH model.
+# FIT_PROFILES -- Get SMOOTH profiles and fit the profiles to the data while
+# replacing deviant pixels by model profile values.
+
+
+# EX_SMOOTH -- Extract spectra using the SMOOTH model.
+#
+# This procedure is called either by t_extract to extract spectra (either
+# integrated or strip) or by t_newimage to extract a new image (either
+# model or cleaned data). It is called only if model SMOOTH must be used
+# for cleaning or model extraction. It outputs the extracted spectra to
+# the output image file. Note that this task does CLIO.
+
+procedure ex_smooth (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, ex_integral)
+
+pointer ms # MULTISPEC pointer
+pointer im_in # Input image descriptor
+pointer im_out # Output image descriptor
+int spectra[ARB] # Spectra range list
+int lines[ARB] # Line range list
+real lower # Lower limit of strips
+real upper # Upper limit of strips
+bool ex_spectra # Extract spectra or image line?
+bool ex_model # Extract model or data?
+bool ex_integral # Extract integrated or strip spectra?
+
+# User input parameters:
+int nlines # Lines to average for smooth model
+int interpolator # Line interpolator type
+
+int len_line, nspectra, len_profile, len_profiles
+int line_in, line_out
+pointer sp, data, data_in, data_out, model, ranges, profiles, coeff
+
+int clgeti(), get_next_number(), clginterp()
+pointer impl2r()
+
+begin
+ # Get parameters for model SMOOTH.
+ nlines = clgeti ("naverage") + 1
+ interpolator = clginterp ("interpolator")
+
+ # Set array lengths.
+ len_line = IM_LEN(im_in, 1)
+ nspectra = MS_NSPECTRA(ms)
+ len_profile = nint (upper - lower + 1)
+ len_profiles = len_profile * nspectra
+
+ # Allocate working memory.
+ call smark (sp)
+ call salloc (data, len_profiles, TY_REAL)
+ call salloc (model, len_profiles, TY_REAL)
+ call salloc (ranges, nspectra * LEN_RANGES, TY_REAL)
+ call salloc (profiles, len_profiles * (nlines + 1), TY_REAL)
+ call salloc (coeff, 2 * len_line + SZ_ASI, TY_REAL)
+
+ # Initialize ranges and interpolation arrays.
+ call amovkr (lower, Memr[ranges + (DX_START-1)*nspectra], nspectra)
+ call asiset (Memr[coeff], interpolator)
+
+ # Get fit position functions from the database.
+ call msgfits (ms, X0_FIT)
+
+ # Loop through the input image lines and write output line.
+ line_in = 0
+ line_out = 0
+ while (get_next_number (lines, line_in) != EOF) {
+ line_out = line_out + 1
+ call ex_prnt2 (line_in, line_out)
+
+ # Get the SMOOTH profiles and the data for the input line.
+ call set_smooth (ms, im_in, line_in, Memr[ranges], Memr[profiles],
+ Memr[coeff], len_profile, nspectra, nlines, Memr[data],
+ Memr[model])
+
+ # Fit and clean the data and model.
+ call fit_smooth (line_in, Memr[data], Memr[model],
+ Memr[profiles], len_profile, nspectra, nlines)
+
+ # Select model or data to be output.
+ if (ex_model)
+ data_in = model
+ else
+ data_in = data
+
+ if (ex_spectra) {
+ # Extract model or data spectra.
+ call ex_out (im_out, line_out, spectra, lower, upper,
+ Memr[ranges], Memr[data_in], len_profile, nspectra,
+ ex_integral)
+ } else {
+ # Extract model or data image line.
+ data_out = impl2r(im_out, line_out)
+ call set_model1 (ms, line_in, Memr[data_in], Memr[coeff],
+ Memr[ranges], len_line, len_profile, nspectra,
+ Memr[data_out])
+ }
+ }
+
+ # Free allocated memory.
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/exstrip.x b/noao/twodspec/multispec/exstrip.x
new file mode 100644
index 00000000..a114b5a8
--- /dev/null
+++ b/noao/twodspec/multispec/exstrip.x
@@ -0,0 +1,203 @@
+include <imhdr.h>
+include <math/interp.h>
+include "ms.h"
+
+# EX_STRIP -- Simple strip extraction of spectra.
+# EX_STRIP1 -- Extract integrated spectra.
+# EX_STRIP2 -- Extract two dimensional strip spectra.
+
+
+# EX_STRIP -- Simple strip extraction of spectra.
+#
+# This procedure is called either by t_extract to extract spectra (either
+# integrated or strip) or by t_newimage to extract a new image.
+# Since there is no modeling only data spectra or image lines are extracted.
+# It outputs the extracted spectra or image lines to the output image file.
+
+procedure ex_strip (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, ex_integral)
+
+pointer ms # MULTISPEC pointer
+pointer im_in # Input image descriptor
+pointer im_out # Output image descriptor
+int spectra[ARB] # Spectra range list
+int lines[ARB] # Line range list
+real lower # Lower limit of strips
+real upper # Upper limit of strips
+bool ex_spectra # Extract spectra or image line
+bool ex_model # Extract model or data
+bool ex_integral # Extract integrated spectra or strip
+
+int line_in, line_out
+pointer data_in, data_out
+
+int get_next_number()
+pointer imgl2r(), impl2r()
+
+begin
+ if (ex_model)
+ call error (MS_ERROR, "Can't extract model")
+
+ if (ex_spectra) {
+ # Extract spectra using ex_strip1 for integrated spectra and
+ # ex_strip2 for strip spectra.
+ if (ex_integral)
+ call ex_strip1 (ms, im_in, im_out, spectra, lines, lower,
+ upper)
+ else
+ call ex_strip2 (ms, im_in, im_out, spectra, lines, lower,
+ upper)
+ } else {
+ # Create a new multi-spectra image by copying the selected
+ # input image lines to the output image.
+ line_in = 0
+ line_out = 0
+ while (get_next_number (lines, line_in) != EOF) {
+ line_out = line_out + 1
+ data_in = imgl2r (im_in, line_in)
+ data_out = impl2r (im_out, line_out)
+ call amovr (Memr[data_in], Memr[data_out], IM_LEN(im_out, 1))
+ }
+ }
+end
+
+# EX_STRIP1 -- Extract integrated spectra.
+#
+# For each spectrum in the spectra range list and for each line in
+# the line range list the pixels between lower and upper (relative
+# to the spectrum center) are summed.
+# The spectra positions are obtained from the MULTISPEC database.
+
+procedure ex_strip1 (ms, im_in, im_out, spectra, lines, lower, upper)
+
+pointer ms # MULTISPEC pointer
+pointer im_in # Input image descriptor
+pointer im_out # Output image descriptor
+int spectra[ARB] # Spectra range list
+int lines[ARB] # Line range list
+real lower # Lower limit of strips
+real upper # Upper limit of strips
+
+int line_in, line_out, spectrum_in, spectrum_out
+real x_center, x_start, x_end
+pointer buf_in, buf_out
+
+real sum_pixels(), cveval()
+int get_next_number()
+pointer imgl2r(), impl3r()
+
+begin
+ # Get fit functions for spectra positions.
+ call msgfits (ms, X0_FIT)
+
+ # Loop through the input lines and write integrated spectra out.
+ line_in = 0
+ line_out = 0
+ while (get_next_number (lines, line_in) != EOF) {
+ line_out = line_out + 1
+
+ # Get the input data line.
+ buf_in = imgl2r (im_in, line_in)
+
+ # Loop the the spectra, calculate the integrated luminosity and
+ # write it to the output image.
+ spectrum_in = 0
+ spectrum_out = 0
+ while (get_next_number (spectra, spectrum_in) != EOF) {
+ spectrum_out = spectrum_out + 1
+
+ buf_out = impl3r (im_out, line_out, spectrum_out)
+
+ # Determine the spectrum limits from spectrum center position.
+ x_center = cveval (CV(ms, X0_FIT, spectrum_in), real (line_in))
+ x_start = max (1., x_center + lower)
+ x_end = min (real (IM_LEN(im_in, 1)), x_center + upper)
+ Memr[buf_out] =
+ sum_pixels (Memr[buf_in], x_start, x_end)
+ }
+ }
+end
+
+# EX_STRIP2 -- Extract two dimensional strip spectra.
+#
+# Each line in the range list is fit by an image interpolator and then for
+# each spectrum in spectra range list the interpolator values between lower
+# and upper (relative to the spectrum center) are written to a three
+# dimensional image. There is one band for each spectrum. The spectra
+# positions are obtained from the MULTISPEC database.
+# The procedure requests the interpolator type using CLIO.
+
+procedure ex_strip2 (ms, im_in, im_out, spectra, lines, lower, upper)
+
+pointer ms # MULTISPEC pointer
+pointer im_in # Input image descriptor
+pointer im_out # Output image descriptor
+int spectra[ARB] # Spectra range list
+int lines[ARB] # Line range list
+real lower # Lower limit of strip
+real upper # Upper limit of strip
+
+int interpolator # Array interpolar type
+
+int i, len_in, len_out, line_in, line_out, spectrum_in, spectrum_out
+real x, x_start
+pointer buf_in, buf_out
+pointer sp, coeff
+
+int get_next_number(), clginterp()
+real asival(), cveval()
+pointer imgl2r(), impl3r()
+errchk salloc, imgl2r, impl3r
+errchk asiset, asifit, asival, clginterp
+
+begin
+ # Get the image interpolator type.
+ interpolator = clginterp ("interpolator")
+
+ len_in = IM_LEN (im_in, 1)
+ len_out = nint (upper - lower + 1)
+
+ # Set up the interpolator coefficient array.
+ call smark (sp)
+ call salloc (coeff, 2 * len_in + SZ_ASI, TY_REAL)
+ call asiset (Memr[coeff], interpolator)
+
+ # Get the spectra position functions from the database.
+ call msgfits (ms, X0_FIT)
+
+ # Loop through the input lines, do the image interpolation and write
+ # the strip spectra to the output.
+ line_in = 0
+ line_out = 0
+ while (get_next_number (lines, line_in) != EOF) {
+ line_out = line_out + 1
+
+ # Get the input data and fit an interpolation function.
+ buf_in = imgl2r (im_in, line_in)
+ call asifit (Memr[buf_in], len_in, Memr[coeff])
+
+ # Loop through the spectra writing the strip spectra.
+ spectrum_in = 0
+ spectrum_out = 0
+ while (get_next_number (spectra, spectrum_in) != EOF) {
+ spectrum_out = spectrum_out + 1
+ buf_out = impl3r (im_out, line_out, spectrum_out)
+
+ # Determine the starting position for the strips and
+ # evaluate the interpolation function at each point in
+ # the strip.
+ x_start = cveval (CV(ms, X0_FIT, spectrum_in), real (line_in)) +
+ lower
+ do i = 1, len_out {
+ x = x_start + i - 1
+ if ((x < 1) || (x > len_in))
+ Memr[buf_out + i - 1] = 0.
+ else
+ Memr[buf_out + i - 1] = asival (x, Memr[coeff])
+ }
+ }
+ }
+
+ # Free interpolator memory.
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/findpeaks.par b/noao/twodspec/multispec/findpeaks.par
new file mode 100644
index 00000000..04d00e1a
--- /dev/null
+++ b/noao/twodspec/multispec/findpeaks.par
@@ -0,0 +1,13 @@
+# FINDPEAKS
+
+image,f,a,,,,Image to be searched
+lines,s,a,,,,Images lines to be searched for peaks
+contrast,r,a,,,,Maximum contrast between peak values
+separation,i,h,5,,,Minimum separation between peaks
+edge,i,h,0,0,,Minimum separation from the image edge
+threshold,r,h,0.,,,Minimum peak threshold for selecting peaks
+min_npeaks,i,h,1,,,Minimum number of peaks to be found
+max_npeaks,i,h,1000,,,Maximum number of peaks to be found
+columns,s,h,"*",,,Image columns to be searched for peaks
+naverage,i,h,20,,,Number of image lines to average
+debug,b,h,no,,,Print debugging information?
diff --git a/noao/twodspec/multispec/fitclean.x b/noao/twodspec/multispec/fitclean.x
new file mode 100644
index 00000000..548f2cf4
--- /dev/null
+++ b/noao/twodspec/multispec/fitclean.x
@@ -0,0 +1,257 @@
+include "ms.h"
+
+# FIT_AND_CLEAN -- Iteratively fit profile scales using banded matrix method
+# and remove deviant pixels.
+#
+# The profile fitting and cleaning are combined in order to minimize
+# the calculations in re-evaluating the least squares fit after rejecting
+# deviant pixels.
+#
+# The sigma of the fit is calculated and deviant pixels are those whose
+# residual is more than +-sigma_cut * sigma.
+# The maximum number of pixels to be replaced is max_replace.
+# If max_replace is zero then only the model fitting is performed.
+#
+# The output of this routine are the cleaned data profiles and the
+# least-square fitted model profiles. Return the number of pixels replaced.
+
+
+procedure fit_and_clean (ms, data, model, ranges, profiles, len_line,
+ len_profile, nspectra, nparams)
+
+pointer ms # MULTISPEC data structure
+real data[len_line] # Input data to be fit
+real model[len_line] # Output model line
+real ranges[nspectra, LEN_RANGES, 3] # Profile ranges
+real profiles[len_profile, nspectra, nparams, 3] # Model profiles
+int len_line # Length of data/model line
+int len_profile # Length of each profile
+int nspectra # Number of spectra
+int nparams # Number model parameters
+
+int max_iterate # Maximum number of iterations
+int max_replace # Maximum number of bad pixels
+real sigma_cut # Rejection cutoff
+int fit_type # Type of I0 fitting
+bool ex_model # Extract model?
+
+bool exmod
+int i_max, nmax, option, npts
+int i, iteration, n_total, n_reject
+real sigma, lower, upper, residual, resid_min, resid_max
+
+begin
+ # Initialize the model and I0 parameters to zero.
+ call aclrr (PARAMETER(ms,I0,1), nspectra)
+ call aclrr (model, len_line)
+
+ # Loop until no further deviant pixels are found.
+ n_total = 0
+
+ do iteration = 1, imax {
+ # Determine I0 for each profile.
+ switch (option) {
+ case 1:
+ call full_solution (ms, data, model, ranges, profiles,
+ len_line, len_profile, nspectra, nparams)
+ case 2:
+ call quick_solution (ms, data, ranges, profiles, len_line,
+ len_profile, nspectra)
+ }
+
+ # Set the model to be used to compare against the data.
+ call set_model (ms, model, profiles, ranges, len_line, len_profile,
+ nspectra)
+
+ # If number of pixels to reject is zero then skip below.
+ n_reject = 0
+ if (n_total == nmax)
+ break
+
+ # Compute sigma of fit.
+ sigma = 0.
+ npts = 0
+ do i = 1, len_line {
+ if ((model[i] > 0.) && (!IS_INDEFR (data[i]))) {
+ sigma = sigma + (data[i] - model[i]) ** 2
+ npts = npts + 1
+ }
+ }
+ sigma = sqrt (sigma / npts)
+ resid_min = -lower * sigma
+ resid_max = upper * sigma
+
+ # Compare each pixel against the model and set deviant pixels
+ # to INDEFR. If the number of pixels replaced is equal to the
+ # maximum allowed stop cleaning. Ignore points with model <= 0.
+ # Thus, points outside the spectra will not be cleaned.
+ # Ignore INDEFR pixels.
+ do i = 1, len_line {
+ if (n_total == nmax)
+ break
+ if ((model[i] <= 0.) || (IS_INDEFR (data[i])))
+ next
+
+ # Determine deviant pixels.
+ residual = data[i] - model[i]
+ if ((residual < resid_min) || (residual > resid_max)) {
+ # Flag deviant pixel.
+ data[i] = INDEFR
+ n_total = n_total + 1
+ n_reject = n_reject + 1
+ }
+ }
+
+ if (n_reject == 0)
+ break
+ }
+ # Refit model if a model extraction is desired and bad pixels were
+ # in the last fit.
+ if (exmod && n_reject != 0) {
+ switch (option) {
+ case 1:
+ call full_solution (ms, data, model, ranges, profiles,
+ len_line, len_profile, nspectra, nparams)
+ case 2:
+ call quick_solution (ms, data, ranges, profiles, len_line,
+ len_profile, nspectra)
+ }
+ }
+
+ # Scale profiles to form model profiles.
+ do i = 1, nspectra
+ call amulkr (profiles[1,i,I0_INDEX,1], PARAMETER(ms,I0,i),
+ profiles[1,i,I0_INDEX,1], len_profile)
+
+ # Replace deviant or INDEF pixels by model values.
+ # Even if no cleaning was done there may have been some INDEF points
+ # in the input data line.
+
+ do i = 1, len_line {
+ if (IS_INDEFR (data[i]))
+ data[i] = model[i]
+ }
+
+ # Print the number of pixels replaced and return.
+ call ex_prnt3 (n_total)
+ return
+
+# SET_FIT_AND_CLEAN -- Set the fitting and cleaning parameters.
+
+entry set_fit_and_clean (max_iterate, max_replace, sigma_cut, fit_type,
+ ex_model)
+
+ imax = max_iterate
+ nmax = max_replace
+ lower = sigma_cut
+ upper = sigma_cut
+ option = fit_type
+ exmod = ex_model
+ return
+end
+
+
+procedure full_solution (ms, data, model, ranges, profiles, len_line,
+ len_profile, nspectra, nparams)
+
+pointer ms # MULTISPEC data structure
+real data[len_line] # Input data to be fit
+real model[len_line] # Output model line
+real ranges[nspectra, LEN_RANGES, 3] # Profile ranges
+real profiles[len_profile, nspectra, nparams, 3] # Model profiles
+int len_line # Length of data/model line
+int len_profile # Length of each profile
+int nspectra # Number of spectra
+int nparams # Number model parameters
+
+real rnorm
+pointer sp, fitparams, solution, offset
+
+begin
+ # Initialize fitparams and ranges arrays.
+ call smark (sp)
+ call salloc (fitparams, nspectra * nparams, TY_REAL)
+ call salloc (solution, nspectra * nparams, TY_REAL)
+
+ offset = (I0_INDEX - 1) * nspectra
+ call amovki (NO, Memr[fitparams], nspectra * nparams)
+ call amovki (YES, Memr[fitparams + offset], nspectra)
+
+ # Do least squares banded matrix solution for I0 parameters.
+ # The solution vector contains the least square fit values which
+ # must be copied to the I0 parameter vector.
+ call solve (ms, data, model, Memr[fitparams], profiles, ranges,
+ len_line, len_profile, nspectra, nparams, Memr[solution], rnorm)
+ call aaddr (PARAMETER(ms, I0, 1), Memr[solution + offset],
+ PARAMETER(ms, I0, 1), nspectra)
+
+ call sfree (sp)
+end
+
+
+# QUICK_SOLUTION -- Quick determination of profile scaling parameters.
+
+procedure quick_solution (ms, data, ranges, profiles, len_line, len_profile,
+ nspectra)
+
+pointer ms # MULTISPEC data structure
+real data[len_line] # Input data to be fit
+real ranges[nspectra, LEN_RANGES] # Profile ranges
+real profiles[len_profile, nspectra, ARB] # Model profiles
+int len_line # Length of data/model line
+int len_profile # Length of each profile
+int nspectra # Number of spectra
+
+int i, ic, j, n, spectrum, xc
+real i0[3]
+
+begin
+ ic = len_profile / 2
+
+ # Determine a value for I0 for each spectrum which is in the image.
+ do spectrum = 1, nspectra {
+ n = 0
+
+ # Check each profile point from ic on until n = 2.
+ do i = ic, len_profile - 1 {
+ xc = ranges[spectrum, X_START] + i
+ if ((xc < 1) || (xc > len_line))
+ next
+ if (IS_INDEFR (data[xc]))
+ next
+ j = i + 1
+ if (profiles[j, spectrum, I0_INDEX] <= 0)
+ next
+ n = n + 1
+ i0[n] = data[xc] / profiles[j, spectrum, I0_INDEX]
+ if (n >= 2)
+ break
+ }
+
+ # Check each profile point from ic - 1 and less until n = 3.
+ do i = ic - 1, 0, -1 {
+ xc = ranges[spectrum, X_START] + i
+ if ((xc < 1) || (xc > len_line))
+ next
+ if (IS_INDEFR (data[xc]))
+ next
+ j = i + 1
+ if (profiles[j, spectrum, I0_INDEX] <= 0)
+ next
+ n = n + 1
+ i0[n] = data[xc] / profiles[j, spectrum, I0_INDEX]
+ break
+ }
+
+ # Determine I0.
+ switch (n) {
+ case 3: # Use median I0
+ call asrtr (i0, i0, n)
+ PARAMETER(ms, I0, spectrum) = i0[2]
+ case 2: # Use mean I0
+ PARAMETER(ms, I0, spectrum) = (i0[1] + i0[2]) / 2
+ case 1: # Use only value
+ PARAMETER(ms, I0, spectrum) = i0[1]
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/fitfunction.par b/noao/twodspec/multispec/fitfunction.par
new file mode 100644
index 00000000..72e61620
--- /dev/null
+++ b/noao/twodspec/multispec/fitfunction.par
@@ -0,0 +1,8 @@
+# FITFUNCTION
+
+image,f,a,,,,Image
+parameter,s,h,x0,,,Database parameter to be fitted
+lines,s,h,"*",,,Images lines in function fit
+spectra,s,h,"*",,,Spectra to be fit
+function,s,h,"spline3",,,Fitting function
+order,i,h,INDEF,,,Order of spline
diff --git a/noao/twodspec/multispec/fitgauss5.com b/noao/twodspec/multispec/fitgauss5.com
new file mode 100644
index 00000000..65bd9bb8
--- /dev/null
+++ b/noao/twodspec/multispec/fitgauss5.com
@@ -0,0 +1,9 @@
+# Common for fitting model GAUSS5.
+
+real factor # Convergence factor
+int spectra[3, MAX_RANGES] # Spectra to fit
+int parameters[MS_NGAUSS5] # Parameters to be fit
+int smooth[MS_NGAUSS5] # Smooth parameters?
+int algorithm # Fitting algorithm
+
+common /g5_fitcom/ factor, spectra, parameters, smooth, algorithm
diff --git a/noao/twodspec/multispec/fitgauss5.par b/noao/twodspec/multispec/fitgauss5.par
new file mode 100644
index 00000000..276a3b19
--- /dev/null
+++ b/noao/twodspec/multispec/fitgauss5.par
@@ -0,0 +1,23 @@
+# FITGAUSS5
+
+image,f,a,,,,Image
+start,i,a,,,,Starting image line
+lower,r,h,-10.,,,Lower limit of model profiles
+upper,r,h,10.,,,Upper limit of model profiles
+lines,s,h,"*",,,Images lines to be fitted
+spectra,s,h,"*",,,Spectra to be fitted
+naverage,i,h,20,1,,Number of image lines to average
+factor,r,h,.05,0,1,RMS iteration improvement stopping criteria
+track,b,h,yes,,,Track solution?
+algorithm,i,h,1,1,2,Fitting algorithm
+fit_i0,b,h,y,,,Fit spectra central intensities?
+fit_x0,b,h,y,,,Fit spectra positions?
+fit_s0,b,h,y,,,Fit spectra shape parameter 0?
+fit_s1,b,h,n,,,Fit spectra shape parameter 1?
+fit_s2,b,h,n,,,Fit spectra shape parameter 2?
+smooth_s0,b,h,no,,,Smooth parameter s0 across spectra?
+smooth_s1,b,h,no,,,Smooth parameter s1 across spectra?
+smooth_s2,b,h,no,,,Smooth parameter s2 across spectra?
+function,s,h,"spline3",,,"Smoothing function (legendre,chebyshev,spline3)"
+order,i,h,4,,,Order for smoothing function
+verbose,b,h,no,,,Print general information about the fitting?
diff --git a/noao/twodspec/multispec/fitgauss5.x b/noao/twodspec/multispec/fitgauss5.x
new file mode 100644
index 00000000..b1e07b58
--- /dev/null
+++ b/noao/twodspec/multispec/fitgauss5.x
@@ -0,0 +1,460 @@
+include <fset.h>
+include "ms.h"
+
+# FITGAUSS5 -- Procedures used in fitting the GAUSS5 model.
+#
+# G5_FIT1 -- Fitting algorithm #1.
+# G5_FIT2 -- Fitting algorithm #2.
+# G5_FIT -- Fit the selected parameters for the best RMS value.
+# SET_VERBOSE -- Verbose output.
+
+########################################################################
+.helpsys g5fit1 Jul84 MULTISPEC
+.ih
+NAME
+G5_FIT1 -- Fitting algorithm #1.
+.ih
+DESCRIPTION
+This algorithm fits the selected parameters simultaneously.
+The parameters are selected by the parameter array (which of the 5
+model parameters in a profile are to be fit) and the spectra range
+array defined in fitgauss5.com. These two arrays are used to generate
+the fitparms array with the routine set_fitparams. The fitparams array
+controls the parameters fit by G5_FIT.
+.ih
+PARAMETERS
+The model parameter values which are part of the MULTISPEC data structure,
+the data array, the model array, the model profiles, and the profile
+ranges must be initialized. Other parameters for this procedure are
+input via the common in file fitgauss5.com. These include the spectra
+to be fit and the parameters array.
+.ih
+OUTPUT
+The returned data are the final parameter values, the model and profiles
+arrays and the Y/N function value indicating if the RMS fit has been improved.
+.endhelp
+########################################################################
+
+int procedure g5_fit1 (ms, data, model, profiles, ranges, lower, len_profile)
+
+pointer ms # MULTISPEC database data
+real data[ARB] # Line of image pixels
+real model[ARB] # Line of model pixels
+real profiles[ARB] # Model profiles
+real ranges[ARB] # Origins of model profiles
+real lower # Profile origin
+int len_profile # Length of a model profile
+
+int improved
+real rms
+pointer sp, fitparams
+
+int g5_fit()
+real armsrr()
+
+include "fitgauss5.com"
+
+begin
+ # Calculate the initial RMS. The parameter values are only changed
+ # if the new RMS is less than this value.
+ rms = armsrr (data, model, MS_LEN(ms, 1))
+ call g5_prnt3 (rms)
+
+ # Allocate and set the fitparams array.
+ call smark (sp)
+ call salloc (fitparams, MS_NSPECTRA(ms) * MS_NGAUSS5, TY_REAL)
+ call set_fitparams (spectra, parameters, MS_NSPECTRA(ms), MS_NGAUSS5,
+ Memr[fitparams])
+
+ # Call the fitting program once to simultaneously minimize the RMS.
+ improved = g5_fit (ms, data, model, profiles, ranges, Memr[fitparams],
+ lower, len_profile, rms)
+
+ call sfree (sp)
+ return (improved)
+end
+
+###############################################################################
+.helpsys g5fit2 Jul84 MULTISPEC
+.ih
+NAME
+G5_FIT2 -- Fitting algorithm #2.
+.ih
+DESCRIPTION
+This algorithm begins by fitting the parameters I0, X0, and S0
+simultaneously. Note that the values of S1 and S2 are used but are
+kept fixed. Next the parameters S0 and S1 (the shape) are fit simultaneously
+keeping I0, X0, and S2 fixed followed by fitting I0 and X0 while
+keeping S0, S1, and S2 (the shape) fixed. If either of these fits
+fails to improve the RMS then the algorithm terminates.
+Also, if after the two steps (the fit of S0 and S1 followed by the fit
+of I0 and X0), the RMS of the fit has not improved by more than the
+user specified factor the algorithm also terminates.
+.ih
+INPUT
+The model parameter values which are part of the MULTISPEC data structure,
+the data array, the model array, the model profiles, and the profile
+ranges must be initialized. Other parameters for this procedure are
+input via the common in file fitgauss5.com. These include the spectra
+to be fit, the parameters array (used as a working array), and the RMS
+stopping factor.
+.ih
+OUTPUT
+The returned data are the final parameter values, the model and profiles
+arrays and the Y/N function value indicating if the RMS fit has been improved.
+.endhelp
+##############################################################################
+
+int procedure g5_fit2 (ms, data, model, profiles, ranges, lower, len_profile)
+
+pointer ms # MULTISPEC database data
+real data[ARB] # Line of image pixels
+real model[ARB] # Line of model pixels
+real profiles[ARB] # Model profiles
+real ranges[ARB] # Origins of model profiles
+real lower # Profile origin
+int len_profile # Length of a model profile
+
+int improved, fit
+real rms, rms_old
+pointer sp, fitparams
+
+int g5_fit()
+real armsrr()
+
+include "fitgauss5.com"
+
+begin
+ # Calculate the initial RMS. The parameter values are only changed
+ # if the new RMS is less than this value.
+ rms = armsrr (data, model, MS_LEN(ms, 1))
+ call g5_prnt3 (rms)
+
+ # Allocate the fitparams array.
+ call smark (sp)
+ call salloc (fitparams, MS_NSPECTRA(ms) * MS_NGAUSS5, TY_REAL)
+
+ # Fit the parameters I0, X0, and S0.
+ parameters[I0_INDEX] = YES
+ parameters[X0_INDEX] = YES
+ parameters[S0_INDEX] = YES
+ parameters[S1_INDEX] = NO
+ parameters[S2_INDEX] = NO
+ call set_fitparams (spectra, parameters, MS_NSPECTRA(ms),
+ MS_NGAUSS5, Memr[fitparams])
+
+ # Call the fitting procedure to minimze the RMS.
+ improved = g5_fit (ms, data, model, profiles, ranges,
+ Memr[fitparams], lower, len_profile, rms)
+
+ # Two step fitting algorithm consisting of a fit to S0 and S1 followed
+ # by a fit to I0 and X0. This loop terminates when either one
+ # of the fits fails to improve the RMS or the RMS has improved
+ # by less than factor after the second step (the I0, X0 fit).
+ repeat {
+ rms_old = rms
+
+ # Fit S0 and S1.
+ parameters[I0_INDEX] = NO
+ parameters[X0_INDEX] = NO
+ parameters[S0_INDEX] = YES
+ parameters[S1_INDEX] = YES
+ call set_fitparams (spectra, parameters, MS_NSPECTRA(ms),
+ MS_NGAUSS5, Memr[fitparams])
+ fit = g5_fit (ms, data, model, profiles, ranges,
+ Memr[fitparams], lower, len_profile, rms)
+ if (fit == NO)
+ break
+ improved = YES
+
+ # Fit I0 and X0.
+ parameters[I0_INDEX] = YES
+ parameters[X0_INDEX] = YES
+ parameters[S0_INDEX] = NO
+ parameters[S1_INDEX] = NO
+ call set_fitparams (spectra, parameters, MS_NSPECTRA(ms),
+ MS_NGAUSS5, Memr[fitparams])
+ fit = g5_fit (ms, data, model, profiles, ranges,
+ Memr[fitparams], lower, len_profile, rms)
+ if (fit == NO)
+ break
+
+ if (rms > (1 - factor) * rms_old)
+ break
+ }
+
+ call sfree (sp)
+ return (improved)
+end
+
+
+##############################################################################
+.helpsys g5fit Jul84 MULTISPEC
+.ih
+NAME
+G5_FIT -- Basic parameter fitting procedure.
+.ih
+INPUT
+The input data are the data array to be fit and the initial model
+parameters (part of the MULTISPEC data structure), the model array
+and model profiles (with the profile ranges array) corresponding to the
+initial model parameters, and the RMS of the model relative to the data.
+The parameters to be fit are selected by the fitparams array.
+Parameters controlling the fitting process are input to this procedure
+via the common block in the include file fitgauss5.com. These parameters are
+the RMS stopping factor and parameters controlling the smoothing of the
+shape parameters.
+.ih
+OUTPUT
+The returned data are the final parameter values, the model and profiles
+arrays and the Y/N function value indicating if the RMS fit has been improved.
+.ih
+DESCRIPTION
+The best RMS fit is obtained by iteration. Correction vectors for the
+parameters being fit are obtained by the simultaneous banded matrix
+method in the procedure solve. Heuristic constraints and smoothing
+are applied to the solution and then the RMS of the new fit to the
+data is calculated. New parameter corrections are computed until the RMS of
+the fit fails to improve by the specified factor.
+.endhelp
+############################################################################
+
+int procedure g5_fit (ms, data, model, profiles, ranges, fitparams, lower,
+ len_profile, rms)
+
+pointer ms # MULTISPEC data structure
+int fitparams[ARB] # Model parameters to be fit
+real data[ARB] # Data line to be fit
+real model[ARB] # Model line
+real profiles[ARB] # Model profiles
+real ranges[ARB] # Profile ranges
+real lower # Lower limit of profiles
+int len_profile # Length of profiles
+real rms # RMS of fit
+
+int improved
+int len_line, nspectra, nparams
+real rms_next, rnorm
+pointer sp, last_i0, last_x0, last_s0, last_s1, last_s2
+pointer solution, sol_i0, sol_x0, sol_s0, sol_s1, sol_s2
+
+real armsrr()
+
+include "fitgauss5.com"
+
+begin
+ # Set array lengths.
+ len_line = MS_LEN(ms, 1)
+ nspectra = MS_NSPECTRA(ms)
+ nparams = MS_NGAUSS5
+
+ # Allocate working memory to temporarily save the previous parameter
+ # values and to hold the correction vector.
+ call smark (sp)
+ call salloc (last_i0, nspectra, TY_REAL)
+ call salloc (last_x0, nspectra, TY_REAL)
+ call salloc (last_s0, nspectra, TY_REAL)
+ call salloc (last_s1, nspectra, TY_REAL)
+ call salloc (last_s2, nspectra, TY_REAL)
+ call salloc (solution, nspectra * nparams, TY_REAL)
+
+ # Offsets in the solution array for the various parameters.
+ sol_i0 = solution + (I0_INDEX - 1) * nspectra
+ sol_x0 = solution + (X0_INDEX - 1) * nspectra
+ sol_s0 = solution + (S0_INDEX - 1) * nspectra
+ sol_s1 = solution + (S1_INDEX - 1) * nspectra
+ sol_s2 = solution + (S2_INDEX - 1) * nspectra
+
+ improved = NO
+ repeat {
+ # Store the last parameter values so that if the parameter values
+ # determined in the next iteration yield a poorer RMS fit to
+ # the data the best fit parameter values can be recovered.
+
+ call amovr (PARAMETER(ms,I0,1), Memr[last_i0], nspectra)
+ call amovr (PARAMETER(ms,X0,1), Memr[last_x0], nspectra)
+ call amovr (PARAMETER(ms,S0,1), Memr[last_s0], nspectra)
+ call amovr (PARAMETER(ms,S1,1), Memr[last_s1], nspectra)
+ call amovr (PARAMETER(ms,S2,1), Memr[last_s2], nspectra)
+
+ # Determine a correction solution vector for the selected
+ # parameters simultaneously, apply heuristic constraints to the
+ # solution vector, apply the correction vector to the parameter
+ # values, and smooth the shape parameters if requested.
+
+ # Find a least squares correction vector.
+ call solve (ms, data, model, fitparams, profiles, ranges,
+ len_line, len_profile, nspectra, nparams, Memr[solution], rnorm)
+
+ # Apply constraints to the correction vector.
+ call constrain_gauss5 (ms, Memr[solution], nspectra, nparams)
+
+ # Add the correction vector to the parameter vector.
+ call aaddr (PARAMETER(ms,I0,1), Memr[sol_i0], PARAMETER(ms,I0,1),
+ nspectra)
+ call aaddr (PARAMETER(ms,X0,1), Memr[sol_x0], PARAMETER(ms,X0,1),
+ nspectra)
+ call aaddr (PARAMETER(ms,S0,1), Memr[sol_s0], PARAMETER(ms,S0,1),
+ nspectra)
+ call aaddr (PARAMETER(ms,S1,1), Memr[sol_s1], PARAMETER(ms,S1,1),
+ nspectra)
+ call aaddr (PARAMETER(ms,S2,1), Memr[sol_s2], PARAMETER(ms,S2,1),
+ nspectra)
+
+ # Smooth the shape parameters.
+ if (smooth[S0_INDEX] == YES)
+ call ms_smooth (PARAMETER(ms, X0, 1), PARAMETER(ms, S0, 1))
+ if (smooth[S1_INDEX] == YES)
+ call ms_smooth (PARAMETER(ms, X0, 1), PARAMETER(ms, S1, 1))
+ if (smooth[S2_INDEX] == YES)
+ call ms_smooth (PARAMETER(ms, X0, 1), PARAMETER(ms, S2, 1))
+
+ # Calculate new model profiles and new model data line.
+ # Determine the RMS fit of the new model to the data.
+ # If the change in the RMS is less than factor times the
+ # previous RMS the interation is terminated else the improvement
+ # in the RMS is recorded and the next iteration is begun.
+
+ # Set new model profiles.
+ call mod_gauss5 (ms, lower, profiles, ranges, len_profile, nspectra)
+
+ # Set new model line from the profiles.
+ call set_model (ms, model, profiles, ranges, len_line,
+ len_profile, nspectra)
+
+ # Calculate the RMS of the new model.
+ rms_next = armsrr (data, model, len_line)
+
+ # Check to see if the RMS is improved enough to continue iteration.
+ if ((rms - rms_next) < factor * rms) {
+
+ # The RMS has not improved enough to continue iteration.
+
+ if (rms_next < rms) {
+ # Keep the latest parameter values, profiles, and model
+ # because the new RMS is lower than the previous RMS.
+ # Record the improvement.
+ rms = rms_next
+ improved = YES
+ call g5_prnt3 (rms)
+
+ } else {
+ # Restore the parameter values, profiles, and model to
+ # previous values because the new RMS is higher.
+ call amovr (Memr[last_i0], PARAMETER(ms,I0,1), nspectra)
+ call amovr (Memr[last_x0], PARAMETER(ms,X0,1), nspectra)
+ call amovr (Memr[last_s0], PARAMETER(ms,S0,1), nspectra)
+ call amovr (Memr[last_s1], PARAMETER(ms,S1,1), nspectra)
+ call amovr (Memr[last_s2], PARAMETER(ms,S2,1), nspectra)
+ call mod_gauss5 (ms, lower, profiles, ranges, len_profile,
+ nspectra)
+ call set_model (ms, model, profiles, ranges, len_line,
+ len_profile, nspectra)
+ }
+
+ # Exit the iteration loop.
+ break
+
+ } else {
+
+ # The RMS has improved significantly. Record the improvement
+ # and continue the iteration loop.
+
+ rms = rms_next
+ improved = YES
+ call g5_prnt3 (rms)
+ }
+ }
+
+ call sfree (sp)
+ return (improved)
+end
+
+
+# G5_SET_VERBOSE -- Output procedures for verbose mode.
+
+procedure g5_set_verbose (verbose)
+
+bool verbose
+bool flag
+
+# entry g5_prnt1 (image, naverage, track, start)
+char image[1]
+int naverage
+bool track
+int start
+
+# entry g5_prnt2 (line, data, len_data)
+int line, len_data
+real data[1]
+real rms, data_rms
+
+real armsr()
+include "fitgauss5.com"
+
+begin
+ # Toggle verbose output.
+ flag = verbose
+ if (flag)
+ call fseti (STDOUT, F_FLUSHNL, YES)
+ else
+ call fseti (STDOUT, F_FLUSHNL, NO)
+ return
+
+entry g5_prnt1 (image, naverage, track, start)
+
+ # Print the values of the various task parameters.
+
+ if (!flag)
+ return
+
+ call printf ("\nMULTISPEC Model Fitting Program\n\n")
+ call printf ("Image file being modeled is %s.\n")
+ call pargstr (image)
+ call printf ("Average %d lines of the image.\n")
+ call pargi (naverage)
+ call printf ("Fitting algorithm %d.\n")
+ call pargi (algorithm)
+ if (algorithm == 1) {
+ if (parameters[I0_INDEX] == YES)
+ call printf ("Fit intensity scales.\n")
+ if (parameters[X0_INDEX] == YES)
+ call printf ("Fit spectra positions.\n")
+ if (parameters[S0_INDEX] == YES)
+ call printf ("Fit spectra widths.\n")
+ if (parameters[S1_INDEX] == YES)
+ call printf ("Fit model parameter s1.\n")
+ if (parameters[S2_INDEX] == YES)
+ call printf ("Fit model parameter s2.\n")
+ }
+ if (track) {
+ call printf ("Track model from line %d.\n")
+ call pargi (start)
+ }
+ call printf (
+ "Iterate model until the fit RMS decreases by less than %g %%.\n\n")
+ call pargr (factor * 100)
+
+ return
+
+entry g5_prnt2 (line, data, len_data)
+
+ # Print the image line being fit and the data RMS.
+ if (flag) {
+ call printf ("Fit line %d:\n")
+ call pargi (line)
+ data_rms = armsr (data, len_data)
+ call printf (" Data RMS = %g\n")
+ call pargr (data_rms)
+ }
+ return
+
+entry g5_prnt3 (rms)
+
+ # Print the RMS of the fit and the ratio to the data RMS.
+ if (flag) {
+ call printf (" Fit RMS = %g Fit RMS / Data RMS = %g\n")
+ call pargr (rms)
+ call pargr (rms / data_rms)
+ }
+end
diff --git a/noao/twodspec/multispec/fitsmooth.x b/noao/twodspec/multispec/fitsmooth.x
new file mode 100644
index 00000000..413f3c46
--- /dev/null
+++ b/noao/twodspec/multispec/fitsmooth.x
@@ -0,0 +1,168 @@
+
+# FIT_SMOOTH -- Least-squares fit of smoothed profiles to data profiles with
+# cleaning of deviant pixels.
+#
+# The profile fitting and cleaning are combined in order to minimize
+# the calculations in re-evaluating the least-squares fit after rejecting
+# deviant pixels.
+#
+# The sigma used for rejection is calculated from the sigma of the fit
+# before rejecting any pixels. Pixels whose residuals exceed
+# +/- sigma_cut * sigma are rejected. The maximum number of pixels to be
+# replaced in each spectrum is max_replace. If max_replace is zero then
+# only the model fitting is performed.
+#
+# The output of this routine are the cleaned data profiles and the least-square
+# fitted model profiles. The number of pixels replaced is returned.
+
+
+procedure fit_smooth (line, data, model, profiles, len_prof, nspectra, nlines)
+
+int line # Image line of data
+real data[len_prof, nspectra] # Data profiles
+real model[len_prof, nspectra] # Model profiles
+real profiles[len_prof, nspectra, ARB] # Work array for SMOOTH profiles
+int len_prof # Length of profile
+int nspectra # Number of spectra
+int nlines # Number of lines profiles
+
+int max_replace # Maximum number of bad pixels
+real sigma_cut # Sigma cutoff on the residuals
+
+int i, spectrum
+int nmax, ntotal, nreplace, nreject, nindef, nsigma
+real sum1, sum2, scale, sigma
+real lower, upper, residual, resid_min, resid_max
+pointer sp, a, b, c
+
+begin
+ # Allocate working memory.
+ call smark (sp)
+ call salloc (a, nspectra, TY_REAL)
+ call salloc (b, nspectra, TY_REAL)
+ call salloc (c, nspectra, TY_INT)
+
+ # Fit each spectrum and compute sigma of fit.
+ sigma = 0.
+ nsigma = 0
+ do spectrum = 1, nspectra {
+ # Accumulate least squares sums.
+ sum1 = 0.
+ sum2 = 0.
+ nindef = 0
+ do i = 1, len_prof {
+ if (IS_INDEFR (data[i, spectrum]))
+ nindef = nindef + 1
+ else if (model[i, spectrum] > 0.) {
+ sum1 = sum1 + data[i, spectrum] * model[i, spectrum]
+ sum2 = sum2 + model[i, spectrum] * model[i, spectrum]
+ }
+ }
+
+ # Compute sigma if cleanning is desired.
+ if (nmax != 0) {
+ scale = sum1 / sum2
+ do i = 1, len_prof {
+ if (!IS_INDEFR (data[i, spectrum]) &&
+ (model[i, spectrum] > 0.)) {
+ sigma = sigma +
+ (data[i,spectrum] - scale * model[i,spectrum]) ** 2
+ nsigma = nsigma + 1
+ }
+ }
+ }
+
+ Memr[a + spectrum - 1] = sum1
+ Memr[b + spectrum - 1] = sum2
+ Memi[c + spectrum - 1] = nindef
+ }
+ sigma = sqrt (sigma / nsigma)
+
+ # Reject deviant pixels from the fit, scale the model to data,
+ # and replace rejected and INDEFR pixels with model values.
+ ntotal = 0
+ do spectrum = 1, nspectra {
+ sum1 = Memr[a + spectrum - 1]
+ sum2 = Memr[b + spectrum - 1]
+ nindef = Memi[c + spectrum - 1]
+
+ # If there are no model data points go to the next spectrum.
+ if (sum2 == 0.)
+ next
+
+ # Reject pixels if desired.
+ nreplace = 0
+ if (nmax != 0) {
+ # Compare each pixel in the profile against the model and set
+ # deviant pixels to INDEFR. If the number of pixels to be
+ # replaced is equal to the maximum allowed or the number of
+ # pixels rejected equals the entire profile or the number of
+ # deviant pixels is zero in an iteration stop cleaning and
+ # exit the loop. Ignore INDEFR pixels.
+
+ repeat {
+ nreject = 0
+ scale = sum1 / sum2
+ resid_min = -lower * sigma
+ resid_max = upper * sigma
+ do i = 1, len_prof {
+ if (IS_INDEFR (data[i, spectrum]))
+ next
+
+ # Compute the residual and remove point if it exceeds
+ # the residual limits.
+
+ residual = data[i,spectrum] - scale * model[i,spectrum]
+ if ((residual < resid_min) || (residual > resid_max)) {
+ # Remove point from the least squares fit
+ # and flag the deviant pixel with INDEFR.
+ sum1 = sum1 - data[i,spectrum] * model[i,spectrum]
+ sum2 = sum2 - model[i,spectrum] * model[i,spectrum]
+ data[i,spectrum] = INDEFR
+ nreplace = nreplace + 1
+ nreject = nreject + 1
+ }
+ if (nreplace == nmax)
+ break
+ }
+ } until ((nreplace == nmax) || (nreject == 0) || (sum2 == 0.))
+ }
+
+ # If there are good pixels remaining scale the model to the
+ # data profile.
+ if (sum2 > 0.)
+ call amulkr (model[1, spectrum], sum1 / sum2,
+ model[1, spectrum], len_prof)
+
+ # Replace bad pixels by the model values.
+ if ((nindef > 0) || (nreplace > 0)) {
+ do i = 1, len_prof {
+ if (IS_INDEFR (data[i, spectrum]))
+ data[i, spectrum] = model[i, spectrum]
+ }
+ ntotal = ntotal + nreplace
+ }
+ }
+
+ # Print the number of pixel replaced.
+ call ex_prnt3 (ntotal)
+
+ # Replace the cleaned data profiles in future SMOOTH profiles.
+ if (ntotal > 0)
+ call update_smooth (line, data, profiles, len_prof, nspectra,
+ nlines)
+
+ # Free allocated memory.
+ call sfree (sp)
+
+ return
+
+# SET_FIT_SMOOTH -- Set the cleaning parameters.
+
+entry set_fit_smooth (max_replace, sigma_cut)
+
+ nmax = max_replace
+ lower = sigma_cut
+ upper = sigma_cut
+ return
+end
diff --git a/noao/twodspec/multispec/history.x b/noao/twodspec/multispec/history.x
new file mode 100644
index 00000000..9c79965b
--- /dev/null
+++ b/noao/twodspec/multispec/history.x
@@ -0,0 +1,29 @@
+include <time.h>
+include "ms.h"
+
+# HISTORY - Add a dated comment string to the MULTISPEC database.
+
+procedure history (ms, comment)
+
+pointer ms
+char comment[ARB]
+
+char time_string[SZ_TIME]
+
+long clktime()
+
+begin
+ # Get the clock time and convert to a date string.
+ call cnvdate (clktime(0), time_string, SZ_TIME)
+
+ # Append the following to the comment block:
+ # (date string)(: )(comment string)(newline)
+
+ call strcat (time_string, COMMENT(ms,1), SZ_MS_COMMENTS)
+ call strcat (": ", COMMENT(ms,1), SZ_MS_COMMENTS)
+ call strcat (comment, COMMENT(ms,1), SZ_MS_COMMENTS)
+ call strcat ("\n", COMMENT(ms,1), SZ_MS_COMMENTS)
+
+ # Write the updated comment block to the database.
+ call mspcomments (ms)
+end
diff --git a/noao/twodspec/multispec/intgauss5.x b/noao/twodspec/multispec/intgauss5.x
new file mode 100644
index 00000000..20118802
--- /dev/null
+++ b/noao/twodspec/multispec/intgauss5.x
@@ -0,0 +1,140 @@
+include "ms.h"
+
+# INT_GAUSS5 -- Interpolate the GAUSS5 profiles between sample lines.
+#
+# Because calculation of the model profiles from parameter values interpolated
+# from the sample lines is very slow the profiles at the sample lines are
+# calculated (only when needed) and the profiles are then interpolated.
+
+procedure int_gauss5 (ms, lower, profiles, ranges, len_profile, nspectra,
+ nparams, line)
+
+pointer ms # MULTISPEC data structure
+real lower # Lower limit of profiles rel. to center
+real profiles[len_profile, nspectra, nparams, 3] # Model profiles
+real ranges[nspectra, LEN_RANGES, 3] # Range array for profiles
+int len_profile # Length of each profile
+int nspectra # Number of spectra
+int nparams # Number of parameters
+int line # Image line to be interpolated to
+
+real f, x
+int i, a, b, line1, line2
+
+real cveval()
+
+begin
+ # The values of the static variables are used in successive calls
+ # to record the state of the interpolation endpoints. To initialize
+ # this routine the value of the first element of the ranges array
+ # is checked for the flag INDEFR. The profiles array must be
+ # dimensioned to have three sets of profiles (each set consisting
+ # of nspectra * nparams profiles). The first set is the interpolated
+ # profiles, profiles[*,*,*,1], and the other two sets hold the
+ # latest profiles from the interpolation endpoint sample lines,
+ # profiles[*,*,*,2] and profiles[*,*,*,3].
+
+ # If there is only one sample line then calculate the profiles
+ # only once (when the ranges array has been flagged) and return
+ # the same profiles for every image line.
+ if (MS_NSAMPLES(ms) == 1) {
+ if (IS_INDEFR (ranges[1,1,1])) {
+ call msggauss5 (ms, line1)
+ call mod_gauss5 (ms, lower, profiles, ranges, len_profile,
+ nspectra)
+ }
+ return
+ }
+
+ # If there is more than one sample line then interpolation makes
+ # sense. Initialize the interpolation algorithm if the ranges array
+ # has been flagged.
+
+ if (IS_INDEFR (ranges[1,1,1])) {
+ call msgparam (ms, I0, 1)
+ call msgparam (ms, X0, 1)
+ call msgfits (ms, X0_FIT)
+ a = 1
+ line1 = 0
+ line2 = 0
+ }
+
+ # Find the nearest sample line which is less than the desired
+ # image line and is not the last sample line and mark this as
+ # endpoint sample line a. Start from the last endpoint for efficiency.
+ # Search forward if the desired image line is greater than the
+ # endpoint sample line and backwards otherwise.
+
+ if (line > LINE(ms, a)) {
+ do i = a + 1, MS_NSAMPLES(ms) - 1 {
+ if (line > LINE(ms, i))
+ a = i
+ else
+ break
+ }
+ } else {
+ do i = a, 1, -1 {
+ if (line <= LINE(ms, a))
+ a = i
+ else
+ break
+ }
+ }
+
+ # Since endpoint a is not allowed to be the last sample line then
+ # the upper interpolation endpoint is the next sample line.
+ b = a + 1
+
+ # Check to see if the new endpoints are different than the previous
+ # endpoints. If so then read the model parameters from the database
+ # for the endpoints and evaluate the model profiles.
+ if ((line1 == a) && (line2 == b))
+ ; # Endpoints are the same.
+ else if ((line1 == b) && (line2 == a))
+ ; # Endpoints are the same.
+ else if ((line1 == a) && (line2 != b)) {
+ line2 = b # One endpoint is different.
+ call msggauss5 (ms, line2)
+ call mod_gauss5 (ms, lower, profiles[1,1,1,3], ranges[1,1,3],
+ len_profile, nspectra)
+ } else if ((line1 == b) && (line2 != a)) {
+ line2 = a # One endpoint is different.
+ call msggauss5 (ms, line2)
+ call mod_gauss5 (ms, lower, profiles[1,1,1,3], ranges[1,1,3],
+ len_profile, nspectra)
+ } else if ((line1 != b) && (line2 == a)) {
+ line1 = b # One endpoint is different.
+ call msggauss5 (ms, line1)
+ call mod_gauss5 (ms, lower, profiles[1,1,1,2], ranges[1,1,2],
+ len_profile, nspectra)
+ } else if ((line1 != a) && (line2 == b)) {
+ line1 = a # One endpoint is different.
+ call msggauss5 (ms, line1)
+ call mod_gauss5 (ms, lower, profiles[1,1,1,2], ranges[1,1,2],
+ len_profile, nspectra)
+ } else {
+ line1 = a # Both endpoints are different.
+ call msggauss5 (ms, line1)
+ call mod_gauss5 (ms, lower, profiles[1,1,1,2], ranges[1,1,2],
+ len_profile, nspectra)
+ line2 = b
+ call msggauss5 (ms, line2)
+ call mod_gauss5 (ms, lower, profiles[1,1,1,3], ranges[1,1,3],
+ len_profile, nspectra)
+ }
+
+ # Calculate the ranges for the interpolated range array to the
+ # interpolated spectra position.
+ f = real (line)
+ do i = 1, nspectra {
+ x = cveval (CV(ms, X0_FIT, i), f)
+ ranges[i, X_START, 1] = int(x) + lower
+ ranges[i, DX_START, 1] = ranges[i, X_START, 1] - x
+ }
+
+ # Do the profile interpolation.
+ f = float (line - LINE(ms, line1)) /
+ (LINE(ms, line2) - LINE(ms, line1))
+ call profile_interpolation (f, len_profile, nspectra, nparams,
+ profiles, ranges)
+end
diff --git a/noao/twodspec/multispec/mkpkg b/noao/twodspec/multispec/mkpkg
new file mode 100644
index 00000000..be03b41f
--- /dev/null
+++ b/noao/twodspec/multispec/mkpkg
@@ -0,0 +1,66 @@
+# MULTISPEC Package.
+
+$call relink
+$exit
+
+update:
+ $call relink
+ $call install
+ ;
+
+relink:
+ $update libpkg.a
+ $call multispec
+ ;
+
+install:
+ $move x_multispec.e noaobin$
+ ;
+
+multispec:
+ $omake x_multispec.x
+ $set LIBS = "-lxtools -lllsq -lcurfit -ldeboor -linterp"
+ $link x_multispec.o libpkg.a $(LIBS)
+ ;
+
+libpkg.a:
+ @dbio
+
+ armsr.x
+ clinput.x
+ exgauss5.x ms.h
+ exsmooth.x ms.h
+ exstrip.x ms.h
+ fitclean.x ms.h
+ fitgauss5.x ms.h fitgauss5.com
+ fitsmooth.x ms.h
+ history.x ms.h
+ intgauss5.x ms.h
+ modgauss5.x ms.h
+ msextract.x ms.h
+ msget.x ms.h
+ msio.x ms.h
+ msnames.x ms.h
+ msput.x ms.h
+ mssmooth.x
+ peaks.x
+ profinterp.x ms.h
+ ranges.x
+ sampline.x ms.h
+ setfitparams.x ms.h
+ setmodel.x ms.h
+ setranges.x ms.h
+ setsmooth.x ms.h
+ solve.x ms.h
+ unblend.x ms.h
+ msplot.x <imhdr.h>
+ t_findpeaks.x ms.h
+ t_fitfunc.x ms.h
+ t_fitgauss5.x ms.h fitgauss5.com
+ t_modellist.x ms.h
+ t_msextract.x ms.h
+ t_mslist.x ms.h
+ t_msset.x ms.h
+ t_newextract.x ms.h
+ t_newimage.x ms.h
+ ;
diff --git a/noao/twodspec/multispec/modellist.par b/noao/twodspec/multispec/modellist.par
new file mode 100644
index 00000000..2c622668
--- /dev/null
+++ b/noao/twodspec/multispec/modellist.par
@@ -0,0 +1,9 @@
+# MODELLIST
+
+image,f,a,,,,Image
+lines,s,a,,,,Sample image lines to be listed
+model,s,h,"gauss5",,,Model to be listed
+columns,s,h,"*",,,Image columns to be listed
+naverage,i,h,20,,,Number of image lines to average
+lower,r,h,-10,,,Lower limit of model profiles
+upper,r,h,10,,,Upper limit of model profiles
diff --git a/noao/twodspec/multispec/modgauss5.x b/noao/twodspec/multispec/modgauss5.x
new file mode 100644
index 00000000..437b1a85
--- /dev/null
+++ b/noao/twodspec/multispec/modgauss5.x
@@ -0,0 +1,164 @@
+include "ms.h"
+
+# MOD_GAUSS5 -- Set GAUSS5 model profiles and ranges.
+#
+# This routine can be speeded up with look up tables for a and exp(-z).
+
+define ZMIN 0 # Issue warning if z < ZMIN
+define ZMAX 10 # The profile values are zero for z > ZMAX
+
+procedure mod_gauss5 (ms, lower, profiles, ranges, len_profile, nspectra)
+
+pointer ms # MULTISPEC data structure
+real lower # Lower limit of profiles
+real profiles[len_profile, nspectra, ARB] # The profiles to be set
+ # The third dim must be >= 5
+real ranges[nspectra, LEN_RANGES] # The ranges to be set
+int len_profile # The length of each profile
+int nspectra # The number of spectra
+
+int i, j, warn
+real dx, dx2, y, z
+real x1, a, s, s0, s1, s2, s3, profile
+real dIdx0, dIdI0, dIds0, dIds1, dIds2
+real dydx0, dzdx0
+
+begin
+ # First set the ranges array.
+ call set_ranges (ms, lower, ranges, nspectra)
+
+ # The model quantity x1 is set to 1/4 the profile length.
+ # This could someday become a model parameter.
+ x1 = len_profile / 4
+
+ # For each spectrum and each point in the profile set the
+ # profile/derivative values for the 5 Gauss5 parameters.
+
+ warn = YES
+ do i = 1, nspectra {
+ s0 = PARAMETER(ms, S0, i)
+ s1 = PARAMETER(ms, S1, i)
+ s2 = PARAMETER(ms, S2, i)
+ do j = 1, len_profile {
+ dx = ranges[i, DX_START] + j - 1
+ dx2 = dx * dx
+ a = 1 / sqrt (dx2 + x1 ** 2)
+ y = a * dx
+ if (y < 0)
+ s3 = s2 - s1
+ else
+ s3 = s2 + s1
+ s = s0 + y * s3
+ z = s * dx2
+ if (z < ZMIN) {
+ # Issue warning only once.
+ if (warn == YES) {
+ call printf ("WARNING: mod_gauss5 error.\n")
+ warn = NO
+ }
+ }
+ if (z < ZMAX) {
+ profile = exp(-z)
+ dydx0 = -(a ** 3) * (x1 ** 2)
+ dzdx0 = -2 * s * dx + dydx0 * s3 * dx2
+ dIdI0 = profile
+ dIdx0 = -dzdx0 * profile
+ dIds0 = -dx2 * profile
+ dIds1 = -dx2 * y * profile
+ dIds2 = dIds1
+ if (y < 0)
+ dIds1 = -dIds1
+
+ profiles[j,i,I0_INDEX] = dIdI0
+ profiles[j,i,X0_INDEX] = dIdx0
+ profiles[j,i,S0_INDEX] = dIds0
+ profiles[j,i,S1_INDEX] = dIds1
+ profiles[j,i,S2_INDEX] = dIds2
+ } else {
+ profiles[j,i,I0_INDEX] = 0.
+ profiles[j,i,X0_INDEX] = 0.
+ profiles[j,i,S0_INDEX] = 0.
+ profiles[j,i,S1_INDEX] = 0.
+ profiles[j,i,S2_INDEX] = 0.
+ }
+ }
+ }
+end
+
+# CONSTRAIN_GAUSS5 -- Apply constraints to the solution vector for GAUSS5.
+#
+# The constraints are:
+#
+# DI0 > -I0/2, abs(DX0) < MAX_DX0, DS0 > -S0/2,
+# (S0+DS0)+-(S1+DS1)+(S2+DS2) > 0.
+#
+# where DI0, DX0, DS0, DS1, DS2 are the solution corrections and I0, S0,
+# S1, and S2 are the original parameter values. The constraints on DI0,
+# and DS0 insure that I0 and S0 remain positive and the last constraint
+# insures that (S0+-S1+S2) always remains positive so that the profiles
+# always decrease from the center.
+
+define MAX_DX0 1. # Maximum change in position
+
+procedure constrain_gauss5 (ms, solution, nspectra, nparams)
+
+pointer ms
+real solution[nspectra, nparams]
+int nspectra
+int nparams
+
+int i
+real max_delta
+real sa, sb, dsa, dsb, scalea, scaleb, scale
+
+begin
+ do i = 1, nspectra {
+
+ # Limit any decrease in I0 to 1/2 I0. This insures I0 > 0.
+ if (solution[i, I0_INDEX] != 0.) {
+ max_delta = PARAMETER(ms, I0, i) / 2.
+ solution[i, I0_INDEX] = max (solution[i, I0_INDEX], -max_delta)
+ }
+
+ # Limit the correction for X0 to MAX_DX0.
+ # Set the position to INDEF if it falls outside the image.
+ if (solution[i, X0_INDEX] != 0.) {
+ max_delta = MAX_DX0
+ solution[i, X0_INDEX] = max (solution[i, X0_INDEX], -max_delta)
+ solution[i, X0_INDEX] = min (solution[i, X0_INDEX], max_delta)
+ }
+
+ # Limit any decrease in S0 to 1/2 of S0. This insures S0 > 0.
+ if (solution[i, S0_INDEX] != 0.) {
+ max_delta = PARAMETER(ms, S0, i) / 2.
+ solution[i, S0_INDEX] = max (solution[i, S0_INDEX], -max_delta)
+ }
+
+ # Limit the final S0+-S1+S2 to be positive. If the value would be
+ # negative scale the correction vector (ds0, ds1, ds2) to make
+ # the final S0+-S1+S2 be 1/2 the old value.
+ if ((solution[i,S0_INDEX] != 0.) || (solution[i,S1_INDEX] != 0.) ||
+ (solution[i,S2_INDEX] != 0.)) {
+ sa = PARAMETER(ms, S0, i) + PARAMETER(ms, S1, i) +
+ PARAMETER(ms, S2, i)
+ sb = PARAMETER(ms, S0, i) - PARAMETER(ms, S1, i) +
+ PARAMETER(ms, S2, i)
+ dsa = solution[i, S0_INDEX] + solution[i, S1_INDEX] +
+ solution[i, S2_INDEX]
+ dsb = solution[i, S0_INDEX] - solution[i, S1_INDEX] +
+ solution[i, S2_INDEX]
+ if (sa + dsa < 0.)
+ scalea = -sa / 2 / dsa
+ else
+ scalea = 1.
+ if (sb + dsb < 0.)
+ scaleb = -sb / 2 / dsb
+ else
+ scaleb = 1.
+ scale = min (scalea, scaleb)
+ solution[i, S0_INDEX] = scale * solution[i, S0_INDEX]
+ solution[i, S1_INDEX] = scale * solution[i, S1_INDEX]
+ solution[i, S2_INDEX] = scale * solution[i, S2_INDEX]
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/ms.h b/noao/twodspec/multispec/ms.h
new file mode 100644
index 00000000..7343e765
--- /dev/null
+++ b/noao/twodspec/multispec/ms.h
@@ -0,0 +1,77 @@
+
+# MULTISPEC Definitions
+
+define SZ_MS_IMAGE 79 # Size of image filename string
+define SZ_MS_TITLE 79 # Size of the image title string
+define SZ_MS_COMMENTS 1024 # Size of MULTISPEC comment block
+define SZ_MS_KEY 20 # Size of the database reference strings
+
+define MS_DB_ENTRIES 20 # Max number of database entries
+define MS_MAX_DES 1 # Max number of MULTISPEC descriptors
+define MAX_RANGES 30 # Maximum range dimension.
+
+define MS_ERROR 1000 # General MULTISPEC error code
+
+# MULTISPEC I/O Descriptor
+
+define LEN_MS_DES 2 + MS_DB_ENTRIES
+
+define MS_DB Memi[$1] # DBIO descriptor
+define MS_NAMES Memi[$1+1] # Pointer to database names array
+define MS_DATA Memi[$1+1+$2] # Pointers to data from database
+
+# MULTISPEC Header stored in database.
+
+define LEN_MS_HDR 84 # Length of MULTISPEC Header
+
+define MS_IMAGE Memi[MS_DATA($1,HDR)] # Image filename
+define MS_TITLE Memi[MS_DATA($1,HDR)+40] # Title from the image
+define MS_NSPECTRA Memi[MS_DATA($1,HDR)+80] # Number of spectra
+define MS_LEN Memi[MS_DATA($1,HDR)+($2-1)+81] # Image dimensions
+define MS_NSAMPLES Memi[MS_DATA($1,HDR)+83] # Number of sample lines
+
+# User callable macros
+
+define NAME Memc[MS_NAMES($1)+($2-1)*(SZ_MS_KEY+1)]
+define HEADER Memi[MS_DATA($1,HDR)]
+define COMMENT Memc[MS_DATA($1,COMMENTS)+($2-1)]
+define LINE Memi[MS_DATA($1,SAMPLE)+($2-1)]
+define PARAMETER Memr[MS_DATA($1,$2)+($3-1)]
+define CV Memi[MS_DATA($1,$2)+($3-1)]
+
+# Ranges
+
+define LEN_RANGES 2
+
+define X_START 1 # Start of profile in image pixel coordinates
+define DX_START 2 # Start of profile relative to spectra center
+
+# MULTISPEC parameter identifiers
+
+define HDR 1 # MULTISPEC header
+define COMMENTS 2 # MULTISPEC comments
+define SAMPLE 3 # Sample line array
+define I0 4 # Profile scale parameter
+define X0 5 # Profile position parameter
+define X0_FIT 6 # Spectra position fit
+
+define S0 7 # GAUSS5 shape parameter
+define S1 8 # GAUSS5 shape parameter
+define S2 9 # GAUSS5 shape parameter
+define S0_FIT 10 # GAUSS5 shape paramter fit
+define S1_FIT 11 # GAUSS5 shape paramter fit
+define S2_FIT 12 # GAUSS5 shape paramter fit
+
+
+# Models
+define NONE 0 # No model
+define GAUSS5 1 # Five parameter Gaussian model
+define SMOOTH 2 # Data profile smoothing
+
+# Five parameter Gaussian model -- GAUSS5
+define MS_NGAUSS5 5 # Number of GAUSS5 model parameters
+define I0_INDEX 1 # Index values for parameter arrays
+define X0_INDEX 2
+define S0_INDEX 3
+define S1_INDEX 4
+define S2_INDEX 5
diff --git a/noao/twodspec/multispec/msextract.par b/noao/twodspec/multispec/msextract.par
new file mode 100644
index 00000000..f85081c5
--- /dev/null
+++ b/noao/twodspec/multispec/msextract.par
@@ -0,0 +1,20 @@
+# MSEXTRACT
+
+image,f,a,,,,Image to be extracted
+output,f,a,,,,Output extraction image file
+lower,r,h,-10,,,Lower limit of extraction
+upper,r,h,10,,,Upper limit of extraction
+spectra,s,h,"*",,,Spectra to be extracted
+lines,s,h,"*",,,Image lines to be extracted
+ex_model,b,h,no,,,Extract model spectra?
+integrated,b,h,yes,,,Extract integrated spectra?
+unblend,b,h,no,,,Correct spectra for blending?
+clean,b,h,yes,,,Clean bad and discrepant pixels?
+nreplace,i,h,1000,0,,Maximum number of pixels to be cleaned
+sigma_cut,r,h,4.,,,Sigma cutoff for cleaning
+niterate,i,h,1,1,,Maximum number of cleaning iterations per line
+model,s,h,smooth,,,Model for cleaning and/or model extraction
+naverage,i,h,20,,,Number of image lines in average profile model
+fit_type,i,h,2,1,2,Model fitting type for model gauss5
+interpolator,s,h,"spline3",,,Type of image interpolation
+verbose,b,h,no,,,Verbose output?
diff --git a/noao/twodspec/multispec/msextract.x b/noao/twodspec/multispec/msextract.x
new file mode 100644
index 00000000..e3017065
--- /dev/null
+++ b/noao/twodspec/multispec/msextract.x
@@ -0,0 +1,154 @@
+include <fset.h>
+include <imhdr.h>
+include "ms.h"
+
+# EX_OUT -- Write and format the extracted spectra to the output image.
+# SUM_PIXELS -- Sum pixel array between the limits lower and upper.
+# EX_SET_VEBOSE -- Set and print verbose output.
+
+
+# EX_OUT -- Write and format the extracted spectra to the output image.
+#
+# The type of output is selected by the value of ex_integral.
+# If ex_integral = yes then sum the spectra profiles and output one value
+# per spectrum otherwise output the strip spectra profiles.
+
+procedure ex_out (im_out, line_out, spectra, lower, upper, ranges, profiles,
+ len_profile, nspectra, ex_integral)
+
+pointer im_out # Output image file descriptor
+int line_out # Output line
+int spectra[ARB] # Spectra range list
+real lower # Lower integral limit
+real upper # Upper integral limit
+real ranges[nspectra, LEN_RANGES] # Starting points of profiles
+real profiles[len_profile, nspectra] # Real spectra profiles
+int len_profile # Length of spectra profiles
+int nspectra # Number of spectra profiles
+bool ex_integral
+
+int i, spectrum_in, spectrum_out
+real x_min, x_max
+pointer buf_out
+
+int get_next_number()
+real sum_pixels()
+pointer impl3r()
+
+begin
+ # Loop through the selected spectra write an image line for one.
+ spectrum_in = 0
+ spectrum_out = 0
+ while (get_next_number (spectra, spectrum_in) != EOF) {
+ spectrum_out = spectrum_out + 1
+ buf_out = impl3r (im_out, line_out, spectrum_out)
+
+ # Select between integrated and strip spectra output. If
+ # integrated spectra call sum_pixels to integrate the spectrum
+ # profile else output the spectrum profile.
+ if (ex_integral) {
+ x_min = lower - ranges[spectrum_in, DX_START] + 1
+ x_max = upper - ranges[spectrum_in, DX_START] + 1
+ Memr[buf_out] =
+ sum_pixels (profiles[1, spectrum_in], x_min, x_max)
+ } else {
+ do i = 1, len_profile
+ Memr[buf_out + i - 1] = profiles[i, spectrum_in]
+ }
+ }
+end
+
+
+# SUM_PIXELS -- Sum pixel array between the limits lower and upper.
+# The limits may be partial pixels. There is no checking for out of
+# array range limits.
+
+real procedure sum_pixels (pixels, x_min, x_max)
+
+real pixels[ARB] # Pixel array to be summed
+real x_min # Lower limit of sum
+real x_max # Upper limit of sum
+
+int i, i_min, i_max
+real f, value
+
+begin
+ # Determine bounding integer limits.
+ i_min = x_min + 0.5
+ i_max = x_max + 0.5
+
+ # Add partial pixel endpoints.
+
+ f = min (x_max, i_min + 0.5) - x_min
+ value = f * pixels[i_min]
+ if (i_min >= i_max)
+ return (value)
+
+ f = x_max - (i_max - 0.5)
+ value = value + f * pixels[i_max]
+ if (i_min + 1 > i_max - 1)
+ return (value)
+
+ # Sum non-endpoint pixels.
+
+ do i = i_min + 1, i_max - 1
+ value = value + pixels[i]
+
+ return (value)
+end
+
+# EX_SET_VERBOSE -- Output procedures for verbose mode.
+
+procedure ex_set_verbose (verbose)
+
+bool verbose
+
+#entry ex_prnt1 (image_in, image_out)
+char image_in[1]
+char image_out[1]
+
+# entry ex_prnt2 (line_in, line_out)
+int line_in, line_out, nreplaced
+
+bool flag
+
+begin
+ # Toggle verbose output.
+ flag = verbose
+ if (flag)
+ call fseti (STDOUT, F_FLUSHNL, YES)
+ else
+ call fseti (STDOUT, F_FLUSHNL, NO)
+ return
+
+entry ex_prnt1 (image_in, image_out)
+
+ # Set the verbose flag and print general header information.
+ if (flag) {
+ call printf ("\nMULTISPEC Extraction Program\n\n")
+ call printf ("Image being extracted is %s.\n")
+ call pargstr (image_in)
+ call printf ("Output extraction image is %s.\n")
+ call pargstr (image_out)
+ }
+ return
+
+entry ex_prnt2 (line_in, line_out)
+
+ # Print the image line being extracted.
+ if (flag) {
+ call printf ("Input image line = %d and output image line = %d.\n")
+ call pargi (line_in)
+ call pargi (line_out)
+ }
+ return
+
+entry ex_prnt3 (nreplaced)
+
+ # Print the number of pixels replaced in cleaning.
+ if (flag && (nreplaced > 0)) {
+ call printf (" Number of pixels replaced: %d\n")
+ call pargi (nreplaced)
+ }
+ return
+end
diff --git a/noao/twodspec/multispec/msget.x b/noao/twodspec/multispec/msget.x
new file mode 100644
index 00000000..e187015a
--- /dev/null
+++ b/noao/twodspec/multispec/msget.x
@@ -0,0 +1,208 @@
+include <imhdr.h>
+include "ms.h"
+
+# MSGET -- Allocate memory and get data from the MULTISPEC database
+# and associated image.
+#
+# MSGHDR -- Allocate memory and get MULTISPEC header information.
+# MSGCOMMENTS -- Allocate memory and get MULTISPEC comments.
+# MSGPARAM -- Allocate memory and get a line of MULTISPEC parameter data.
+# MSGSAMPLE -- Allocate memory and get SAMPLE line array.
+# MSGFIT -- Get parameter fit for a spectrum.
+# MSGFITS -- Get parameter fit for all spectra.
+# MSGGAUSS5 -- Get a line of GAUSS5 parameter data.
+# MSGIMAGE -- Get a line of the image with possible averaging.
+
+
+# MSGHDR -- Allocate memory and get MULTISPEC header information.
+
+procedure msghdr (ms)
+
+pointer ms # MULTISPEC data structure
+
+int i
+
+int dbread()
+
+begin
+ if (MS_DATA(ms, HDR) == NULL)
+ call calloc (MS_DATA(ms, HDR), LEN_MS_HDR, TY_STRUCT)
+ i = dbread (MS_DB(ms), NAME(ms, HDR), HEADER(ms), 1)
+end
+
+# MSGCOMMENTS -- Allocate memory and get MULTISPEC comments.
+
+procedure msgcomments (ms)
+
+pointer ms # MULTISPEC data structure
+
+int i
+
+int dbread()
+
+begin
+ if (MS_DATA(ms, COMMENTS) == NULL)
+ call calloc (MS_DATA(ms, COMMENTS), SZ_MS_COMMENTS, TY_CHAR)
+ i = dbread (MS_DB(ms), NAME(ms, COMMENTS), COMMENT(ms, 1), 1)
+end
+
+# MSGPARAM -- Allocate memory and get a line of MULTISPEC parameter data.
+
+procedure msgparam (ms, parameter, line)
+
+pointer ms # MULTISPEC data structure
+int parameter # Parameter ID
+int line # Sample line to be obtained
+
+int i
+char reference[SZ_MS_KEY]
+
+bool is_param_id()
+int dbread()
+
+begin
+ # Check if the the requested parameter is valid.
+ if (!is_param_id (parameter))
+ call error (MS_ERROR, "Bad parameter identifier")
+
+ if (MS_DATA(ms, parameter) == NULL)
+ call calloc (MS_DATA(ms, parameter), MS_NSPECTRA(ms), TY_REAL)
+
+ # Make reference to the desired database record.
+ call sprintf (reference, SZ_MS_KEY, "%s[%d]")
+ call pargstr (NAME(ms, parameter))
+ call pargi (line)
+
+ i = dbread (MS_DB(ms), reference, PARAMETER(ms, parameter, 1), 1)
+end
+
+# MSGSAMPLE -- Allocate memory and get SAMPLE line array.
+
+procedure msgsample (ms)
+
+pointer ms # MULTISPEC data structure
+
+int i
+
+int dbread()
+
+begin
+ if (MS_DATA(ms, SAMPLE) == NULL)
+ call malloc (MS_DATA(ms, SAMPLE), MS_NSAMPLES(ms), TY_INT)
+ i = dbread (MS_DB(ms), NAME(ms, SAMPLE), LINE(ms,1), 1)
+end
+
+
+# MSGFIT -- Get parameter fit for a spectrum.
+
+procedure msgfit (ms, parameter, spectrum)
+
+pointer ms # MULTISPEC data structure
+int parameter # Parameter ID for desired fit
+int spectrum # Spectrum
+
+int i
+char reference[SZ_MS_KEY]
+pointer sp, fit
+
+bool is_fit_id()
+int dbread()
+
+errchk cvrestore
+
+begin
+ # Check if for valid parameter id.
+ if (!is_fit_id (parameter))
+ call error (MS_ERROR, "Bad fit identifier")
+
+ # Allocate memory for the curfit pointers.
+ if (MS_DATA(ms, parameter) == NULL)
+ call malloc (MS_DATA(ms, parameter), MS_NSPECTRA(ms), TY_INT)
+
+ # Allocate memory for the curfit coefficients.
+ call smark (sp)
+ call salloc (fit, 7 + MS_NSAMPLES(ms), TY_REAL)
+
+ # Reference appropriate data.
+ call sprintf (reference, SZ_MS_KEY, "%s[%d]")
+ call pargstr (NAME(ms, parameter))
+ call pargi (spectrum)
+
+ i = dbread (MS_DB(ms), reference, Memr[fit], 1)
+ iferr (call cvrestore (CV(ms, parameter, spectrum), Memr[fit]))
+ ;
+
+ call sfree (sp)
+end
+
+
+# MSGFITS -- Get parameter fits.
+
+procedure msgfits (ms, parameter)
+
+pointer ms # MULTISPEC data structure
+int parameter # Parameter ID for desired fit
+
+int i
+
+begin
+ do i = 1, MS_NSPECTRA(ms)
+ call msgfit (ms, parameter, i)
+end
+
+
+# MSGGAUSS5 -- Get a line of GAUSS5 parameter data.
+
+procedure msggauss5 (ms, line)
+
+pointer ms # MULTISPEC data structure
+int line # Sample line to be obtained
+
+begin
+ call msgparam (ms, I0, line)
+ call msgparam (ms, X0, line)
+ call msgparam (ms, S0, line)
+ call msgparam (ms, S1, line)
+ call msgparam (ms, S2, line)
+end
+
+
+# MSGIMAGE -- Get a line of the image with possible averaging.
+
+procedure msgimage (im, line, naverage, data)
+
+pointer im # Image descriptor
+int line # Line to be gotten from the image
+int naverage # Number of line to use in average
+real data[ARB] # The output data array
+
+int i, line_start, line_end
+real nlines
+pointer buf
+
+pointer imgl2r()
+
+begin
+ # If naverage is <= 1 copy the image line to the data array
+ # Else average the several lines.
+
+ if (naverage <= 1) {
+ call amovr (Memr[imgl2r (im, line)], data, IM_LEN(im,1))
+ } else {
+ # Determine starting and ending lines for the average.
+ line_start = max (1, line - naverage / 2)
+ line_end = min (IM_LEN(im, 2), line_start + naverage - 1)
+
+ # Clear data array for accumulating sum and then vector
+ # add the image lines.
+ call aclrr (data, IM_LEN(im, 1))
+ do i = line_start, line_end {
+ buf = imgl2r (im, i)
+ call aaddr (Memr[buf], data, data, IM_LEN(im, 1))
+ }
+
+ # Vector divide by the number of lines to form average.
+ nlines = line_end - line_start + 1
+ call adivkr (data, nlines, data, IM_LEN(im, 1))
+ }
+end
diff --git a/noao/twodspec/multispec/msio.x b/noao/twodspec/multispec/msio.x
new file mode 100644
index 00000000..583c2253
--- /dev/null
+++ b/noao/twodspec/multispec/msio.x
@@ -0,0 +1,194 @@
+include <error.h>
+include <imhdr.h>
+include "ms.h"
+
+# MSIO -- MULTISPEC interface to DBMS.
+#
+# MSMAP -- Map a MULTISPEC database.
+# MSUNMAP -- Close MULTISPEC database and free MSIO memory allocation.
+# MSGDES -- Allocate and return a MSIO descriptor. Post error recovery.
+# MS_FREE_DES -- Close a database and free allocated memory.
+# MS_ERROR -- Take error recovery action by closing all open databases.
+
+
+# MSMAP -- Map a MULTISPEC database.
+#
+# The database name is formed by adding the extension '.db' to the image.
+#
+# For a new database:
+# Create the database, make entries for the header and comments,
+# allocate memory for the header and comments and return MSIO descriptor.
+# For an existing database:
+# Open the database, allocate memory and read the header, comments, and
+# sample line array, and return MSIO descriptor.
+
+pointer procedure msmap (image, mode, max_entries)
+
+# Procedure msmap parameters:
+char image[ARB] # Image
+int mode # Access mode for database
+int max_entries # Maximum number of entries
+
+char database[SZ_FNAME] # MULTISPEC database filename
+pointer db, ms
+
+pointer dbopen()
+
+begin
+ # Create the database filename.
+ call sprintf (database, SZ_FNAME, "%s.db")
+ call pargstr (image)
+
+ # Open the database with specified mode and max_entries.
+ db = dbopen (database, mode, max_entries)
+
+ # Get an MSIO descriptor.
+ call msgdes (ms)
+ MS_DB(ms) = db
+
+ if (mode == NEW_FILE) {
+ # For a NEW_FILE enter the header and comment records and
+ # call msghdr and msgcomments to allocate memory.
+ call dbenter (db, NAME(ms, HDR), LEN_MS_HDR * SZ_STRUCT, 1)
+ call dbenter (db, NAME(ms, COMMENTS), SZ_MS_COMMENTS + 1, 1)
+ call msghdr (ms)
+ call msgcomments (ms)
+ } else {
+ # For an existing database read the header, comments, and
+ # sample line array.
+ call msghdr (ms)
+ call msgcomments (ms)
+ call msgsample (ms)
+ }
+
+ # Return MSIO descriptor.
+ return (ms)
+end
+
+
+# MSUNMAP -- Close MULTISPEC database and free MSIO memory allocation.
+
+procedure msunmap (ms)
+
+pointer ms # MSIO descriptor
+
+begin
+ call dbclose (MS_DB(ms))
+ call ms_free_des (ms)
+end
+
+
+# Procedures accessing the MSIO descriptor list.
+#
+# MSGDES -- Allocate and return a MSIO descriptor. Post error recovery.
+# MS_FREE_DES -- Close a database and free allocated memory.
+# MS_ERROR -- Take error recovery action by closing all open databases.
+
+procedure msgdes (ms)
+
+pointer ms # MSIO descriptor
+
+int init
+
+extern ms_error()
+
+int ndes # Number of allocated MSIO descriptors
+pointer msdes[MS_MAX_DES] # MSIO descriptor list
+
+common /msiocom/ ndes, msdes
+
+data init/YES/
+
+begin
+ # Initialize and post error recovery.
+ if (init == YES) {
+ ndes = 0
+ call onerror (ms_error)
+ init = NO
+ }
+
+ # Check if requested descriptor would overflow the descriptor list.
+ if (ndes == MS_MAX_DES)
+ call error (MS_ERROR, "Attempt to open too many MULTISPEC files")
+
+ # Allocate memory for the descriptor and enter in pointer in list.
+ ndes = ndes + 1
+ call malloc (msdes[ndes], LEN_MS_DES, TY_STRUCT)
+ ms = msdes[ndes]
+
+ # Initialize descriptor to NULL.
+ call amovki (NULL, Memi[ms], LEN_MS_DES)
+
+ # Initialize the MULTISPEC database name list.
+ call msnames (ms)
+end
+
+# MS_FREE_DES -- Close a database and free allocated memory.
+
+procedure ms_free_des (ms)
+
+pointer ms # MSIO descriptor to be freed
+
+int i, j
+
+int ndes # Number of allocated MSIO descriptors
+pointer msdes[MS_MAX_DES] # MSIO descriptor list
+
+common /msiocom/ ndes, msdes
+
+begin
+ # Locate the specified descriptor in the descriptor list.
+ # If the descriptor is not in the list do nothing.
+ # If the descriptor is in the list free allocated memory and remove
+ # the entry from the list.
+
+ for (i = 1; (i <= ndes) && (ms != msdes[i]); i = i + 1)
+ ;
+ if (i > ndes)
+ return
+
+ call mfree (MS_DATA(ms, HDR), TY_STRUCT)
+ call mfree (MS_DATA(ms, COMMENTS), TY_CHAR)
+ call mfree (MS_DATA(ms, SAMPLE), TY_INT)
+ call mfree (MS_DATA(ms, I0), TY_REAL)
+ call mfree (MS_DATA(ms, X0), TY_REAL)
+ call mfree (MS_DATA(ms, S0), TY_REAL)
+ call mfree (MS_DATA(ms, S1), TY_REAL)
+ call mfree (MS_DATA(ms, S2), TY_REAL)
+ if (MS_DATA(ms, X0_FIT) != NULL) {
+ do j = 1, MS_NSPECTRA(ms)
+ if (CV(ms, X0_FIT, j) != NULL)
+ call cvfree (CV(ms, X0_FIT, j))
+ call mfree (MS_DATA(ms, X0_FIT), TY_INT)
+ }
+ call mfree (ms, TY_STRUCT)
+
+ if (i < ndes)
+ msdes[i] = msdes[ndes]
+ ndes = ndes - 1
+end
+
+# MS_ERROR -- Take error recovery action by closing all open databases.
+
+procedure ms_error (error_code)
+
+int error_code # Error code for error recovery
+
+int i, ndes1
+
+int ndes # Number of allocated MSIO descriptors
+pointer msdes[MS_MAX_DES] # MSIO descriptor list
+
+common /msiocom/ ndes, msdes
+
+begin
+ # Let DBMS deal with the database descriptor,
+ # fio_cleanup deal with the open files, and the system
+ # restart deal with freeing the stack. This procedure
+ # cleans up the msio descriptors and memory allocations.
+ # The system may eventually deal with heap memory recovery.
+
+ ndes1 = ndes
+ do i = 1, ndes1
+ call ms_free_des (msdes[i])
+end
diff --git a/noao/twodspec/multispec/mslist.par b/noao/twodspec/multispec/mslist.par
new file mode 100644
index 00000000..77f3998b
--- /dev/null
+++ b/noao/twodspec/multispec/mslist.par
@@ -0,0 +1,7 @@
+# MSLIST
+
+image,f,a,,,,Image to be listted
+keyword,s,a,,,,Keyword for data to be listed
+lines,s,a,,,,Images lines to be listed
+spectra,s,a,,,,Spectra to be listed
+titles,b,h,no,,,Print additional titles?
diff --git a/noao/twodspec/multispec/msnames.x b/noao/twodspec/multispec/msnames.x
new file mode 100644
index 00000000..93651b18
--- /dev/null
+++ b/noao/twodspec/multispec/msnames.x
@@ -0,0 +1,140 @@
+include "ms.h"
+
+# The procedures in this file deal with the mapping of the
+# database names to the MULTISPEC identifiers and relations between the
+# identifiers and their meaning.
+#
+# MSNAMES -- Allocate memory and set name array in MULTISPEC data structure.
+# MS_DB_ID -- Associate a database name to the MULTISPEC identifier.
+# IS_PARAM_ID -- Test if an identifier refers to a model parameter.
+# IS_FIT_ID -- Test if an identifier refers to a curfit parameter fit.
+# MS_FIT_ID -- Return fit identifier for specified parameter identifier.
+# MS_MODEL_ID -- CL get a model name and map to a MULTISPEC identifier.
+
+# MSNAMES -- Allocate memory and set the name array in MULTISPEC data structure.
+#
+# The name array maps the integer identifiers with the names in the
+# database. The name array is also allocated if necessary.
+# This is the only place where the database names are explicitly known.
+
+procedure msnames (ms)
+
+pointer ms
+
+begin
+ if (MS_NAMES(ms) == NULL)
+ call calloc (MS_NAMES(ms), MS_DB_ENTRIES * (SZ_MS_KEY + 1), TY_CHAR)
+
+ # Set name array mapping the MULTISPEC IDs to the database names.
+ call sprintf (NAME(ms, HDR), SZ_MS_KEY, "header")
+ call sprintf (NAME(ms, COMMENTS), SZ_MS_KEY, "comments")
+ call sprintf (NAME(ms, SAMPLE), SZ_MS_KEY, "samples")
+ call sprintf (NAME(ms, I0), SZ_MS_KEY, "i0")
+ call sprintf (NAME(ms, X0), SZ_MS_KEY, "x0")
+ call sprintf (NAME(ms, X0_FIT), SZ_MS_KEY, "x0 fit")
+ call sprintf (NAME(ms, S0), SZ_MS_KEY, "s0")
+ call sprintf (NAME(ms, S1), SZ_MS_KEY, "s1")
+ call sprintf (NAME(ms, S2), SZ_MS_KEY, "s2")
+ call sprintf (NAME(ms, S0_FIT), SZ_MS_KEY, "s0 fit")
+ call sprintf (NAME(ms, S1_FIT), SZ_MS_KEY, "s1 fit")
+ call sprintf (NAME(ms, S2_FIT), SZ_MS_KEY, "s2 fit")
+end
+
+
+# MS_DB_ID -- Associate a database name to the MULTISPEC identifier.
+#
+# The input entry name is matched with a database name and the
+# MULTISPEC identifier is returned.
+
+int procedure ms_db_id (ms, entry)
+
+pointer ms
+char entry[ARB]
+
+int i
+
+bool streq()
+
+begin
+ do i = 1, MS_DB_ENTRIES
+ if (streq (entry, NAME(ms, i)))
+ return (i)
+
+ return (0)
+end
+
+
+# IS_PARAM_ID -- Test if an identifier refers to a model parameter.
+
+bool procedure is_param_id (param_id)
+
+int param_id
+
+begin
+ switch (param_id) {
+ case X0, I0, S0, S1, S2:
+ return (TRUE)
+ default:
+ return (FALSE)
+ }
+end
+
+
+# IS_FIT_ID -- Test if an identifier refers to a parameter fit.
+
+bool procedure is_fit_id (fit_id)
+
+int fit_id
+
+begin
+ switch (fit_id) {
+ case X0_FIT, S0_FIT, S1_FIT, S2_FIT:
+ return (TRUE)
+ default:
+ return (FALSE)
+ }
+end
+
+
+# MS_FIT_ID -- Return fit identifier for specified parameter identifier.
+
+int procedure ms_fit_id (param_id)
+
+int param_id
+
+begin
+ switch (param_id) {
+ case X0:
+ return (X0_FIT)
+ case S0:
+ return (S0_FIT)
+ case S1:
+ return (S1_FIT)
+ case S2:
+ return (S2_FIT)
+ default:
+ return (ERR)
+ }
+end
+
+# MS_MODEL_ID -- CL get a model name and map to a MULTISPEC identifier.
+#
+# This procedure isolates the model definitions to protect against
+# changes in the model names or the order and choice of identifiers
+# in ms.h.
+
+int procedure ms_model_id (param)
+
+char param[ARB] # CL parameter name
+char str[SZ_LINE]
+int i, clgwrd()
+
+begin
+ i = clgwrd (param, str, SZ_LINE, ",gauss5,smooth,")
+ switch (i) {
+ case 1:
+ return (GAUSS5)
+ case 2:
+ return (SMOOTH)
+ }
+end
diff --git a/noao/twodspec/multispec/msplot.par b/noao/twodspec/multispec/msplot.par
new file mode 100644
index 00000000..013a40de
--- /dev/null
+++ b/noao/twodspec/multispec/msplot.par
@@ -0,0 +1,9 @@
+# Parameter file for MSPLOT
+
+image,f,a,,,,Image to be plotted
+line,i,a,,,,Image line to be plotted
+naverage,i,h,20,,,Number of image lines to average
+lower,r,h,-10,,,Lower limit of model profiles
+upper,r,h,10,,,Upper limit of model profiles
+graphics,s,h,"stdgraph",,,Graphics output device
+cursor,*gcur,h,"",,,Graphics cursor input
diff --git a/noao/twodspec/multispec/msplot.x b/noao/twodspec/multispec/msplot.x
new file mode 100644
index 00000000..4e02367f
--- /dev/null
+++ b/noao/twodspec/multispec/msplot.x
@@ -0,0 +1,104 @@
+include <imhdr.h>
+include "ms.h"
+
+# MSPLOT -- Plot image and model values.
+#
+# The output list format is column, image line, data value, model value.
+# This task differs from t_new_image primarily in that there is no profile
+# interpolation. The model is evaluated only at the sample lines. It
+# is used to check the results of the model fitting tasks.
+
+procedure msplot ()
+
+char image[SZ_FNAME] # Image
+int line # Image line to plot
+int naverage # Number of image lines to average
+real lower # Lower limit of profile model
+real upper # Upper limit of profile model
+
+int sample
+pointer ms, im
+pointer sp, data, model
+
+int clgeti(), get_sample_line
+real clgetr()
+pointer msmap(), immap()
+
+begin
+ # Get the task parameters.
+
+ call clgstr ("image", image, SZ_FNAME)
+ line = clgeti ("line")
+ naverage = clgeti ("naverage")
+ lower = clgetr ("lower")
+ upper = clgetr ("upper")
+
+ # Access the database and image.
+
+ ms = msmap (image, READ_ONLY, 0)
+ im = immap (image, READ_ONLY, 0)
+
+ # Allocate memory for the data and model.
+
+ call smark (sp)
+ call salloc (data, IM_LEN(im, 1), TY_REAL)
+ call salloc (model, IM_LEN(im, 1), TY_REAL)
+
+ sample = get_sample_line (ms, line)
+ line = LINE(ms, sample)
+ call msgimage (im, line, naverage, Memr[data])
+ call gauss5_model (ms, sample, lower, upper, Memr[model])
+
+ call ms_graph (Memr[data], Memr[model], IM_LEN(im, 1))
+
+ call sfree (sp)
+ call msunmap (ms)
+ call imunmap (im)
+end
+
+
+include <gset.h>
+
+# MS_GRAPH -- For the selected line get the data line and compute a model line.
+# Graph the data and model values.
+
+procedure ms_graph (data, model, npts)
+
+real data[npts] # Image data
+real model[npts] # Model data
+int npts # Number of data points
+
+char str[SZ_LINE]
+real x1, x2
+pointer gp, gt
+
+real wx, wy # Cursor position
+int wcs, key # WCS and cursor key
+
+int gt_gcur()
+pointer gopen(), gt_init()
+
+begin
+ call clgstr ("graphics", str, SZ_LINE)
+ gp = gopen (str, NEW_FILE, STDGRAPH)
+ gt = gt_init ()
+
+ x1 = 1
+ x2 = npts
+ call gswind (gp, x1, x2, INDEF, INDEF)
+ call gascale (gp, data, npts, 2)
+ call grscale (gp, model, npts, 2)
+ call gt_swind (gp, gt)
+ call gt_labax (gp, gt)
+
+ call gseti (gp, G_PLTYPE, 1)
+ call gvline (gp, data, npts, x1, x2)
+ call gseti (gp, G_PLTYPE, 2)
+ call gvline (gp, model, npts, x1, x2)
+
+ while (gt_gcur ("cursor", wx, wy, wcs, key, str, SZ_LINE) != EOF)
+ ;
+
+ call gclose (gp)
+ call gt_free (gt)
+end
diff --git a/noao/twodspec/multispec/msput.x b/noao/twodspec/multispec/msput.x
new file mode 100644
index 00000000..e24e825b
--- /dev/null
+++ b/noao/twodspec/multispec/msput.x
@@ -0,0 +1,123 @@
+include "ms.h"
+
+# MSPUT -- Put information in the MULTISPEC database.
+#
+# MSPHDR -- Put MULTISPEC header record in the database.
+# MSPCOMMENTS -- Put MULTISPEC comment record into the database.
+# MSPSAMPLE -- Put MULTISPEC sample record into the database.
+# MSPPARAM -- Put a line of MULTISPEC parameter data.
+# MSPGAUSS5 -- Put a line of GAUSS5 parameter data.
+# MSPFIT -- Put fit coefficients for a spectrum.
+# MSPFITS -- Put fit coefficients for all spectra.
+
+
+# MSPHDR -- Put MULTISPEC header record in the database.
+
+procedure msphdr (ms)
+
+pointer ms # MSIO descriptor
+
+begin
+ call dbwrite (MS_DB(ms), NAME(ms, HDR), HEADER(ms), 1)
+end
+
+
+# MSPCOMMENTS -- Put MULTISPEC comment record into the database.
+
+procedure mspcomments (ms)
+
+pointer ms # MSIO descriptor
+
+begin
+ call dbwrite (MS_DB(ms), NAME(ms, COMMENTS), COMMENT(ms, 1), 1)
+end
+
+
+# MSPSAMPLE -- Put MULTISPEC sample record into the database.
+
+procedure mspsample (ms)
+
+pointer ms # MSIO descriptor
+
+begin
+ call dbwrite (MS_DB(ms), NAME(ms, SAMPLE), LINE(ms,1), 1)
+end
+
+# MSPPARAM -- Put a line of MULTISPEC parameter data.
+
+procedure mspparam (ms, parameter, line)
+
+pointer ms # MSIO descriptor
+int parameter # Index to parameter array
+int line # Line to be read
+
+char reference[SZ_MS_KEY]
+
+bool is_param_id()
+
+begin
+ if (!is_param_id (parameter))
+ call error (MS_ERROR, "Bad parameter identifier")
+
+ call sprintf (reference, SZ_MS_KEY, "%s[%d]")
+ call pargstr (NAME(ms, parameter))
+ call pargi (line)
+
+ call dbwrite (MS_DB(ms), reference, PARAMETER(ms,parameter,1), 1)
+end
+
+
+# MSPGAUSS5 -- Put a line of GAUSS5 parameter data.
+
+procedure mspgauss5 (ms, line)
+
+pointer ms
+int line
+
+begin
+ call mspparam (ms, I0, line)
+ call mspparam (ms, X0, line)
+ call mspparam (ms, S0, line)
+ call mspparam (ms, S1, line)
+ call mspparam (ms, S2, line)
+end
+
+# MSPFIT -- Put parameter fit data.
+
+procedure mspfit (ms, parameter, spectrum)
+
+pointer ms # MSIO descriptor
+int parameter # Parameter to be put
+int spectrum # Spectrum to be put
+
+char reference[SZ_MS_KEY]
+pointer sp, fit
+
+begin
+ call smark (sp)
+ call salloc (fit, 7 + MS_NSAMPLES(ms), TY_REAL)
+
+ call sprintf (reference, SZ_MS_KEY, "%s[%d]")
+ call pargstr (NAME(ms, parameter))
+ call pargi (spectrum)
+
+ call cvsave (CV(ms, parameter, spectrum), Memr[fit])
+ call dbwrite (MS_DB(ms), reference, Memr[fit], 1)
+
+ call sfree (sp)
+end
+
+
+# MSPFITS -- Put parameter fits.
+
+procedure mspfits (ms, parameter)
+
+pointer ms # MULTISPEC data structure
+int parameter # Parameter ID for desired fit
+
+int i
+
+begin
+ do i = 1, MS_NSPECTRA(ms)
+ call mspfit (ms, parameter, i)
+end
diff --git a/noao/twodspec/multispec/msset.par b/noao/twodspec/multispec/msset.par
new file mode 100644
index 00000000..8c11c205
--- /dev/null
+++ b/noao/twodspec/multispec/msset.par
@@ -0,0 +1,9 @@
+# MSSET
+
+image,f,a,,,,Image
+keyword,s,a,,,,Keyword for data to be set
+value,s,a,,,,Input value
+lines,s,h,"*",,,Images lines to be affected
+spectra,s,h,"*",,,Spectra to be affected
+read_list,b,h,no,,,Read values from a list?
+list,*s,h,,,,Input list
diff --git a/noao/twodspec/multispec/mssmooth.x b/noao/twodspec/multispec/mssmooth.x
new file mode 100644
index 00000000..be7e01ca
--- /dev/null
+++ b/noao/twodspec/multispec/mssmooth.x
@@ -0,0 +1,81 @@
+include <math/curfit.h>
+
+# MS_SMOOTH -- Smooth MULTISPEC parameters with the CURFIT package.
+# MS_SET_SMOOTH -- Initialize and define function for smoothing.
+# MS_FREE_SMOOTH -- Free allocated memory from smoothing.
+
+# This procedure is numerical and does not depend on the MULTISPEC
+# package.
+
+procedure ms_smooth (x, y)
+
+real x[ARB] # Array of x values
+real y[ARB] # Array of y values
+int curve_type # Curfit function
+int order # Order of function
+real xmin # Minimum x value
+real xmax # Maximum x value
+int npoints # Number of points in fits
+
+int i, npts, ier
+real xmn, xmx
+pointer cv, w
+
+real cveval()
+
+data cv/NULL/, w/NULL/
+
+begin
+ # Check for a valid curfit pointer.
+ if (cv == NULL)
+ call error (0, "param_smooth: Undefined smoothing function")
+
+ # Zero and fit the data with uniform weights.
+ call cvzero (cv)
+ # call cvfit (cv, x, y, Memr[w], npts, WTS_UNIFORM, ier)
+
+ # Accumulate points and check for out of bounds points.
+ do i = 1, npts
+ if ((x[i] >= xmn) && (x[i] <= xmx))
+ call cvaccum (cv, x[i], y[i], Memr[w+i-1], WTS_UNIFORM)
+ call cvsolve (cv, ier)
+
+ if (ier != OK)
+ call error (0, "param_smooth: Error in function fit")
+
+ # Evaluate fit placing fit values back in y array.
+ # call cvvector (cv, x, y, npts)
+ do i = 1, npts
+ if ((x[i] >= xmn) && (x[i] <= xmx))
+ y[i] = cveval (cv, x[i])
+
+ return
+
+entry ms_set_smooth (xmin, xmax, npoints)
+
+ # Set or reset curfit data structure and allocate memory for weights.
+ if (cv != NULL)
+ call cvfree (cv)
+ if (w == NULL)
+ call malloc (w, npoints, TY_REAL)
+
+ # Determine curve_type and order.
+ call clgcurfit ("function", "order", curve_type, order)
+
+ # Initialize curfit data structure and record number of points.
+ xmn = xmin
+ xmx = xmax
+ call cvinit (cv, curve_type, order, xmn, xmx)
+ npts = npoints
+
+ return
+
+entry ms_free_smooth ()
+
+ # Free allocated memory.
+ if (cv != NULL)
+ call cvfree (cv)
+ if (w != NULL)
+ call mfree (w, TY_REAL)
+
+end
diff --git a/noao/twodspec/multispec/multispec.cl b/noao/twodspec/multispec/multispec.cl
new file mode 100644
index 00000000..12229f83
--- /dev/null
+++ b/noao/twodspec/multispec/multispec.cl
@@ -0,0 +1,21 @@
+#{ MULTISPEC -- The MULTISPEC package.
+
+package multispec
+
+task newextraction,
+ findpeaks,
+ msset,
+ mslist,
+ fitfunction,
+ msextract,
+ newimage,
+ modellist,
+ msplot,
+ fitgauss5 = multispec$x_multispec.e
+
+# Scripts
+task _msfindspec1 = multispec$_msfindspec1.cl
+task _msfindspec2 = multispec$_msfindspec2.cl
+task _msfindspec3 = multispec$_msfindspec3.cl
+
+clbye
diff --git a/noao/twodspec/multispec/multispec.hd b/noao/twodspec/multispec/multispec.hd
new file mode 100644
index 00000000..a798e54a
--- /dev/null
+++ b/noao/twodspec/multispec/multispec.hd
@@ -0,0 +1,14 @@
+# Help directory for the MULTISPEC package.
+
+$doc = "./doc/"
+
+findpeaks hlp=doc$findpeaks.hlp, src=t_findpeaks.x
+fitfunction hlp=doc$fitfunc.hlp, src=t_fitfunc.x
+fitgauss5 hlp=doc$fitgauss5.hlp, src=t_fitgauss5.x
+modellist hlp=doc$modellist.hlp, src=t_modellist.x
+msextract hlp=doc$msextract.hlp, src=t_msextract.x
+mslist hlp=doc$mslist.hlp, src=t_mslist.x
+msplot hlp=doc$msplot.hlp, src=t_msplot.cl
+msset hlp=doc$msset.hlp, src=t_msset.x
+newextraction hlp=doc$newextract.hlp, src=t_newextract.x
+newimage hlp=doc$newimage.hlp, src=t_newimage.x
diff --git a/noao/twodspec/multispec/multispec.hlp b/noao/twodspec/multispec/multispec.hlp
new file mode 100644
index 00000000..b7083fb3
--- /dev/null
+++ b/noao/twodspec/multispec/multispec.hlp
@@ -0,0 +1,14 @@
+.help multispec OCT85 noao.twodspec.multispec
+.nf
+ findpeaks - Find the peaks
+ fitfunction - Fit a function to the spectra parameter values
+ fitgauss5 - Fit spectra profiles with five parameter Gaussian model
+ modellist - List data and model pixel values
+ msextract - Extract spectra
+ mslist - List entries in a MULTISPEC database
+ msplot - Plot a line of image and model data
+ msset - Set entries in a MULTISPEC database
+ newextraction - Create a new MULTISPEC extraction database
+ newimage - Create a new multi-spectra image
+.fi
+.endhelp
diff --git a/noao/twodspec/multispec/multispec.men b/noao/twodspec/multispec/multispec.men
new file mode 100644
index 00000000..4425164f
--- /dev/null
+++ b/noao/twodspec/multispec/multispec.men
@@ -0,0 +1,10 @@
+ findpeaks - Find the peaks
+ fitfunction - Fit a function to the spectra parameter values
+ fitgauss5 - Fit spectra profiles with five parameter Gaussian model
+ modellist - List data and model pixel values
+ msextract - Extract spectra
+ mslist - List entries in a MULTISPEC database
+ msplot - Plot a line of image and model data
+ msset - Set entries in a MULTISPEC database
+ newextraction - Create a new MULTISPEC extraction database
+ newimage - Create a new multi-spectra image
diff --git a/noao/twodspec/multispec/multispec.par b/noao/twodspec/multispec/multispec.par
new file mode 100644
index 00000000..ce7cb587
--- /dev/null
+++ b/noao/twodspec/multispec/multispec.par
@@ -0,0 +1,3 @@
+# MULTISPEC Package parameter file.
+
+version,s,h,"October 1984"
diff --git a/noao/twodspec/multispec/newextraction.par b/noao/twodspec/multispec/newextraction.par
new file mode 100644
index 00000000..17a0bda5
--- /dev/null
+++ b/noao/twodspec/multispec/newextraction.par
@@ -0,0 +1,5 @@
+# NEWEXTRACTION
+
+image,f,a,,,,Image to be extracted
+template,f,a,"",,,Template image to use for initialization
+sample_lines,s,h,"10x50",,,Sample image lines
diff --git a/noao/twodspec/multispec/newimage.par b/noao/twodspec/multispec/newimage.par
new file mode 100644
index 00000000..24465786
--- /dev/null
+++ b/noao/twodspec/multispec/newimage.par
@@ -0,0 +1,17 @@
+# NEWIMAGE
+
+image,f,a,,,,Image to be used as a model
+outpu,f,a,,,,Output image to be created
+lower,r,h,-10,,,Lower limit of extraction
+upper,r,h,10,,,Upper limit of extraction
+lines,s,h,"*",,,Image lines to be extracted
+ex_model,b,h,no,,,Extract model spectra?
+clean,b,h,yes,,,Clean bad and discrepant pixels?
+nreplace,i,h,1000,0,,Maximum number of pixels to be cleaned
+sigma_cut,r,h,4.,,,Sigma cutoff for cleaning
+niterate,i,h,1,1,,Maximum number of cleaning iterations per line
+model,s,h,smooth,,,Model for cleaning and/or model extraction
+naverage,i,h,20,,,Number of image lines in average profile model
+fit_type,i,h,2,1,2,Model fitting type for model gauss5
+interpolator,s,h,"spline3",,,Type of image interpolation
+verbose,b,h,no,,,Verbose output?
diff --git a/noao/twodspec/multispec/peaks.x b/noao/twodspec/multispec/peaks.x
new file mode 100644
index 00000000..910e66a5
--- /dev/null
+++ b/noao/twodspec/multispec/peaks.x
@@ -0,0 +1,397 @@
+# PEAKS -- The following procedures are general numerical functions
+# dealing with finding peaks in a data array.
+#
+# FIND_PEAKS Find the peaks in the data array.
+# FIND_LOCAL_MAXIMA Find the local maxima in the data array.
+# IS_LOCAL_MAX Test a point to determine if it is a local maximum.
+# FIND_THRESHOLD Find the peaks with positions satisfying threshold
+# and contrast constraints.
+# FIND_ISOLATED Flag peaks which are within separation of a peak
+# with a higher peak value.
+# FIND_NMAX Select up to the nmax highest ranked peaks.
+# COMPARE Compare procedure for sort used in FIND_PEAKS.
+
+# FIND_PEAKS -- Find the peaks in the data array.
+#
+# The peaks are found using the following algorithm:
+#
+# 1. Find the local maxima.
+# 2. Reject peaks below the threshold.
+# 3. Determine the ranks of the remaining peaks.
+# 4. Flag weaker peaks within separation of a stronger peak.
+# 5. Accept at most the nmax strongest peaks.
+#
+# Indefinite points are ignored. The peak positions are returned in the
+# array x.
+
+int procedure find_peaks (data, x, npoints, contrast, separation, edge, nmax,
+ threshold, debug)
+
+# Procedure parameters:
+real data[npoints] # Input data array
+real x[npoints] # Output peak position array
+int npoints # Number of data points
+real contrast # Maximum contrast between strongest and weakest
+int separation # Minimum separation between peaks
+int edge # Minimum distance from the edge
+int nmax # Maximum number of peaks to be returned
+real threshold # Minimum threshold level for peaks
+bool debug # Print diagnostic information?
+
+int i, j
+int nlmax, nthreshold, nisolated, npeaks
+pointer sp, y, rank
+
+int find_local_maxima(), find_threshold(), find_isolated(), find_nmax()
+int compare()
+
+extern compare()
+
+common /sort/ y
+
+begin
+ # Find the local maxima in data and put column positions in x..
+ nlmax = find_local_maxima (data, x, npoints, debug)
+
+ # Reject local maxima near the edge.
+ if (edge > 0) {
+ j = 0
+ do i = 1, nlmax {
+ if ((x[i] > edge) && (x[i] <= npoints - edge)) {
+ j = j + 1
+ x[j] = x[i]
+ }
+ }
+ nlmax = j
+ }
+
+ # Allocate a working array y.
+ call smark (sp)
+ call salloc (y, npoints, TY_REAL)
+
+ # Reject the local maxima which do not satisfy the thresholds.
+ # The array y is set to the peak values of the remaining peaks.
+ nthreshold = find_threshold (data, x, Memr[y], nlmax,
+ contrast, threshold, debug)
+
+ # Rank the peaks by peak value.
+ call salloc (rank, nthreshold, TY_INT)
+ do i = 1, nthreshold
+ Memi[rank + i - 1] = i
+ call qsort (Memi[rank], nthreshold, compare)
+
+ # Reject the weaker peaks within sep of a stronger peak.
+ nisolated = find_isolated (x, Memi[rank], nthreshold, separation,
+ debug)
+
+ # Select the strongest nmax peaks.
+ npeaks = find_nmax (data, x, Memi[rank], nthreshold, nmax, debug)
+
+ call sfree (sp)
+ return (npeaks)
+end
+
+
+# FIND_LOCAL_MAXIMA -- Find the local maxima in the data array.
+#
+# A data array is input and the local maxima positions array is output.
+# The number of local maxima found is returned.
+
+int procedure find_local_maxima (data, x, npoints, debug)
+
+real data[npoints] # Input data array
+real x[npoints] # Output local maxima positions array
+int npoints # Number of input points
+bool debug # Print debugging information?
+
+int i, nlmax
+
+bool is_local_max()
+
+begin
+ nlmax = 0
+ do i = 1, npoints {
+ if (is_local_max (i, data, npoints)) {
+ nlmax = nlmax + 1
+ x[nlmax] = i
+ }
+ }
+
+ if (debug) {
+ call printf (" Number of local maxima found = %d.\n")
+ call pargi (nlmax)
+ }
+
+ return (nlmax)
+end
+
+
+# IS_LOCAL_MAX -- Test a point to determine if it is a local maximum.
+#
+# Indefinite points are ignored.
+
+bool procedure is_local_max (index, data, npoints)
+
+# Procedure parameters:
+int index # Index to test for local maximum
+real data[npoints] # Data values
+int npoints # Number of points in the data vector
+
+int i, j, nright, nleft
+
+begin
+ # INDEFR points cannot be local maxima.
+ if (IS_INDEFR (data[index]))
+ return (false)
+
+ # Find the left and right indices where data values change and the
+ # number of points with the same value. Ignore INDEFR points.
+ nleft = 0
+ for (i = index - 1; i >= 1; i = i - 1) {
+ if (!IS_INDEFR (data[i])) {
+ if (data[i] != data[index])
+ break
+ nleft = nleft + 1
+ }
+ }
+ nright = 0
+ for (j = index + 1; i <= npoints; j = j + 1) {
+ if (!IS_INDEFR (data[j])) {
+ if (data[j] != data[index])
+ break
+ nright = nright + 1
+ }
+ }
+
+ # Test for failure to be a local maxima
+ if ((i == 0) && (j == npoints)) {
+ return (FALSE) # Data is constant
+ } else if (i == 0) {
+ if (data[j] > data[index])
+ return (FALSE) # Data increases to right
+ } else if (j == npoints) {
+ if (data[i] > data[index]) # Data increase to left
+ return (FALSE)
+ } else if ((data[i] > data[index]) || (data[j] > data[index])) {
+ return (FALSE) # Not a local maximum
+ } else if (!((nleft - nright == 0) || (nleft - nright == 1))) {
+ return (FALSE) # Not center of plateau
+ }
+
+ # Point is a local maxima
+ return (TRUE)
+end
+
+
+
+
+# FIND_THRESHOLD -- Find the peaks with positions satisfying threshold
+# and contrast constraints.
+#
+# The input is the data array, data, and the peak positions array, x.
+# The x array is resorted to the nthreshold peaks satisfying the constraints.
+# The corresponding nthreshold data values are returned the y array.
+# The number of peaks satisfying the constraints (nthreshold) is returned.
+
+int procedure find_threshold (data, x, y, npoints, contrast, threshold, debug)
+
+real data[ARB] # Input data values
+real x[npoints] # Input/Output peak positions
+real y[npoints] # Output peak data values
+int npoints # Number of peaks input
+real contrast # Contrast constraint
+real threshold # Threshold constraint
+bool debug # Print debugging information?
+
+int i, j, nthreshold
+real minval, maxval, lcut
+
+begin
+ # Set the y array to be the values at the peak positions.
+ do i = 1, npoints {
+ j = x[i]
+ y[i] = data[j]
+ }
+
+ # Determine the min and max values of the peaks.
+ call alimr (y, npoints, minval, maxval)
+
+ # Set the threshold based on the max of the absolute threshold and the
+ # contrast. Use arltr to set peaks below threshold to INDEFR.
+ lcut = max (threshold, contrast * maxval)
+ call arltr (y, npoints, lcut, INDEFR)
+
+ if (debug) {
+ call printf (" Highest peak value = %g.\n")
+ call pargr (maxval)
+ call printf (" Peak cutoff threshold = %g.\n")
+ call pargr (lcut)
+ do i = 1, npoints {
+ if (IS_INDEFR (y[i])) {
+ j = x[i]
+ call printf (
+ " Peak at column %d with value %g below threshold.\n")
+ call pargi (j)
+ call pargr (data[j])
+ }
+ }
+ }
+
+ # Determine the number of acceptable peaks & resort the x and y arrays.
+ nthreshold = 0
+ do i = 1, npoints {
+ if (IS_INDEFR (y[i]))
+ next
+ nthreshold = nthreshold + 1
+ x[nthreshold] = x[i]
+ y[nthreshold] = y[i]
+ }
+
+ if (debug) {
+ call printf (" Number of peaks above the threshold = %d.\n")
+ call pargi (nthreshold)
+ }
+
+ return (nthreshold)
+end
+
+# FIND_ISOLATED -- Flag peaks which are within separation of a peak
+# with a higher peak value.
+#
+# The peak positions, x, and their ranks, rank, are input.
+# The rank array contains the indices of the peak positions in order from
+# the highest peak value to the lowest peak value. Starting with
+# highest rank (rank[1]) all peaks of lower rank within separation
+# are marked by setting their positions to INDEFR. The number of
+# unflaged peaks is returned.
+
+int procedure find_isolated (x, rank, npoints, separation, debug)
+
+# Procedure parameters:
+real x[npoints] # Positions of points
+int rank[npoints] # Rank of peaks
+int npoints # Number of peaks
+int separation # Minimum allowed separation
+bool debug # Print diagnostic information
+
+int i, j
+int nisolated
+
+begin
+ # Eliminate close neighbors. The eliminated
+ # peaks are marked by setting their positions to INDEFR.
+ nisolated = 0
+ do i = 1, npoints {
+ if (IS_INDEFR (x[rank[i]]))
+ next
+ nisolated = nisolated + 1
+ do j = i + 1, npoints {
+ if (IS_INDEFR (x[rank[j]]))
+ next
+ if (abs (x[rank[i]] - x[rank[j]]) < separation) {
+ if (debug) {
+ call printf (
+ " Peak at column %d too near peak at column %d.\n")
+ call pargi (int (x[rank[j]]))
+ call pargi (int (x[rank[i]]))
+ }
+ x[rank[j]] = INDEFR
+ }
+ }
+ }
+
+ if (debug) {
+ call printf (" Number of peaks separated by %d pixels = %d.\n")
+ call pargi (separation)
+ call pargi (nisolated)
+ }
+
+ # Return number of isolated peaks.
+ return (nisolated)
+end
+
+
+# FIND_NMAX -- Select up to the nmax highest ranked peaks.
+#
+# The data values, data, peak positions, x, and their ranks, rank, are input.
+# The data values are used only in printing debugging information.
+# Peak positions previously eliminated are flaged by the value INDEFR.
+# The rank array contains the indices to the peak positions in order from
+# the highest peak value to the lowest peak value.
+# First all but the nmax highest ranked peaks (which have not been previously
+# eliminated) are eliminated by marking their positions with the value INDEFR.
+# Then the remaining peaks are resorted to contain only the unflaged
+# peaks and the number of such peaks is returned.
+
+int procedure find_nmax (data, x, rank, npoints, nmax, debug)
+
+real data[ARB] # Input data values
+real x[npoints] # Peak positions
+int rank[npoints] # Ranks of peaks
+int npoints # Number of input peaks
+int nmax # Max number of peaks to be selected
+bool debug # Print debugging information?
+
+int i, j, npeaks
+
+begin
+ # Only mark peaks to reject if the number peaks is greater than nmax.
+ if (nmax < npoints) {
+ npeaks = 0
+ do i = 1, npoints {
+ if (IS_INDEFR (x[rank[i]]))
+ next
+ npeaks = npeaks + 1
+ if (npeaks > nmax) {
+ if (debug) {
+ j = x[rank[i]]
+ call printf (
+ " Reject peak at column %d with rank %d and value %g.\n")
+ call pargi (j)
+ call pargi (i)
+ call pargr (data[j])
+ }
+ x[rank[i]] = INDEFR
+ }
+ }
+ }
+
+ # Eliminate INDEFR points and determine the number of spectra found.
+ npeaks = 0
+ do i = 1, npoints {
+ if (IS_INDEFR (x[i]))
+ next
+ npeaks = npeaks + 1
+ x[npeaks] = x[i]
+ }
+
+ return (npeaks)
+end
+
+
+# COMPARE -- Compare procedure for sort used in FIND_PEAKS.
+# Larger values are indexed first. INDEFR values are indexed last.
+
+int procedure compare (index1, index2)
+
+# Procedure parameters:
+int index1 # Comparison index
+int index2 # Comparison index
+
+pointer y
+
+common /sort/ y
+
+begin
+ # INDEFR points are considered to be smallest possible values.
+ if (IS_INDEFR (Memr[y - 1 + index1]))
+ return (1)
+ else if (IS_INDEFR (Memr[y - 1 + index2]))
+ return (-1)
+ else if (Memr[y - 1 + index1] < Memr[y - 1 + index2])
+ return (1)
+ else if (Memr[y - 1 + index1] > Memr[y - 1 + index2])
+ return (-1)
+ else
+ return (0)
+end
diff --git a/noao/twodspec/multispec/profinterp.x b/noao/twodspec/multispec/profinterp.x
new file mode 100644
index 00000000..9af3af15
--- /dev/null
+++ b/noao/twodspec/multispec/profinterp.x
@@ -0,0 +1,186 @@
+include "ms.h"
+
+.help profile_interpolation Jul84 MULTISPEC
+ The input to this procedure are the intensity profiles and the derivatives
+of the profiles with position for each spectrum at two sample lines y(2) and
+y(3). The profiles are gridded on unit position intervals starting at two
+different points x(2) and x(3). Let us denote the i_th point in these profiles
+(for some given spectrum) by
+
+ I(x(j)+i,y(j)), dI/dx(x(j)+i,y(j))
+
+where j takes the values 2 and 3 in the remaining discussion.
+Note that the profiles contain dI/dx0, the derivative with respect to the
+profile center. This is related to the derivative with respect to x by
+
+ dI/dx = -dI/dx0
+
+ We want interpolated profiles at line y(1) gridded with a starting point
+x(1). Denote this profile by
+
+ I(x(1)+i,y(1))
+
+ The algorithm is to first interpolate to the point x(1)+i from each of
+the two neighboring points at each endpoint. This yields the quantities:
+
+.nf
+(1) a(j) = I(x(j)+ileft,y(j)) + dI/dx(x(j)+ileft,y(j)) * dxa(j)
+ b(j) = I(x(j)+iright,y(j)) + dI/dx(x(j)+iright,y(j)) * dxb(j)
+.fi
+
+where
+
+.nf
+(2) dxa(j) = x(1) - x(j) x(1) > x(j)
+ dxb(j) = x(1) - (x(j) + 1) x(1) > x(j)
+ dxa(2) = x(1) - (x(j) - 1) x(1) < x(j)
+ dxb(2) = x(1) - x(j) x(1) < x(j)
+.fi
+
+The final value is then obtained by the bi-linear interpolation formula:
+
+.nf
+(3) I(x(1)+i,y(1)) = a(2) * wta(2) + b(2) * wtb(2) +
+ a(3) * wta(3) + b(3) * wtb(3)
+.fi
+
+where
+
+.nf
+(4) f(2) = 1 - (y(1) - y(2)) / (y(3) - y(2))
+ f(3) = 1 - (y(3) - y(1)) / (y(3) - y(2)) = 1 - f(2)
+ wta(j) = -dxb(j) * f(j)
+ wtb(j) = dxa(j) * f(j)
+.fi
+
+ If x(1) > x(j) then b(j) does not exist at the rightmost profile point.
+In this case in equation 1 replace the term
+
+.nf
+(5) a(j) * wta(j) + b(j) * wtb(j)
+.fi
+
+with
+
+.nf
+(6) a(j) * f(j)
+.fi
+
+for the rightmost endpoint.
+Similarly, if x(1) < x(j) then a(j) does not exist for the leftmost profile
+point. Then replace the term (5) with
+
+.nf
+(7) b(j) * f(j).
+.fi
+
+ Procedure profile_interpolation implements this interpolation scheme.
+The only difference is that instead of equation 3 the profiles are built up
+by accumulation of the terms.
+.endhelp
+
+# PROFILE_INTERPOLATION -- Interpolate between two profiles.
+#
+# The equation references are to those in the help text.
+
+procedure profile_interpolation (fraction, len_profile, nspectra, nparams,
+ profiles, ranges)
+
+real fraction # The interpolation point
+int len_profile # The length of the profiles
+int nspectra # The number of spectra
+int nparams # The number of model parameters
+real profiles[len_profile, nspectra, nparams, 3] # The profiles
+real ranges[nspectra, LEN_RANGES, 3] # The ranges array
+
+int i, j, spectrum
+real dx, f[3], dxa[3], dxb[3], wta[3], wtb[3], a, b
+
+begin
+ # Clear the final profiles because we accumulate the terms in
+ # equations 3 and 5.
+ call aclrr (profiles[1, 1, I0_INDEX, 1], len_profile * nspectra)
+
+ # Equation 4.
+ f[2] = 1 - fraction
+ f[3] = fraction
+
+ # Do each endpoint and each spectrum.
+ do j = 2, 3 {
+ do spectrum = 1, nspectra {
+ dx = ranges[spectrum, DX_START, 1] -
+ ranges[spectrum, DX_START, j]
+
+ if (dx < 0.) {
+ # x(1) < x(j) and ileft = i - 1, iright = i.
+
+ # Equation 2.
+ dxa[j] = 1 + dx
+ dxb[j] = dx
+
+ # Equation 4.
+ wta[j] = -dxb[j] * f[j]
+ wtb[j] = dxa[j] * f[j]
+
+ # Accumulate the terms from the left neighbor. Eq. 1 & 3
+ do i = 2, len_profile {
+ a = profiles[i - 1, spectrum, I0_INDEX, j] -
+ profiles[i - 1, spectrum, X0_INDEX, j] * dxa[j]
+ profiles[i, spectrum, I0_INDEX, 1] =
+ profiles[i, spectrum, I0_INDEX, 1] + a * wta[j]
+ }
+
+ # Accumulate the terms from the right neighbor. Eq. 1 & 3
+ do i = 2, len_profile {
+ b = profiles[i, spectrum, I0_INDEX, j] -
+ profiles[i, spectrum, X0_INDEX, j] * dxb[j]
+ profiles[i, spectrum, I0_INDEX, 1] =
+ profiles[i, spectrum, I0_INDEX, 1] + b * wtb[j]
+ }
+
+ # There is no left neighbor for the left profile endpoint.
+ # Eq. 1 & 7
+ b = profiles[1, spectrum, I0_INDEX, j] -
+ profiles[1, spectrum, X0_INDEX, j] * dxb[j]
+ profiles[1, spectrum, I0_INDEX, 1] =
+ profiles[1, spectrum, I0_INDEX, 1] + b * f[j]
+ }
+
+ else {
+ # x(1) > x(j) and ileft = i, iright = i + 1.
+ # Equation 2.
+ dxa[j] = dx
+ dxb[j] = dx - 1
+
+ # Equation 4.
+ wta[j] = -dxb[j] * f[j]
+ wtb[j] = dxa[j] * f[j]
+
+ # Accumulate the terms from the left neighbor. Eq. 1 & 3.
+ do i = 1, len_profile - 1 {
+ a = profiles[i, spectrum, I0_INDEX, j] -
+ profiles[i, spectrum, X0_INDEX, j] * dxa[j]
+ profiles[i, spectrum, I0_INDEX, 1] =
+ profiles[i, spectrum, I0_INDEX, 1] + a * wta[j]
+ }
+
+ # Accumulate the terms from the right neighbor. Eq. 1 & 3.
+ do i = 1, len_profile - 1 {
+ b = profiles[i + 1, spectrum, I0_INDEX, j] -
+ profiles[i + 1, spectrum, X0_INDEX, j] * dxb[j]
+ profiles[i, spectrum, I0_INDEX, 1] =
+ profiles[i, spectrum, I0_INDEX, 1] + b * wtb[j]
+ }
+
+ # There is no right neighbor for the right profile endpoint.
+ # Eq. 1 & 6
+ a = profiles[len_profile, spectrum, I0_INDEX, j] -
+ profiles[len_profile, spectrum, X0_INDEX, j] * dxa[j]
+ profiles[len_profile, spectrum, I0_INDEX, 1] =
+ profiles[len_profile, spectrum, I0_INDEX, 1] + a * f[j]
+ }
+ }
+ }
+ call amaxkr (profiles[1, 1, I0_INDEX, 1], 0.,
+ profiles[1, 1, I0_INDEX, 1], len_profile * nspectra)
+end
diff --git a/noao/twodspec/multispec/ranges.x b/noao/twodspec/multispec/ranges.x
new file mode 100644
index 00000000..6704b192
--- /dev/null
+++ b/noao/twodspec/multispec/ranges.x
@@ -0,0 +1,385 @@
+include <mach.h>
+include <ctype.h>
+
+.help ranges xtools "Range Parsing Tools"
+.ih
+PURPOSE
+
+These tools
+parse a string using a syntax to represent integer values, ranges, and
+steps. The parsed string is used to generate a list of integers for various
+purposes such as specifying lines or columns in an image or tape file numbers.
+.ih
+SYNTAX
+
+The syntax for the range string consists of positive integers, '-' (minus),
+'x', ',' (comma), and whitespace. The commas and whitespace are ignored
+and may be freely used for clarity. The remainder of the string consists
+of sequences of five fields. The first field is the beginning of a range,
+the second is a '-', the third is the end of the range, the fourth is
+a 'x', and the fifth is a step size. Any of the five fields may be
+missing causing various default actions. The defaults are illustrated in
+the following table.
+
+.nf
+-3x1 A missing starting value defaults to 1.
+2-x1 A missing ending value defaults to MAX_INT.
+2x1 A missing ending value defaults to MAX_INT.
+2-4 A missing step defaults to 1.
+4 A missing ending value and step defaults to an ending
+ value equal to the starting value and a step of 1.
+x2 Missing starting and ending values defaults to
+ the range 1 to MAX_INT with the specified step.
+"" The null string is equivalent to "1 - MAX_INT x 1",
+ i.e all positive integers.
+.fi
+
+The specification of several ranges yields the union of the ranges.
+.ih
+EXAMPLES
+
+The following examples further illustrate the range syntax.
+
+.nf
+- All positive integers.
+1,5,9 A list of integers equivalent to 1-1x1,5-5x1,9-9x1.
+x2 Every second positive integer starting with 1.
+2x3 Every third positive integer starting with 2.
+-10 All integers between 1 and 10.
+5- All integers greater than or equal to 5.
+9-3x1 The integers 3,6,9.
+.fi
+.ih
+PROCEDURES
+
+.ls 4 decode_ranges
+
+.nf
+int procedure decode_ranges (range_string, ranges, max_ranges, minimum,
+ maximum, nvalues)
+
+char range_string[ARB] # Range string to be decoded
+int ranges[3, max_ranges] # Range array
+int max_ranges # Maximum number of ranges
+int minimum, maximum # Minimum and maximum range values allowed
+int nvalues # The number of values in the ranges
+.fi
+
+The range string is decoded into an integer array of maximum dimension
+3 * max_ranges. Each range consists of three consecutive integers
+corresponding to the starting and ending points of the range and the
+step size. The number of integers covered by the ranges is returned
+as nvalue. The end of the set of ranges is marked by a NULL.
+The returned status is either ERR or OK.
+.le
+.ls 4 get_next_number, get_last_number
+
+.nf
+int procedure get_next_number (ranges, number)
+int procedure get_previous_number (ranges, number)
+
+int ranges[ARB] # Range array
+int number # Both input and output parameter
+.fi
+
+Given a value for number the procedures find the next (previous) number in
+increasing (decreasing)
+value within the set of ranges. The next (previous) number is returned in
+the number argument. A returned status is either OK or EOF.
+EOF indicates that there are no greater values. The usual usage would
+be in a loop of the form:
+
+.nf
+ number = 0
+ while (get_next_number (ranges, number) != EOF) {
+ <Statements using number>
+ }
+.fi
+.le
+.ls 4 is_in_range
+
+.nf
+bool procedure is_in_range (ranges, number)
+
+int ranges[ARB] # Ranges array
+int number # Number to check againts ranges
+.fi
+
+A boolean value is returned indicating whether number is covered by
+the ranges.
+
+.endhelp
+
+
+# DECODE_RANGES -- Parse a string containing a list of integer numbers or
+# ranges, delimited by either spaces or commas. Return as output a list
+# of ranges defining a list of numbers, and the count of list numbers.
+# Range limits must be positive nonnegative integers. ERR is returned as
+# the function value if a conversion error occurs. The list of ranges is
+# delimited by a single NULL.
+
+
+int procedure decode_ranges (range_string, ranges, max_ranges, minimum,
+ maximum, nvalues)
+
+char range_string[ARB] # Range string to be decoded
+int ranges[3, max_ranges] # Range array
+int max_ranges # Maximum number of ranges
+int minimum, maximum # Minimum and maximum range values allowed
+int nvalues # The number of values in the ranges
+
+int ip, nrange, out_of_range, a, b, first, last, step, ctoi()
+
+begin
+ ip = 1
+ nrange = 1
+ nvalues = 0
+ out_of_range = 0
+
+ while (nrange < max_ranges) {
+ # Default values
+ a = minimum
+ b = maximum
+ step = 1
+
+ # Skip delimiters
+ while (IS_WHITE(range_string[ip]) || range_string[ip] == ',')
+ ip = ip + 1
+
+ # Get first limit.
+ # Must be a number, '*', '-', 'x', or EOS. If not return ERR.
+ if (range_string[ip] == EOS) { # end of list
+ if (nrange == 1) {
+ if (out_of_range == 0) {
+ # Null string defaults
+ ranges[1, 1] = a
+ ranges[2, 1] = b
+ ranges[3, 1] = step
+ ranges[1, 2] = NULL
+ nvalues = (b - a) / step + 1
+ return (OK)
+ } else {
+ # Only out of range data
+ return (ERR)
+ }
+ } else {
+ ranges[1, nrange] = NULL
+ return (OK)
+ }
+ } else if (range_string[ip] == '-')
+ ;
+ else if (range_string[ip] == '*')
+ ;
+ else if (range_string[ip] == 'x')
+ ;
+ else if (IS_DIGIT(range_string[ip])) { # ,n..
+ if (ctoi (range_string, ip, a) == 0)
+ return (ERR)
+ } else
+ return (ERR)
+
+ # Skip delimiters
+ while (IS_WHITE(range_string[ip]) || range_string[ip] == ',')
+ ip = ip + 1
+
+ # Get last limit
+ # Must be '-', '*', or 'x' otherwise b = a.
+ if (range_string[ip] == 'x')
+ ;
+ else if ((range_string[ip] == '-') || (range_string[ip] == '*')) {
+ ip = ip + 1
+ while (IS_WHITE(range_string[ip]) || range_string[ip] == ',')
+ ip = ip + 1
+ if (range_string[ip] == EOS)
+ ;
+ else if (IS_DIGIT(range_string[ip])) {
+ if (ctoi (range_string, ip, b) == 0)
+ return (ERR)
+ } else if (range_string[ip] == 'x')
+ ;
+ else
+ return (ERR)
+ } else
+ b = a
+
+ # Skip delimiters
+ while (IS_WHITE(range_string[ip]) || range_string[ip] == ',')
+ ip = ip + 1
+
+ # Get step.
+ # Must be 'x' or assume default step.
+ if (range_string[ip] == 'x') {
+ ip = ip + 1
+ while (IS_WHITE(range_string[ip]) || range_string[ip] == ',')
+ ip = ip + 1
+ if (range_string[ip] == EOS)
+ ;
+ else if (IS_DIGIT(range_string[ip])) {
+ if (ctoi (range_string, ip, step) == 0)
+ ;
+ } else if (range_string[ip] == '-')
+ ;
+ else if (range_string[ip] == '*')
+ ;
+ else
+ return (ERR)
+ }
+
+ # Output the range triple.
+ first = min (a, b)
+ last = max (a, b)
+ if (first < minimum)
+ first = minimum + mod (step - mod (minimum - first, step), step)
+ if (last > maximum)
+ last = maximum - mod (last - maximum, step)
+ if (first <= last) {
+ ranges[1, nrange] = first
+ ranges[2, nrange] = last
+ ranges[3, nrange] = step
+ nvalues = nvalues + (last - first) / step + 1
+ nrange = nrange + 1
+ } else
+ out_of_range = out_of_range + 1
+ }
+
+ return (ERR) # ran out of space
+end
+
+
+# GET_NEXT_NUMBER -- Given a list of ranges and the current file number,
+# find and return the next file number. Selection is done in such a way
+# that list numbers are always returned in monotonically increasing order,
+# regardless of the order in which the ranges are given. Duplicate entries
+# are ignored. EOF is returned at the end of the list.
+
+int procedure get_next_number (ranges, number)
+
+int ranges[ARB] # Range array
+int number # Both input and output parameter
+
+int ip, first, last, step, next_number, remainder
+
+begin
+ # If number+1 is anywhere in the list, that is the next number,
+ # otherwise the next number is the smallest number in the list which
+ # is greater than number+1.
+
+ number = number + 1
+ next_number = MAX_INT
+
+ for (ip=1; ranges[ip] != NULL; ip=ip+3) {
+ first = ranges[ip]
+ last = ranges[ip+1]
+ step = ranges[ip+2]
+ if (number >= first && number <= last) {
+ remainder = mod (number - first, step)
+ if (remainder == 0)
+ return (number)
+ if (number - remainder + step <= last)
+ next_number = number - remainder + step
+ } else if (first > number)
+ next_number = min (next_number, first)
+ }
+
+ if (next_number == MAX_INT)
+ return (EOF)
+ else {
+ number = next_number
+ return (number)
+ }
+end
+
+
+# GET_PREVIOUS_NUMBER -- Given a list of ranges and the current file number,
+# find and return the previous file number. Selection is done in such a way
+# that list numbers are always returned in monotonically decreasing order,
+# regardless of the order in which the ranges are given. Duplicate entries
+# are ignored. EOF is returned at the end of the list.
+
+int procedure get_previous_number (ranges, number)
+
+int ranges[ARB] # Range array
+int number # Both input and output parameter
+
+int ip, first, last, step, next_number, remainder
+
+begin
+ # If number-1 is anywhere in the list, that is the previous number,
+ # otherwise the previous number is the largest number in the list which
+ # is less than number-1.
+
+ number = number - 1
+ next_number = 0
+
+ for (ip=1; ranges[ip] != NULL; ip=ip+3) {
+ first = ranges[ip]
+ last = ranges[ip+1]
+ step = ranges[ip+2]
+ if (number >= first && number <= last) {
+ remainder = mod (number - first, step)
+ if (remainder == 0)
+ return (number)
+ if (number - remainder >= first)
+ next_number = number - remainder
+ } else if (last < number) {
+ remainder = mod (last - first, step)
+ if (remainder == 0)
+ next_number = max (next_number, last)
+ else if (last - remainder >= first)
+ next_number = max (next_number, last - remainder)
+ }
+ }
+
+ if (next_number == 0)
+ return (EOF)
+ else {
+ number = next_number
+ return (number)
+ }
+end
+
+
+# IS_IN_RANGE -- Test number to see if it is in range.
+
+bool procedure is_in_range (ranges, number)
+
+int ranges[ARB] # Range array
+int number # Number to be tested against ranges
+
+int ip, first, last, step
+
+begin
+ for (ip=1; ranges[ip] != NULL; ip=ip+3) {
+ first = ranges[ip]
+ last = ranges[ip+1]
+ step = ranges[ip+2]
+ if (number >= first && number <= last)
+ if (mod (number - first, step) == 0)
+ return (TRUE)
+ }
+
+ return (FALSE)
+end
+
+# EXPAND_RANGES -- Expand a range string into a array of values.
+
+int procedure expand_ranges (ranges, array, max_nvalues)
+
+int ranges[ARB] # Range array
+int array[max_nvalues] # Array of values
+int max_nvalues # Maximum number of values
+
+int n, value
+
+int get_next_number()
+
+begin
+ n = 0
+ value = 0
+ while ((n < max_nvalues) && (get_next_number (ranges, value) != EOF)) {
+ n = n + 1
+ array[n] = value
+ }
+
+ return (n)
+end
diff --git a/noao/twodspec/multispec/response.par b/noao/twodspec/multispec/response.par
new file mode 100644
index 00000000..0d6cdf60
--- /dev/null
+++ b/noao/twodspec/multispec/response.par
@@ -0,0 +1,11 @@
+# RESPONSE
+
+input_image,f,a,,,,Input image to be smoothed and cleaned
+output_image,f,a,,,,Smoothed and cleaned output image
+spline_order,i,h,4,2,,Smoothing spline order
+width,i,a,1,1,,Width of smoothing region
+sigma_min,r,h,1,,,Minimum pixel sigma
+above,r,h,5,,,Upper cleaning threshold
+below,r,h,5,,,Lower cleaning threshold
+window,i,h,0,0,,Rejection window radiu
+div_threshold,r,h,1000.,,,Division threshold
diff --git a/noao/twodspec/multispec/sampline.x b/noao/twodspec/multispec/sampline.x
new file mode 100644
index 00000000..37583f9f
--- /dev/null
+++ b/noao/twodspec/multispec/sampline.x
@@ -0,0 +1,73 @@
+include <mach.h>
+include "ms.h"
+
+
+# GET_SAMPLE_LINE -- Get the nearest sample line to the given image lines.
+#
+# The nearest sample line to each image line is found an returned
+# as the function value.
+
+int procedure get_sample_line (ms, line)
+
+pointer ms # MULTISPEC data structure
+int line # Image line
+
+int sample, midpoint
+
+begin
+ sample = 0
+ midpoint = 0
+
+ repeat {
+ sample = sample + 1
+ if (sample < MS_NSAMPLES(ms))
+ midpoint = (LINE(ms, sample) + LINE(ms, sample + 1)) / 2
+ else if (sample == MS_NSAMPLES(ms))
+ midpoint = MAX_INT
+ else
+ break
+ } until (line < midpoint)
+
+ return (sample)
+end
+
+
+# GET_SAMPLE_LINES -- Get the sample lines for the given image lines.
+#
+# Image lines in the form of a range array are given.
+# The nearest sample line to each image line is found. The array of
+# sample lines is returned and the function value is the number of
+# sample lines.
+
+int procedure get_sample_lines (ms, lines, samples)
+
+pointer ms # MULTISPEC data structure
+int lines[ARB] # Image line range array
+int samples[ARB] # Return sample lines
+
+int nsamples, sample, line, midpoint
+int get_next_number()
+
+begin
+ nsamples = 0
+ sample = 0
+ midpoint = 0
+ line = 0
+
+ while (get_next_number (lines, line) != EOF) {
+ repeat {
+ sample = sample + 1
+ if (sample < MS_NSAMPLES(ms))
+ midpoint = (LINE(ms, sample) + LINE(ms, sample + 1)) / 2
+ else if (sample == MS_NSAMPLES(ms))
+ midpoint = MAX_INT
+ else
+ return (nsamples)
+ } until (line < midpoint)
+
+ nsamples = nsamples + 1
+ samples[nsamples] = sample
+ line = midpoint - 1
+ }
+ return (nsamples)
+end
diff --git a/noao/twodspec/multispec/setfitparams.x b/noao/twodspec/multispec/setfitparams.x
new file mode 100644
index 00000000..780572c3
--- /dev/null
+++ b/noao/twodspec/multispec/setfitparams.x
@@ -0,0 +1,27 @@
+include "ms.h"
+
+# SET_FITPARAMS -- Set the fitparams array from the spectra range array
+# and the parameters array.
+
+procedure set_fitparams (spectra, parameters, nspectra, nparams, fitparams)
+
+int spectra[ARB]
+int parameters[nparams]
+int nspectra
+int nparams
+int fitparams[nspectra, nparams]
+
+int i, j
+
+bool is_in_range()
+
+begin
+ do i = 1, nspectra {
+ do j = 1, nparams {
+ if (is_in_range (spectra, i) && (parameters[j] == YES))
+ fitparams[i, j] = YES
+ else
+ fitparams[i, j] = NO
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/setmodel.x b/noao/twodspec/multispec/setmodel.x
new file mode 100644
index 00000000..98c1a630
--- /dev/null
+++ b/noao/twodspec/multispec/setmodel.x
@@ -0,0 +1,86 @@
+include "ms.h"
+
+# SET_MODEL -- Set a line of model data from profiles based on their
+# ranges starting values.
+
+procedure set_model (ms, model, model_profiles, ranges, len_line, len_profile,
+ nspectra)
+
+pointer ms # MULTISPEC data structure
+real model[len_line] # Model line created
+real model_profiles[len_profile, nspectra] # Model profiles
+real ranges[nspectra, LEN_RANGES] # Ranges array for the profiles
+int len_line # The length of the model line
+int len_profile # The length of the profiles
+int nspectra # The number of spectra
+
+int i, x, spectrum
+
+begin
+ # Set the model background to zero.
+ call aclrr (model, len_line)
+
+ # For each spectrum and each profile point add contribution to model.
+ do spectrum = 1, nspectra {
+ do i = 1, len_profile {
+ # Column corresponding to profile point i and spectrum.
+ x = ranges[spectrum, X_START] + i - 1
+
+ # Scale the model profile by the model parameter I0 and
+ # add to the model line.
+ if ((x >= 1) && (x <= len_line))
+ model[x] = model[x] + PARAMETER(ms, I0, spectrum) *
+ model_profiles[i, spectrum]
+ }
+ }
+end
+
+# SET_MODEL1 -- Set a line of model data from profiles based on the spectra
+# function fit position centers and the ranges dx_start value.
+
+procedure set_model1 (ms, line, profiles, coeff, ranges, len_line, len_profile,
+ nspectra, model)
+
+pointer ms # MULTISPEC data structure
+int line # Image line for model
+real profiles[len_profile, nspectra] # Profiles
+real coeff[ARB] # Image interpolation coeff.
+real ranges[nspectra, LEN_RANGES] # Ranges array for profiles
+int len_line # Length of model line
+int len_profile # Length of profiles
+int nspectra # Number of spectra
+real model[len_line] # Model line to be created
+
+int i, x, spectrum
+real x_start, dx
+
+real cveval(), asival()
+
+begin
+ # Clear the model to a zero background.
+ call aclrr (model, len_line)
+
+ # Add the contribution for each spectrum.
+ do spectrum = 1, nspectra {
+ # Fit image interpolator to profile.
+ call asifit (profiles[1,spectrum], len_profile, coeff)
+
+ # Determine starting column corresponding to spectrum at specified
+ # line whose central position is given by the fit function.
+ x_start = cveval (CV(ms, X0_FIT, spectrum), real (line)) +
+ ranges[spectrum, DX_START]
+
+ # For each column corresponding to a point in the profile determine
+ # the interpolation point dx within the profile and evaluate the
+ # the image interpolation function.
+
+ x = x_start
+ do i = 1, len_profile - 1 {
+ x = x + 1
+ if ((x >= 1) && (x <= len_line)) {
+ dx = x - x_start + 1
+ model[x] = model[x] + asival (dx, coeff)
+ }
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/setranges.x b/noao/twodspec/multispec/setranges.x
new file mode 100644
index 00000000..46247a08
--- /dev/null
+++ b/noao/twodspec/multispec/setranges.x
@@ -0,0 +1,23 @@
+include "ms.h"
+
+# SET_RANGES -- Set profile starting range array.
+#
+# The ranges array relates the starting point of the profiles relative
+# to the center of profile and relative to the image line. For more
+# details see the MULTISPEC system documentation.
+
+procedure set_ranges (ms, lower, ranges, nspectra)
+
+pointer ms # MULTISPEC data structure
+real lower # Relative lower limit of profiles
+real ranges[nspectra, LEN_RANGES] # Ranges array to be set
+int nspectra # Number of spectra
+
+int i
+
+begin
+ do i = 1, nspectra {
+ ranges[i, X_START] = int (PARAMETER(ms, X0, i)) + lower
+ ranges[i, DX_START] = ranges[i, X_START] - PARAMETER(ms, X0, i)
+ }
+end
diff --git a/noao/twodspec/multispec/setsmooth.x b/noao/twodspec/multispec/setsmooth.x
new file mode 100644
index 00000000..10740dce
--- /dev/null
+++ b/noao/twodspec/multispec/setsmooth.x
@@ -0,0 +1,250 @@
+include <imhdr.h>
+include "ms.h"
+
+.help set_smooth Jul84 MULTISPEC
+.sh
+Procedure set_smooth
+
+ This procedure returns data profiles for the requested image line and model
+profiles consisting of the sum of (naverage =) nlines - 1 image line data
+profiles surrounding (and excluding) the data image line.
+
+ A buffer of nlines + 1 set of profiles is kept. The first set of profiles
+is used to keep the sum of the nlines - 1 profiles which excludes the current
+line of data profiles (thus, the number of lines in the sum = nlines - 1).
+The remaining sets of profiles (2 to nlines + 1) contain data profiles for all
+the lines in the sum plus the current data line. The lines are stored in a
+cyclic fashion with the buffer line being related to the data line by
+
+ buffer line = 2 + mod (data line - 1, nlines)
+
+Each data line is read and converted into a set of profiles and put into the
+buffer only if it is not already in the buffer.
+
+ The algorithm first checks the state of the previous profiles buffer.
+If it would be unchanged then it returns. Otherwise, it subtracts the profiles
+which are not in common with the new set of summation lines from the
+sum profiles before replacing those lines in the profiles buffer with new data
+profiles. If the number of vector subtractions exceeds the number of vector
+additions to readd the common lines to the sum profiles then the common
+profiles are readded instead. The new data profiles are then obtained from
+the image (with procedure msgprofiles) and added, if needed, to the sum
+profiles. Finally, the sum profiles are copied to the model profiles and
+the profiles from the profiles buffer corresponding to the requested data
+line are copied to the data profiles array.
+
+ This algorithm is maximumally efficient with its imageio. If the model
+lines are requested sequentially through the image then each image data line
+will be read only once and each new model line will, on average, require only
+one image read, two vector additions, and two vector subtractions. The
+number of vector additions and subtractions is two because the current data
+line is excluded from the sum.
+.sh
+Procedure msgprofiles
+
+ In order to obtain model profiles based on summing the profiles from
+a number of neighboring lines, the profiles from each line must be
+shifted to the relative profile centers. The procedure msgprofiles reads
+an image line and computes an interpolation function for the line.
+The spectra profiles are then extracted using the position interpolation
+function to determine the spectra centers in the image line. The
+profiles are aligned to the same relative positions in the profiles
+array based on the ranges array.
+.endhelp
+
+
+# SET_SMOOTH -- Set the SMOOTH model profiles.
+
+procedure set_smooth (ms, im, line, ranges, profiles, coeff,
+ len_prof, nspectra, nlines, data, model)
+
+pointer ms # MULTISPEC data structure
+int im # IMIO image descriptor
+int line # Image line to be modeled
+real ranges[nspectra, LEN_RANGES] # Ranges array
+real profiles[len_prof, nspectra, ARB] # Profiles array
+real coeff[ARB] # Image interpolator coeffs.
+int len_prof # Length of profiles
+int nspectra # Number of spectra
+int nlines # Number of lines in average
+real data[len_prof, nspectra] # Data profiles for line
+real model[len_prof, nspectra] # SMOOTH model profiles
+
+int i, j, navg
+int len_profs, last_line, last_start, last_end, line_start, line_end
+pointer k
+
+data len_profs/0/
+
+begin
+ # Initialize
+ if (len_profs == 0) {
+ navg = nlines - 1
+ len_profs = len_prof * nspectra
+ last_line = 0
+ last_start = -nlines
+ last_end = 0
+ }
+
+ # Determine range of lines for averaging.
+
+ # The following is to use the center of the averaging region.
+ #line_start = max (1, line - nlines / 2)
+
+ # The following uses the preceeding nlines - 1 lines.
+ line_start = max (1, line - (nlines - 1))
+
+ line_start = min (line_start, IM_LEN(im, 2) - nlines)
+ line_end = line_start + nlines - 1
+
+ # Return if the same line is the same and the lines used in the
+ # sum profile are the same.
+
+ if ((line_start == last_start) && (line == last_line))
+ return
+
+ # If the number of lines in common with the previous sum profile is
+ # < nlines / 2 then it is more efficient to clear the sum profile
+ # and readd the common lines.
+
+ if (abs (line_start - last_start) > nlines / 2) {
+ call aclrr (profiles[1,1,1], len_profs)
+ do i = last_start, last_end {
+ j = i - line_start
+
+ # If the old line i is within the new sum add it to the sum.
+ # However, if the line is the new data line do not add it.
+ if ((j >= 0) && (j < nlines) && (i != line)) {
+ k = 2 + mod (i - 1, nlines)
+ call aaddr (profiles[1,1,1], profiles[1,1,k],
+ profiles[1,1,1], len_profs)
+ }
+ }
+
+ # If the number in lines in common is >= nlines / 2 then it is more
+ # efficient to subtract the lines not in common from the sum.
+
+ } else {
+ do i = last_start, last_end {
+ j = i - line_start
+ k = 2 + mod (i - 1, nlines)
+
+ # If the old line i is not within the new sum subtract it.
+ # Also, if the line is the new data line subtract it.
+ if ((j < 0) || (j >= nlines) || (i == line)) {
+ # However, don't subtract the last data line since it was
+ # not in the previous sum.
+ if (i != last_line) {
+ call asubr (profiles[1,1,1], profiles[1,1,k],
+ profiles[1,1,1], len_profs)
+ }
+
+ # If the old line is within the new sum but it was the old data
+ # line then add it to the sum since it was not in the old sum.
+ } else if (i == last_line) {
+ call aaddr (profiles[1,1,1], profiles[1,1,k],
+ profiles[1,1,1], len_profs)
+ }
+ }
+ }
+
+ # Get the new profiles into the profile buffer and the add to the sum.
+ do i = line_start, line_end {
+ j = i - last_start
+ if ((j < 0) || (j >= nlines)) {
+ k = 2 + mod (i - 1, nlines)
+
+ # Get the data profile for line i and put it in profiles k.
+ call msgprofiles (ms, im, i, ranges, profiles[1,1,k], coeff,
+ len_prof, nspectra)
+
+ # If the new line in the buffer is not the data line then
+ # add it to the sum profile.
+ if (i != line) {
+ call aaddr (profiles[1,1,1], profiles[1,1,k],
+ profiles[1,1,1], len_profs)
+ }
+ }
+ }
+
+ # Record current state of the average.
+ last_line = line
+ last_start = line_start
+ last_end = line_end
+
+ # Set the data profiles and model profiles. The copies are
+ # made rather than working directly from the profiles buffer so that
+ # changes can be made in the data and model profiles without affecting
+ # the buffer.
+
+ k = 2 + mod (line - 1, nlines)
+ call amovr (profiles[1,1,k], data, len_profs)
+ call amovr (profiles[1,1,1], model, len_profs)
+end
+
+
+# UPDATE_SMOOTH -- Replace an updated data profile in the profiles buffer.
+
+procedure update_smooth (line, data, profiles, len_prof, nspectra, nlines)
+
+int line # Data image line
+real data[len_prof, nspectra] # Data profiles
+real profiles[len_prof, nspectra, ARB] # Profiles buffer
+int len_prof # Length of profiles
+int nspectra # Number of spectra
+int nlines # Number of lines in buffer
+
+int i
+
+begin
+ i = 2 + mod (line - 1, nlines)
+ call amovr (data, profiles[1,1,i], len_prof * nspectra)
+end
+
+
+# MSGPROFILES -- Read image line and extract profiles in standard positions.
+
+procedure msgprofiles (ms, im, line, ranges, profile, coeff, len_prof,
+ nspectra)
+
+pointer ms # MULTISPEC data structure
+pointer im # IMIO image descriptor
+int line # Image line to be read
+real ranges[nspectra, LEN_RANGES] # Ranges array for profiles
+real profile[len_prof, nspectra] # Profiles to be obtained
+real coeff[ARB] # Image interpolator coeffs.
+int len_prof # Length of profiles
+int nspectra # Number of spectra
+
+int i, j
+real x
+pointer im_buf
+
+pointer imgl2r()
+real cveval(), asival()
+
+begin
+ # Read image line.
+ im_buf = imgl2r (im, line)
+
+ # Fit image interpolation function.
+ call asifit (Memr[im_buf], IM_LEN(im, 1), coeff)
+
+ # For each spectrum extract the profiles.
+ do j = 1, nspectra {
+
+ # Determine profile starting point in image coordinates using the
+ # fit function for the spectrum center.
+ x = cveval (CV(ms, X0_FIT, j), real (line)) +
+ ranges[j, DX_START] - 1
+
+ # For each point in the profile evaluate the image interpolator.
+ do i = 1, len_prof {
+ x = x + 1
+ if ((x < 1) || (x > IM_LEN(im, 1)))
+ profile[i, j] = 0.
+ else
+ profile[i, j] = asival (x, coeff)
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/solve.x b/noao/twodspec/multispec/solve.x
new file mode 100644
index 00000000..b7249242
--- /dev/null
+++ b/noao/twodspec/multispec/solve.x
@@ -0,0 +1,312 @@
+include "ms.h"
+
+# SOLVE:
+# Solve for the parameter correction vector using the banded matrix
+# technique decribed in Lawson and Hanson.
+#
+# The variables g, mdg, nb, ip, ir, mt, jt, rnorm, x and n have the
+# same meaning as described in Lawson and Hanson.
+
+procedure solve (ms, data, model, fitparams, profiles, ranges, len_line,
+ len_profile, nspectra, nparams, solution, norm)
+
+# Procedure parameters:
+pointer ms # MULTISPEC data structure
+real data[len_line] # Data to be fit
+real model[len_line] # Model to be corrected
+int fitparams[nspectra, nparams] # Model parameters to be fit
+real profiles[len_profile, nspectra, nparams]# Model parameter derivatives
+real ranges[nspectra, LEN_RANGES] # Ranges array for profiles
+int len_line # Length of data line
+int len_profile # Length of profiles
+int nspectra # Number of spectra
+int nparams # Number of model parameters
+real solution[nspectra, nparams] # Solution correction vector
+real norm # Measure of fit
+
+# Lawson and Hanson parameters:
+pointer g # Working array
+pointer x # Working vector
+int mdg # Maximum dimension of g
+int n # Number of parameters to be determined
+int nb # Parameter bandwith
+int ip, ir, mt, jt, jt_next # Array pointers
+real rnorm # Deviation from fit
+int ier # Error flag
+
+int ns # Maximum spectra bandwidth
+pointer columns # Columns to be used.
+int ncolumns # Number of columns
+pointer spectra # Spectra to be used.
+int nspectra_to_solve # Number of spectra
+int k_start, k_next # Indices to the spectra array
+int column, spectrum, parameter # Column, spectrum and parameter values
+int ns_in_band # Number of spectra in band
+int i, j, k, l, m
+bool is_zero
+pointer sp
+
+begin
+ # Determine columns, spectra, and parameters contributing to
+ # the solution matrix and the bandwidth of the matrix.
+ call smark (sp)
+ call salloc (columns, len_line, TY_INT)
+ call salloc (spectra, nspectra, TY_INT)
+ call band_set (ms, fitparams, data, profiles, ranges, Memi[columns],
+ Memi[spectra], len_line, len_profile, nspectra, nparams, ncolumns,
+ nspectra_to_solve, n, ns, nb)
+ if (n == 0) {
+ call sfree (sp)
+ return
+ }
+
+ # Allocate working memory for the Lawson and Hanson routines.
+ mdg = ncolumns
+ call salloc (g, mdg * (nb + 1), TY_REAL)
+ call salloc (x, n, TY_REAL)
+
+ # Initialize array indices.
+ ip = 1
+ ir = 1
+ jt = 1
+ mt = 0
+ jt_next = jt
+ k_next = 1
+
+ # Accumulate banded matrix for the specifed columns, spectra, and
+ # parameters.
+ do i = 1, ncolumns {
+ column = Memi[columns + i - 1]
+
+ k_start = k_next
+ j = jt
+ ns_in_band = 0
+ do k = k_start, nspectra_to_solve {
+ spectrum = Memi[spectra + k - 1]
+
+ # Evalute parameter derivatives and determine if all
+ # derivatives for the spectrum are zero.
+ is_zero = TRUE
+ do parameter = 1, nparams {
+ if (fitparams[spectrum, parameter] == NO)
+ next
+ j = j + 1
+ m = column - ranges[spectrum, X_START] + 1
+ if ((m < 1) || (m > len_profile))
+ Memr[x + j - 2] = 0.
+ else {
+ Memr[x + j - 2] = profiles[m, spectrum, parameter]
+ if (parameter != I0_INDEX)
+ Memr[x + j - 2] = Memr[x + j - 2] *
+ PARAMETER (ms, I0, spectrum)
+ if (Memr[x + j - 2] != 0.)
+ is_zero = FALSE
+ }
+ }
+
+ # If the spectrum has a non-zero contribution to the parameter
+ # matrix then increment the number of spectra in the
+ # band (ns_in_band).
+ # Else if the number of spectra in the band is still zero then
+ # increment the spectrum and parameter pointers.
+ # Else the band is assumed complete so break to accumulate
+ # the band.
+
+ if (!is_zero)
+ ns_in_band = ns_in_band + 1
+ else if (ns_in_band == 0) {
+ k_next = min (k + 1, nspectra_to_solve - ns + 1)
+ jt_next = min (j, n - nb + 1)
+ } else {
+ do l = j, jt_next + nb - 1
+ Memr[x + (l - 1)] = 0.
+ break
+ }
+ }
+
+ # If the number of spectra in the band is zero then reset the
+ # spectrum pointer (k_next) and go to the next column.
+ # Else if the number of spectra in the band exceeds the specified
+ # bandwidth return an error.
+ # Else accumulate the new band.
+
+ if (ns_in_band == 0) {
+ k_next = k_start
+ jt_next = jt
+ next
+ } else if (ns_in_band > ns)
+ call error (MS_ERROR, "Bandwidth too small")
+
+ # If a new submatrix is being started accumulate last submatrix.
+ if ((jt_next != jt) && (mt > 0)) {
+ call bndacc (Memr[g], mdg, nb, ip, ir, mt, jt)
+ mt = 0
+ }
+
+ # Increment the submatrix line pointer (mt) and add the band to
+ # submatrix being accumulated.
+
+ mt = mt + 1
+ jt = jt_next
+ do k = 1, nb
+ Memr[g+ir+mt-2 + (k-1)*mdg] = Memr[x + (jt - 1) + (k - 1)]
+ # INDEFR data may already be ignored in the column selection in
+ # band_set.
+ if (IS_INDEFR (data[column]))
+ Memr[g+ir+mt-2 + nb*mdg] = 0.
+ else
+ Memr[g+ir+mt-2 + nb*mdg] = data[column] - model[column]
+ }
+
+ # Accumulate last submatrix and calculate banded matrix solution vector.
+ call bndacc (Memr[g], mdg, nb, ip, ir, mt, jt)
+ call bndsol (1, Memr[g], mdg, nb, ip, ir, Memr[x], n, rnorm, ier)
+ if (ier != 0) {
+ call error (MS_ERROR, "bandsol: Solution error")
+ }
+
+ # Compute error matrix here. Not yet implemented.
+
+ # The solution from bndsol is in array x. Copy x to solution.
+ j = 0
+ do i = 1, nspectra_to_solve {
+ spectrum = Memi[spectra + i - 1]
+ do parameter = 1, nparams {
+ if (fitparams[spectrum, parameter] == YES) {
+ solution[spectrum, parameter] = Memr[x + j]
+ j = j + 1
+ } else
+ solution[spectrum, parameter] = 0.
+ }
+ }
+ norm = rnorm
+
+ call sfree (sp)
+end
+
+
+# Reject parameters which have only zero derivatives. Determine spectra,
+# columns, and number of parameters contributing to the solution.
+# Determine bandwidth of the banded matrix.
+
+procedure band_set (ms, fitparams, data, profiles, ranges, columns, spectra,
+ len_line, len_profile, nspectra, nparams, ncolumns, nspectra_to_solve,
+ n, ns, nb)
+
+pointer ms # MULTISPEC data structure
+int fitparams[nspectra, nparams] # Parameters to be fit
+real data[len_line] # Data being fit
+real profiles[len_profile, nspectra, nparams]# Parameter derivatives
+real ranges[nspectra, LEN_RANGES] # Ranges array for profiles
+int columns[len_line] # Return columns to be used
+int spectra[nspectra] # Return spectra to used
+int len_line # Length of data being fit
+int len_profile # Length of profiles
+int nspectra # Number of spectra
+int nparams # Number of parameters
+int ncolumns # Number of useful columns
+int nspectra_to_solve # Number of useful spectra
+int n # Number of parameters in fit
+int ns # Number of spectra in band
+int nb # Bandwith of matrix
+
+int i, j, k
+int column, spectrum, parameter
+int col_start
+real dx
+int xmin, xmax
+
+begin
+ # Initially set the spectra and columns to NO.
+ call amovki (NO, spectra, nspectra)
+ call amovki (NO, columns, len_line)
+
+ # Determine the spectra and columns in which the fitparams have
+ # non-zero derivatives. Flag those fitparams which do not have
+ # non-zero derivatives with NO. Count the number of parameters
+ # which have non-zero derivatives.
+
+ n = 0
+ do spectrum = 1, nspectra {
+ do parameter = 1, nparams {
+ if (fitparams[spectrum, parameter] == YES) {
+ fitparams[spectrum, parameter] = NO
+ col_start = ranges[spectrum, X_START]
+ do k = 1, len_profile {
+ if (profiles[k, spectrum, parameter] != 0.) {
+ column = col_start + k - 1
+ if ((column >= 1) && (column <= len_line)) {
+
+ # If the INDEFR data points are not to be
+ # ignored but replaced by the model in solve,
+ # replace the if clause with the following.
+ # columns[column] = YES
+ # fitparams[spectrum, parameter] = YES
+
+ if (!IS_INDEFR (data[column])) {
+ columns[column] = YES
+ fitparams[spectrum, parameter] = YES
+ }
+ }
+ }
+ }
+ if (fitparams[spectrum, parameter] == YES) {
+ n = n + 1
+ spectra[spectrum] = YES
+ }
+ }
+ }
+ }
+
+ # Count the number spectra to be used and set the spectra array.
+ nspectra_to_solve = 0
+ do spectrum = 1, nspectra {
+ if (spectra[spectrum] == YES) {
+ nspectra_to_solve = nspectra_to_solve + 1
+ spectra[nspectra_to_solve] = spectrum
+ }
+ }
+
+ # Count the number of columns to be used and set the columns array.
+ ncolumns = 0
+ do column = 1, len_line {
+ if (columns[column] == YES) {
+ ncolumns = ncolumns + 1
+ columns[ncolumns] = column
+ }
+ }
+
+ # Determine the maximum number spectra contributing to any column.
+ ns = 1
+ do i = 1, nspectra_to_solve - 1 {
+ xmax = 0
+ do parameter = 1, nparams {
+ if (fitparams[spectra[i], parameter] == YES)
+ xmax = max (xmax,
+ int (ranges[spectra[i], X_START] + len_profile - 1))
+ }
+ do j = i + 1, nspectra_to_solve {
+ xmin = len_line
+ do parameter = 1, nparams {
+ if (fitparams[spectra[j], parameter] == YES)
+ xmin = min (xmin, int (ranges[spectra[j], X_START]))
+ }
+ dx = xmax - xmin
+ if (dx < 0)
+ break
+ else
+ ns = max (ns, j - i + 1)
+ }
+ }
+
+ # Determine the banded matrix bandwidth.
+ nb = 0
+ do parameter = 1, nparams {
+ do i = 1, nspectra_to_solve {
+ if (fitparams[spectra[i], parameter] == YES) {
+ nb = nb + ns
+ break
+ }
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/t_findpeaks.x b/noao/twodspec/multispec/t_findpeaks.x
new file mode 100644
index 00000000..2e4cf79e
--- /dev/null
+++ b/noao/twodspec/multispec/t_findpeaks.x
@@ -0,0 +1,137 @@
+include <imhdr.h>
+include <fset.h>
+include "ms.h"
+
+# T_FIND_PEAKS -- Find the spectra peaks in a MULTISPEC image and record
+# their positions in the database.
+#
+# An average of naverage lines from the MULTISPEC image is searched
+# for peaks satisfying constraints on the minimum and maximum number,
+# columns, peak values, and separation between peaks. The positions
+# of the peaks satisfying these constraints is entered in the database.
+# It is an error if fewer than the minimum number of peaks is found
+# or if the number of peaks differs from a previously determined number.
+# The peak finding is done by the function FIND_PEAKS which is numerical
+# and may be used outside the MULTISPEC package.
+
+procedure t_find_peaks ()
+
+# CL parameters:
+char image[SZ_FNAME] # Image to be searched
+int lines[3, MAX_RANGES] # Image lines in which to find spectra
+int min_npeaks # Minimum number of spectra to be found
+int max_npeaks # Maximum number of spectra to be accepted
+int separation # Minimum pixel separation between spectra
+int edge # Minimum distance to edge of image
+real threshold # Minimum peak value
+real contrast # Max contrast between strongest and weakest
+int columns[3, MAX_RANGES] # Spectra positions limited to these columns
+int naverage # Number of image lines to average
+bool debug # Print debugging information
+
+char comment[SZ_LINE]
+int i, j, k, line, sample, nsamples, npoints, nspectra
+pointer ms, im
+pointer sp, data, x, samples
+
+int find_peaks(), get_sample_lines()
+int clgeti(), clgranges()
+real clgetr()
+bool clgetb(), is_in_range()
+pointer msmap(), immap()
+
+begin
+ # Get task parameters and access files.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_WRITE, 0)
+ im = immap (image, READ_ONLY, 0)
+ i = clgranges ("lines", 1, IM_LEN(im, 2), lines, MAX_RANGES)
+ min_npeaks = clgeti ("min_npeaks")
+ max_npeaks = clgeti ("max_npeaks")
+ separation = clgeti ("separation")
+ edge = clgeti ("edge")
+ threshold = clgetr ("threshold")
+ contrast = clgetr ("contrast")
+ i = clgranges ("columns", 1, IM_LEN(im, 1), columns, MAX_RANGES)
+ naverage = clgeti ("naverage")
+ debug = clgetb ("debug")
+
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate working memory.
+ npoints = IM_LEN(im, 1)
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ call salloc (data, npoints, TY_REAL)
+ call salloc (x, npoints, TY_REAL)
+
+ # Get the sample lines.
+ nsamples = get_sample_lines (ms, lines, Memi[samples])
+
+ # Loop through each sample line.
+ do i = 1, nsamples {
+ sample = Memi[samples + i - 1]
+ line = LINE(ms, sample)
+
+ # Get the image data with averaging.
+ call msgimage (im, line, naverage, Memr[data])
+
+ # Mark columns which are to be ignored with INDEFR.
+ do j = 1, npoints
+ if (!is_in_range (columns, j))
+ Memr[data + j - 1] = INDEFR
+
+ # Find the peaks.
+ nspectra = find_peaks (Memr[data], Memr[x], npoints,
+ contrast, separation, edge, max_npeaks, threshold, debug)
+
+ if (debug) {
+ call printf (" Number of spectra found in line %d = %d.\n")
+ call pargi (line)
+ call pargi (nspectra)
+ }
+ if (nspectra < min_npeaks)
+ call error (MS_ERROR, "Too few spectra found")
+
+ # Enter the spectra found in the database. If the number of
+ # spectra has not been previously set in the database then
+ # enter the number of spectra and make entries in the
+ # database. Otherwise check that the number of spectra found
+ # agrees with that already in the database.
+
+ if (MS_NSPECTRA(ms) == 0) {
+ if (nspectra == 0)
+ next
+ MS_NSPECTRA(ms) = nspectra
+ call dbenter (MS_DB(ms), NAME(ms, I0), nspectra * SZ_REAL,
+ MS_NSAMPLES(ms))
+ call dbenter (MS_DB(ms), NAME(ms, X0), nspectra * SZ_REAL,
+ MS_NSAMPLES(ms))
+ } else if (MS_NSPECTRA(ms) != nspectra)
+ call error (MS_ERROR, "Attempt to change the number of spectra")
+
+ call msgparam (ms, X0, sample)
+ call amovr (Memr[x], PARAMETER(ms, X0, 1), nspectra)
+ call mspparam (ms, X0, sample)
+
+ # The peak scale is taken and the pixel value at the peak.
+ call msgparam (ms, I0, sample)
+ do j = 1, nspectra {
+ k = PARAMETER(ms, X0, j)
+ PARAMETER(ms, I0, j) = Memr[data + k - 1]
+ }
+ call mspparam (ms, I0, sample)
+
+ # Enter a comment in the database.
+ call sprintf (comment, SZ_LINE,
+ "Spectra located in sample line %d.")
+ call pargi (sample)
+ call history (ms, comment)
+ }
+
+ # Update the database and close the database and image.
+ call msphdr (ms)
+ call msunmap (ms)
+ call imunmap (im)
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/t_fitfunc.x b/noao/twodspec/multispec/t_fitfunc.x
new file mode 100644
index 00000000..9f6209ad
--- /dev/null
+++ b/noao/twodspec/multispec/t_fitfunc.x
@@ -0,0 +1,158 @@
+include <math/curfit.h>
+include "ms.h"
+
+# T_FIT_FUNCTION -- Fit a function to selected spectra parameters.
+#
+# A function is fit to the parameter values determined at the sample
+# lines for selected spectra. The function coefficients are stored in
+# the database and the fitted values replace the original values at
+# the sample lines. The type of function, the parameter to be fitted,
+# the sample lines used in the fit, and the spectra to be fitted
+# are all selected by the user.
+
+procedure t_fit_function()
+
+char image[SZ_FNAME] # Image affected
+char parameter[SZ_LINE] # Parameter to be fit
+int function # Type of fitting function
+int order # Order of the fitting function
+int spectra[3, MAX_RANGES] # Spectra to be fitted
+pointer samples # Sample lines to be fitted.
+
+int i, param_id, nsamples
+pointer ms, sp
+
+int ms_db_id(), clgranges(), get_sample_lines()
+pointer msmap()
+
+begin
+ # Access database and determine parameter to be fit and the
+ # fitting function and order.
+
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_WRITE, 0)
+ call clgstr ("parameter", parameter, SZ_LINE)
+ param_id = ms_db_id (ms, parameter)
+ call clgcurfit ("function", "order", function, order)
+
+ # Get the image lines to be used in the fit and convert to sample
+ # lines. Get the spectra to be fit.
+
+ i = clgranges ("lines", 1, MS_LEN(ms, 2), spectra, MAX_RANGES)
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ nsamples = get_sample_lines (ms, spectra, Memi[samples])
+ i = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra,
+ MAX_RANGES)
+
+ # Fit the parameters for each spectrum, store the fits in the database,
+ # and substitute the fitted values for the parameter values at all
+ # the sample lines.
+
+ call fit_function (ms, Memi[samples], nsamples, spectra, param_id,
+ function, order)
+
+ # Finish up.
+ call msphdr (ms)
+ call msunmap (ms)
+ call sfree (sp)
+end
+
+
+
+# FIT_FUNCTION -- Fit a function to the parameter data.
+#
+# If the fit coefficients for the specified parameter are not in
+# the database then the database entry is created.
+
+procedure fit_function (ms, lines, nlines, spectra, param_id, function, order)
+
+pointer ms # MULTISPEC data structure
+int lines[nlines] # Sample lines to be used
+int nlines # Number of sample lines
+int spectra[ARB] # Spectra to be fitted
+int param_id # Parameter being fit
+int function # Function to be fit
+int order # Order of the function
+
+char comment[SZ_LINE]
+int i, spectrum, fit_id, ier
+real x, wt
+
+int ms_fit_id(), get_next_number()
+real cveval()
+bool dbaccess()
+
+begin
+ # Determine the MULTISPEC fit id from the parameter id.
+ fit_id = ms_fit_id (param_id)
+ if (fit_id == ERR)
+ call error (MS_ERROR, "Unknown fit identifier")
+
+ # Enter the fit records in the database if necessary.
+ if (!dbaccess (MS_DB(ms), NAME(ms, fit_id)))
+ call dbenter (MS_DB(ms), NAME(ms, fit_id),
+ (7 + MS_NSAMPLES(ms)) * SZ_REAL, MS_NSPECTRA(ms))
+
+ # Allocate memory for the curfit data structures pointers.
+ if (MS_DATA(ms, fit_id) == NULL)
+ call malloc (MS_DATA(ms, fit_id), MS_NSPECTRA(ms), TY_INT)
+
+ # Initialize the curfit data structures.
+ # If the order is INDEF then use maximum order assuming no INDEF points.
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF) {
+ if (IS_INDEFI (order)) {
+ switch (function) {
+ case LEGENDRE, CHEBYSHEV:
+ order = nlines
+ case SPLINE3:
+ order = nlines - 3
+ }
+ }
+ call cvinit (CV(ms, fit_id, spectrum), function, order, 1.,
+ real (MS_LEN(ms, 2)))
+ }
+
+ # Accumulate the parameter values.
+ do i = 1, nlines {
+ x = LINE(ms, lines[i])
+ call msgparam (ms, param_id, lines[i])
+
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF)
+ call cvaccum (CV(ms, fit_id, spectrum), x,
+ PARAMETER(ms, param_id, spectrum), wt, WTS_UNIFORM)
+ }
+
+ # Compute and write the fit coeffients to the database.
+
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF) {
+ call cvsolve (CV(ms, fit_id, spectrum), ier)
+ if (ier == NO_DEG_FREEDOM)
+ call error (MS_ERROR, "Error fitting parameters")
+ call mspfit (ms, fit_id, spectrum)
+ }
+
+ # For each sample line and each selected spectrum replace the
+ # selected parameter value with the fit evaluation.
+
+ do i = 1, MS_NSAMPLES(ms) {
+ x = LINE(ms, i)
+ call msgparam (ms, param_id, i)
+
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF)
+ PARAMETER(ms, param_id, spectrum) =
+ cveval (CV(ms, fit_id, spectrum), x)
+
+ call mspparm (ms, param_id, i)
+ }
+
+ # Add a comment to the database comments.
+
+ call sprintf (comment, SZ_LINE, "Fit a function to parameter %s.")
+ call pargstr (NAME(ms, param_id))
+ call history (ms, comment)
+end
diff --git a/noao/twodspec/multispec/t_fitgauss5.x b/noao/twodspec/multispec/t_fitgauss5.x
new file mode 100644
index 00000000..146d37b6
--- /dev/null
+++ b/noao/twodspec/multispec/t_fitgauss5.x
@@ -0,0 +1,209 @@
+include "ms.h"
+
+# T_FIT_GAUSS5 -- Fit the GAUSS5 model.
+#
+# This task selects the database, the sample lines to be modeled, the
+# model fitting algorithm, whether to track models from one sample line
+# to the next or model them independently.
+
+procedure t_fit_gauss5 ()
+
+char image[SZ_FNAME] # Image
+int lines[3, MAX_RANGES] # Sample lines to be modeled
+bool track # Track model solution
+int start # Starting line for modeling
+int naverage # Number of image lines to average
+real lower # Starting point of profile
+real upper # Ending point of profile
+
+int i, nsamples, sample_start, sample, line, improved
+int len_line, len_profile, nspectra, nparams
+pointer ms, im
+pointer sp, data, model, profiles, ranges, samples
+
+int get_sample_line(), get_sample_lines()
+int g5_fit1(), g5_fit2()
+int clgeti(), clgranges(), btoi()
+bool clgetb()
+real clgetr()
+pointer msmap(), immap()
+
+include "fitgauss5.com"
+
+begin
+ # Access the database and the image.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_WRITE, 0)
+ im = immap (image, READ_ONLY, 0)
+
+ # Get the task parameters.
+ i = clgranges ("lines", 1, MS_LEN(ms, 2), lines, MAX_RANGES)
+ i = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra, MAX_RANGES)
+ track = clgetb ("track")
+ start = clgeti ("start")
+ naverage = clgeti ("naverage")
+ lower = clgetr ("lower")
+ upper = clgetr ("upper")
+ factor = clgetr ("factor")
+
+ # Algorithm 1 fits the parameters selected in the parameters array
+ # simultaneously. Algorithm 2 does not require the user to specify
+ # the parameters.
+
+ algorithm = clgeti ("algorithm")
+ if (algorithm == 1) {
+ parameters[I0_INDEX] = btoi (clgetb ("fit_i0"))
+ parameters[X0_INDEX] = btoi (clgetb ("fit_x0"))
+ parameters[S0_INDEX] = btoi (clgetb ("fit_s0"))
+ parameters[S1_INDEX] = btoi (clgetb ("fit_s1"))
+ parameters[S2_INDEX] = btoi (clgetb ("fit_s2"))
+ }
+
+ # Select whether to smooth the shape parameters after fitting.
+ # If smoothing is desired get the spline smoothing parameters.
+
+ smooth[S0_INDEX] = btoi (clgetb ("smooth_s0"))
+ smooth[S1_INDEX] = btoi (clgetb ("smooth_s1"))
+ smooth[S2_INDEX] = btoi (clgetb ("smooth_s2"))
+ if ((smooth[S0_INDEX] == YES) || (smooth[S1_INDEX] == YES) ||
+ (smooth[S2_INDEX] == YES)) {
+ call ms_set_smooth (1., real(MS_LEN(ms, 1)), MS_NSPECTRA(ms))
+ }
+
+ call g5_set_verbose (clgetb ("verbose"))
+ call g5_prnt1 (image, naverage, track, start)
+
+ # Set the various array dimensions and allocate memory.
+ len_line = MS_LEN(ms, 1)
+ len_profile = nint (upper - lower + 2)
+ nspectra = MS_NSPECTRA(ms)
+ nparams = MS_NGAUSS5
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ call salloc (data, len_line, TY_REAL)
+ call salloc (model, len_line, TY_REAL)
+ call salloc (profiles, len_profile * nspectra * nparams, TY_REAL)
+ call salloc (ranges, nspectra * LEN_RANGES, TY_REAL)
+
+ # Convert from image lines to sample lines.
+ nsamples = get_sample_lines (ms, lines, Memi[samples])
+ sample_start = get_sample_line (ms, start)
+
+ # Initialize forward tracking. If tracking get the initial parameters,
+ # model profiles and model line from the starting line.
+
+ if (track) {
+ call msggauss5 (ms, sample_start)
+ call mod_gauss5 (ms, lower, Memr[profiles], Memr[ranges],
+ len_profile, nspectra)
+ call set_model (ms, Memr[model], Memr[profiles], Memr[ranges],
+ len_line, len_profile, nspectra)
+ }
+
+ # Track forward from the starting line to the specified sample lines.
+
+ do i = 1, nsamples {
+ sample = Memi[samples + i - 1]
+ if (sample < sample_start)
+ next
+ line = LINE(ms, sample)
+
+ # Get the image data line.
+ call msgimage (im, line, naverage, Memr[data])
+
+ # If not tracking get the initial parameters, model profiles, and
+ # model line for the current line. Otherwise record the starting
+ # parameters.
+
+ if (!track) {
+ call msggauss5 (ms, sample)
+ call mod_gauss5 (ms, lower, Memr[profiles], Memr[ranges],
+ len_profile, nspectra)
+ call set_model (ms, Memr[model], Memr[profiles], Memr[ranges],
+ len_line, len_profile, nspectra)
+ } else
+ call mspgauss5 (ms, sample)
+
+ call g5_prnt2 (line, Memr[data], len_line)
+
+ # Do the model fitting using the selected algorithm.
+ switch (algorithm) {
+ case 1:
+ improved = g5_fit1 (ms, Memr[data], Memr[model], Memr[profiles],
+ Memr[ranges], lower, len_profile)
+ case 2:
+ improved = g5_fit2 (ms, Memr[data], Memr[model], Memr[profiles],
+ Memr[ranges], lower, len_profile)
+ }
+
+ # If the new model parameters have improved the fit record them in
+ # the database.
+ if (improved == YES)
+ call mspgauss5 (ms, sample)
+ }
+
+ # Initialize backward tracking. If tracking get the initial parameters,
+ # model profiles and model line from the starting line.
+
+ if (track) {
+ call msggauss5 (ms, sample_start)
+ call mod_gauss5 (ms, lower, Memr[profiles], Memr[ranges],
+ len_profile, nspectra)
+ call set_model (ms, Memr[model], Memr[profiles], Memr[ranges],
+ len_line, len_profile, nspectra)
+ }
+
+ # Track backward from the starting line to the specified sample lines.
+
+ do i = nsamples, 1, -1 {
+ sample = Memi[samples + i - 1]
+ if (sample >= sample_start)
+ next
+ line = LINE(ms, sample)
+
+ # Get the image data line.
+ call msgimage (im, line, naverage, Memr[data])
+
+ # If not tracking get the initial parameters, model profiles, and
+ # model line for the current line. Else record the starting
+ # parameters.
+
+ if (!track) {
+ call msggauss5 (ms, sample)
+ call mod_gauss5 (ms, lower, Memr[profiles], Memr[ranges],
+ len_profile, nspectra)
+ call set_model (ms, Memr[model], Memr[profiles], Memr[ranges],
+ len_line, len_profile, nspectra)
+ } else
+ call mspgauss5 (ms, sample)
+
+ call g5_prnt2 (line, Memr[data], len_line)
+
+
+ # Do the model fitting using the selected algorithm.
+ switch (algorithm) {
+ case 1:
+ improved = g5_fit1 (ms, Memr[data], Memr[model], Memr[profiles],
+ Memr[ranges], lower, len_profile)
+ case 2:
+ improved = g5_fit2 (ms, Memr[data], Memr[model], Memr[profiles],
+ Memr[ranges], lower, len_profile)
+ }
+
+ # If the new model parameters have improved the fit record them in
+ # the database.
+
+ if (improved == YES)
+ call mspgauss5 (ms, sample)
+ }
+
+ # Finish up.
+ if ((smooth[S0_INDEX] == YES) || (smooth[S1_INDEX] == YES) ||
+ (smooth[S2_INDEX] == YES)) {
+ call ms_free_smooth ()
+ }
+ call imunmap (im)
+ call history (ms, "Fit model")
+ call msunmap (ms)
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/t_modellist.x b/noao/twodspec/multispec/t_modellist.x
new file mode 100644
index 00000000..911ec2ee
--- /dev/null
+++ b/noao/twodspec/multispec/t_modellist.x
@@ -0,0 +1,126 @@
+include <imhdr.h>
+include "ms.h"
+
+
+# T_MODEL_LIST -- List model values for selected columns and sample lines.
+#
+# The output list format is column, image line, data value, model value.
+# This task differs from t_new_image primarily in that there is no profile
+# interpolation. The model is evaluated only at the sample lines. It
+# is used to check the results of the model fitting tasks.
+
+procedure t_model_list ()
+
+# User parameters:
+char image[SZ_FNAME] # Image
+int model_type # Model type: gauss5, profile
+int columns[3, MAX_RANGES] # Columns to be listed
+int lines[3, MAX_RANGES] # Sample Lines to be listed
+int naverage # Number of image lines to average
+real lower # Lower limit of profile model
+real upper # Upper limit of profile model
+
+int i, sample, nsamples, line, column
+pointer ms, im
+pointer sp, samples, data, model
+
+int clgeti(), ms_model_id(), clgranges()
+int get_next_number(), get_sample_lines
+real clgetr()
+pointer msmap(), immap()
+
+begin
+ # Access the database and image.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_ONLY, 0)
+ im = immap (image, READ_ONLY, 0)
+
+ # Get the task parameters.
+ model_type = ms_model_id ("model")
+ i = clgranges ("columns", 1, IM_LEN(im, 1), columns, MAX_RANGES)
+ i = clgranges ("lines", 1, IM_LEN(im, 2), lines, MAX_RANGES)
+ naverage = clgeti ("naverage")
+ lower = clgetr ("lower")
+ upper = clgetr ("upper")
+
+ # Currently only model GAUSS5 is available.
+ if (model_type != GAUSS5)
+ return
+
+ # Allocate memory for the sample lines, data and model.
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ call salloc (data, IM_LEN(im, 1), TY_REAL)
+ call salloc (model, IM_LEN(im, 1), TY_REAL)
+
+ # Convert to sample lines.
+ nsamples = get_sample_lines (ms, lines, Memi[samples])
+
+ # For each sample line get the data line and compute a model line.
+ # Print the data and model values for the selected image columns.
+ do i = 1, nsamples {
+ sample = Memi[samples + i - 1]
+ line = LINE(ms, sample)
+
+ call msgimage (im, line, naverage, Memr[data])
+
+ switch (model_type) {
+ case GAUSS5:
+ call gauss5_model (ms, sample, lower, upper, Memr[model])
+ }
+
+ column = 0
+ while (get_next_number (columns, column) != EOF) {
+ call printf ("%d %d %g %g\n")
+ call pargi (column)
+ call pargi (line)
+ call pargr (Memr[data + column - 1])
+ call pargr (Memr[model + column - 1])
+ }
+ }
+
+ call sfree (sp)
+ call imunmap (im)
+ call msunmap (ms)
+end
+
+
+# GAUSS5_MODEL -- Generate a line of the GAUSS5 model.
+
+procedure gauss5_model (ms, line, lower, upper, model)
+
+pointer ms # MULTISPEC data structure
+int line # Sample line
+real lower # Lower profile limit
+real upper # Upper profile limit
+real model[ARB] # Model data array to be returned
+
+int nspectra, nparams, len_line, len_profile
+pointer sp, profiles, ranges
+
+begin
+ # Set the dimensions of the arrays.
+ nspectra = MS_NSPECTRA(ms)
+ nparams = MS_NGAUSS5
+ len_line = MS_LEN(ms, 1)
+ len_profile = nint (upper - lower + 2)
+
+ # Allocate arrays.
+ call smark (sp)
+ call salloc (ranges, nspectra * LEN_RANGES, TY_REAL)
+ call salloc (profiles, len_profile * nspectra * nparams, TY_REAL)
+
+ # Read the model parameters for the specified sample line.
+ call msggauss5 (ms, line)
+
+ # Calculate the model profiles.
+ call mod_gauss5 (ms, lower, Memr[profiles], Memr[ranges], len_profile,
+ nspectra)
+
+ # Make a model line using the model profiles.
+ call set_model (ms, model, Memr[profiles], Memr[ranges], len_line,
+ len_profile, nspectra)
+
+ # Return memory.
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/t_msextract.x b/noao/twodspec/multispec/t_msextract.x
new file mode 100644
index 00000000..da649469
--- /dev/null
+++ b/noao/twodspec/multispec/t_msextract.x
@@ -0,0 +1,112 @@
+include <imhdr.h>
+include "ms.h"
+
+# T_MSEXTRACT -- General MULTISPEC extraction task.
+#
+# The general task parameters are obtained and the desired extraction
+# procedure is called. The input database and image are accessed and
+# the output image is created.
+
+procedure t_msextract ()
+
+# User parameters:
+char image[SZ_FNAME] # Image
+char output[SZ_FNAME] # Output image file
+real lower # Lower limit of strip
+real upper # Upper limit of strip
+int spectra[3, MAX_RANGES] # Spectra to be extracted
+int lines[3, MAX_RANGES] # Lines to be extracted
+bool ex_model # Extract model or data
+bool integrated # Extract integrated spectra?
+bool unblend # Correct for spectra blending
+bool clean # Correct for bad pixels
+int nreplace # Maximum number pixels replaced
+real sigma_cut # Threshold for replacing bad pixels
+int model # Model type: gauss5, profile
+
+bool ex_spectra
+int nlines
+int nspectra
+pointer ms, im_in, im_out
+
+int clgeti(), ms_model_id(), clgranges()
+bool clgetb()
+real clgetr()
+pointer msmap(), immap()
+
+begin
+ # Access input and output files.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_ONLY, 0)
+ im_in = immap (image, READ_ONLY, 0)
+ call clgstr ("output", output, SZ_FNAME)
+ im_out = immap (output, NEW_IMAGE, 0)
+
+ # Determine extraction limits.
+ lower = clgetr ("lower")
+ upper = clgetr ("upper")
+ nlines = clgranges ("lines", 1, IM_LEN(im_in, 2), lines, MAX_RANGES)
+ nspectra = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra,
+ MAX_RANGES)
+
+ # Determine type of extraction.
+ ex_spectra = TRUE
+ ex_model = clgetb ("ex_model")
+ integrated = clgetb ("integrated")
+
+ # Determine whether to clean data spectra and the cleaning parameters.
+ clean = clgetb ("clean")
+ if (clean) {
+ nreplace = clgeti ("nreplace")
+ sigma_cut = clgetr ("sigma_cut")
+ } else
+ nreplace = 0
+
+ # Determine whether to apply blending correction.
+ if (!ex_model)
+ unblend = clgetb ("unblend")
+
+ # Set type of model to be used. If a blending correction is desired
+ # the model must GAUSS5 otherwise the user selects the model.
+ model = NONE
+ if (unblend)
+ model = GAUSS5
+ else if (ex_model || clean)
+ model = ms_model_id ("model")
+
+ # Set verbose output.
+ call ex_set_verbose (clgetb ("verbose"))
+ call ex_prnt1 (MS_IMAGE(ms), output)
+
+ # Set image header for output extraction image file.
+ IM_NDIM(im_out) = 3
+ if (integrated)
+ IM_LEN(im_out, 1) = 1
+ else
+ IM_LEN(im_out, 1) = nint (upper - lower + 1)
+ IM_LEN(im_out, 2) = nlines
+ IM_LEN(im_out, 3) = nspectra
+ IM_PIXTYPE(im_out) = TY_REAL
+ call strcpy (IM_TITLE(im_in), IM_TITLE(im_out), SZ_IMTITLE)
+
+ # Select extraction procedure based on model.
+ switch (model) {
+ case GAUSS5:
+ call set_fit_and_clean (clgeti ("niterate"), nreplace, sigma_cut,
+ clgeti ("fit_type"), ex_model)
+ call ex_gauss5 (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, integrated)
+ case SMOOTH:
+ call set_fit_smooth (nreplace, sigma_cut)
+ call ex_smooth (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, integrated)
+ default:
+ call ex_strip (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, integrated)
+ }
+
+ # Close files.
+ call imunmap (im_in)
+ call imunmap (im_out)
+ call msunmap (ms)
+end
diff --git a/noao/twodspec/multispec/t_mslist.x b/noao/twodspec/multispec/t_mslist.x
new file mode 100644
index 00000000..e21d685e
--- /dev/null
+++ b/noao/twodspec/multispec/t_mslist.x
@@ -0,0 +1,312 @@
+include <fset.h>
+include "ms.h"
+
+# T_MS_LIST -- Print general MULTISPEC database information.
+
+procedure t_ms_list ()
+
+char image[SZ_FNAME]
+char keyword[SZ_LINE]
+bool titles
+
+int ms_id
+pointer ms
+
+bool clgetb(), streq()
+int ms_db_id()
+pointer msmap()
+
+begin
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Get task parameters.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_ONLY, 0)
+ call clgstr ("keyword", keyword, SZ_LINE)
+ titles = clgetb ("titles")
+
+ # Check for special keywords.
+ if (streq (keyword, "gauss5")) {
+ call g5_list (ms, keyword, titles)
+
+ # Keyword is one of the database record names. Convert to a
+ # MULTISPEC id and switch to appropriate listing routine.
+ } else {
+ ms_id = ms_db_id (ms, keyword)
+ switch (ms_id) {
+ case HDR:
+ call hdr_list (ms, keyword, titles)
+ case COMMENTS:
+ call com_list (ms, keyword, titles)
+ case SAMPLE:
+ call sam_list (ms, keyword, titles)
+ case I0, X0, S0, S1, S2:
+ call par_list (ms, ms_id, keyword, titles)
+ case X0_FIT, S0_FIT, S1_FIT, S2_FIT:
+ call fit_list(ms, ms_id, keyword, titles)
+ }
+ }
+
+ call msunmap (ms)
+end
+
+
+# HDR_LIST - List the contents of the MULTISPEC database header
+
+procedure hdr_list (ms, keyword, titles)
+
+pointer ms # MULTISPEC data structure
+char keyword[ARB] # List keyword
+bool titles # Print titles?
+
+begin
+ call printf ("Image: %s\n")
+ call pargstr (MS_IMAGE(ms))
+ call printf ("Keyword: %s\n")
+ call pargstr (keyword)
+ call printf ("Title: %s\n")
+ call pargstr (MS_TITLE(ms))
+ call printf ("Number of spectra: %d\n")
+ call pargi (MS_NSPECTRA(ms))
+ call printf ("Number of sample image lines: %d\n")
+ call pargi (MS_NSAMPLES(ms))
+ call printf ("Image size: %d x %d\n")
+ call pargi (MS_LEN(ms, 1))
+ call pargi (MS_LEN(ms, 2))
+end
+
+procedure com_list (ms, keyword, titles)
+
+pointer ms # MULTISPEC data structure
+char keyword[ARB] # List keyword
+bool titles # Print titles?
+int i
+
+begin
+ if (titles) {
+ call printf ("Image: %s\n")
+ call pargstr (MS_IMAGE(ms))
+ call printf ("Keyword: %s\n")
+ call pargstr (keyword)
+ call printf ("Comments:\n")
+ }
+
+ for (i=1; (i <= SZ_MS_COMMENTS) && (COMMENT(ms, i) != EOS); i=i+1)
+ call putchar (COMMENT(ms, i))
+end
+
+
+# SAM_LIST -- List the sample image lines.
+
+procedure sam_list (ms, keyword, titles)
+
+pointer ms # MULTISPEC data structure
+char keyword[ARB] # List keyword
+bool titles # Print titles?
+int i
+
+begin
+ if (titles) {
+ call printf ("Image: %s\n")
+ call pargstr (MS_IMAGE(ms))
+ call printf ("Keyword: %s\n")
+ call pargstr (keyword)
+ call printf ("Sample Image Lines:\n")
+ }
+
+ do i = 1, MS_NSAMPLES(ms) {
+ call printf ("%8d\n")
+ call pargi (LINE(ms, i))
+ }
+end
+
+
+# PAR_LIST -- Print MULTISPEC profile parameters.
+#
+# This procedure does some CLIO.
+
+procedure par_list (ms, ms_id, keyword, titles)
+
+pointer ms # MULTISPEC data structure
+int ms_id # MULTISPEC parameter id
+char keyword[ARB] # List keyword
+bool titles # Print titles?
+
+int lines[3, MAX_RANGES], spectra[3, MAX_RANGES]
+int i, nsamples, sample, spectrum
+pointer sp, samples
+
+int clgranges(), get_next_number(), get_sample_lines()
+
+begin
+ if ((MS_NSAMPLES(ms) == 0) || (MS_NSPECTRA(ms) == 0))
+ return
+
+ # Get desired image lines and spectra to be listed.
+ i = clgranges ("lines", 1, MS_LEN(ms, 2), lines, MAX_RANGES)
+ i = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra, MAX_RANGES)
+
+ # Convert image lines to sample lines.
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ nsamples = get_sample_lines (ms, lines, Memi[samples])
+
+ # Print header titles if needed.
+ if (titles) {
+ call printf ("Image: %s\n")
+ call pargstr (MS_IMAGE(ms))
+ call printf ("Keyword: %s\n")
+ call pargstr (keyword)
+ call printf ("%8s %8s %8s\n")
+ call pargstr ("Line")
+ call pargstr ("Spectrum")
+ call pargstr (NAME(ms, ms_id))
+ }
+
+ # For each sample line get the parameter values for the selected
+ # parameter and list those for the selected spectra.
+ do i = 1, nsamples {
+ sample = Memi[samples + i - 1]
+
+ call msgparam (ms, ms_id, sample)
+
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF) {
+ call printf ("%8d %8d %8.3g\n")
+ call pargi (LINE(ms, sample))
+ call pargi (spectrum)
+ call pargr (PARAMETER(ms, ms_id, spectrum))
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# FIT_LIST -- Print MULTISPEC fit.
+#
+# This procedure does CLIO.
+
+procedure fit_list (ms, ms_id, keyword, titles)
+
+pointer ms # MULTISPEC data structure
+int ms_id # MULTISPEC parameter id
+char keyword[ARB] # List keyword
+bool titles # Print header titles?
+
+int lines[3, MAX_RANGES]
+int spectra[3, MAX_RANGES]
+
+int i, line, spectrum
+
+real cveval()
+int clgranges(), get_next_number()
+
+begin
+ if (MS_NSPECTRA(ms) == 0)
+ return
+
+ # Get the image lines at which to evaluate the function and
+ # the spectra to be listed.
+
+ i = clgranges ("lines", 1, MS_LEN(ms, 2), lines, MAX_RANGES)
+ i = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra, MAX_RANGES)
+
+ # Get the fits.
+ call msgfits (ms, ms_id)
+
+ # Print header titles if needed.
+ if (titles) {
+ call printf ("Image: %s\n")
+ call pargstr (MS_IMAGE(ms))
+ call printf ("Keyword: %s\n")
+ call pargstr (keyword)
+ call printf ("%8s %8s %8s\n")
+ call pargstr ("Line")
+ call pargstr ("Spectrum")
+ call pargstr (NAME(ms, ms_id))
+ }
+
+ # For each selected image line evalute the functions for the
+ # selected spectra and print the values.
+
+ line = 0
+ while (get_next_number (lines, line) != EOF) {
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF) {
+ call printf ("%8d %8d %8.3g\n")
+ call pargi (line)
+ call pargi (spectrum)
+ call pargr (cveval (CV(ms, ms_id, spectrum), real (line)))
+ }
+ }
+end
+
+
+# G5_LIST -- Print MULTISPEC model gauss5 profile parameters.
+#
+# This procedure does CLIO.
+
+procedure g5_list (ms, keyword, titles)
+
+pointer ms # MULTISPEC data structure
+char keyword[ARB] # List keyword
+bool titles # Print header titles?
+
+int lines[3, MAX_RANGES], spectra[3, MAX_RANGES]
+int i, nsamples, sample, spectrum
+pointer sp, samples
+
+int clgranges(), get_next_number(), get_sample_lines()
+
+begin
+ if ((MS_NSAMPLES(ms) == 0) || (MS_NSPECTRA(ms) == 0))
+ return
+
+ # Get desired image lines and spectra to be listed.
+ i = clgranges ("lines", 1, MS_LEN(ms, 2), lines, MAX_RANGES)
+ i = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra, MAX_RANGES)
+
+ # Convert image lines to sample lines.
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ nsamples = get_sample_lines (ms, lines, Memi[samples])
+
+ # Print header titles if needed.
+ if (titles) {
+ call printf ("Image: %s\n")
+ call pargstr (MS_IMAGE(ms))
+ call printf ("Keyword: %s\n")
+ call pargstr (keyword)
+ call printf ("%8s %8s %8s %8s %8s %8s %8s\n")
+ call pargstr ("Line")
+ call pargstr ("Spectrum")
+ call pargstr (NAME (ms, X0))
+ call pargstr (NAME (ms, I0))
+ call pargstr (NAME (ms, S0))
+ call pargstr (NAME (ms, S1))
+ call pargstr (NAME (ms, S2))
+ }
+
+ # For each sample line get the GAUSS5 values and list for the
+ # selected spectra.
+ do i = 1, nsamples {
+ sample = Memi[samples + i - 1]
+
+ call msggauss5 (ms, sample)
+
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF) {
+ call printf ("%8d %8d %8.3g %8.3g %8.3g %8.3g %8.3g\n")
+ call pargi (LINE(ms, sample))
+ call pargi (spectrum)
+ call pargr (PARAMETER(ms, X0, spectrum))
+ call pargr (PARAMETER(ms, I0, spectrum))
+ call pargr (PARAMETER(ms, S0, spectrum))
+ call pargr (PARAMETER(ms, S1, spectrum))
+ call pargr (PARAMETER(ms, S2, spectrum))
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/twodspec/multispec/t_msset.x b/noao/twodspec/multispec/t_msset.x
new file mode 100644
index 00000000..81d94f0c
--- /dev/null
+++ b/noao/twodspec/multispec/t_msset.x
@@ -0,0 +1,189 @@
+include "ms.h"
+
+# T_MS_SET -- Set profile parameters in database.
+
+procedure t_ms_set ()
+
+char image[SZ_FNAME]
+char keyword[SZ_LINE]
+
+char comment[SZ_LINE]
+int i, nspectra, ms_id
+pointer ms
+
+bool streq(), clgetb()
+int clscan(), nscan(), ms_db_id()
+pointer msmap()
+
+begin
+ # Get the task parameters and access the database.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_WRITE, 0)
+ call clgstr ("keyword", keyword, SZ_LINE)
+
+ # Decode the keyword for the desired database quantity.
+ if (streq (keyword, "nspectra")) {
+ # Set the value of MS_NSPECTRA in the MULTISPEC header record.
+ if (clgetb ("read_list"))
+ i = clscan ("list")
+ else
+ i = clscan ("value")
+ call gargi (nspectra)
+ if (nscan () != 1)
+ call error (MS_ERROR, "Bad parameter value")
+
+ # It is an error to attempt to change the value previously set.
+ if (MS_NSPECTRA(ms) == 0)
+ MS_NSPECTRA(ms) = nspectra
+ else if (MS_NSPECTRA(ms) != nspectra)
+ call error (MS_ERROR, "Attempt to change number of spectra")
+ } else {
+ # Keyword is one of the database record names. Convert to
+ # a MULTISPEC parameter ID and call the appropriate procedure.
+
+ ms_id = ms_db_id (ms, keyword)
+ switch (ms_id) {
+ case COMMENTS:
+ call com_set (ms, comment)
+ case I0, X0, S0, S1, S2:
+ call par_set (ms, ms_id, comment)
+ }
+ }
+
+ # Finish up.
+ call msphdr (ms)
+ call msunmap (ms)
+end
+
+
+# COM_SET -- Add a comment to the MULTISPEC database comment block.
+#
+# This procedure does CLIO.
+
+procedure com_set (ms, comment)
+
+pointer ms # MULTISPEC data structure
+char comment[SZ_LINE] # Input comment buffer.
+
+int i
+
+bool clgetb()
+int clscan()
+
+begin
+ # Desire whether to use list input or CL parameter input.
+ if (clgetb ("read_list")) {
+ # Read a list of comment strings.
+ while (clscan ("list") != EOF) {
+ call gargstr (comment, SZ_LINE)
+ call history (ms, comment)
+ }
+ } else {
+ # Read a comment line from the parameter "value".
+ i = clscan ("value")
+ call gargstr (comment, SZ_LINE)
+ call history (ms, comment)
+ }
+end
+
+
+# PAR_SET -- Set the values of the model parameters.
+#
+# This procedure does CLIO.
+
+procedure par_set (ms, ms_id, comment)
+
+pointer ms # MULTISPEC data structure
+int ms_id # MULTISPEC ID
+char comment[SZ_LINE] # Comment buffer
+
+int i, line, nsamples, sample, last_sample, spectrum
+int lines[3, MAX_RANGES], spectra[3, MAX_RANGES]
+real value
+pointer sp, samples
+
+int clscan(), nscan(), clgranges(), get_next_number()
+int get_sample_line(), get_sample_lines()
+bool dbaccess(), clgetb()
+
+begin
+ if ((MS_NSAMPLES(ms) == 0) || (MS_NSPECTRA(ms) == 0))
+ return
+
+ # Enter the parameter in the database if necessary.
+ if (!dbaccess (MS_DB(ms), NAME(ms, ms_id)))
+ call dbenter (MS_DB(ms), NAME(ms, ms_id),
+ MS_NSPECTRA(ms) * SZ_REAL, MS_NSAMPLES(ms))
+
+ # Determine input source.
+ if (clgetb ("read_list")) {
+ # Read values from a list.
+ last_sample = 0
+ while (clscan ("list") != EOF) {
+
+ # Get line, spectrum, and value from the list.
+ call gargi (line)
+ call gargi (spectrum)
+ call gargr (value)
+
+ # Check that the data is valid otherwise go to next input.
+ if (nscan () != 3)
+ next
+ if ((spectrum < 1) || (spectrum > MS_NSPECTRA(ms)))
+ next
+
+ # If the last sample is not the same as the previous sample
+ # flush the last parameter values if the last sample is not
+ # zero and get the next line of parameter values.
+
+ sample = get_sample_line (ms, line)
+ if (sample != last_sample) {
+ if (last_sample != 0)
+ call mspparam (ms, ms_id, last_sample)
+ call msgparam (ms, ms_id, sample)
+ last_sample = sample
+ }
+
+ # Set the parameter value.
+ PARAMETER(ms, ms_id, spectrum) = value
+ }
+
+ # Flush the last line of parameter values.
+ call mspparam (ms, ms_id, last_sample)
+
+ } else {
+ # Set the parameter values for the selected lines and spectra
+ # to the CL parameter "value".
+
+ i = clgranges ("lines", 1, MS_LEN(ms, 2), lines, MAX_RANGES)
+ i = clgranges ("spectra", 1, MS_NSPECTRA(ms), spectra, MAX_RANGES)
+ i = clscan ("value")
+
+ # Convert the image lines to sample lines.
+ call smark (sp)
+ call salloc (samples, MS_NSAMPLES(ms), TY_INT)
+ nsamples = get_sample_lines (ms, lines, Memi[samples])
+
+ # Check that the parameter value is a real number.
+ call gargr (value)
+ if (nscan () != 1)
+ call error (MS_ERROR, "Bad parameter value")
+
+ # Go through the selected sample lines and spectra setting the
+ # parameter value.
+
+ do i = 1, nsamples {
+ sample = Memi[samples + i - 1]
+ call msgparam (ms, ms_id, sample)
+ spectrum = 0
+ while (get_next_number (spectra, spectrum) != EOF)
+ PARAMETER (ms, ms_id, spectrum) = value
+ call mspparam (ms, ms_id, sample)
+ }
+ }
+
+ # Add a history comment.
+ call sprintf (comment, SZ_LINE, "Values of parameter %s set.")
+ call pargstr (NAME(ms, ms_id))
+ call history (ms, comment)
+end
diff --git a/noao/twodspec/multispec/t_newextract.x b/noao/twodspec/multispec/t_newextract.x
new file mode 100644
index 00000000..0e695222
--- /dev/null
+++ b/noao/twodspec/multispec/t_newextract.x
@@ -0,0 +1,99 @@
+include <imhdr.h>
+include "ms.h"
+
+# T_NEW_EXTRACTION -- Create a new extraction database.
+#
+# This is the first step in using the MULTISPEC package. The new database
+# may be created from scratch or intialized from an template image.
+
+procedure t_new_extraction ()
+
+# Task parameters:
+char image[SZ_FNAME] # Multi-spectra image
+char template[SZ_FNAME] # Template image
+int samples[3, MAX_RANGES] # Sample line range array
+
+char comment[SZ_LINE]
+char database[SZ_FNAME], old_database[SZ_FNAME]
+pointer im, ms
+
+bool strne()
+int clgranges(), expand_ranges()
+pointer immap(), msmap()
+
+begin
+ # Get database and image name. Map the image and check that
+ # it is two dimensional.
+ call clgstr ("image", image, SZ_FNAME)
+ im = immap (image, READ_ONLY, 0)
+ if (IM_NDIM(im) != 2)
+ call error (MS_ERROR, "Image file must be two dimensional.")
+
+ # Get the template image name.
+ call clgstr ("template", template, SZ_FNAME)
+
+ if (strne (template, "")) {
+ # If a template is given then map the template and check
+ # that the new image dimensions agree with the old dimensions.
+
+ ms = msmap (template, READ_ONLY, 0)
+ if ((MS_LEN(ms, 1) != IM_LEN(im, 1)) ||
+ (MS_LEN(ms, 2) != IM_LEN(im, 2)))
+ call error (MS_ERROR,
+ "New image size does not agree with the old image size.")
+ call msunmap (ms)
+
+ # Copy the old database. Map the new database and clear the
+ # the old comments before adding a new comment.
+
+ call sprintf (database, SZ_FNAME, "%s.db")
+ call pargstr (image)
+ call sprintf (old_database, SZ_FNAME, "%s.db")
+ call pargstr (template)
+ call fcopy (old_database, database)
+
+ ms = msmap (image, READ_WRITE, 0)
+ COMMENT(ms, 1) = EOS
+ call sprintf (comment, SZ_LINE,
+ "Database initialized from the template image %s.")
+ call pargstr (template)
+ call history (ms, comment)
+
+ } else {
+ # For a new database initialize the header parameters.
+ ms = msmap (image, NEW_FILE, MS_DB_ENTRIES)
+ MS_LEN(ms, 1) = IM_LEN(im, 1)
+ MS_LEN(ms, 2) = IM_LEN(im, 2)
+ MS_NSPECTRA(ms) = 0
+
+ # Get the sample line ranges and set the number of sample lines
+ # in the database header.
+ MS_NSAMPLES(ms) = clgranges ("sample_lines", 1, MS_LEN (ms, 2),
+ samples, MAX_RANGES)
+
+ # Make an entry in the database for the sample lines and then
+ # access the entry in order to allocate memory for the sample
+ # line array.
+ call dbenter (MS_DB(ms), NAME(ms, SAMPLE), MS_NSAMPLES(ms)*SZ_INT,1)
+ call msgsample (ms)
+
+ # Expand the sample line range array into the sample line array.
+ # Then put the sample line array in the database.
+ MS_NSAMPLES(ms) = expand_ranges (samples, LINE(ms, 1),
+ MS_NSAMPLES(ms))
+ call mspsample (ms)
+
+ # Add a history line.
+ call history (ms, "New MULTISPEC database created.")
+ }
+
+ # Set the image name and image title in the database.
+ call strcpy (image, MS_IMAGE(ms), SZ_MS_IMAGE)
+ call strcpy (IM_TITLE(im), MS_TITLE(ms), SZ_MS_TITLE)
+
+ # Close image and database. Write the database header record before
+ # closing the database.
+ call imunmap (im)
+ call msphdr (ms)
+ call msunmap (ms)
+end
diff --git a/noao/twodspec/multispec/t_newimage.x b/noao/twodspec/multispec/t_newimage.x
new file mode 100644
index 00000000..c74ce22a
--- /dev/null
+++ b/noao/twodspec/multispec/t_newimage.x
@@ -0,0 +1,97 @@
+include <imhdr.h>
+include "ms.h"
+
+# T_NEW_IMAGE -- General MULTISPEC extraction task.
+#
+# The general task parameters are obtained and the desired extraction
+# procedure is called. The input database and image are accessed and
+# the output image is created.
+
+procedure t_new_image ()
+
+# User parameters:
+char image[SZ_FNAME] # MULTISPEC database
+char output[SZ_FNAME] # Output image file
+real lower # Lower limit of strip
+real upper # Upper limit of strip
+int lines[3, MAX_RANGES] # Lines to be extracted
+int spectra[3, MAX_RANGES] # Spectra to be extracted
+bool ex_model # Extract model or data
+bool clean # Correct for bad pixels
+int nreplace # Maximum number of bad pixels replaced
+real sigma_cut # Threshold for replacing bad pixels
+int model # Model type: gauss5, profile
+
+bool ex_spectra # Extract spectra or image line
+bool integrated # Extract integrated spectra or strip
+int nlines
+pointer ms, im_in, im_out
+
+int clgeti(), ms_model_id(), clgranges()
+bool clgetb()
+real clgetr()
+pointer msmap(), immap()
+
+begin
+ # Access input and output files.
+ call clgstr ("image", image, SZ_FNAME)
+ ms = msmap (image, READ_ONLY, 0)
+ im_in = immap (image, READ_ONLY, 0)
+ call clgstr ("output", output, SZ_FNAME)
+ im_out = immap (output, NEW_IMAGE, 0)
+
+ # Determine extraction limits.
+ nlines = clgranges ("lines", 1, IM_LEN(im_in, 2), lines, MAX_RANGES)
+ lower = clgetr ("lower")
+ upper = clgetr ("upper")
+
+ # Determine type of extraction.
+ ex_spectra = FALSE
+ ex_model = clgetb ("ex_model")
+ integrated = FALSE
+
+ # Determine whether to clean data lines and the cleaning parameters.
+ clean = clgetb ("clean")
+ if (clean) {
+ nreplace = clgeti ("nreplace")
+ sigma_cut = clgetr ("sigma_cut")
+ } else
+ nreplace = 0
+
+ # Set type of model to be used.
+ model = NONE
+ if (ex_model || clean)
+ model = ms_model_id ("model")
+
+ # Set verbose output.
+ call ex_set_verbose (clgetb ("verbose"))
+ call ex_prnt1 (image, output)
+
+ # Set image header for output extraction image file.
+ IM_NDIM(im_out) = IM_NDIM(im_in)
+ IM_LEN(im_out, 1) = IM_LEN(im_in, 1)
+ IM_LEN(im_out, 2) = nlines
+ IM_PIXTYPE(im_out) = IM_PIXTYPE(im_in)
+ call strcpy (IM_TITLE(im_in), IM_TITLE(im_out), SZ_IMTITLE)
+
+ # Select extraction procedure based on model.
+ switch (model) {
+ case GAUSS5:
+ call set_fit_and_clean (clgeti ("niterate"), nreplace,
+ sigma_cut, clgeti ("fit_type"), ex_model)
+ call ex_gauss5 (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, integrated)
+ case SMOOTH:
+ call set_fit_smooth (nreplace, sigma_cut)
+ call ex_smooth (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, integrated)
+ default:
+ call ex_strip (ms, im_in, im_out, spectra, lines, lower, upper,
+ ex_spectra, ex_model, integrated)
+ }
+
+ # Close files.
+ call imunmap (im_in)
+ call imunmap (im_out)
+ call msunmap (ms)
+end
diff --git a/noao/twodspec/multispec/unblend.x b/noao/twodspec/multispec/unblend.x
new file mode 100644
index 00000000..707c6b49
--- /dev/null
+++ b/noao/twodspec/multispec/unblend.x
@@ -0,0 +1,38 @@
+include "ms.h"
+
+# UNBLEND -- Create unblended data profiles from a blended data line.
+#
+# For each point in each spectrum profile determine the corresponding column
+# in the data line from the ranges array. If the model is non-zero then the
+# data profile value for that spectrum is a fraction of the total data value
+# at that point given by the fraction of that model profile to the total
+# model at that point.
+
+procedure unblend (data, data_profiles, model, model_profiles, ranges,
+ len_line, len_profile, nspectra)
+
+real data[len_line] # Data line to be unblended
+real data_profiles[len_profile, nspectra] # Output data profiles
+real model[len_line] # Model line
+real model_profiles[len_profile, nspectra] # Model profiles
+real ranges[nspectra, LEN_RANGES] # Ranges for model profiles
+int len_line # Length of data/model line
+int len_profile # Length of each profile
+int nspectra # Number of spectra
+
+int i, x, spectrum
+
+begin
+ do spectrum = 1, nspectra {
+ do i = 1, len_profile {
+ x = ranges[spectrum, X_START] + i - 1
+ if ((x >= 1) && (x <= len_line)) {
+ if (model[x] > 0.)
+ data_profiles[i, spectrum] =
+ data[x] * model_profiles[i, spectrum] / model[x]
+ else
+ data_profiles[i, spectrum] = data[x]
+ }
+ }
+ }
+end
diff --git a/noao/twodspec/multispec/x_multispec.x b/noao/twodspec/multispec/x_multispec.x
new file mode 100644
index 00000000..accdb148
--- /dev/null
+++ b/noao/twodspec/multispec/x_multispec.x
@@ -0,0 +1,10 @@
+task newextraction = t_new_extraction,
+ findpeaks = t_find_peaks,
+ mslist = t_ms_list,
+ msset = t_ms_set,
+ fitfunction = t_fit_function,
+ modellist = t_model_list,
+ fitgauss5 = t_fit_gauss5,
+ msextract = t_msextract,
+ newimage = t_new_image,
+ msplot