aboutsummaryrefslogtreecommitdiff
path: root/noao/nproto/ace
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /noao/nproto/ace
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'noao/nproto/ace')
-rw-r--r--noao/nproto/ace/Notes12
-rw-r--r--noao/nproto/ace/Revisions89
-rw-r--r--noao/nproto/ace/ace.h32
-rw-r--r--noao/nproto/ace/acedetect.h27
-rw-r--r--noao/nproto/ace/aceoverlay.x76
-rw-r--r--noao/nproto/ace/acesky.h21
-rw-r--r--noao/nproto/ace/bndry.x194
-rw-r--r--noao/nproto/ace/cat.h45
-rw-r--r--noao/nproto/ace/catdef.desc73
-rw-r--r--noao/nproto/ace/catdefine.x192
-rw-r--r--noao/nproto/ace/catio.x931
-rw-r--r--noao/nproto/ace/colors.dat8
-rw-r--r--noao/nproto/ace/convolve.x971
-rw-r--r--noao/nproto/ace/detect.h16
-rw-r--r--noao/nproto/ace/detect.par65
-rw-r--r--noao/nproto/ace/detect.x795
-rw-r--r--noao/nproto/ace/diffdetect.par59
-rw-r--r--noao/nproto/ace/display.h42
-rw-r--r--noao/nproto/ace/doc/detect.hlp470
-rw-r--r--noao/nproto/ace/doc/installation.hlp208
-rw-r--r--noao/nproto/ace/doc/objmasks.hlp710
-rw-r--r--noao/nproto/ace/edgewts.xNEW56
-rw-r--r--noao/nproto/ace/evaluate.h6
-rw-r--r--noao/nproto/ace/evaluate.par32
-rw-r--r--noao/nproto/ace/evaluate.x641
-rw-r--r--noao/nproto/ace/filter.h14
-rw-r--r--noao/nproto/ace/filter.x134
-rw-r--r--noao/nproto/ace/grow.h6
-rw-r--r--noao/nproto/ace/grow.x959
-rw-r--r--noao/nproto/ace/gwindow.h49
-rw-r--r--noao/nproto/ace/mapio.x406
-rw-r--r--noao/nproto/ace/maskcolor.x54
-rw-r--r--noao/nproto/ace/mgs.x321
-rw-r--r--noao/nproto/ace/mim.x544
-rw-r--r--noao/nproto/ace/mkpkg60
-rw-r--r--noao/nproto/ace/noisemodel.x102
-rw-r--r--noao/nproto/ace/objmasks.cl28
-rw-r--r--noao/nproto/ace/objmasks.par22
-rw-r--r--noao/nproto/ace/objmasks1.par30
-rw-r--r--noao/nproto/ace/objs.h139
-rw-r--r--noao/nproto/ace/omwrite.x98
-rw-r--r--noao/nproto/ace/overlay.par30
-rw-r--r--noao/nproto/ace/pars.x375
-rw-r--r--noao/nproto/ace/reviewproto.cl215
-rw-r--r--noao/nproto/ace/sky.h14
-rw-r--r--noao/nproto/ace/sky.x118
-rw-r--r--noao/nproto/ace/skyblock.h50
-rw-r--r--noao/nproto/ace/skyblock.x1039
-rw-r--r--noao/nproto/ace/skyfit.h24
-rw-r--r--noao/nproto/ace/skyfit.x393
-rw-r--r--noao/nproto/ace/skygrow.xNEW89
-rw-r--r--noao/nproto/ace/skyimages.par10
-rw-r--r--noao/nproto/ace/skyimages.x120
-rw-r--r--noao/nproto/ace/split.h13
-rw-r--r--noao/nproto/ace/split.x625
-rw-r--r--noao/nproto/ace/t_acedetect.x1195
-rw-r--r--noao/nproto/ace/t_acedisplay.x639
-rw-r--r--noao/nproto/ace/t_imext.x533
-rw-r--r--noao/nproto/ace/t_mscext.x180
-rw-r--r--noao/nproto/ace/tables.x197
-rw-r--r--noao/nproto/ace/x_ace.x4
-rw-r--r--noao/nproto/ace/xtmaskname.x114
-rw-r--r--noao/nproto/ace/xtpmmap.x603
63 files changed, 15317 insertions, 0 deletions
diff --git a/noao/nproto/ace/Notes b/noao/nproto/ace/Notes
new file mode 100644
index 00000000..3cdd5b07
--- /dev/null
+++ b/noao/nproto/ace/Notes
@@ -0,0 +1,12 @@
+o Evaluate centroid in detection phase so that evaluation can
+ include quantities based on distance from centroid.
+o Kron magnitudes
+o Add partial pixel
+o What to do about contaminating objects in the apertures.
+
+TODO
+
+o MapIO - match coordinates for sky fit
+o match object mask as part of aceall when outobjmask is given
+o reorganize to remove xap,yap,etc from detection phase
+o errors in ra/dec and mags
diff --git a/noao/nproto/ace/Revisions b/noao/nproto/ace/Revisions
new file mode 100644
index 00000000..df309bd9
--- /dev/null
+++ b/noao/nproto/ace/Revisions
@@ -0,0 +1,89 @@
+
+convolve.x
+ An amovki() call was mistakenly used as amovi() (6/3/13, MJF)
+
+convolve.x
+ The 'bpbuf' pointer was declared as TY_REAL instead of TY_INT (5/4/13)
+
+objs.h
+ Added P2R for 64-bit systems.
+
+skyfit.x
+ If a complete line is exceptionally deviant from the true sky it
+ will bias the sky surface. As a quick fix for the possibly common
+ case that the first or last lines are high due to charge transfer
+ effects, the lines to use was changed to start and end a half step
+ from the ends. This is only a quick fix and a more sophisticated
+ solutions is needed. (10/17/02, Valdes)
+
+skyblock.x
+ There was another bug in interp2. (10/17/02, Valdes)
+
+skyblock.x
+ There was a bug in interp2. (9/30/02, Valdes)
+
+convolve.x
+ Fixed error when reference image does not overlap target image on
+ the right. (9/23/02, Valdes)
+
+detect.x
+ The flux comparison in difference detection used sigma normalized
+ fluxes. This was changed to unnormalized fluxes which is done
+ by using the same sigmas for the target and reference images.
+ (9/23/02, Valdes)
+
+t_acedetect.x
+pars.x
+diffdetect.pars
+ Made changes for diffdetect. (9/23/02, Valdes)
+
+t_acedetect.x
+ Switched over to the xtools version of xt_pmmap.
+ (9/10/02, Valdes)
+
+t_acedetect.x
+skyblock.x
+omwrite.x
+ If DATASEC is present then it is automatically applied to the
+ image. It is also deleted from the output sky and mask since
+ they will be the size of the data section. (9/10/02, Valdes)
+
+skyfit.x
+ The sigma fit is now always a constant. (8/6/02, Valdes)
+
+skyblock.x
+ Fixed a type mismatch in a min() function. (6/13/02, Valdes)
+
+=====
+V2.12
+=====
+
+skyblock.x
+ Changed algorithm for updating sky to do in place updates so that
+ extensions might be used. (12/21/01, Valdes)
+
+detect.x
+ The number of sky block lines was being wrong in using nc instead of nl.
+ (12/21/01, Valdes)
+
+t_acedetect.x
+ The check on the number of catalog definitions files did not allow
+ just one file when there was input list. (12/20/01, Valdes)
+
+t_acedetect.x
+ The default catalog output is now STSDAS. (5/7/01, Valdes)
+
+mim.x
+ When deleting the image name returned from imstats any image section
+ needed to be stripped. (5/7/01, Valdes)
+
+mim.x
+convolve.x
+skyfit.x
+skyimages.x
+ Added error checking for calls to mim_glr. (5/7/01, Valdes)
+
+catdefine.x
+ The reference to acesrc$ was replaced with ace$src/ in order to run
+ standalone without additional environment definitions. (5/7/01, Valdes)
+
diff --git a/noao/nproto/ace/ace.h b/noao/nproto/ace/ace.h
new file mode 100644
index 00000000..988ffd05
--- /dev/null
+++ b/noao/nproto/ace/ace.h
@@ -0,0 +1,32 @@
+define NUMSTART 11 # First object number
+
+# Header structure.
+define HDR_SZFNAME 99 # Length of filename strings.
+define HDR_LEN 101
+define HDR_MAGZERO Memr[P2R($1)] # Magnitude zero point
+define HDR_IMAGE Memc[P2C($1+1)] # Image name
+define HDR_MASK Memc[P2C($1+51)] # Object mask name
+
+# Mask Flags.
+define MASK_NUM 0077777777B # Mask number
+define MASK_BNDRY 0100000000B # Boundary flag
+define MASK_SPLIT 0200000000B # Split flag
+define MASK_DARK 0400000000B # Dark flag
+
+define MSETFLAG ori($1,$2)
+define MUNSETFLAG andi($1,noti($2))
+
+define MNUM (andi($1,MASK_NUM))
+define MNOTDARK (andi($1,MASK_DARK)==0)
+define MDARK (andi($1,MASK_DARK)!=0)
+define MNOTSPLIT (andi($1,MASK_SPLIT)==0)
+define MSPLIT (andi($1,MASK_SPLIT)!=0)
+define MNOTBNDRY (andi($1,MASK_BNDRY)==0)
+define MBNDRY (andi($1,MASK_BNDRY)!=0)
+
+# Output object masks types.
+define OM_TYPES "|boolean|numbers|colors|all|"
+define OM_BOOL 1 # Boolean (0=sky, 1=object+bad)
+define OM_ONUM 2 # Object number only
+define OM_COLORS 3 # Bad=1, Objects=2-9
+define OM_ALL 4 # All values
diff --git a/noao/nproto/ace/acedetect.h b/noao/nproto/ace/acedetect.h
new file mode 100644
index 00000000..111d324e
--- /dev/null
+++ b/noao/nproto/ace/acedetect.h
@@ -0,0 +1,27 @@
+# ACEDETECT parameter structure.
+define PAR_SZSTR 199 # Length of strings in par structure
+define PAR_LEN 128 # Length of parameter structure
+
+define PAR_IMLIST Memi[$1+$2-1] # List of images (2)
+define PAR_BPMLIST Memi[$1+$2+1] # List of bad pixel masks (2)
+define PAR_SKYLIST Memi[$1+$2+3] # List of skys (2)
+define PAR_SIGLIST Memi[$1+$2+5] # List of sigmas (2)
+define PAR_EXPLIST Memi[$1+$2+7] # List of sigmas (2)
+define PAR_GAINLIST Memi[$1+$2+9] # List of measurement gain maps (2)
+define PAR_SCALELIST Memi[$1+$2+11] # List of scales (2)
+define PAR_OMLIST Memi[$1+14] # List of object masks
+define PAR_INCATLIST Memi[$1+15] # List of input catalogs
+define PAR_OUTCATLIST Memi[$1+16] # List of output catalogs
+define PAR_CATDEFLIST Memi[$1+17] # List of catalog definitions
+define PAR_LOGLIST Memi[$1+18] # List of log files
+define PAR_OUTSKYLIST Memi[$1+19] # List of output sky images
+define PAR_OUTSIGLIST Memi[$1+20] # List of output sigma images
+
+define PAR_SKY Memi[$1+21] # Sky parameters
+define PAR_DET Memi[$1+22] # Detection parameters
+define PAR_SPT Memi[$1+23] # Split parameters
+define PAR_GRW Memi[$1+24] # Grow parameters
+define PAR_EVL Memi[$1+25] # Evaluate parameters
+
+define PAR_OMTYPE Memi[$1+26] # Output object mask type
+define PAR_EXTNAMES Memc[P2C($1+27)] # Extensions names
diff --git a/noao/nproto/ace/aceoverlay.x b/noao/nproto/ace/aceoverlay.x
new file mode 100644
index 00000000..d8622568
--- /dev/null
+++ b/noao/nproto/ace/aceoverlay.x
@@ -0,0 +1,76 @@
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include <pmset.h>
+include "ace.h"
+
+
+pointer procedure overlay (ovrly, im)
+
+char ovrly[ARB] #I Overlay name
+pointer im #I Reference image
+pointer ovr #O Overlay pointer
+
+int i, j, nc, nl, val
+long v[2]
+pointer sp, fname, pm, buf
+
+int nowhite(), andi()
+bool pm_linenotempty()
+pointer ods_pmmap(), imstati()
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ if (nowhite (ovrly, Memc[fname], SZ_FNAME) == 0) {
+ call sfree (sp)
+ return (NULL)
+ }
+
+ if (Memc[fname] == '!') {
+ iferr (call imgstr (im, Memc[fname+1], Memc[fname], SZ_FNAME)) {
+ call sfree (sp)
+ return (NULL)
+ }
+ }
+
+ iferr (ovr = ods_pmmap (Memc[fname], im)) {
+ call sfree (sp)
+ call erract (EA_WARN)
+ return (NULL)
+ }
+
+ nc = IM_LEN(ovr,1)
+ nl = IM_LEN(ovr,2)
+ pm = imstati (ovr, IM_PMDES)
+
+ call salloc (buf, nc, TY_INT)
+
+ v[1] = 1
+ do i = 1, nl {
+ v[2] = i
+ if (!pm_linenotempty(pm, v))
+ next
+ call pmglpi (pm, v, Memi[buf], 0, nc, 0)
+ do j = 0, nc-1 {
+ val = Memi[buf+j]
+ if (val == 0)
+ next
+ else if (val < NUMSTART)
+ val = 1
+ else {
+ val = andi (val, MASK_BNDRY)
+ if (val != 0)
+ val = mod (andi (Memi[buf+j], MASK_NUM), 9) + 2
+ #val = 1
+ }
+ Memi[buf+j] = val
+ }
+ call pmplpi (pm, v, Memi[buf], 0, nc, PIX_SRC)
+ }
+
+ call sfree (sp)
+
+ return (ovr)
+end
diff --git a/noao/nproto/ace/acesky.h b/noao/nproto/ace/acesky.h
new file mode 100644
index 00000000..5773f1a7
--- /dev/null
+++ b/noao/nproto/ace/acesky.h
@@ -0,0 +1,21 @@
+# Sky parameter structure.
+define SKY_LEN 5 # Length of parameter structure
+
+define SKY_TYPE Memi[$1] # Type of sky algorithm
+define SKY_NEWSKY Memi[$1+1] # Determine a new sky sigma?
+define SKY_NEWSIG Memi[$1+2] # Determine a new sky sigma?
+define SKY_SURPARS Memi[$1+3] # Pointer to parameters for surface fit
+define SKY_BLKPARS Memi[$1+4] # Pointer to parameters for block stat
+
+define SKY_TYPES "|surface|block|"
+define SKY_SURFACE 1 # Surface fitting
+define SKY_BLOCK 2 # Block statistics
+
+define SKY_SURPARSLEN 7 # Length of parameter structure
+define SKY_NSKYLINES Memi[$1] # Number of sky lines to sample
+define SKY_SKYBLK1D Memi[$1+1] # Sky block size for 1D averages
+define SKY_SKYHCLIP Memr[P2R($1+2)] # Sky fitting high sigma clip
+define SKY_SKYLCLIP Memr[P2R($1+3)] # Sky fitting low sigma clip
+define SKY_SKYXORDER Memi[$1+4] # Sky fitting x order
+define SKY_SKYYORDER Memi[$1+5] # Sky fitting y order
+define SKY_SKYXTERMS Memi[$1+6] # Sky fitting cross terms
diff --git a/noao/nproto/ace/bndry.x b/noao/nproto/ace/bndry.x
new file mode 100644
index 00000000..0abb0acd
--- /dev/null
+++ b/noao/nproto/ace/bndry.x
@@ -0,0 +1,194 @@
+include <pmset.h>
+include "ace.h"
+
+
+# BNDRY -- Flag boundary pixels of unsplit objects.
+# Assume the boundary flag is not set.
+
+procedure bndry (om, logfd)
+
+pointer om #I Object mask
+int logfd #I Logfile
+
+int i, c, c1, c2, l, nc, nl, num, bndryval, val, vallast
+pointer sp, v, irl, irlptr, orl, orlptr, bufs, buf1, buf2, buf3
+
+int andi(), ori()
+
+begin
+ call smark (sp)
+ call salloc (v, PM_MAXDIM, TY_LONG)
+
+ if (logfd != NULL)
+ call fprintf (logfd, " Set boundary mask:\n")
+
+ call pm_gsize (om, nc, Meml[v], nl)
+ nc = Meml[v]; nl = Meml[v+1]
+ Meml[v] = 1
+
+ # Allocate buffers.
+ call salloc (irl, 3+3*nc, TY_INT)
+ call salloc (orl, 3+3*nc, TY_INT)
+ call salloc (bufs, 3, TY_POINTER)
+ call salloc (Memi[bufs], nc, TY_INT)
+ call salloc (Memi[bufs+1], nc, TY_INT)
+ call salloc (Memi[bufs+2], nc, TY_INT)
+
+ Memi[orl+1] = nc
+
+ # First line.
+ l = 1
+ buf2 = Memi[bufs+mod(l,3)]
+ buf3 = Memi[bufs+mod(2,3)]
+
+ Meml[v+1] = l + 1
+ call pmglpi (om, Meml[v], Memi[buf3], 0, nc, 0)
+ Meml[v+1] = l
+ call pmglpi (om, Meml[v], Memi[buf2], 0, nc, 0)
+ call pmglri (om, Meml[v], Memi[irl], 0, nc, 0)
+
+ irlptr = irl
+ orlptr = orl
+ do i = 2, Memi[irl] {
+ irlptr = irlptr + 3
+ num = Memi[irlptr+2]
+
+ if (num < NUMSTART || MSPLIT(num)) {
+ orlptr = orlptr + 3
+ Memi[orlptr] = Memi[irlptr]
+ Memi[orlptr+1] = Memi[irlptr+1]
+ Memi[orlptr+2] = num
+ next
+ }
+
+ bndryval = MSETFLAG (num, MASK_BNDRY)
+ c1 = Memi[irlptr] - 1
+ c2 = c1 + Memi[irlptr+1] - 1
+ do c = c1, c2
+ Memi[buf2+c] = bndryval
+
+ orlptr = orlptr + 3
+ Memi[orlptr] = Memi[irlptr]
+ Memi[orlptr+1] = Memi[irlptr+1]
+ Memi[orlptr+2] = bndryval
+ }
+ Memi[orl] = 1 + (orlptr - orl) / 3
+ call pmplri (om, Meml[v], Memi[orl], 0, nc, PIX_SRC)
+
+ # Interior lines.
+ do l = 2, nl-1 {
+ buf1 = Memi[bufs+mod(l-1,3)]
+ buf2 = Memi[bufs+mod(l,3)]
+ buf3 = Memi[bufs+mod(l+1,3)]
+
+ Meml[v+1] = l + 1
+ call pmglpi (om, Meml[v], Memi[buf3], 0, nc, 0)
+ Meml[v+1] = l
+ call pmglri (om, Meml[v], Memi[irl], 0, nc, 0)
+
+ irlptr = irl
+ orlptr = orl
+ do i = 2, Memi[irl] {
+ irlptr = irlptr + 3
+ num = Memi[irlptr+2]
+
+ if (num < NUMSTART || MSPLIT(num)) {
+ orlptr = orlptr + 3
+ Memi[orlptr] = Memi[irlptr]
+ Memi[orlptr+1] = Memi[irlptr+1]
+ Memi[orlptr+2] = num
+ next
+ }
+
+ c1 = Memi[irlptr] - 1
+ c2 = c1 + Memi[irlptr+1] - 1
+ bndryval = MSETFLAG (num, MASK_BNDRY)
+
+ Memi[buf2+c1] = bndryval
+
+ orlptr = orlptr + 3
+ Memi[orlptr] = c1 + 1
+ Memi[orlptr+2] = bndryval
+ vallast = bndryval
+
+ do c = c1+1, c2-1 {
+ val = num
+ if (Memi[buf3+c-1] != num)
+ val = bndryval
+ else if (Memi[buf3+c] != num)
+ val = bndryval
+ else if (Memi[buf3+c+1] != num)
+ val = bndryval
+ else if (Memi[buf1+c-1] != num && Memi[buf1+c-1]!=bndryval)
+ val = bndryval
+ else if (Memi[buf1+c] != num && Memi[buf1+c] != bndryval)
+ val = bndryval
+ else if (Memi[buf1+c+1] != num && Memi[buf1+c+1]!=bndryval)
+ val = bndryval
+
+ if (val == bndryval)
+ Memi[buf2+c] = val
+
+ if (val != vallast) {
+ Memi[orlptr+1] = c - Memi[orlptr] + 1
+ orlptr = orlptr + 3
+
+ Memi[orlptr] = c + 1
+ Memi[orlptr+2] = val
+ vallast = val
+ }
+ }
+
+ Memi[buf2+c2] = bndryval
+
+ if (vallast != bndryval) {
+ Memi[orlptr+1] = c2 - Memi[orlptr] + 1
+ orlptr = orlptr + 3
+ Memi[orlptr] = c2 + 1
+ Memi[orlptr+1] = 1
+ Memi[orlptr+2] = bndryval
+ } else
+ Memi[orlptr+1] = c2 - Memi[orlptr] + 2
+ }
+
+ Memi[orl] = 1 + (orlptr - orl) / 3
+ call pmplri (om, Meml[v], Memi[orl], 0, nc, PIX_SRC)
+ }
+
+ # Last line.
+ l = nl
+ buf2 = Memi[bufs+mod(l,3)]
+
+ Meml[v+1] = l
+ call pmglri (om, Meml[v], Memi[irl], 0, nc, 0)
+
+ irlptr = irl
+ orlptr = orl
+ do i = 2, Memi[irl] {
+ irlptr = irlptr + 3
+ num = Memi[irlptr+2]
+
+ if (num < NUMSTART || MSPLIT(num)) {
+ orlptr = orlptr + 3
+ Memi[orlptr] = Memi[irlptr]
+ Memi[orlptr+1] = Memi[irlptr+1]
+ Memi[orlptr+2] = num
+ next
+ }
+
+ bndryval = MSETFLAG (num, MASK_BNDRY)
+ c1 = Memi[irlptr] - 1
+ c2 = c1 + Memi[irlptr+1] - 1
+ do c = c1, c2
+ Memi[buf2+c] = bndryval
+
+ orlptr = orlptr + 3
+ Memi[orlptr] = Memi[irlptr]
+ Memi[orlptr+1] = Memi[irlptr+1]
+ Memi[orlptr+2] = bndryval
+ }
+ Memi[orl] = 1 + (orlptr - orl) / 3
+ call pmplri (om, Meml[v], Memi[orl], 0, nc, PIX_SRC)
+
+ call sfree (sp)
+end
diff --git a/noao/nproto/ace/cat.h b/noao/nproto/ace/cat.h
new file mode 100644
index 00000000..39a7ed8a
--- /dev/null
+++ b/noao/nproto/ace/cat.h
@@ -0,0 +1,45 @@
+# Catalog structure.
+define CAT_SZSTR 99 # Length of catalog string
+define CAT_LEN 160 # Length of catalog structure
+define CAT_OBJS Memi[$1] # Array of objects (ptr)
+define CAT_APFLUX Memi[$1+1] # Array of aperture fluxes (ptr)
+define CAT_NOBJS Memi[$1+2] # Number of objects
+define CAT_NUMMAX Memi[$1+3] # Maximum object number
+define CAT_FLAGS Memi[$1+4] # Catalog flags
+define CAT_HDR Memi[$1+5] # Header structure
+define CAT_INTBL Memi[$1+6] # Input table structure
+define CAT_OUTTBL Memi[$1+7] # Output table structure
+define CAT_MAGZERO Memr[P2R($1+8)] # Magnitude zero
+define CAT_CATALOG Memc[P2C($1+10)] # Catalog name
+define CAT_OBJID Memc[P2C($1+60)] # Default ID
+define CAT_STRPTR P2C($1+110) # Working string buffer
+define CAT_STR Memc[CAT_STRPTR($1)] # Working string buffer
+
+# Table structure.
+define TBL_LEN 2
+define TBL_TP Memi[$1] # Table pointer
+define TBL_STP Memi[$1+1] # Symbol table of entries
+
+# Entry structure.
+define ENTRY_ULEN 19 # Length of units string
+define ENTRY_FLEN 19 # Length of format string
+define ENTRY_DLEN 99 # Length of description string
+define ENTRY_LEN 95 # Length of entry structure
+define ENTRY_CDEF Memi[$1] # Column descriptor
+define ENTRY_ID Memi[$1+1] # Entry id
+define ENTRY_TYPE Memi[$1+2] # Datatype in object record
+define ENTRY_CTYPE Memi[$1+3] # Datatype in catalog
+define ENTRY_FUNC Memi[$1+4] # Entry function
+define ENTRY_RAP Memr[P2R($1+5)] # Entry aperture radius
+define ENTRY_UNITS Memc[P2C($1+6)] # Entry units (19)
+define ENTRY_FORMAT Memc[P2C($1+26)] # Entry format (19)
+define ENTRY_DESC Memc[P2C($1+46)] # Entry description (99)
+
+define FUNCS "|MAG|"
+define FUNC_MAG 1 # Magnitude
+
+# Catalog extensions.
+define CATEXTNS "|fits|tab|"
+
+# Catalog Parameters.
+define CATPARAMS "|image|mask|objid|catalog|nobjects|magzero|"
diff --git a/noao/nproto/ace/catdef.desc b/noao/nproto/ace/catdef.desc
new file mode 100644
index 00000000..2c1a989c
--- /dev/null
+++ b/noao/nproto/ace/catdef.desc
@@ -0,0 +1,73 @@
+# This describes the currently available catalog definition entries
+# available and the format.
+
+# Comments begining with '#' are ignored.
+# Order of lines determines order in catalog.
+# Case is ignored though labels in catalog will be as given in file.
+
+ACE_NAME [OPTIONAL USER NAME FOR CATALOG]
+
+# There are a few functions currently available.
+
+MAG(ACE_NAME)
+APFLUX(radius_in_pixels)
+MAG(APFLUX(radius_in_pixels))
+
+
+# Basic quantities.
+NUM Object number
+PNUM Parent number (0 if original detection)
+NPIX Number of pixels
+NDETECT Number of detected pixels (before growing)
+FLAGS Flags (currently on M for multiple object)
+
+# Fluxes
+FLUX Isophotal flux
+FRACFLUX Apportioned flux (TOTMAG)
+APFLUX(radius) Aperture fluxes (radius in pixels)
+SKY Mean sky
+PEAK Peak pixel value above sky
+ISIGAVG Average (I - sky) / sig
+ISIGMAX Maximum (I - sky) / sig
+
+# Positions
+WX X world coordinate (requires WCS in header)
+WY Y world coordinate (requires WCS in header)
+X1 X centroid (pixels)
+Y1 Y centroid (pixels)
+XAP X aperture coordinate (centroid initially then not changed)
+YAP Y aperture coordinate (centroid initially then not changed)
+XMIN Minimum X (pixels)
+XMAX Maxium X (pixels)
+YMIN Minimum Y (pixels)
+YMAX Maxium Y (pixels)
+
+# Miscellaneous
+SIG Mean sky sigma
+FRAC Apportioned fraction
+
+X2 X 2nd moment (pixels)
+Y2 Y 2nd moment (pixels)
+XY X 2nd cross moment (pixels)
+
+# Derived quantities.
+A Semimajor axis
+B Semiminor axis
+THETA Position angle (degrees)
+ELONG Elongation = A/B
+ELLIP Ellipticity = 1 - B/A
+R Second moment radius (pixels)
+CXX Second moment ellipse (pixels)
+CYY Second moment ellipse (pixels)
+CXY Second moment ellipse (pixels)
+
+# Error estimates.
+FLUXERR Error in flux
+XERR Error in X centroid (pixels)
+YERR Error in Y centroid (pixels)
+AERR Error in A
+BERR Error in B
+THETAERR Error in THETA (degrees)
+CXXERR Error in CXX (pixels)
+CYYERR Error in CYY (pixels)
+CXYERR Error in CXY (pixels)
diff --git a/noao/nproto/ace/catdefine.x b/noao/nproto/ace/catdefine.x
new file mode 100644
index 00000000..038f85d9
--- /dev/null
+++ b/noao/nproto/ace/catdefine.x
@@ -0,0 +1,192 @@
+include "ace.h"
+include "cat.h"
+include "objs.h"
+
+
+define CATDEF "ace$lib/catdef.dat"
+
+# CATDEF -- Read catalog definition file and create symbol table.
+
+procedure catdefine (tbl, mode, catdef)
+
+pointer tbl #I Table pointer
+int mode #I Table access mode
+char catdef[ARB] #I Catalog definition file
+
+int i, n, fd, args, func, ncols
+pointer sp, fname, name, label, str, entry, sym
+pointer stp1, stp2, tp
+
+bool strne()
+int open(), fscan(), nscan(), strncmp(), ctoi(), ctor()
+int stridxs(), strldxs(), strdic()
+pointer stopen(), stenter(), stfind(), sthead(), stnext(), stname()
+errchk open, stopen, tbcdef1, tbcfnd1
+
+define err_ 10
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (name, SZ_FNAME, TY_CHAR)
+ call salloc (label, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (entry, ENTRY_LEN, TY_STRUCT)
+ call aclri (Memi[entry], ENTRY_LEN)
+
+ # Build a symbol table from ace$objs.h.
+ fd = open ("ace$src/objs.h", READ_ONLY, TEXT_FILE)
+ stp1 = stopen ("catdefine", 100, ENTRY_LEN, SZ_LINE)
+ while (fscan(fd) != EOF) {
+ Memc[fname] = EOS
+ call gargwrd (Memc[fname], SZ_FNAME)
+ if (strne (Memc[fname], "define"))
+ next
+ call gargwrd (Memc[name], SZ_FNAME)
+ if (strncmp (Memc[name], "ID_", 3) != 0)
+ next
+ call gargi (ENTRY_ID(entry))
+ call gargwrd (Memc[label], SZ_LINE)
+ if (Memc[label] != '#')
+ next
+ call gargwrd (Memc[label], SZ_LINE)
+ call gargwrd (ENTRY_UNITS(entry), ENTRY_ULEN)
+ call gargwrd (ENTRY_FORMAT(entry), ENTRY_FLEN)
+ call gargstr (ENTRY_DESC(entry), ENTRY_DLEN)
+ if (nscan() < 7)
+ next
+ switch (Memc[label]) {
+ case 'i':
+ ENTRY_TYPE(entry) = TY_INT
+ case 'r':
+ ENTRY_TYPE(entry) = TY_REAL
+ case 'd':
+ ENTRY_TYPE(entry) = TY_DOUBLE
+ default:
+ i = 1
+ if (ctoi (Memc[label], i, ENTRY_TYPE(entry)) == 0)
+ next
+ ENTRY_TYPE(entry) = -ENTRY_TYPE(entry)
+ }
+ ENTRY_CTYPE(entry) = ENTRY_TYPE(entry)
+ sym = stenter (stp1, Memc[name+3], ENTRY_LEN)
+ call amovi (Memi[entry], Memi[sym], ENTRY_LEN)
+ }
+ call close (fd)
+
+ if (tbl != NULL)
+ tp = TBL_TP(tbl)
+
+ # Read the definition file.
+ if (catdef[1] == EOS)
+ call strcpy (CATDEF, Memc[fname], SZ_FNAME)
+ else
+ call strcpy (catdef, Memc[fname], SZ_FNAME)
+ fd = open (Memc[fname], READ_ONLY, TEXT_FILE)
+ stp2 = stopen ("catdefine", 100, ENTRY_LEN, SZ_LINE)
+ ncols = 0
+ while (fscan(fd) != EOF) {
+ call gargwrd (Memc[name], SZ_FNAME)
+ call gargwrd (Memc[label], SZ_LINE)
+ n = nscan()
+ if (n == 0)
+ next
+ if (Memc[name] == '#')
+ next
+
+ # Parse the name.
+ call strcpy (Memc[name], Memc[str], SZ_LINE)
+ call strupr (Memc[str])
+ args = stridxs ("(", Memc[str]) + 1
+ if (args > 1) {
+ i = strldxs (")", Memc[str])
+ Memc[str+args-2] = EOS
+ Memc[str+i-1] = EOS
+ func = strdic (Memc[str], Memc[fname], SZ_FNAME, FUNCS)
+ if (func == 0) {
+ call strcpy (Memc[name], Memc[str], SZ_LINE)
+ call strupr (Memc[str])
+ } else
+ call strcpy (Memc[str+args-1], Memc[str], SZ_LINE)
+
+ args = stridxs ("(", Memc[str]) + 1
+ if (args > 1) {
+ i = strldxs (")", Memc[str])
+ Memc[str+args-2] = EOS
+ Memc[str+i-1] = EOS
+ sym = stfind (stp1, Memc[str])
+ } else
+ sym = stfind (stp1, Memc[str])
+ } else {
+ sym = stfind (stp1, Memc[str])
+ func = 0
+ }
+
+ if (sym == NULL) {
+err_
+ call stclose (stp1)
+ call stclose (stp2)
+ call close (fd)
+ call sprintf (Memc[label], SZ_LINE,
+ "Unknown or ambiguous catalog quantity `%s' in definition file `%s'")
+ call pargstr (Memc[name])
+ call pargstr (Memc[fname])
+ call error (1, Memc[label])
+ }
+ ncols = ncols + 1
+ if (tbl == NULL)
+ next
+
+ if (n == 1)
+ call strcpy (Memc[name], Memc[label], SZ_LINE)
+
+ entry = stenter (stp2, Memc[label], ENTRY_LEN)
+ call amovi (Memi[sym], Memi[entry], ENTRY_LEN)
+ ENTRY_FUNC(entry) = func
+
+ switch (ENTRY_FUNC(entry)) {
+ case FUNC_MAG:
+ ENTRY_CTYPE(entry) = TY_REAL
+ call strcpy ("magnitudes", ENTRY_UNITS(entry), ENTRY_ULEN)
+ ENTRY_FORMAT(entry) = EOS
+ }
+
+ if (mode == NEW_FILE)
+ call tbcdef1 (tp, ENTRY_CDEF(entry), Memc[label],
+ ENTRY_UNITS(sym), ENTRY_FORMAT(sym), ENTRY_CTYPE(sym), 1)
+ else
+ call tbcfnd1 (tp, Memc[label], ENTRY_CDEF(entry))
+
+ # Get arguments.
+ switch (ENTRY_ID(entry)) {
+ case ID_APFLUX:
+ if (ctor (Memc[name], args, ENTRY_RAP(entry)) == 0)
+ goto err_
+ }
+ }
+ call close (fd)
+ call stclose (stp1)
+
+ if (tbl == NULL)
+ return
+
+ if (ncols == 0) {
+ call stclose (stp2)
+ call sprintf (Memc[label], SZ_LINE,
+ "No catalog quantity definitions in file `%s'")
+ call pargstr (Memc[fname])
+ call error (1, Memc[label])
+ }
+
+ # Reverse order of symbol table.
+ stp1 = stopen ("catdef", ncols, ENTRY_LEN, SZ_LINE)
+ for (sym=sthead(stp2); sym!=NULL; sym=stnext(stp2,sym)) {
+ entry = stenter (stp1, Memc[stname(stp2,sym)], ENTRY_LEN)
+ call amovi (Memi[sym], Memi[entry], ENTRY_LEN)
+ }
+ call stclose (stp2)
+
+ TBL_STP(tbl) = stp1
+
+ call sfree (sp)
+end
diff --git a/noao/nproto/ace/catio.x b/noao/nproto/ace/catio.x
new file mode 100644
index 00000000..1fbae947
--- /dev/null
+++ b/noao/nproto/ace/catio.x
@@ -0,0 +1,931 @@
+include <imset.h>
+#include <tbset.h>
+define TBL_NROWS 0
+include <math.h>
+include "ace.h"
+include "cat.h"
+include "objs.h"
+
+
+# CATOPEN -- Open a catalog.
+# This may be used just to allocate the structure or to actually open
+# a catalog file. It does not read the objects. Use catrobjs.
+
+procedure catopen (cat, input, output, catdef)
+
+pointer cat #U Catalog structure
+char input[ARB] #I Input catalog name
+char output[ARB] #I Output catalog name
+char catdef[ARB] #I Catalog definition file
+
+pointer tbl
+
+bool streq()
+pointer tbtopn()
+
+begin
+ if (cat == NULL)
+ call calloc (cat, CAT_LEN, TY_STRUCT)
+
+ if (input[1] == EOS && output[1] == EOS)
+ return
+
+ if (streq (input, output)) { # READ_WRITE
+ call calloc (tbl, TBL_LEN, TY_STRUCT)
+ CAT_INTBL(cat) = tbl
+ CAT_OUTTBL(cat) = tbl
+
+ TBL_TP(tbl) = tbtopn (input, READ_WRITE, 0)
+ call catdefine (tbl, READ_ONLY, catdef)
+ call catrhdr (cat)
+ } else if (output[1] == EOS) { # READ_ONLY
+ call calloc (tbl, TBL_LEN, TY_STRUCT)
+ CAT_INTBL(cat) = tbl
+ CAT_OUTTBL(cat) = NULL
+
+ TBL_TP(tbl) = tbtopn (input, READ_ONLY, 0)
+ call catdefine (tbl, READ_ONLY, catdef)
+ call catrhdr (cat)
+ } else if (input[1] == EOS) { # NEW_FILE
+ call calloc (tbl, TBL_LEN, TY_STRUCT)
+ CAT_INTBL(cat) = NULL
+ CAT_OUTTBL(cat) = tbl
+
+ TBL_TP(tbl) = tbtopn (output, NEW_FILE, 0)
+ call catdefine (tbl, NEW_FILE, catdef)
+ } else { # NEW_COPY
+ call calloc (tbl, TBL_LEN, TY_STRUCT)
+ CAT_INTBL(cat) = tbl
+
+ TBL_TP(tbl) = tbtopn (input, READ_ONLY, 0)
+ call catdefine (tbl, NEW_COPY, catdef)
+ call catrhdr (cat)
+
+ call calloc (tbl, TBL_LEN, TY_STRUCT)
+ CAT_OUTTBL(cat) = tbl
+ TBL_TP(tbl) = tbtopn (output, NEW_COPY, TBL_TP(CAT_INTBL(cat)))
+ call catdefine (tbl, NEW_COPY, catdef)
+ }
+end
+
+
+procedure catcreate (cat)
+
+pointer cat #I Catalog structure
+
+pointer tbl, tp
+
+begin
+ if (cat == NULL)
+ return
+ tbl = CAT_OUTTBL(cat)
+ if (tbl == NULL)
+ return
+ tp = TBL_TP(tbl)
+ if (tp == NULL)
+ return
+ if (CAT_INTBL(cat) != NULL) {
+ if (tp == TBL_TP(CAT_INTBL(cat)))
+ return
+ }
+ call tbtcre (tp)
+end
+
+
+# CATCLOSE -- Close a catalog.
+
+procedure catclose (cat)
+
+pointer cat #I Catalog pointer
+
+int i
+pointer tbl, objs
+
+begin
+ if (cat == NULL)
+ return
+
+ tbl = CAT_INTBL(cat)
+ if (tbl != NULL) {
+ if (TBL_STP(tbl) != NULL)
+ call stclose (TBL_STP(tbl))
+ if (tbl == CAT_OUTTBL(cat))
+ CAT_OUTTBL(cat) = NULL
+ call tbtclo (TBL_TP(tbl))
+ }
+ tbl = CAT_OUTTBL(cat)
+ if (tbl != NULL) {
+ if (TBL_STP(tbl) != NULL)
+ call stclose (TBL_STP(tbl))
+ call tbtclo (TBL_TP(tbl))
+ }
+
+ objs = CAT_OBJS(cat)
+ if (objs != NULL) {
+ do i = 0, CAT_NUMMAX(cat)-1
+ call mfree (Memi[objs+i], TY_STRUCT)
+ }
+
+ call mfree (CAT_APFLUX(cat), TY_REAL)
+ call mfree (CAT_OBJS(cat), TY_POINTER)
+ call mfree (CAT_INTBL(cat), TY_STRUCT)
+ call mfree (CAT_OUTTBL(cat), TY_STRUCT)
+ call mfree (CAT_HDR(cat), TY_STRUCT)
+ call mfree (cat, TY_STRUCT)
+end
+
+
+# CATGETS -- Get a string parameter from the catalog header.
+
+procedure catgets (cat, param, value, maxchar)
+
+pointer cat #I Catalog pointer
+char param[ARB] #I Parameter to get
+char value[ARB] #O Returned value
+int maxchar #I Maximum characters in value
+
+int i, strdic()
+
+begin
+ value[1] = EOS
+
+ if (cat == NULL)
+ return
+
+ i = strdic (param, CAT_STR(cat), CAT_SZSTR, CATPARAMS)
+ switch (i) {
+ case 1:
+ if (CAT_HDR(cat) == NULL)
+ i = 0
+ else
+ call strcpy (HDR_IMAGE(CAT_HDR(cat)), value, maxchar)
+ case 2:
+ if (CAT_HDR(cat) == NULL)
+ i = 0
+ else
+ call strcpy (HDR_MASK(CAT_HDR(cat)), value, maxchar)
+ case 3:
+ call strcpy (CAT_OBJID(cat), value, maxchar)
+ case 4:
+ call strcpy (CAT_CATALOG(cat), value, maxchar)
+ default:
+ call sprintf (CAT_STR(cat), CAT_SZSTR,
+ "catgets: unknown catalog parameter `%s'")
+ call pargstr (param)
+ call error (1, CAT_STR(cat))
+ }
+
+ if (i == 0) {
+ call sprintf (CAT_STR(cat), CAT_SZSTR,
+ "catgets: parameter `%s' not found")
+ call pargstr (param)
+ call error (1, CAT_STR(cat))
+ }
+end
+
+
+procedure catgeti (cat, param, value)
+
+pointer cat #I Catalog pointer
+char param[ARB] #I Parameter to get
+int value #O Returned value
+
+int i, strdic()
+
+begin
+ value = INDEFI
+
+ if (cat == NULL)
+ return
+
+ i = strdic (param, CAT_STR(cat), CAT_SZSTR, CATPARAMS)
+ switch (i) {
+ case 5:
+ value = CAT_NOBJS(cat)
+ default:
+ call sprintf (CAT_STR(cat), CAT_SZSTR,
+ "catgeti: unknown catalog parameter `%s'")
+ call pargstr (param)
+ call error (1, CAT_STR(cat))
+ }
+end
+
+
+procedure catputs (cat, param, value)
+
+pointer cat #I Catalog pointer
+char param[ARB] #I Parameter to get
+char value[ARB] #I Value
+
+int i, strdic()
+
+begin
+ if (cat == NULL)
+ return
+
+ i = strdic (param, CAT_STR(cat), CAT_SZSTR, CATPARAMS)
+ switch (i) {
+ case 0:
+ call sprintf (CAT_STR(cat), CAT_SZSTR,
+ "catgets: unknown catalog parameter `%s'")
+ call pargstr (param)
+ call error (1, CAT_STR(cat))
+ case 1:
+ if (CAT_HDR(cat) == NULL)
+ call calloc (CAT_HDR(cat), HDR_LEN, TY_STRUCT)
+ call strcpy (value, HDR_IMAGE(CAT_HDR(cat)), HDR_SZFNAME)
+ case 2:
+ if (CAT_HDR(cat) == NULL)
+ call calloc (CAT_HDR(cat), HDR_LEN, TY_STRUCT)
+ call strcpy (value, HDR_MASK(CAT_HDR(cat)), HDR_SZFNAME)
+ case 3:
+ call strcpy (value, CAT_OBJID(cat), CAT_SZSTR)
+ case 4:
+ call strcpy (value, CAT_CATALOG(cat), CAT_SZSTR)
+ }
+end
+
+
+procedure catputr (cat, param, value)
+
+pointer cat #I Catalog pointer
+char param[ARB] #I Parameter to get
+real value #I Value
+
+int i, strdic()
+
+begin
+ if (cat == NULL)
+ return
+
+ i = strdic (param, CAT_STR(cat), CAT_SZSTR, CATPARAMS)
+ switch (i) {
+ case 6:
+ if (CAT_HDR(cat) == NULL)
+ call calloc (CAT_HDR(cat), HDR_LEN, TY_STRUCT)
+ HDR_MAGZERO(CAT_HDR(cat)) = value
+ default:
+ call sprintf (CAT_STR(cat), CAT_SZSTR,
+ "catgetr: unknown catalog parameter `%s'")
+ call pargstr (param)
+ call error (1, CAT_STR(cat))
+ }
+end
+
+
+procedure catrobjs (cat, filt)
+
+pointer cat #I Catalog pointer
+char filt[ARB] #I Filter string
+
+int i, num, nrows, nobjs, nummax, nalloc, tbpsta()
+pointer tbl, tp, objs, obj
+bool filter()
+
+begin
+ if (cat == NULL)
+ return
+
+ tbl = CAT_INTBL(cat)
+ if (tbl == NULL)
+ return
+ tp = TBL_TP(tbl)
+
+ nrows = tbpsta (tp, TBL_NROWS)
+ nalloc = nrows + NUMSTART - 1
+ call calloc (objs, nalloc, TY_POINTER)
+
+ nobjs = 0
+ nummax = 0
+ obj = NULL
+ do i = 1, nrows {
+ call catrobj (cat, obj, i)
+ if (!filter (obj, filt))
+ next
+ num = OBJ_NUM(obj)
+ if (num > nalloc) {
+ nalloc = nalloc + 1000
+ call realloc (objs, nalloc, TY_POINTER)
+ call aclri (Memi[objs+nalloc-1000], 1000)
+ }
+ if (Memi[objs+num-1] == NULL)
+ nobjs = nobjs + 1
+ nummax = max (num, nummax)
+ Memi[objs+num-1] = obj
+ obj = NULL
+ }
+
+ CAT_OBJS(cat) = objs
+ CAT_NOBJS(cat) = nobjs
+ CAT_NUMMAX(cat) = nummax
+end
+
+
+procedure catrobj (cat, obj, row)
+
+pointer cat #I Catalog pointer
+pointer obj #U Object pointer
+int row #I Table row
+
+int id, type, ori()
+pointer tbl, tp, stp, sym, cdef, sthead(), stnext()
+
+begin
+ if (cat == NULL)
+ return
+
+ tbl = CAT_INTBL(cat)
+ if (tbl == NULL)
+ return
+
+ tp = TBL_TP(tbl)
+ stp = TBL_STP(tbl)
+
+ if (obj == NULL)
+ call calloc (obj, OBJ_LEN, TY_STRUCT)
+
+ for (sym=sthead(stp); sym!=NULL; sym=stnext(stp,sym)) {
+ id = ENTRY_ID(sym)
+ if (id > 1000 || id == ID_APFLUX)
+ next
+ switch (id) {
+ case ID_FLAGS:
+ OBJ_FLAGS(obj) = 0
+ ifnoerr (call tbegtt (tp, cdef, row, CAT_STR(cat), CAT_SZSTR)) {
+ if (Memc[CAT_STRPTR(cat)] == 'M')
+ SETFLAG(obj,OBJ_SPLIT)
+ }
+ next
+ }
+
+ type = ENTRY_TYPE(sym)
+ cdef = ENTRY_CDEF(sym)
+ switch (type) {
+ case TY_INT:
+ iferr (call tbegti (tp, cdef, row, OBJI(obj,id)))
+ OBJI(obj,id) = INDEFI
+ case TY_REAL:
+ iferr (call tbegtr (tp, cdef, row, OBJR(obj,id)))
+ OBJR(obj,id) = INDEFR
+ case TY_DOUBLE:
+ iferr (call tbegtd (tp, cdef, row, OBJD(obj,id)))
+ OBJD(obj,id) = INDEFD
+ default:
+ iferr (call tbegtt (tp, cdef, row, OBJC(obj,id), -type))
+ OBJC(obj,id) = EOS
+ }
+ }
+
+ OBJ_ROW(obj) = row
+end
+
+
+procedure catwobj (cat, obj, row)
+
+pointer cat #I Catalog pointer
+pointer obj #I Object pointer
+int row #I Table row
+
+int ival
+real rval
+double dval
+pointer sval
+
+int id, type, func, napr, andi()
+real magzero, a, b, theta, elong, ellip, r, cxx, cyy, cxy
+real aerr, berr, thetaerr, cxxerr, cyyerr, cxyerr
+bool doshape
+pointer tbl, tp, stp, sym, cdef, sthead(), stnext()
+
+begin
+ if (obj == NULL)
+ return
+
+ tbl = CAT_OUTTBL(cat)
+ if (tbl == NULL)
+ return
+ tp = TBL_TP(tbl)
+ stp = TBL_STP(tbl)
+
+ #call sprintf (CAT_STR(cat), CAT_SZSTR, "%s-%d")
+ # if (OBJ_OBJID(obj) != NULL)
+ # call pargstr (Memc[OBJ_OBJID(obj)])
+ # else
+ # call pargstr (CAT_OBJID(cat))
+ # call pargi (OBJ_NUM(obj))
+ #call tbeptt (tp, TBL_BJID(tbl), row, CAT_STR(cat))
+ #call tbeptt (tp, TBL_CLASS(tbl), row, OBJ_CLASS(obj))
+
+ magzero = CAT_MAGZERO(cat)
+ if (IS_INDEFR(magzero))
+ magzero = 0.
+ sval = CAT_STRPTR(cat)
+ napr = 0
+ doshape = false
+ for (sym=sthead(stp); sym!=NULL; sym=stnext(stp,sym)) {
+ id = ENTRY_ID(sym)
+ func = ENTRY_FUNC(sym)
+ type = ENTRY_TYPE(sym)
+ cdef = ENTRY_CDEF(sym)
+ if (id > 1000) {
+ switch (id) {
+ case ID_A, ID_B, ID_THETA, ID_ELONG, ID_ELLIP, ID_R, ID_CXX,
+ ID_CYY, ID_CXY:
+ if (!doshape) {
+ call catshape (obj, a, b, theta, elong, ellip, r,
+ cxx, cyy, cxy, aerr, berr, thetaerr, cxxerr,
+ cyyerr, cxyerr)
+ doshape = true
+ }
+ switch (id) {
+ case ID_A:
+ rval = a
+ case ID_B:
+ rval = b
+ case ID_THETA:
+ rval = theta
+ case ID_ELONG:
+ rval = elong
+ case ID_ELLIP:
+ rval = ellip
+ case ID_R:
+ rval = r
+ case ID_CXX:
+ rval = cxx
+ case ID_CYY:
+ rval = cyy
+ case ID_CXY:
+ rval = cxy
+ }
+ case ID_FLUXERR, ID_XERR, ID_YERR:
+ switch (id) {
+ case ID_FLUXERR:
+ rval = OBJ_FLUXVAR(obj)
+ case ID_XERR:
+ rval = OBJ_XVAR(obj)
+ case ID_YERR:
+ rval = OBJ_YVAR(obj)
+ }
+ if (IS_INDEFR(rval) || rval < 0.)
+ rval = INDEFR
+ else
+ rval = sqrt (rval)
+ case ID_AERR, ID_BERR, ID_THETAERR, ID_CXXERR, ID_CYYERR,
+ ID_CXYERR:
+ if (!doshape) {
+ call catshape (obj, a, b, theta, elong, ellip, r,
+ cxx, cyy, cxy, aerr, berr, thetaerr, cxxerr,
+ cyyerr, cxyerr)
+ doshape = true
+ }
+ switch (id) {
+ case ID_AERR:
+ rval = aerr
+ case ID_BERR:
+ rval = aerr
+ case ID_THETAERR:
+ rval = aerr
+ case ID_CXXERR:
+ rval = aerr
+ case ID_CYYERR:
+ rval = aerr
+ case ID_CXYERR:
+ rval = aerr
+ }
+ }
+ } else if (id == ID_FLAGS) {
+ if (SPLIT(obj))
+ call strcpy ("M", Memc[sval], SZ_LINE)
+ else
+ call strcpy ("-", Memc[sval], SZ_LINE)
+ } else if (id == ID_APFLUX) {
+ if (OBJ_APFLUX(obj) == NULL)
+ rval = INDEFR
+ else {
+ rval = Memr[OBJ_APFLUX(obj)+napr]
+ napr = napr + 1
+ }
+ } else {
+ switch (type) {
+ case TY_INT:
+ ival = OBJI(obj,id)
+ case TY_REAL:
+ rval = OBJR(obj,id)
+ case TY_DOUBLE:
+ dval = OBJD(obj,id)
+ default:
+ call strcpy (OBJC(obj,id), Memc[sval], SZ_LINE)
+ }
+ }
+
+ # Apply function.
+ if (func > 0) {
+ if (ENTRY_CTYPE(sym) != type) {
+ # For now all function types are real.
+ switch (type) {
+ case TY_INT:
+ rval = ival
+ case TY_DOUBLE:
+ rval = dval
+ }
+ }
+ type = ENTRY_CTYPE(sym)
+ switch (func) {
+ case FUNC_MAG:
+ if (!IS_INDEFR(rval)) {
+ if (rval <= 0.)
+ rval = INDEFR
+ else
+ rval = -2.5 * log10 (rval) + magzero
+ }
+ }
+ }
+
+ # Write to catalog.
+ switch (type) {
+ case TY_INT:
+ call tbepti (tp, cdef, row, ival)
+ case TY_REAL:
+ call tbeptr (tp, cdef, row, rval)
+ case TY_DOUBLE:
+ call tbeptd (tp, cdef, row, dval)
+ default:
+ call tbeptt (tp, cdef, row, Memc[sval])
+ }
+ }
+ OBJ_ROW(obj) = row
+end
+
+
+# CATWCS -- Set catalog WCS information.
+
+procedure catwcs (cat, im)
+
+pointer cat #I Catalog pointer
+pointer im #I IMIO pointer
+
+int i
+pointer sp, axtype, label, units, format
+pointer mw, tbl, tp, stp, sym, cdef
+
+bool streq()
+pointer mw_openim(), sthead(), stnext(), stname()
+errchk mw_openim
+
+begin
+ if (cat == NULL)
+ return
+ if (CAT_OUTTBL(cat) == NULL)
+ return
+
+ call smark (sp)
+ call salloc (axtype, SZ_FNAME, TY_CHAR)
+ call salloc (label, SZ_FNAME, TY_CHAR)
+ call salloc (units, SZ_FNAME, TY_CHAR)
+ call salloc (format, SZ_FNAME, TY_CHAR)
+
+ tbl = CAT_OUTTBL(cat)
+ tp = TBL_TP(tbl)
+ stp = TBL_STP(tbl)
+
+ mw = mw_openim (im)
+ do i = 1, 2 {
+ iferr (call mw_gwattrs (mw, i, "axtype", Memc[axtype], SZ_FNAME))
+ Memc[axtype] = EOS
+ iferr (call mw_gwattrs (mw, i, "label", Memc[label], SZ_FNAME)) {
+ if (streq (Memc[axtype], "ra"))
+ call strcpy ("RA", Memc[label], SZ_FNAME)
+ else if (streq (Memc[axtype], "dec"))
+ call strcpy ("DEC", Memc[label], SZ_FNAME)
+ else
+ Memc[label] = EOS
+ }
+ iferr (call mw_gwattrs (mw, i, "units", Memc[units], SZ_FNAME)) {
+ if (streq (Memc[axtype], "ra") || streq (Memc[axtype], "dec"))
+ call strcpy ("deg", Memc[units], SZ_FNAME)
+ else
+ Memc[units] = EOS
+ }
+ iferr (call mw_gwattrs (mw, i, "format", Memc[format], SZ_FNAME)) {
+ if (streq (Memc[axtype], "ra"))
+ call strcpy ("%.2H", Memc[format], SZ_FNAME)
+ else if (streq (Memc[axtype], "dec"))
+ call strcpy ("%.1h", Memc[format], SZ_FNAME)
+ else
+ Memc[format] = EOS
+ }
+
+ if (i == 1) {
+ for (sym=sthead(stp); sym!=NULL; sym=stnext(stp,sym)) {
+ if (ENTRY_ID(sym) != ID_WX)
+ next
+ if (!(streq (Memc[stname(stp,sym)], "WX") ||
+ streq (Memc[stname(stp,sym)], "wx")))
+ Memc[label] = EOS
+ break
+ }
+ } else {
+ for (sym=sthead(stp); sym!=NULL; sym=stnext(stp,sym)) {
+ if (ENTRY_ID(sym) != ID_WY)
+ next
+ if (!(streq (Memc[stname(stp,sym)], "WY") ||
+ streq (Memc[stname(stp,sym)], "wy")))
+ Memc[label] = EOS
+ break
+ }
+ }
+
+ if (sym != NULL) {
+ cdef = ENTRY_CDEF(sym)
+ if (Memc[label] != EOS)
+ call tbcnam (tp, cdef, Memc[label])
+ if (Memc[units] != EOS)
+ call tbcnit (tp, cdef, Memc[units])
+ if (Memc[format] != EOS)
+ call tbcfmt (tp, cdef, Memc[format])
+ }
+ }
+ call mw_close (mw)
+
+ call sfree (sp)
+end
+
+
+procedure catrhdr (cat)
+
+pointer cat #I Catalog pointer
+
+pointer tp, hdr
+
+begin
+ if (cat == NULL)
+ return
+
+ if (CAT_HDR(cat) != NULL)
+ call mfree (CAT_HDR(cat), TY_STRUCT)
+ if (CAT_INTBL(cat) == NULL)
+ return
+ tp = TBL_TP(CAT_INTBL(cat))
+
+ call calloc (CAT_HDR(cat), HDR_LEN, TY_STRUCT)
+ hdr = CAT_HDR(cat)
+
+ iferr (call tbhgtt (tp, "IMAGE", HDR_IMAGE(hdr), HDR_SZFNAME))
+ HDR_IMAGE(hdr) = EOS
+ iferr (call tbhgtt (tp, "MASK", HDR_MASK(hdr), HDR_SZFNAME))
+ HDR_MASK(hdr) = EOS
+ iferr (call tbhgtr (tp, "MAGZERO", HDR_MAGZERO(hdr)))
+ HDR_MAGZERO(hdr) = INDEFR
+end
+
+
+procedure catwhdr (cat, im)
+
+pointer cat #I Catalog pointer
+pointer im #I Image pointer
+
+pointer tp, hdr
+
+begin
+ if (cat == NULL)
+ return
+
+ tp = CAT_OUTTBL(cat)
+ hdr = CAT_HDR(cat)
+ if (tp == NULL || hdr == NULL)
+ return
+ tp = TBL_TP(tp)
+
+ if (HDR_IMAGE(hdr) != EOS)
+ call tbhadt (tp, "IMAGE", HDR_IMAGE(hdr))
+ if (HDR_MASK(hdr) != EOS)
+ call tbhadt (tp, "MASK", HDR_MASK(hdr))
+ if (!IS_INDEFR(HDR_MAGZERO(hdr)))
+ call tbhadr (tp, "MAGZERO", HDR_MAGZERO(hdr))
+end
+
+
+procedure catwobjs (cat)
+
+pointer cat #I Catalog pointer
+
+int i, j
+pointer objs, obj
+
+begin
+ if (cat == NULL)
+ return
+ if (CAT_OUTTBL(cat) == NULL)
+ return
+ if (CAT_OBJS(cat) == NULL)
+ return
+
+ objs = CAT_OBJS(cat)
+ j = 0
+ do i = 1, CAT_NUMMAX(cat) {
+ obj = Memi[objs+i-1]
+ if (obj == NULL)
+ next
+ j = j + 1
+ call catwobj (cat, obj, j)
+ }
+end
+
+
+
+procedure catdump (cat)
+
+pointer cat #I Catalog pointer
+
+int i
+pointer objs, obj
+
+begin
+ if (CAT_OBJS(cat) == NULL)
+ return
+
+ objs = CAT_OBJS(cat)
+ do i = 1, CAT_NUMMAX(cat) {
+ obj = Memi[objs+i-1]
+ if (obj == NULL)
+ next
+
+ call printf ("%d %d %g %g\n")
+ call pargi (OBJ_NUM(obj))
+ call pargi (OBJ_NPIX(obj))
+ call pargr (OBJ_XAP(obj))
+ call pargr (OBJ_YAP(obj))
+ }
+end
+
+
+# CATGOBJ -- Get object given the object number.
+#
+# Currently this relies on the object pointer array being indexed by
+# object number.
+
+pointer procedure catgobj (cat, num)
+
+pointer cat #I Catalog
+int num #I Object number
+
+begin
+ return (Memi[CAT_OBJS(cat)+num-1])
+end
+
+
+# These currently work on the object number but eventually there will be
+# an array of indices to allow traversing the objects in some sorted order.
+
+pointer procedure cathead (cat)
+
+pointer cat #I Catalog pointer
+
+int i
+pointer objs, obj
+
+begin
+ objs = CAT_OBJS(cat)
+ do i = 0, CAT_NUMMAX(cat)-1 {
+ obj = Memi[objs+i]
+ if (obj != NULL)
+ return (obj)
+ }
+ return (NULL)
+end
+
+
+pointer procedure catnext (cat, obj)
+
+pointer cat #I Catalog pointer
+pointer obj #I Input object pointer
+
+int i
+pointer objs, objnext
+
+begin
+ if (obj == NULL)
+ return (NULL)
+
+ objs = CAT_OBJS(cat)
+ do i = OBJ_NUM(obj), CAT_NUMMAX(cat)-1 {
+ objnext = Memi[objs+i]
+ if (objnext != NULL)
+ return (objnext)
+ }
+ return (NULL)
+end
+
+
+procedure catshape (obj, a, b, theta, elong, ellip, r, cxx, cyy, cxy,
+ aerr, berr, thetaerr, cxxerr, cyyerr, cxyerr)
+
+pointer obj #I Object structure
+real a #O Semimajor axis based on second moments
+real b #O Semiminor axis based on second moments
+real theta #O Position angle based on second moments
+real elong #O Elongation (A/B)
+real ellip #O Ellipticity (1 - B/A)
+real r #O Radius based on second moments
+real cxx, cyy, cxy #O Ellipse parameters based on second moments
+real aerr, berr, thetaerr #O Errors
+real cxxerr, cyyerr, cxyerr #O Errors
+
+bool doerr
+real x2, y2, xy, r2, d, f
+real xvar, yvar, xycov, rvar, dvar, fvar
+
+begin
+ a = INDEFR
+ b = INDEFR
+ theta = INDEFR
+ elong = INDEFR
+ ellip = INDEFR
+ r = INDEFR
+ aerr = INDEFR
+ berr = INDEFR
+ thetaerr = INDEFR
+ cxxerr = INDEFR
+ cyyerr = INDEFR
+ cxyerr = INDEFR
+
+ x2 = OBJ_X2(obj)
+ y2 = OBJ_Y2(obj)
+ xy = OBJ_XY(obj)
+ xvar = OBJ_XVAR(obj)
+ yvar = OBJ_YVAR(obj)
+ xycov = OBJ_XYCOV(obj)
+
+ if (IS_INDEFR(x2) || IS_INDEFR(y2) || IS_INDEFR(xy))
+ return
+
+ r2 = x2 + y2
+ if (r2 < 0.)
+ return
+
+ doerr = !(IS_INDEF(xvar) || IS_INDEF(yvar) || IS_INDEF(xycov))
+ if (doerr) {
+ rvar = xvar + yvar
+ if (rvar < 0.)
+ doerr = false
+ }
+
+ r = sqrt (r2)
+
+ d = x2 - y2
+ theta = RADTODEG (atan2 (2 * xy, d) / 2.)
+
+ if (doerr) {
+ dvar = xvar - yvar
+ thetaerr = atan2 (2 * xycov, dvar) / 2.
+ if (thetaerr < 0.)
+ thetaerr = INDEF
+ else
+ thetaerr = DEGTORAD (sqrt (thetaerr))
+ }
+
+ f = sqrt (d**2 + 4 * xy**2)
+ if (f > r2)
+ return
+
+ if (doerr) {
+ fvar = sqrt (dvar**2 + 4 * xycov**2)
+ if (fvar > rvar)
+ doerr = false
+ }
+
+ a = sqrt ((r2 + f) / 2)
+ b = sqrt ((r2 - f) / 2)
+
+ if (doerr) {
+ aerr = sqrt ((rvar + fvar) / 2)
+ berr = sqrt ((rvar - fvar) / 2)
+ }
+
+ ellip = 1 - b / a
+ if (b > 0.)
+ elong = a / b
+
+ if (f == 0) {
+ cxx = 1. / (a * a)
+ cyy = 1. / (a * a)
+ cxy = 0
+ } else {
+ cxx = y2 / f
+ cyy = x2 / f
+ cxy = -2 * xy / f
+ }
+
+ if (doerr) {
+ if (fvar == 0) {
+ cxxerr = 1. / (aerr * aerr)
+ cyyerr = 1. / (berr * berr)
+ cxyerr = 0.
+ } else {
+ cxxerr = yvar / fvar
+ cyyerr = xvar / fvar
+ cxyerr = -2 * xycov / fvar
+ }
+ }
+
+end
diff --git a/noao/nproto/ace/colors.dat b/noao/nproto/ace/colors.dat
new file mode 100644
index 00000000..553ce35c
--- /dev/null
+++ b/noao/nproto/ace/colors.dat
@@ -0,0 +1,8 @@
+black 202
+white 203
+red 204
+green 205
+blue 206
+yellow 207
+cyan 208
+magenta 209
diff --git a/noao/nproto/ace/convolve.x b/noao/nproto/ace/convolve.x
new file mode 100644
index 00000000..af9734ef
--- /dev/null
+++ b/noao/nproto/ace/convolve.x
@@ -0,0 +1,971 @@
+include <ctype.h>
+include <imhdr.h>
+
+
+# ODCNV -- Get a line of data possibly convolved. Also get the unconvolved
+# data, the sky data, and the sky sigma data.
+#
+# This routine must be called sequentially starting with the first line.
+# It is initialized when the first line. Memory is freed by using a final
+# call with a line of zero.
+
+procedure convolve (in, bpm, sky, sig, exp, offset, scale, line, cnv,
+ indata, bp, cnvdata, skydata, sigdata, expdata, cnvwt, logfd)
+
+pointer in[2] #I Image pointers
+pointer bpm[2] #I BPM pointer
+pointer sky[2] #I Sky map
+pointer sig[2] #I Sigma map
+pointer exp[2] #I Exposure map
+int offset[2] #I Offsets
+real scale[2] #I Image scales
+int line #I Line
+char cnv[ARB] #I Convolution string
+pointer indata[2] #O Pointers to unconvolved image data
+pointer bp #O Bad pixel data
+pointer cnvdata #O Pointer to convolved image data
+pointer skydata[2] #O Pointer to sky data
+pointer sigdata[2] #O Pointer to sigma data corrected by exposure map
+pointer expdata[2] #O Pointer to exposure map data
+real cnvwt #O Weight for convolved sigma
+int logfd #I Logfile
+
+int i, j, k, nx, ny, nx2, ny2, nc, nl, mode, off
+real wts, wts1
+pointer bpm2, kptr, ptr, symptr, symwptr
+bool dobpm, overlap, fp_equalr()
+
+pointer kernel, sym, symbuf, symwts, buf, buf2, buf3, bpbuf, bpwts, wtsl, scales
+data kernel/NULL/, sym/NULL/, symbuf/NULL/, symwts/NULL/
+data buf/NULL/, buf2/NULL/, buf3/NULL/, bpbuf/NULL/, bpwts/NULL/
+data wtsl/NULL/, scales/NULL/
+
+errchk cnvparse, cnvgline2
+
+begin
+ # If no convolution.
+ if (cnv[1] == EOS) {
+ if (line == 0)
+ return
+
+ call cnvgline1 (line, offset, in, bpm, indata, bp)
+ call cnvgline2 (line, offset, in, sky, sig, exp, skydata,
+ sigdata, expdata)
+ cnvwt = 1
+ if (in[2] == NULL)
+ cnvdata = indata[1]
+ else
+ call asubr_scale (Memr[indata[1]], scale[1],
+ Memr[indata[2]], scale[2], Memr[cnvdata], IM_LEN(in[1],1))
+ return
+ }
+
+ # Free memory.
+ if (line == 0) {
+ if (symbuf != NULL) {
+ do i = 0, ARB {
+ ptr = Memi[symbuf+i]
+ if (ptr == -1)
+ break
+ call mfree (ptr, TY_REAL)
+ }
+ }
+ if (symwts != NULL) {
+ do i = 0, ARB {
+ ptr = Memi[symwts+i]
+ if (ptr == -1)
+ break
+ call mfree (ptr, TY_REAL)
+ }
+ }
+ call mfree (scales, TY_REAL)
+ call mfree (wtsl, TY_REAL)
+ call mfree (kernel, TY_REAL)
+ call mfree (scales, TY_REAL)
+ call mfree (sym, TY_INT)
+ call mfree (symbuf, TY_POINTER)
+ call mfree (symwts, TY_POINTER)
+ call mfree (buf, TY_REAL)
+ call mfree (buf2, TY_REAL)
+ call mfree (buf3, TY_REAL)
+ call mfree (bpbuf, TY_INT)
+ call mfree (bpwts, TY_REAL)
+
+ return
+ }
+
+ # Initialize by getting the kernel coefficients, setting the
+ # image I/O buffers using a scrolling array, and allocate memory.
+
+ if (line == 1 || buf == NULL) {
+ if (buf != NULL) {
+ if (symbuf != NULL) {
+ do i = 0, ARB {
+ ptr = Memi[symbuf+i]
+ if (ptr == -1)
+ break
+ call mfree (ptr, TY_REAL)
+ }
+ }
+ if (symwts != NULL) {
+ do i = 0, ARB {
+ ptr = Memi[symwts+i]
+ if (ptr == -1)
+ break
+ call mfree (ptr, TY_REAL)
+ }
+ }
+ call mfree (scales, TY_REAL)
+ call mfree (wtsl, TY_REAL)
+ call mfree (kernel, TY_REAL)
+ call mfree (scales, TY_REAL)
+ call mfree (sym, TY_INT)
+ call mfree (symbuf, TY_POINTER)
+ call mfree (symwts, TY_POINTER)
+ call mfree (buf, TY_REAL)
+ call mfree (buf2, TY_REAL)
+ call mfree (buf3, TY_REAL)
+ call mfree (bpbuf, TY_INT)
+ call mfree (bpwts, TY_REAL)
+ }
+
+ nc = IM_LEN(in[1],1)
+ nl = IM_LEN(in[1],2)
+
+ call cnvparse (cnv, kernel, nx, ny, logfd)
+ nx2 = nx / 2
+ ny2 = ny / 2
+ call malloc (scales, ny, TY_REAL)
+ call calloc (wtsl, ny, TY_REAL)
+ call amovkr (1., Memr[scales], ny)
+
+ # Check for lines which are simple scalings of the first line.
+ do i = 2, ny {
+ kptr = kernel + (i - 1) * nx
+ wts = 0.
+ do k = 0, nx-1 {
+ if (Memr[kptr+k] == 0. || Memr[kernel+k] == 0.) {
+ wts = 0.
+ break
+ }
+ if (wts == 0.)
+ wts = Memr[kptr+k] / Memr[kernel+k]
+ else {
+ wts1 = Memr[kptr+k] / Memr[kernel+k]
+ if (!fp_equalr (wts, wts1))
+ break
+ }
+ }
+ if (wts != 0. && fp_equalr (wts, wts1)) {
+ Memr[scales+i-1] = wts
+ call amovr (Memr[kernel], Memr[kptr], nx)
+ }
+ }
+
+ wts = 0
+ do i = 1, ny {
+ kptr = kernel + (i - 1) * nx
+ wts1 = 0.
+ do j = 1, nx {
+ wts1 = wts1 + Memr[kptr]
+ kptr = kptr + 1
+ }
+ Memr[wtsl+i-1] = wts1
+ wts = wts + wts1
+ }
+ if (wts != 0.) {
+ call adivkr (Memr[wtsl], wts, Memr[wtsl], ny)
+ call adivkr (Memr[kernel], wts, Memr[kernel], nx*ny)
+ }
+ cnvwt = sqrt (wts)
+
+ if (in[2] == NULL)
+ bpm2 = NULL
+ else
+ bpm2 = bpm[2]
+ if (bpm[1] == NULL && bpm2 == NULL)
+ dobpm = false
+ else
+ dobpm = true
+ if (dobpm) {
+ call malloc (bpbuf, nc*ny, TY_INT)
+ call malloc (bpwts, nc, TY_REAL)
+ call calloc (symwts, ny*ny+1, TY_POINTER)
+ Memi[symwts+ny*ny] = -1
+ }
+
+ # Check for any line symmetries in the kernel.
+ call malloc (sym, ny, TY_INT)
+ call calloc (symbuf, ny*ny+1, TY_POINTER)
+ Memi[symbuf+ny*ny] = -1
+ do i = ny, 1, -1 {
+ kptr = kernel + (i - 1) * nx
+ do j = ny, 1, -1 {
+ ptr = kernel + (j - 1) * nx
+ do k = 0, nx-1 {
+ if (Memr[kptr+k] != Memr[ptr+k])
+ break
+ }
+ if (k == nx) {
+ Memi[sym+i-1] = j
+ break
+ }
+ }
+ }
+ do i = ny, 1, -1 {
+ k = 0
+ do j = ny, 1, -1
+ if (Memi[sym+j-1] == i)
+ k = k + 1
+ if (k == 1)
+ Memi[sym+i-1] = 0
+ }
+
+ call malloc (buf, nc*ny, TY_REAL)
+ if (in[2] != NULL) {
+ call malloc (buf2, nc*ny, TY_REAL)
+ call malloc (buf3, nc*ny, TY_REAL)
+ }
+
+ if (in[2] != NULL) {
+ overlap = true
+ if (1-offset[1] < 1 || nc-offset[1] > IM_LEN(in[2],1))
+ overlap = false
+ if (1-offset[2] < 1 || nl-offset[2] > IM_LEN(in[2],2))
+ overlap = false
+ }
+ do i = 1, ny {
+ call cnvgline1 (i, offset, in, bpm, indata, bp)
+ off = mod (i, ny) * nc
+ call amovr (Memr[indata[1]], Memr[buf+off], nc)
+ if (in[2] != NULL) {
+ call amovr (Memr[indata[2]], Memr[buf2+off], nc)
+ call asubr_scale (Memr[buf+off], scale[1],
+ Memr[buf2+off], scale[2], Memr[buf3+off], nc)
+ }
+ if (dobpm)
+ call amovi (Memi[bp], Memi[bpbuf+off], nc)
+ }
+ }
+
+ # Get new line.
+ j = line + ny2
+ if (j > ny && j <= nl) {
+ call cnvgline1 (j, offset, in, bpm, indata, bp)
+ off = mod (j, ny) * nc
+ call amovr (Memr[indata[1]], Memr[buf+off], nc)
+ if (in[2] != NULL) {
+ call amovr (Memr[indata[2]], Memr[buf2+off], nc)
+ call asubr_scale (Memr[buf+off], scale[1],
+ Memr[buf2+off], scale[2], Memr[buf3+off], nc)
+ }
+ if (dobpm) {
+ ptr = bpbuf + off
+ call amovi (Memi[bp], Memi[ptr], nc)
+ }
+ }
+
+ # Compute the convolution vector with boundary reflection.
+ # Save and reuse lines with the same kernel weights apart
+ # from a scale factor.
+
+ kptr = kernel
+ call aclrr (Memr[cnvdata], nc)
+ if (dobpm)
+ call aclrr (Memr[bpwts], nc)
+ do i = 1, ny {
+ j = line + i - ny2 - 1
+ if (j < 1)
+ j = 2 - j
+ else if (j > nl)
+ j = 2 * nl - j
+ off = mod (j, ny) * nc
+ if (in[2] == NULL)
+ ptr = buf
+ else
+ ptr = buf3
+ k = Memi[sym+i-1]
+ if (k == 0) {
+ mode = 1
+ symptr = ptr
+ symwptr = bpwts
+ } else {
+ if (k == i)
+ mode = 2
+ else
+ mode = 3
+ symptr = Memi[symbuf+(k-1)*ny+mod(j,ny)]
+ if (symptr == NULL) {
+ call malloc (symptr, nc, TY_REAL)
+ Memi[symbuf+(k-1)*ny+mod(j,ny)] = symptr
+ mode = 2
+ }
+ if (dobpm) {
+ symwptr = Memi[symwts+(k-1)*ny+mod(j,ny)]
+ if (symwptr == NULL) {
+ call malloc (symwptr, nc, TY_REAL)
+ Memi[symwts+(k-1)*ny+mod(j,ny)] = symwptr
+ }
+ }
+ }
+ if (dobpm)
+ call convolve2 (Memr[ptr+off], Memr[cnvdata], Memr[symptr],
+ nc, Memr[kptr], Memr[scales+i-1], nx, Memi[bpbuf+off],
+ Memr[wtsl+i-1], Memr[bpwts], Memr[symwptr], mode)
+ else
+ call convolve1 (Memr[ptr+off], Memr[cnvdata], Memr[symptr],
+ nc, Memr[kptr], Memr[scales+i-1], nx, mode)
+ kptr = kptr + nx
+ }
+ if (dobpm) {
+ do i = 0, nc-1
+ if (Memr[bpwts+i] != 0.)
+ Memr[cnvdata+i] = Memr[cnvdata+i] / Memr[bpwts+i]
+ }
+
+ # Set the output vectors.
+ off = mod (line, ny) * nc
+ indata[1] = buf + off
+ if (dobpm) {
+ if (bpm2 == NULL)
+ bp = bpbuf + off
+ else
+ call amovi (Memi[bpbuf+off], Memi[bp], nc)
+ }
+ if (in[2] != NULL) {
+ if (overlap)
+ indata[2] = buf2 + off
+ else
+ call amovr (Memr[buf2+off], Memr[indata[2]], nc)
+ }
+ call cnvgline2 (line, offset, in, sky, sig, exp, skydata, sigdata,
+ expdata)
+end
+
+
+
+# ODCNV1 -- One dimensional convolution with boundary reflection.
+#
+# The convolution is added to the output so that it might be used
+# as part of a 2D convolution.
+
+procedure convolve1 (in, out, save, nc, xkernel, scale, nx, mode)
+
+real in[nc] #I Input data to be convolved
+real out[nc] #O Output convolved data
+real save[nc] #U Output saved data
+int nc #I Number of data points
+real xkernel[nx] #I Convolution weights
+real scale #I Scale for saved vector
+int nx #I Number of convolution points (must be odd)
+int mode #I Mode (1=no save, 2=save, 3=use save)
+
+int i, j, k, nx2
+real val
+bool fp_equalr()
+
+begin
+ if (mode == 1) {
+ nx2 = nx / 2
+ do i = 1, nx2 {
+ val = 0
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k < 1)
+ k = 2 - k
+ val = val + in[k] * xkernel[j]
+ }
+ out[i] = out[i] + val
+ }
+ do i = nx2+1, nc-nx2 {
+ k = i - nx2
+ val = 0
+ do j = 1, nx {
+ val = val + in[k] * xkernel[j]
+ k = k + 1
+ }
+ out[i] = out[i] + val
+ }
+ do i = nc-nx2+1, nc {
+ val = 0
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k > nc)
+ k = 2 * nc - k
+ val = val + in[k] * xkernel[j]
+ }
+ out[i] = out[i] + val
+ }
+ } else if (mode == 2) {
+ nx2 = nx / 2
+ do i = 1, nx2 {
+ val = 0
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k < 1)
+ k = 2 - k
+ val = val + in[k] * xkernel[j]
+ }
+ out[i] = out[i] + val
+ save[i] = val
+ }
+ do i = nx2+1, nc-nx2 {
+ k = i - nx2
+ val = 0
+ do j = 1, nx {
+ val = val + in[k] * xkernel[j]
+ k = k + 1
+ }
+ out[i] = out[i] + val
+ save[i] = val
+ }
+ do i = nc-nx2+1, nc {
+ val = 0
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k > nc)
+ k = 2 * nc - k
+ val = val + in[k] * xkernel[j]
+ }
+ out[i] = out[i] + val
+ save[i] = val
+ }
+ } else {
+ if (fp_equalr (1., scale)) {
+ do i = 1, nc
+ out[i] = out[i] + save[i]
+ } else {
+ do i = 1, nc
+ out[i] = out[i] + scale * save[i]
+ }
+ }
+end
+
+
+# ODCNV2 -- One dimensional convolution with boundary reflection and masking.
+#
+# The convolution is added to the output so that it might be used
+# as part of a 2D convolution.
+
+procedure convolve2 (in, out, save, nc, xkernel, scale, nx, bp,
+ wtssum, wts, wtsave, mode)
+
+real in[nc] #I Input data to be convolved
+real out[nc] #O Output convolved data
+real save[nc] #U Output saved data
+int nc #I Number of data points
+real xkernel[nx] #I Convolution weights
+real scale #I Scale for saved vector
+int nx #I Number of convolution points (must be odd)
+int bp[nc] #I Bad pixel data
+real wtssum #I Sum of weights
+real wts[nc] #I Weights
+real wtsave[nc] #U Output saved weight data
+int mode #I Mode (1=no save, 2=save, 3=use save)
+
+int i, j, k, nx2
+real val, wt
+bool fp_equalr()
+
+begin
+ if (mode == 1) {
+ nx2 = nx / 2
+ do i = 1, nx2 {
+ val = 0
+ wt = wtssum
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k < 1)
+ k = 2 - k
+ if (bp[k] == 0)
+ val = val + in[k] * xkernel[j]
+ else
+ wt = wt - xkernel[j]
+ }
+ out[i] = out[i] + val
+ wts[i] = wts[i] + wt
+ }
+ do i = nx2+1, nc-nx2 {
+ k = i - nx2
+ val = 0
+ wt = wtssum
+ do j = 1, nx {
+ if (bp[k] == 0)
+ val = val + in[k] * xkernel[j]
+ else
+ wt = wt - xkernel[j]
+ k = k + 1
+ }
+ out[i] = out[i] + val
+ wts[i] = wts[i] + wt
+ }
+ do i = nc-nx2+1, nc {
+ val = 0
+ wt = wtssum
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k > nc)
+ k = 2 * nc - k
+ if (bp[k] == 0)
+ val = val + in[k] * xkernel[j]
+ else
+ wt = wt - xkernel[j]
+ }
+ out[i] = out[i] + val
+ wts[i] = wts[i] + wt
+ }
+ } else if (mode == 2) {
+ nx2 = nx / 2
+ do i = 1, nx2 {
+ val = 0
+ wt = wtssum
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k < 1)
+ k = 2 - k
+ if (bp[k] == 0)
+ val = val + in[k] * xkernel[j]
+ else
+ wt = wt - xkernel[j]
+ }
+ out[i] = out[i] + val
+ wts[i] = wts[i] + wt
+ save[i] = val
+ wtsave[i] = wt
+ }
+ do i = nx2+1, nc-nx2 {
+ k = i - nx2
+ val = 0
+ wt = wtssum
+ do j = 1, nx {
+ if (bp[k] == 0)
+ val = val + in[k] * xkernel[j]
+ else
+ wt = wt - xkernel[j]
+ k = k + 1
+ }
+ out[i] = out[i] + val
+ wts[i] = wts[i] + wt
+ save[i] = val
+ wtsave[i] = wt
+ }
+ do i = nc-nx2+1, nc {
+ val = 0
+ wt = wtssum
+ do j = 1, nx {
+ k = i + j - nx2 - 1
+ if (k > nc)
+ k = 2 * nc - k
+ if (bp[k] == 0)
+ val = val + in[k] * xkernel[j]
+ else
+ wt = wt - xkernel[j]
+ }
+ out[i] = out[i] + val
+ wts[i] = wts[i] + wt
+ save[i] = val
+ wtsave[i] = wt
+ }
+ } else {
+ if (fp_equalr (1., scale)) {
+ do i = 1, nc {
+ out[i] = out[i] + save[i]
+ wts[i] = wts[i] + wtsave[i]
+ }
+ } else {
+ do i = 1, nc {
+ out[i] = out[i] + scale * save[i]
+ wts[i] = wts[i] + scale * wtsave[i]
+ }
+ }
+ }
+end
+
+
+# ASUBR_SCALE -- out = in1 * scale1 - in2 * scale2
+
+procedure asubr_scale (in1, scale1, in2, scale2, out, n)
+
+real in1[n] #I Input vector
+real scale1 #I Scale
+real in2[n] #I Input vector
+real scale2 #I Scale
+real out[n] #O Output vector
+int n #I Number of points
+
+int i
+
+begin
+ if (scale1 == 1. && scale2 == 1.)
+ call asubr (in1, in2, out, n)
+ else if (scale1 == 1.) {
+ do i = 1, n
+ out[i] = in1[i] - in2[i] * scale2
+ } else if (scale2 == 1.) {
+ do i = 1, n
+ out[i] = in1[i] * scale1 - in2[i]
+ } else {
+ do i = 1, n
+ out[i] = in1[i] * scale1 - in2[i] * scale2
+ }
+end
+
+
+procedure cnvgline1 (line, offset, im, bpm, imdata, bp)
+
+int line #I Line to be read
+int offset[2] #I Offsets
+pointer im[2] #I Image pointers
+pointer bpm[2] #I Bad pixel mask pointers
+pointer imdata[2] #U Image data
+pointer bp #U Bad pixel data
+
+bool overlap
+int nl1, nl2, loff, l2
+int nc1, nc2, nc3, off1, off2, off3, c1, c2
+pointer imgl2r(), imgl2i()
+
+
+begin
+ # Get data for first image. Use IMIO buffers except the
+ # bad pixel buffer is not used if there is a second image.
+
+ imdata[1] = imgl2r (im[1], line)
+ if (bpm[1] != NULL) {
+ if (im[2] == NULL)
+ bp = imgl2i (bpm[1], line)
+ else
+ call amovi (Memi[imgl2i(bpm[1],line)], Memi[bp],
+ IM_LEN(bpm[1],1))
+ }
+ if (im[2] == NULL)
+ return
+
+ # Initialize.
+ if (line == 1) {
+ nc1 = IM_LEN(im[1],1)
+ nc2 = IM_LEN(im[2],1)
+ nl1 = IM_LEN(im[1],2)
+ nl2 = IM_LEN(im[2],2)
+
+ overlap = true
+ if (1-offset[1] < 1 || nc1-offset[1] > nc2)
+ overlap = false
+ if (1-offset[2] < 1 || nl1-offset[2] > nl2)
+ overlap = false
+
+ off2 = -offset[1]
+ c1 = max (1, 1+off2)
+ c2 = min (nc2, nc1+off2)
+ nc2 = c2 - c1 + 1
+ off1 = c1 - off2 - 1
+ off3 = c2 - off2
+ off2 = max (0, off2)
+ nc3 = nc1 - off3
+ if (off1 > 0) {
+ call aclrr (Memr[imdata[2]], off1)
+ if (bpm[1] == NULL)
+ call amovki (1, Memi[bp], off1)
+ }
+ if (nc3 > 0) {
+ call aclrr (Memr[imdata[2]+off3], nc3)
+ if (bpm[1] == NULL)
+ call amovki (1, Memi[bp+off3], nc3)
+ }
+
+ loff = -offset[2]
+ if (loff < 0)
+ call aclrr (Memr[imdata[2]], nc1)
+ }
+
+ l2 = line + loff
+ if (l2 < 1 || l2 > nl2) {
+ call amovki (1, Memi[bp], nc1)
+ return
+ }
+
+ if (overlap) {
+ imdata[2] = imgl2r (im[2], l2) + off2
+ if (bpm[1] != NULL && bpm[2] != NULL)
+ call amaxi (Memi[imgl2i(bpm[2],l2)+off2], Memi[bp], Memi[bp],
+ nc1)
+ else if (bpm[2] != NULL)
+ call amovi (Memi[imgl2i(bpm[2],l2)+off2], Memi[bp], nc1)
+ } else {
+ # Copy the overlapping parts of the second image to the output
+ # buffers which must be allocated externally. Use the bad pixel
+ # mask to flag regions where there is no overlap.
+
+ call amovr (Memr[imgl2r(im[2],l2)+off2], Memr[imdata[2]+off1], nc2)
+ if (bpm[1] != NULL && bpm[2] != NULL) {
+ call amaxi (Memi[imgl2i(bpm[2],l2)+off2], Memi[bp+off1],
+ Memi[bp+off1], nc2)
+ if (off1 > 0)
+ call amovki (1, Memi[bp], off1)
+ if (nc3 > 0)
+ call amovki (1, Memi[bp+off3], nc3)
+ } else if (bpm[2] != NULL)
+ call amovi (Memi[imgl2i(bpm[2],l2)+off2], Memi[bp+off1], nc2)
+ }
+end
+
+
+procedure cnvgline2 (line, offset, im, skymap, sigmap, expmap,
+ skydata, sigdata, expdata)
+
+int line #I Line to be read
+int offset[2] #I Offsets
+pointer im[2] #I Image pointers
+pointer skymap[2] #I Sky map
+pointer sigmap[2] #I Sky sigma map
+pointer expmap[2] #I Exposure map
+pointer skydata[2] #U Sky data
+pointer sigdata[2] #U Sky sigma data
+pointer expdata[2] #U Exposure map data
+
+bool overlap
+int nl1, nl2, loff, l2
+int nc1, nc2, nc3, off1, off2, off3, c1, c2
+pointer ptr
+
+pointer map_glr()
+errchk map_glr
+
+begin
+ # Get data for first image.
+
+ skydata[1] = map_glr (skymap[1], line, READ_ONLY)
+ if (expmap[1] == NULL)
+ sigdata[1] = map_glr (sigmap[1], line, READ_ONLY)
+ else {
+ sigdata[1] = map_glr (sigmap[1], line, READ_WRITE)
+ expdata[1] = map_glr (expmap[1], line, READ_ONLY)
+ call expsigma (Memr[sigdata[1]], Memr[expdata[1]],
+ IM_LEN(im[1],1), 0)
+ }
+ if (im[2] == NULL)
+ return
+
+ # Initialize.
+ if (line == 1) {
+ nc1 = IM_LEN(im[1],1)
+ nc2 = IM_LEN(im[2],1)
+ nl1 = IM_LEN(im[1],2)
+ nl2 = IM_LEN(im[2],2)
+
+ overlap = true
+ if (1-offset[1] < 1 || nc1-offset[1] > nc2)
+ overlap = false
+ if (1-offset[2] < 1 || nl1-offset[2] > nl2)
+ overlap = false
+
+ off2 = -offset[1]
+ c1 = max (1, 1+off2)
+ c2 = min (nc2, nc1+off2)
+ nc2 = c2 - c1 + 1
+ off1 = c1 - off2 - 1
+ off3 = c2 - off2
+ nc3 = nc1 - off3
+ if (off1 > 0) {
+ call aclrr (Memr[skydata[2]], off1)
+ call aclrr (Memr[sigdata[2]], off1)
+ if (expmap[2] != NULL)
+ call aclrr (Memr[expdata[2]], off1)
+ }
+ if (nc3 > 0) {
+ call aclrr (Memr[skydata[2]+off3], nc3)
+ call aclrr (Memr[sigdata[2]+off3], nc3)
+ if (expmap[2] != NULL)
+ call aclrr (Memr[expdata[2]+off3], nc3)
+ }
+
+ loff = -offset[2]
+ if (loff < 0) {
+ call aclrr (Memr[skydata[2]], nc1)
+ call aclrr (Memr[sigdata[2]], nc1)
+ if (expmap[2] != NULL)
+ call aclrr (Memr[expdata[2]], nc1)
+ }
+ }
+
+ l2 = line + loff
+ if (l2 < 1 || l2 > nl2)
+ return
+
+ if (overlap) {
+ skydata[2] = map_glr (skymap[2], l2, READ_ONLY) + off2
+ if (expmap[2] == NULL)
+ sigdata[2] = map_glr (sigmap[2], l2, READ_ONLY) + off2
+ else {
+ sigdata[2] = map_glr (sigmap[2], l2, READ_WRITE) + off2
+ expdata[2] = map_glr (expmap[2], l2, READ_ONLY) + off2
+ call expsigma (Memr[sigdata[2]], Memr[expdata[2]], nc2, 0)
+ }
+ } else {
+ # Copy the overlapping parts of the second image to the output
+ # buffers which must be allocated externally.
+
+ ptr = map_glr(skymap[2],l2,READ_ONLY)
+ call amovr (Memr[ptr+off2], Memr[skydata[2]+off1], nc2)
+ ptr = map_glr(sigmap[2],l2,READ_ONLY)
+ call amovr (Memr[ptr+off2], Memr[sigdata[2]+off1], nc2)
+ if (expmap[2] != NULL) {
+ ptr = map_glr(expmap[2],l2,READ_ONLY)
+ call amovr (Memr[ptr+off2], Memr[expdata[2]+off1], nc2)
+ call expsigma (Memr[sigdata[2]], Memr[expdata[2]], nc2, 0)
+ }
+ }
+end
+
+
+# CNVPARSE -- Parse convolution string.
+
+procedure cnvparse (cnvstr, kernel, nx, ny, logfd)
+
+char cnvstr[ARB] #I Convolution string
+pointer kernel #O Pointer to convolution kernel elements
+int nx, ny #O Convolution size
+int logfd #I Log file descriptor
+
+int i, j, nx2, ny2
+int ip, fd, open(), fscan(), nscan(), ctor(), ctoi(), strncmp()
+real val, sx, sy
+pointer ptr
+errchk open
+
+define unknown_ 10
+
+begin
+ kernel = NULL
+
+ for (ip=1; IS_WHITE(cnvstr[ip]); ip=ip+1)
+ ;
+
+ if (cnvstr[ip] == EOS) {
+ nx = 1
+ ny = 1
+ call malloc (kernel, 1, TY_REAL)
+ Memr[kernel] = 1
+ } else if (cnvstr[ip] == '@') {
+ fd = open (cnvstr[ip+1], READ_ONLY, TEXT_FILE)
+ call malloc (kernel, 100, TY_REAL)
+ i = 0
+ nx = 0
+ ny = 0
+ while (fscan (fd) != EOF) {
+ do j = 1, ARB {
+ call gargr (val)
+ if (nscan() < j)
+ break
+ Memr[kernel+i] = val
+ i = i + 1
+ if (mod (i, 100) == 0)
+ call realloc (kernel, i+100, TY_REAL)
+ }
+ j = j - 1
+ if (nx == 0)
+ nx = j
+ else if (j != nx) {
+ call close (fd)
+ call error (1,
+ "Number of convolution elements inconsistent")
+ }
+ ny = ny + 1
+ }
+ call close (fd)
+ } else if (IS_ALPHA(cnvstr[ip])) {
+ if (strncmp ("block", cnvstr[ip], 5) == 0) {
+ i = 6
+ if (ctoi (cnvstr[ip], i, nx) == 0 ||
+ ctoi (cnvstr[ip], i, ny) == 0)
+ goto unknown_
+ call malloc (kernel, nx*ny, TY_REAL)
+ call amovkr (1., Memr[kernel], nx*ny)
+ } else if (strncmp ("bilinear", cnvstr[ip], 8) == 0) {
+ i = 9
+ if (ctoi (cnvstr[ip], i, nx) == 0 ||
+ ctoi (cnvstr[ip], i, ny) == 0)
+ goto unknown_
+ call malloc (kernel, nx*ny, TY_REAL)
+
+ nx2 = nx / 2
+ ny2 = ny / 2
+ ptr = kernel
+ do j = 0, ny-1 {
+ do i = 0, nx-1 {
+ Memr[ptr] = (nx2-abs(nx2-i)+1) * (ny2-abs(ny2-j)+1)
+ ptr = ptr + 1
+ }
+ }
+ } else if (strncmp ("gauss", cnvstr[ip], 5) == 0) {
+ i = 6
+ if (ctoi (cnvstr[ip], i, nx) == 0 ||
+ ctoi (cnvstr[ip], i, ny) == 0)
+ goto unknown_
+ if (ctor (cnvstr[ip], i, sx) == 0 ||
+ ctor (cnvstr[ip], i, sy) == 0)
+ goto unknown_
+ call malloc (kernel, nx*ny, TY_REAL)
+
+ nx2 = nx / 2
+ ny2 = ny / 2
+ val = 2 * sx * sy
+ ptr = kernel
+ do j = 0, ny-1 {
+ do i = 0, nx-1 {
+ Memr[ptr] = exp (-((i-nx2)**2+(j-ny2)**2) / val)
+ ptr = ptr + 1
+ }
+ }
+ }
+ } else {
+ call malloc (kernel, 100, TY_REAL)
+ i = 0
+ nx = 0
+ ny = 0
+ while (cnvstr[ip] != EOS) {
+ do j = 1, ARB {
+ if (ctor (cnvstr, ip, val) == 0)
+ break
+ Memr[kernel+i] = val
+ i = i + 1
+ if (mod (i, 100) == 0)
+ call realloc (kernel, i+100, TY_REAL)
+ }
+ j = j - 1
+ if (nx == 0)
+ nx = j
+ else if (j != nx)
+ call error (1,
+ "Number of convolution elements inconsistent")
+ ny = ny + 1
+ if (cnvstr[ip] != EOS)
+ ip = ip + 1
+ for (; IS_WHITE(cnvstr[ip]); ip=ip+1)
+ ;
+ }
+ }
+
+ if (kernel == NULL)
+unknown_ call error (1, "Unrecognized convolution")
+
+ if (mod (nx, 2) != 1 || mod (ny, 2) != 1) {
+ call mfree (kernel, TY_REAL)
+ call error (1, "Convolution size must be odd")
+ }
+
+ if (logfd != NULL) {
+ ptr = kernel
+ call eprintf (" Convolution:\n")
+ do j = 1, ny {
+ call eprintf (" ")
+ do i = 1, nx {
+ call eprintf (" %7.3g")
+ call pargr (Memr[ptr])
+ ptr = ptr + 1
+ }
+ call eprintf ("\n")
+ }
+ }
+
+end
diff --git a/noao/nproto/ace/detect.h b/noao/nproto/ace/detect.h
new file mode 100644
index 00000000..1c807e7c
--- /dev/null
+++ b/noao/nproto/ace/detect.h
@@ -0,0 +1,16 @@
+# Detection parameter structure.
+define DET_LEN 62 # Length of parameter structure
+define DET_STRLEN 99 # Length of strings in structure
+
+define DET_CNV P2C($1) # Convolution string
+define DET_HSIG Memr[P2R($1+51)] # High detection sigma
+define DET_LSIG Memr[P2R($1+52)] # Low detection sigma
+define DET_HDETECT Memi[$1+53] # Detect above sky?
+define DET_LDETECT Memi[$1+54] # Detect below sky?
+define DET_NEIGHBORS Memi[$1+55] # Neighbor type
+define DET_MINPIX Memi[$1+56] # Minimum number of pixels
+define DET_SIGAVG Memr[P2R($1+57)] # Minimum average above sky in sigma
+define DET_SIGPEAK Memr[P2R($1+58)] # Minimum peak above sky in sigma
+define DET_FRAC2 Memr[P2R($1+59)] # Fraction of difference relative to 2
+define DET_BPVAL Memi[$1+60] # Output bad pixel value
+define DET_SKB Memi[$1+61] # Parameters for sky update
diff --git a/noao/nproto/ace/detect.par b/noao/nproto/ace/detect.par
new file mode 100644
index 00000000..fd39b83f
--- /dev/null
+++ b/noao/nproto/ace/detect.par
@@ -0,0 +1,65 @@
+# ACEDETECT
+
+images,f,a,,,,"List of images"
+masks,s,h,"!BPM",,,"List of bad pixel masks"
+skys,s,h,"",,,"List of sky maps"
+sigmas,s,h,"",,,"List of sigma maps"
+exps,s,h,"",,,"List of exposure maps"
+gains,s,h,"",,,"List of gain maps"
+objmasks,s,h,"",,,"List of object masks"
+omtype,s,h,"all","boolean|numbers|colors|all",,"Object mask type"
+catalogs,s,h,"",,,"List of catalogs"
+extnames,s,h,"",,,"Extension names"
+catdefs,s,h,"ace$lib/catdef.dat",,,"List of catalog definitions"
+logfiles,s,h,"STDOUT",,,"List of log files
+
+# Steps"
+dodetect,b,h,yes,,,"Detect objects?"
+dosplit,b,h,yes,,,"Split merged objects?"
+dogrow,b,h,yes,,,"Grow object regions?"
+doevaluate,b,h,yes,,,"Evaluate objects?
+
+# Sky"
+skytype,s,h,"block","fit|block",,"Type of sky estimation
+
+# Sky Fitting"
+fitstep,i,h,100,1,,"Line step for sky sampling"
+fitblk1d,i,h,10,,,"Block average for line fitting"
+fithclip,r,h,2.,,,"High sky clipping during 1D sky estimation"
+fitlclip,r,h,3.,,,"Low sky clippling during 1D sky estimation"
+fitxorder,i,h,1,1,,"Sky fitting x order"
+fityorder,i,h,1,1,,"Sky fitting y order"
+fitxterms,s,h,"half","none|full|half",,"Sky fitting cross terms
+
+# Sky Blocks"
+blkstep,i,h,1,1,,"Line step for sky sampling"
+blksize,i,h,-10,,,"Block size (+=pixels, -=blocks)"
+blknsubblks,i,h,2,1,,"Number of subblocks per axis
+
+# Detection"
+updatesky,b,h,yes,,,"Update sky during detection?"
+convolve,s,h,"block 3 3",,,"Convolution kernel"
+hsigma,r,h,3.,.1,,"Sigma threshold above sky"
+lsigma,r,h,10.,.1,,"Sigma threshold below sky"
+hdetect,b,h,yes,,,"Detect objects above sky?"
+ldetect,b,h,no,,,"Detect objects below sky?"
+neighbors,s,h,"8","4|8",,Neighbor type
+minpix,i,h,6,1,,"Minimum number of pixels in detected objects"
+sigavg,r,h,4.,0.,,"Sigma of mean flux cutoff"
+sigmax,r,h,4.,0.,,"Sigma of maximum pixel"
+bpval,i,h,INDEF,,,"Output bad pixel value
+
+# Splitting"
+splitmax,r,h,INDEF,,,"Maximum sigma above sky for splitting"
+splitstep,r,h,0.4,,,"Splitting steps in convolved sigma"
+splitthresh,r,h,5.,,,"Splitting threshold in sigma"
+sminpix,i,h,8,1,,"Minimum number of pixels in split objects"
+ssigavg,r,h,10.,0.,,"Sigma of mean flux cutoff"
+ssigmax,r,h,5.,0.,,"Sigma of maximum pixel
+
+# Growing"
+ngrow,i,h,2,0,,"Number of grow rings"
+agrow,r,h,2.,0,,"Area grow factor
+
+# Evaluate"
+magzero,s,h,"INDEF",,,"Magnitude zero point"
diff --git a/noao/nproto/ace/detect.x b/noao/nproto/ace/detect.x
new file mode 100644
index 00000000..681951db
--- /dev/null
+++ b/noao/nproto/ace/detect.x
@@ -0,0 +1,795 @@
+include <imhdr.h>
+include <pmset.h>
+include <mach.h>
+include "ace.h"
+include "cat.h"
+include "objs.h"
+include "skyblock.h"
+include "detect.h"
+include "split.h"
+
+
+# DETECT - Object detection.
+#
+# Get input image data (possibly convolved) and compare to sky using sky
+# sigma and threshold factors. Catagorize as bad pixel, sky, above sky, and
+# below sky. Write catagories to output mask.
+
+procedure detect (det, spt, dosky, dosig, skyname, signame, im, bpm,
+ skymap, sigmap, expmap, scale, offset, out, siglevmap, siglevels,
+ logfd, cat)
+
+pointer det #I Detection parameter structure
+pointer spt #I Split parameter structure
+bool dosky #I Do sky update?
+bool dosig #I Do sigma update?
+char skyname[ARB] #I Sky name for updating sky
+char signame[ARB] #I Sigma name for updating sigma
+pointer im[2] #I Input image pointers
+pointer bpm[2] #I Bad pixel mask pointer
+pointer skymap[2] #U Sky map
+pointer sigmap[2] #U Sigma map
+pointer expmap[2] #I Exposure map
+real scale[2] #I Image scales
+int offset[2] #I Offsets of second image
+pointer out #I Output pixel mask (PMIO) pointer
+pointer siglevmap #I Mask for sigma levels
+pointer siglevels #O Sigma levels for mask
+int logfd #I Verbose?
+pointer cat #O Catalog of objects
+
+pointer cnv # Convolution string pointer
+real hsig # Detection threshold
+real splitstep # Minimum split step in convolved sigma
+real splitthresh # Transition convolved sigma
+bool hdetect # Detection above sky
+bool ldetect # Detection below sky
+
+bool dosky1, dosig1, overlap
+int i, c, l, nc, nl, nc2, siglevmax
+int nobjs, nalloc, navail
+long v[2]
+real z, cnvwt
+pointer sp, str, iptr, rptr, outdata, lastdata, orl, srl
+pointer skb, objs, ids, links
+pointer indata[2], bp, skydata[2], sigdata[2], expdata[2], cnvdata
+
+errchk convolve, drenum
+errchk detect, salloc, malloc, calloc, realloc
+
+
+begin
+ # Initialize parameters.
+ call det_pars ("open", "", det)
+
+ # The sky update requires the doxxx parameter to be true, a filename
+ # to be specified and the skb pointer to be non-null. The skb
+ # pointer is set depending on the "updatesky" task parameter.
+
+ dosky1 = (dosky && skyname[1] != EOS)
+ dosig1 = (dosig && signame[1] != EOS)
+ if (dosky1 || dosig1)
+ skb = DET_SKB(det)
+ else
+ skb = NULL
+
+ cnv = DET_CNV(det)
+ hsig = DET_HSIG(det)
+ if (spt != NULL) {
+ splitstep = SPT_SPLITSTEP(spt)
+ splitthresh = SPT_SPLITTHRESH(spt)
+ }
+ hdetect = (DET_HDETECT(det) == YES)
+ ldetect = (DET_LDETECT(det) == YES)
+
+ # Set sizes.
+ nc = IM_LEN(im[1],1)
+ nl = IM_LEN(im[1],2)
+ if (ldetect)
+ nc2 = 2 * (nc + 2)
+ else
+ nc2 = nc + 2
+
+ # Allocate memory.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (outdata, nc2, TY_INT)
+ call salloc (lastdata, nc2, TY_INT)
+ call salloc (orl, 3+3*nc, TY_INT)
+ call salloc (iptr, 1, TY_REAL)
+ call salloc (rptr, 1, TY_REAL)
+
+ Memr[iptr] = INDEFI
+ Memr[rptr] = INDEFR
+
+ if (siglevmap != NULL)
+ call salloc (srl, 3+3*nc, TY_INT)
+ else
+ srl = iptr
+
+ if (expmap[1] == NULL)
+ expdata[1] = rptr
+ if (expmap[2] == NULL)
+ expdata[2] = rptr
+
+ if (im[2] == NULL) {
+ indata[2] = rptr
+ skydata[2] = rptr
+ sigdata[2] = rptr
+ expdata[2] = rptr
+ if (bpm[1] == NULL) {
+ call salloc (bp, nc, TY_INT)
+ call aclri (Memi[bp], nc)
+ }
+ if (Memc[cnv] != EOS)
+ call salloc (cnvdata, nc, TY_REAL)
+ } else {
+ overlap = true
+ if (1-offset[1] < 1 || nc-offset[1] > IM_LEN(im[2],1))
+ overlap = false
+ if (1-offset[2] < 1 || nl-offset[2] > IM_LEN(im[2],2))
+ overlap = false
+ if (!overlap) {
+ call salloc (indata[2], nc, TY_REAL)
+ call salloc (skydata[2], nc, TY_REAL)
+ call salloc (sigdata[2], nc, TY_REAL)
+ call salloc (expdata[2], nc, TY_REAL)
+ }
+ call salloc (bp, nc, TY_INT)
+ call aclri (Memi[bp], nc)
+ call salloc (cnvdata, nc, TY_REAL)
+ }
+
+ navail = (nc * nl) / 100
+ call calloc (ids, navail, TY_INT)
+ call calloc (links, navail, TY_INT)
+ call calloc (objs, navail, TY_POINTER)
+ nalloc = 0
+
+ # Setup sky updating.
+ if (skb!=NULL && !overlap) {
+ l = 1
+ call skb_iminit (skb, im[1], expmap, l, NULL)
+ }
+
+ if (logfd != NULL)
+ call fprintf (logfd, " Detect objects:\n")
+
+ # Go through image.
+ nobjs = NUMSTART - 1
+ call aclri (Memi[outdata], nc2)
+ if (siglevmap == NULL)
+ siglevmax = INDEFI
+ else
+ siglevmax = 0
+
+ v[1] = 1
+ do l = 1, nl {
+ # Get data.
+ call convolve (im, bpm, skymap, sigmap, expmap, offset,
+ scale, l, Memc[cnv], indata, bp, cnvdata, skydata,
+ sigdata, expdata, cnvwt, logfd)
+ call amovi (Memi[outdata], Memi[lastdata], nc2)
+
+ call detect1 (det, spt, skb, Memr[indata[1]], Memr[skydata[1]],
+ Memr[sigdata[1]], Memr[expdata[1]], Memr[indata[2]],
+ Memr[skydata[2]], Memr[sigdata[2]], Memr[expdata[2]],
+ scale, Memi[bp], Memr[cnvdata], cnvwt, Memi[outdata],
+ Memi[lastdata], nc, nl, l, objs, ids, links, nobjs,
+ nalloc, navail, Memi[orl], Memi[srl], siglevmax)
+
+ # Write to output masks.
+ v[2] = l
+ call pmplri (out, v, Memi[orl], 0, nc, PIX_SRC)
+ if (siglevmap != NULL)
+ call pmplri (siglevmap, v, Memi[srl], 0, nc, PIX_SRC)
+ }
+
+ # Free convolution memory.
+ call convolve (im, bpm, skymap, sigmap, expmap, offset,
+ scale, 0, Memc[cnv], indata, bp, cnvdata, skydata,
+ sigdata, expdata, cnvwt, logfd)
+
+ # Free extra object structures.
+ do c = nobjs, nalloc-1
+ call mfree (Memi[objs+c], TY_STRUCT)
+
+ # Renumber and reject objects with less than a minimum area.
+ call drenum (det, out, Memi[ids], Memi[objs], nobjs)
+
+ call mfree (ids, TY_INT)
+ call mfree (links, TY_INT)
+ call realloc (objs, nobjs, TY_POINTER)
+
+ CAT_NOBJS(cat) = nobjs
+ CAT_NUMMAX(cat) = nobjs
+ CAT_OBJS(cat) = objs
+
+ # Set sigma levels if needed.
+ if (spt != NULL) {
+ call calloc (siglevels, siglevmax+1, TY_REAL)
+ do i = 1, siglevmax {
+ z = i * splitstep
+ if (z > splitthresh) {
+ z = z / splitthresh
+ z = (z + 3) / 4
+ z = z * z * z * z
+ z = z * splitthresh
+ }
+ if (z > hsig)
+ Memr[siglevels+i-1] = z
+ }
+ Memr[siglevels+siglevmax] = MAX_REAL
+ } else
+ siglevels = NULL
+
+ if (logfd != NULL) {
+ call fprintf (logfd, " %d objects detected\n")
+ call pargi (nobjs - NUMSTART + 1)
+ }
+
+ if (skb != NULL) {
+ call skb_update (skb, dosky1, dosig1, im[1], skyname, signame,
+ skymap, sigmap, logfd)
+ call skb_imfree (skb)
+ }
+
+ call sfree (sp)
+end
+
+
+procedure detect1 (det, spt, skb, in, sky, sig, exp, in2, sky2, sig2, exp2,
+ scale, bp, cnv, cnvwt, out, lastout, nc, nl, line, objs, ids,
+ links, nobjs, nalloc, navail, orl, srl, siglevmax)
+
+pointer det #I Parameters
+pointer spt #I Parameters
+pointer skb #I Sky block pointer
+real in[nc] #I Image data
+real sky[nc] #I Sky data
+real sig[nc] #I Sky sigma data
+real exp[nc] #I Exposure map data
+real in2[nc] #I Image data
+real sky2[nc] #I Sky data
+real sig2[nc] #I Sky sigma data
+real exp2[nc] #I Exposure map data
+real scale[2] #I Image scales
+int bp[nc] #I Bad pixel values
+real cnv[nc] #I Convolved image data
+real cnvwt #I Sigma weight
+int out[ARB] #I Output data (extra pixel on each end)
+int lastout[ARB] #I Last output data (extra pixel on each end)
+int nc #I Number of columns
+int nl #I Number of lines
+int line #I Current line
+
+pointer objs #I Pointer to array of object pointers
+pointer ids #I Pointer to array of IDs
+pointer links #I Pointer to array links to other IDs
+int nobjs #I Number of objects pointers
+int nalloc #I Number of object pointers allocated
+int navail #I Size of allocated arrays
+
+int orl[3,ARB] #O Output object mask range list
+int srl[3,ARB] #O Output sigma level range list
+int siglevmax #O Maximum sigma level (INDEF if not used)
+
+real hsig # High detection sigma
+real lsig # Low detection sigma
+int bpval # Output bad pixel value
+real splitstep # Minimum split step in convolved sigma
+real splitthresh # Transition convolved sigma
+bool hdetect # Detection above sky
+bool ldetect # Detection below sky
+int neighbors # Neighbor type
+
+int i, j, c, c1, c2, clast, nc2, nc3, num, numlast, bin, binlast
+int n, ncmax, nlmax, nbins, csky
+real z, s, t, z1, s1, t1, z2, s2, t2, zcnv, rcnv, tcnv, low, high, binscale
+real explast
+bool dodiff, dosrl
+
+real a, b
+pointer bins, skys, sigs, exps, nsky
+
+errchk dadd, realloc
+
+begin
+ # Parameters
+ hsig = DET_HSIG(det)
+ lsig = DET_LSIG(det)
+ bpval = DET_BPVAL(det)
+ hdetect = (DET_HDETECT(det) == YES)
+ ldetect = (DET_LDETECT(det) == YES)
+ neighbors = DET_NEIGHBORS(det)
+
+ # Do sky updating?
+ nlmax = 0
+ if (skb != NULL) {
+ ncmax = min (nc, SKB_NCSBLK(skb) * SKB_NCSPIX(skb))
+ nlmax = min (nl, SKB_NLSBLK(skb) * SKB_NLSPIX(skb))
+
+ a = SKB_A(skb)
+ b = SKB_B(skb)
+ n = SKB_NCSPIX(skb)
+ nbins = SKB_NBINS(skb)
+ bins = SKB_BINS(skb)
+ skys = SKB_SKY(skb)
+ sigs = SKB_SIG(skb)
+ exps = SKB_EXP(skb)
+ nsky = SKB_NSKY(skb)
+ }
+
+ # Do difference detection?
+ if (IS_INDEFR(in2[1])) {
+ dodiff = false
+ z1 = 0; s1 = 0; t1 = 1
+ z2 = 0; s2 = 0; t2 = 1
+ } else
+ dodiff = true
+
+ # Initialize output mask range lists.
+ i = 1
+ orl[1,i] = 0
+ if (spt != NULL) {
+ splitstep = SPT_SPLITSTEP(spt)
+ splitthresh = SPT_SPLITTHRESH(spt)
+ binscale = splitthresh / splitstep
+
+ j = 1
+ srl[1,j] = 0
+ dosrl = true
+ } else
+ dosrl = false
+ clast = 0
+
+ nc2 = nc + 2
+ if (ldetect)
+ nc3 = nc2 + 1
+ else
+ nc3 = 1
+
+ explast = INDEFR
+
+ # Find pixels which are masked, sky, above sky, and below sky.
+ do c = 1, nc {
+ c1 = c + 1
+ c2 = c + nc3
+ out[c1] = 0
+ out[c2] = 0
+
+ # Mark masked pixels if any.
+ if (bp[c] != 0) {
+ if (IS_INDEFI(bpval))
+ num = min (bp[c], NUMSTART-1)
+ else
+ num = min (bpval, NUMSTART-1)
+
+ if (num > 0) {
+ out[c1] = num
+ out[c2] = num
+
+ if (num != numlast || c != clast) {
+ orl[2,i] = clast - orl[1,i]
+ i = i + 1
+
+ numlast = num
+ orl[1,i] = c
+ orl[3,i] = numlast
+ }
+ clast = c1
+ }
+
+ next
+ }
+
+ # Find sky and object pixels.
+ if (dodiff) {
+ z1 = in[c]
+ s1 = sky[c]
+ t1 = sig[c]
+ z2 = in2[c]
+ s2 = sky2[c]
+ t2 = sig2[c]
+ z = scale[1] * z1 - scale[2] * z2
+ s = scale[1] * s1 - scale[2] * s2
+ t = sqrt ((scale[1]*t1)**2 + (scale[2]*t2)**2)
+ } else {
+ z = in[c]
+ s = sky[c]
+ t = sig[c]
+ }
+ zcnv = cnv[c]
+ rcnv = zcnv - s
+ tcnv = t / cnvwt
+ low = -lsig * tcnv
+ high = hsig * tcnv
+
+ if (rcnv > high) {
+ if (hdetect) {
+ call dadd (c1, line, out, lastout, nc2,
+ Memi[ids], Memi[links], Memi[objs], nobjs, nalloc,
+ z, s, t, z2, s2, t2, neighbors, 0, num)
+
+ if (nalloc == navail) {
+ navail = max (100*nalloc*(nl+1)/line/100, nalloc+10000)
+ call realloc (ids, navail, TY_INT)
+ call realloc (links, navail, TY_INT)
+ call realloc (objs, navail, TY_POINTER)
+ }
+
+ # Add to output masks.
+ if (num != numlast || c != clast) {
+ orl[2,i] = clast - orl[1,i]
+ i = i + 1
+
+ numlast = num
+ orl[1,i] = c
+ orl[3,i] = numlast
+ }
+
+ if (dosrl) {
+ rcnv = rcnv / tcnv / splitthresh
+ if (rcnv > 1.)
+ rcnv = (4 * rcnv**0.25 - 3)
+ bin = nint (rcnv * binscale)
+ if (bin != binlast || c != clast) {
+ srl[2,j] = clast - srl[1,j]
+ j = j + 1
+
+ binlast = bin
+ srl[1,j] = c
+ srl[3,j] = binlast
+
+ siglevmax = max (bin, siglevmax)
+ }
+ }
+ clast = c1
+ }
+ } else if (rcnv < low) {
+ if (ldetect) {
+ call dadd (c1, line, out[nc3], lastout[nc3], nc2,
+ Memi[ids], Memi[links], Memi[objs], nobjs, nalloc,
+ 2*s-z, s, t, z1, s1, t1, neighbors, OBJ_DARK, num)
+
+ if (nalloc == navail) {
+ navail = max (100*nalloc*(nl+1)/line/100, nalloc+10000)
+ call realloc (ids, navail, TY_INT)
+ call realloc (links, navail, TY_INT)
+ call realloc (objs, navail, TY_POINTER)
+ }
+
+ # Add to output masks.
+ if (num != numlast || c != clast) {
+ orl[2,i] = clast - orl[1,i]
+ i = i + 1
+
+ numlast = num
+ orl[1,i] = c
+ orl[3,i] = numlast
+ }
+ clast = c1
+ }
+ }
+
+ if (line <= nlmax && c <= ncmax) {
+ bin = a * (z - s) / t + b
+ if (bin >= 1 && bin <= nbins) {
+ csky = (c-1) / n
+ bin = bins + csky * nbins + bin - 1
+ Memi[bin] = Memi[bin] + 1
+ Memr[skys+csky] = Memr[skys+csky] + s
+ Memr[sigs+csky] = Memr[sigs+csky] + t
+ Memi[nsky+csky] = Memi[nsky+csky] + 1
+ if (!IS_INDEFR(Memr[exps]))
+ Memr[exps+csky] = Memr[exps+csky] + exp[c]
+ }
+ }
+ }
+
+ # Finish up range lists.
+ orl[2,i] = clast - orl[1,i]
+ orl[1,1] = i
+ orl[2,1] = nc
+ if (dosrl) {
+ srl[2,j] = clast - srl[1,j]
+ srl[1,1] = j
+ srl[2,1] = nc
+ }
+
+ # Evaluate histogram sky values if all lines have been accumulated.
+ if (line <= nlmax) {
+ if (mod (line, SKB_NLSPIX(skb)) == 0) {
+ n = SKB_NCSBLK(skb)
+ call skb_blkeval (Memi[bins], nbins, a, b, Memr[skys],
+ Memr[sigs], Memr[exps], Memi[nsky], n,
+ SKB_NSKYMIN(skb), SKB_NAV(skb), SKB_HISTWT(skb),
+ SKB_SIGFAC(skb))
+
+ # Initialize for accumulation of next line of blocks.
+ SKB_SKY(skb) = skys + n
+ SKB_SIG(skb) = sigs + n
+ if (!IS_INDEFR(Memr[exps]))
+ call aclrr (Memr[exps], n)
+ call aclri (Memi[nsky], n)
+ call aclri (Memi[bins], n*nbins)
+ }
+ }
+end
+
+
+# OBJADD -- Add a pixel to the object list and set the mask value.
+
+procedure dadd (c, l, z, zlast, nc, ids, links, objs, nobjs, nalloc,
+ data, sky, sigma, data2, sky2, sigma2, neighbors, flags, num)
+
+int c, l #I Pixel coordinate
+int z[nc] #I Pixel values for current line
+int zlast[nc] #I Pixel values for last line
+int nc #I Number of pixels in a line
+int ids[ARB] #I Mask ids
+int links[ARB] #I Link to other mask ids with same number
+pointer objs[ARB] #I Objects
+int nobjs #U Number of objects
+int nalloc #U Number of allocated objects
+real data #I Data value (not sky subtracted)
+real sky #I Sky value
+real sigma #I Sky sigma value
+real data2 #I Data value (not sky subtracted)
+real sky2 #I Sky value
+real sigma2 #I Sky sigma value
+int neighbors #I Neighbor type
+int flags #I Flags
+int num #O Object number assigned
+
+int i, num1, c1, c2
+real val
+bool merge
+pointer obj, obj1
+
+begin
+ # Inherit number of a neighboring pixel.
+ num = INDEFI
+ merge = false
+ if (neighbors == 4) {
+ c1 = c - 1
+ c2 = c
+ if (z[c1] >= NUMSTART) {
+ num = z[c1]
+ merge = true
+ } else if (zlast[c] >= NUMSTART)
+ num = ids[zlast[c]]
+ } else {
+ c1 = c - 1
+ c2 = c + 1
+ if (z[c1] >= NUMSTART) {
+ num = z[c1]
+ merge = true
+ } else if (zlast[c1] >= NUMSTART)
+ num = ids[zlast[c1]]
+ else if (zlast[c] >= NUMSTART)
+ num = ids[zlast[c]]
+ else if (zlast[c2] >= NUMSTART)
+ num = ids[zlast[c2]]
+ }
+
+ # If no number assign a new number.
+ if (num == INDEFI) {
+ nobjs = nobjs + 1
+ num = nobjs
+ ids[num] = num
+ links[num] = 0
+ if (nalloc < nobjs) {
+ call calloc (objs[num], OBJ_DETLEN, TY_STRUCT)
+ nalloc = nobjs
+ }
+ obj = objs[num]
+ OBJ_XAP(obj) = 0.
+ OBJ_YAP(obj) = 0.
+ OBJ_FLUX(obj) = 0.
+ OBJ_NPIX(obj) = 0
+ OBJ_ISIGMAX(obj) = 0.
+ OBJ_ISIGAVG(obj) = 0.
+ OBJ_ISIGAVG2(obj) = 0.
+ OBJ_FLAGS(obj) = flags
+ }
+ obj = objs[num]
+
+ # Merge overlapping objects from previous line.
+ if (merge) {
+ i = zlast[c2]
+ if (i >= NUMSTART && num != ids[i]) {
+ num1 = ids[i]
+
+ obj1 = objs[num1]
+ OBJ_XAP(obj) = OBJ_XAP(obj) + OBJ_XAP(obj1)
+ OBJ_YAP(obj) = OBJ_YAP(obj) + OBJ_YAP(obj1)
+ OBJ_FLUX(obj) = OBJ_FLUX(obj) + OBJ_FLUX(obj1)
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + OBJ_NPIX(obj1)
+ OBJ_ISIGMAX(obj) = max (OBJ_ISIGMAX(obj), OBJ_ISIGMAX(obj1))
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) + OBJ_ISIGAVG(obj1)
+ OBJ_ISIGAVG2(obj) = OBJ_ISIGAVG2(obj) + OBJ_ISIGAVG2(obj1)
+
+ i = num
+ while (links[i] != 0)
+ i = links[i]
+ links[i] = num1
+ repeat {
+ i = links[i]
+ ids[i] = num
+ } until (links[i] == 0)
+
+ nalloc = nalloc + 1
+ objs[nalloc] = obj1
+ objs[num1] = NULL
+ }
+ }
+
+ z[c] = num
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ val = (data - sky) / sigma
+ OBJ_XAP(obj) = OBJ_XAP(obj) + val * c1
+ OBJ_YAP(obj) = OBJ_YAP(obj) + val * l
+ OBJ_FLUX(obj) = OBJ_FLUX(obj) + val
+ OBJ_ISIGMAX(obj) = max (OBJ_ISIGMAX(obj), val)
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) + val
+ #OBJ_ISIGAVG2(obj) = OBJ_ISIGAVG2(obj) + (data2 - sky2) / sigma2
+ OBJ_ISIGAVG2(obj) = OBJ_ISIGAVG2(obj) + (data2 - sky2) / sigma
+end
+
+
+procedure drenum (det, out, ids, objs, nobjs)
+
+pointer det #I Parameters
+pointer out #I Output PMIO pointer
+int ids[nobjs] #I Mask IDs
+pointer objs[nobjs] #U Input and output object list
+int nobjs #U Number of objects
+
+int minpix # Minimum number of pixels
+real sigavg # Cutoff of SIGAVG
+real sigmax # Cutoff of SIGMAX
+real frac # Fraction of sigavg2
+
+int i, j, n, nc, nl
+real rval
+pointer sp, v, rl, buf, obj
+
+begin
+ # Parameters.
+ minpix = DET_MINPIX(det)
+ sigavg = DET_SIGAVG(det)
+ sigmax = DET_SIGPEAK(det)
+ frac = DET_FRAC2(det)
+
+ # Assign object numbers. Eliminate objects, by setting object number
+ # to zero, based on selection # critera (size, peak, etc.).
+
+ j = NUMSTART - 1
+ do i = NUMSTART, nobjs {
+ obj = objs[i]
+ if (obj == NULL)
+ next
+
+ n = OBJ_NPIX(obj)
+ if (n < minpix) {
+ OBJ_NUM(obj) = 0
+ next
+ }
+ rval = sqrt (real(n))
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) / rval
+ if ((OBJ_ISIGMAX(obj) < sigmax && OBJ_ISIGAVG(obj) < sigavg)) {
+ OBJ_NUM(obj) = 0
+ next
+ }
+ OBJ_ISIGAVG2(obj) = OBJ_ISIGAVG2(obj) / rval
+ if (OBJ_ISIGAVG(obj) < frac * OBJ_ISIGAVG2(obj)) {
+ OBJ_NUM(obj) = 0
+ next
+ }
+
+ rval = OBJ_FLUX(obj)
+ if (rval > 0.) {
+ OBJ_XAP(obj) = OBJ_XAP(obj) / rval
+ OBJ_YAP(obj) = OBJ_YAP(obj) / rval
+ } else {
+ OBJ_XAP(obj) = INDEFR
+ OBJ_YAP(obj) = INDEFR
+ }
+
+ j = j + 1
+ OBJ_NUM(obj) = j
+ }
+
+ # Set object mask.
+ call smark (sp)
+ call salloc (v, PM_MAXDIM, TY_LONG)
+ call pm_gsize (out, i, Meml[v], j)
+ nc = Meml[v]; nl = Meml[v+1]
+ call salloc (rl, 3+3*nc, TY_INT)
+ call salloc (buf, nc, TY_INT)
+ call drenum1 (out, nc, nl, ids, objs, Meml[v], Memi[rl], Memi[buf])
+ call sfree (sp)
+
+ # Reorder the arrays and expand object structures.
+ j = NUMSTART - 1
+ do i = NUMSTART, nobjs {
+ obj = objs[i]
+ if (obj == NULL)
+ next
+ if (OBJ_NUM(obj) == 0) {
+ call mfree (objs[i], TY_STRUCT)
+ next
+ }
+
+ call newobj (obj)
+
+ j = j + 1
+ objs[j] = obj
+ }
+ nobjs = j
+end
+
+
+procedure drenum1 (om, nc, nl, ids, objs, v, rl, buf)
+
+pointer om #I Object mask pointer
+int nc, nl #I Dimensions
+int ids[ARB] #I Mask IDs
+pointer objs[ARB] #I Objects
+long v[PM_MAXDIM] #I Work array
+int rl[3,nc] #I Work array
+int buf[nc] #I Work array
+
+int i, j, l, id, andi(), ori()
+pointer obj
+
+begin
+ v[1] = 1
+ do l = 1, nl {
+ v[2] = l
+ call pmglri (om, v, rl, 0, nc, 0)
+ j = 1
+ do i = 2, rl[1,1] {
+ id = rl[3,i]
+ if (id >= NUMSTART) {
+ obj = objs[ids[id]]
+ id = OBJ_NUM(obj)
+ if (DARK(obj) && id > 0)
+ id = MSETFLAG(id, MASK_DARK)
+ }
+ if (id > 0) {
+ j = j + 1
+ rl[1,j] = rl[1,i]
+ rl[2,j] = rl[2,i]
+ rl[3,j] = id
+ }
+ }
+ rl[1,1] = j
+ call pmplri (om, v, rl, 0, nc, PIX_SRC)
+ }
+end
+
+
+procedure newobj (obj)
+
+pointer obj #U Object structure
+
+begin
+ if (obj == NULL)
+ return
+
+ call realloc (obj, OBJ_LEN, TY_STRUCT)
+ OBJ_FLUX(obj) = INDEFR
+ OBJ_SKY(obj) = INDEFR
+ OBJ_SIG(obj) = INDEFR
+ OBJ_PEAK(obj) = INDEFR
+ OBJ_X1(obj) = INDEFR
+ OBJ_Y1(obj) = INDEFR
+ OBJ_WX(obj) = INDEFD
+ OBJ_WY(obj) = INDEFD
+ OBJ_XMIN(obj) = INDEFI
+ OBJ_XMAX(obj) = INDEFI
+ OBJ_YMIN(obj) = INDEFI
+ OBJ_YMAX(obj) = INDEFI
+end
diff --git a/noao/nproto/ace/diffdetect.par b/noao/nproto/ace/diffdetect.par
new file mode 100644
index 00000000..6a0c0084
--- /dev/null
+++ b/noao/nproto/ace/diffdetect.par
@@ -0,0 +1,59 @@
+images,f,a,,,,"List of images"
+masks,s,h,"!BPM",,,"List of bad pixel masks"
+skys,s,h,"",,,"List of sky maps"
+sigmas,s,h,"",,,"List of sigma maps"
+exps,s,h,"",,,"List of exposure maps"
+gains,s,h,"",,,"List of gain maps"
+scales,s,h,"",,,"List of image intensity scale factors
+
+# Reference Image(s)"
+rimages,f,h,,,,"List of reference images"
+rmasks,s,h,"!BPM",,,"List of reference bad pixel masks"
+rskys,s,h,"",,,"List of reference skys"
+rsigmas,s,h,"",,,"List of reference sky sigmas"
+rexps,s,h,"",,,"List of reference exposure maps"
+rscales,s,h,"",,,"List of reference intensity scale factors
+
+# Output"
+objmasks,f,a,,,,"List of output object masks"
+catalogs,f,a,,,,"List of output catalogs"
+catdefs,s,h,"ace$lib/catdef.dat",,,"List of catalog definitions"
+logfiles,s,h,"STDOUT",,,"List of log files
+
+# Sky"
+skytype,s,h,"block","fit|block",,"Type of sky estimation
+
+# Sky Fitting"
+fitstep,i,h,100,1,,"Line step for sky sampling"
+fitblk1d,i,h,10,,,"Block average for line fitting"
+fithclip,r,h,2.,,,"High sky clipping during 1D sky estimation"
+fitlclip,r,h,3.,,,"Low sky clippling during 1D sky estimation"
+fitxorder,i,h,1,1,,"Sky fitting x order"
+fityorder,i,h,1,1,,"Sky fitting y order"
+fitxterms,s,h,"half","none|full|half",,"Sky fitting cross terms
+
+# Sky Blocks"
+blkstep,i,h,1,1,,"Line step for sky sampling"
+blksize,i,h,-10,,,"Block size (+=pixels, -=blocks)"
+blknsubblks,i,h,2,1,,"Number of subblocks per axis
+
+# Detection"
+updatesky,b,h,yes,,,"Update sky during detection?"
+convolve,s,h,"block 3 3",,,"Convolution kernel"
+hsigma,r,h,3.,.1,,"Sigma threshold above sky"
+lsigma,r,h,10.,.1,,"Sigma threshold below sky"
+hdetect,b,h,yes,,,"Detect objects above sky?"
+ldetect,b,h,no,,,"Detect objects below sky?"
+neighbors,s,h,"8","4|8",,Neighbor type
+minpix,i,h,6,1,,"Minimum number of pixels in detected objects"
+sigavg,r,h,4.,0.,,"Sigma of mean flux cutoff"
+sigmax,r,h,4.,0.,,"Sigma of maximum pixel"
+bpval,i,h,INDEF,,,"Output bad pixel value"
+rfrac,r,h,0.5,,,"Minimum fraction of reference flux in difference
+
+# Growing"
+ngrow,i,h,2,0,,"Number of grow rings"
+agrow,r,h,2.,0,,"Area grow factor
+
+# Evaluate"
+magzero,s,h,"INDEF",,,"Magnitude zero point"
diff --git a/noao/nproto/ace/display.h b/noao/nproto/ace/display.h
new file mode 100644
index 00000000..fa89a479
--- /dev/null
+++ b/noao/nproto/ace/display.h
@@ -0,0 +1,42 @@
+# Display modes:
+
+define RGB 1 # True color mode
+define FRAME 2 # Single frame mode
+
+# Color selections:
+
+define BLUE 1B # BLUE Select
+define GREEN 2B # GREEN Select
+define RED 4B # RED Select
+define MONO 7B # RED + GREEN + BLUE
+
+# Size limiting parameters.
+
+define MAXCHAN 2
+define SAMPLE_SIZE 600
+
+# If a logarithmic greyscale transformation is desired, the input range Z1:Z2
+# will be mapped into the range 1.0 to 10.0 ** MAXLOG before taking the log
+# to the base 10.
+
+define MAXLOG 3
+
+# The following parameter is used to compare display pixel coordinates for
+# equality. It determines the maximum permissible magnification. The machine
+# epsilon is not used because the computations are nontrivial and accumulation
+# of error is a problem.
+
+define DS_TOL (1E-4)
+
+# These parameters are needed for user defined transfer functions.
+
+define U_MAXPTS 4096
+define U_Z1 0
+define U_Z2 4095
+
+# BPDISPLAY options:
+
+define BPDISPLAY "|none|overlay|interpolate|"
+define BPDNONE 1 # Ignore bad pixel mask
+define BPDOVRLY 2 # Overlay bad pixels
+define BPDINTERP 3 # Interpolate bad pixels
diff --git a/noao/nproto/ace/doc/detect.hlp b/noao/nproto/ace/doc/detect.hlp
new file mode 100644
index 00000000..ac18c675
--- /dev/null
+++ b/noao/nproto/ace/doc/detect.hlp
@@ -0,0 +1,470 @@
+.help detect Sep00 ace
+.ih
+NAME
+detect -- detect and catalog objects in images
+.ih
+SYNOPSIS
+.ih
+USAGE
+detect images objmasks catalogs
+.ih
+PARAMETERS
+.ls images
+List of images containing objects to be detected. The images should generally
+have read and write permission to allow addition of header information.
+However, the task will still run without write access with the consequence
+that the header will not be updated.
+.le
+.ls masks = "!BPM"
+List of bad pixel masks for the images. This may consist of no bad pixel
+mask specified as the empty string "", a single bad pixel mask to apply to
+all images or a list of bad pixel masks which must match the images list.
+Mask names beginning with "!" are image header keywords which point to the
+bad pixel mask.
+.le
+.ls skys = "SKYFIT"
+List of sky images, constant values, sky fit names, or keyword
+indirection. If only one value is specified then it applies to all input
+images otherwise the list must match the images list. Values beginning
+with "!" specify image header keywords containing the image name, constant
+value, or sky fit name to be used. The value is first checked to see
+if an image with that name exists, then if sky fit keywords are in the
+header, and finally if it is a number. Sky fit keywords are formed from
+the sky fit name with two digit sequence numbers and are interpreted as
+surface fit coefficients.
+
+If none of these are found then the value is treated as the sky fit name
+to be used to save sky fitting performed by this task.
+.le
+.ls sigmas = "SKYSIG"
+List of sky sigma images, constant values, sigma fit names, or keyword
+indirection. If only one value is specified then it applies to all input
+images otherwise the list must match the images list. Values beginning
+with "!" specify image header keywords containing the image name, constant
+value, or sigma fit name to be used. The value is first checked to see
+if an image with that name exists, then if sigma fit keywords are in the
+header, and finally if it is a number. Sigma fit keywords are formed from
+the sigma fit name with two digit sequence numbers and are interpreted as
+surface fit coefficients.
+
+If none of these are found then the value is treated as the sigma fit name
+to be used to save sky fitting performed by this task.
+.le
+
+The following parameters specify the output.
+.ls objmasks
+List of output object masks. If no list is given then no object masks
+will be created. Otherwise there must be one object mask name for each
+input image. The object mask name will be recorded in the input image
+header and in any output catalog.
+.le
+.ls catalogs
+List of output catalogs. If no list is given then no catalogs will be
+created. Otherwise there must be one catalog name for each input image.
+The catalog name will be recorded in the input image header and in any
+object mask. The catalog is created as a "table" (see \fBtables\fR
+for information about the tables and general tools to interact with the
+tables). If the name has an explicit ".fits" extension then a FITS binary
+table is created otherwise an IRAF table (".tab" extension) is created.
+.le
+.ls logfiles = "STDOUT"
+List of output log files. If no list is given then no output log information
+will be produced. If only one file is specified it applies to all input
+images otherwise the list of files must match the images list. Note that
+the special name "STDOUT" corresponds to terminal output.
+.le
+
+The following parameters define the initial sky fit determination. This is
+only done if no sky image or sky constant value and sigma image or sigma
+constant value are specified.
+# Sky
+.ls newsky = no
+Determine new sky fit if one already exists? When the specified sky
+corresponds to an existing sky fit (the sky fit coefficients are in the
+image header) then this parameter is used to override that fit with a new
+fit. Otherwise the fit is used and the initial sky fitting is skipped.
+The sky fitting is also skipped if the specified sky is an image or
+constant.
+.le
+.ls nskylines = 100
+Number of sky sample lines to use. This number of lines spread evenly
+through the image are used to determine the initial sky fit.
+.le
+.ls skyblk1d = 10
+Sky block size for 1D sky estimation.
+.le
+.ls skyhclip = 2.
+High sky clipping during 1D sky estimation
+.le
+.ls skylclip = 3.
+Low sky clippling during 1D sky estimation
+.le
+.ls skyxorder = 4
+Sky fitting x order
+.le
+.ls skyyorder = 4
+Sky fitting y order
+.le
+.ls skyxterms = "half" (none|half|full)
+Sky fitting y order
+.le
+
+# Iterated Sky
+.ls skyupdate = no
+Update sky after detection iterations?
+.le
+.ls niterate = 1
+Maximum number of sky iterations
+.le
+.ls skyblk2d = 50
+Sky block size during detection
+.le
+.ls maxskyres = 0.2
+Maximum sky residual for iteration
+.le
+
+# Detection
+.ls convolve = "block 3 3"
+Convolution kernel
+.le
+.ls hsigma = 3.
+Sigma threshold above sky
+.le
+.ls lsigma = 10.
+Sigma threshold below sky
+.le
+.ls hdetect = yes
+Detect objects above sky?
+.le
+.ls ldetect = yes
+Detect objects below sky?
+.le
+.ls minpix = 10
+Minimum number of pixels in detected objects
+.le
+.ls sigavg = 4.
+Sigma of mean flux cutoff
+.le
+.ls sigmax = 4.
+Sigma of maximum pixel
+.le
+.ls bpval = 1
+Output bad pixel value
+.le
+
+# Splitting"
+.ls split = yes
+Split objects?
+.le
+.ls splitmax = INDEF
+Maximum sigma above sky for splitting
+.le
+.ls splitstep = 0.4
+Splitting steps in convolved sigma
+.le
+.ls splitthresh = 5.
+Splitting threshold in sigma
+.le
+.ls sminpix = 10
+Minimum number of pixels in split objects
+.le
+.ls ssigavg = 10.
+Sigma of mean flux cutoff
+.le
+.ls ssigmax = 5.
+Sigma of maximum pixel
+.le
+
+
+# Growing"
+.ls ngrow = 2
+Number of grow rings
+.le
+.ls agrow = 2.
+Area grow factor
+.le
+.ih
+DESCRIPTION
+
+SKY DETERMINATION
+
+A critical part of detecting objects in astronomical images is determining
+the background sky and sky sigma at each point in the image. In the
+following discussion sky means both the mean sky level and the sky sigma.
+\fBDetect\fR provides for either the user to specify the sky or for the
+task to use a sky fitting algorithm. The user may specify a sky either as
+another image or as a constant value. Note that the image name or
+value may be specified either explicitly or with a keyword associated
+with the image.
+
+If the sky is not specified by an image or constant value then a surface
+fit to the sky is used. The surface fit is recorded in the image header as
+a sequence of keywords with a specified name (the keyword prefix which may
+be up to six characters) and two digit sequence number. The values of the
+keywords contain the coefficients of the fit. The the surface fit
+coefficients are defined in the SURFACE FIT section.
+
+Note that it is possible to specify the mean sky and the sky sigma in
+different ways. When one is given as an image or constant and the other
+as a fit. The one given as an image or constant will be kept fixed and
+the fit determination and updating will be done only on the other.
+
+The sky surface fit is computed in two stages. There is an initial
+determination using a subsample of image lines. Then there is an
+optional update of the sky sample during the object detection step.
+The detection step with sky updating may be iterated a specified number
+of times until the maximum difference in the mean sky is less than some
+amount.
+
+INITIAL SKY DETERMINATION
+
+If an existing surface fit is specified then the parameter \fInewsky\fR
+selects whether a new surface fit is to be computed. If the value is "no"
+then the initial sky determination is skipped though the detection update
+may still be selected.
+
+The initial sky fit uses a combination of block averaging to reduce the
+number of points in the fitting, one dimensional line fitting with sigma
+clipping rejection to eliminate objects, and finally fitting a two
+dimensional surface to the set of block averages over all the sample lines
+which cover the image.
+
+The parameter \fInskylines\fR defines the number of sample lines across
+the image to be used. The lines are evenly spaced starting with the
+first line and ending with the last line. The number of lines affects
+how fast the sky estimation is done.
+
+The pixels from the input line are initially all given unit weight. Bad
+pixels identified by the input bad pixel mask are excluded by setting their
+weights to zero. A weighted block average, with the weight of each block
+being the sum of the weights, is computed. The size of the blocks is given
+by the \fIskyblk1d\fR parameter. This is done to speed the fitting by
+reducing the number of points. Note that when all pixels in a block have
+zero weight due to the bad pixel mask or subsequent rejection the weight of
+the composite block average point is zero.
+
+If only one of sky mean and sky sigma quantities is being determined with
+the other quantity given by an input image, constant, or previous fit
+then those values are simple block averaged with the same block size
+to produce sample points for the mean sky or sky sigma. Note that the
+sky sigma of the sample points also requires division by the square root
+of the block size to give the sky sigma per block average point. The
+line fitting described next is then skipped for this quantity.
+
+The weighted one dimensional line fitting to the block averages uses
+Chebyshev polynomials of order given by the \fIskyxorder\fR. Note that
+this order is the number of polynomial terms, which is one higher than the
+maximum power of the polynomial so that a value of 3 corresponds to a
+quadratic polynomial.
+
+When the mean sky is being determined, the line fitting is performed and
+the fitted values at the block centers are evaluated.
+
+When the sky sigma is being determined, the absolute value of the residuals
+relative to the mean sky divided by 0.7979 are computed. A gaussian noise
+distribution will have a mean value of this quantity equal to the sigma of
+the distribution. In other words, the mean of the absolute deviations of a
+gaussian distribution is 0.7979 times sigma. By fitting a function to
+these residual values a position variable estimate of the sky sigma is
+obtained without needing to compute standard deviations over some set of
+points. The fitted values at the block centers are evaluated to give the
+sky sigmas for the block averaged data.
+
+With the set of block averaged data points and estimated mean skys and sky
+sigmas points that deviate by more than the number of sigma given by the
+\fIskyhclip\fR and \fIskylclip\fR parameters are rejected by setting their
+weights to zero. The line fitting is then repeated until no points are
+rejected with a maximum of 10 iterations.
+
+When the iteration completes the block average points for that image line
+are accumulated for a two dimensional surface fit. Note that the weights
+are used to exclude rejected averages and to weight blocks that had fewer
+points due to bad pixels. The surface fit is a two dimensional Chebyshev
+polynomial of orders given by the \fIskyxorder\fR and \fIskyyorder\fR. The
+orders have the same meaning as in the one dimensional polynomial, namely
+the number of terms in powers of x and y. There are also cross terms which
+are a mixture of powers of both x and y. The \fIskyxterms\fR select
+whether to use any cross terms, only cross terms whose total power does not
+exceed the maximum of the pure x and y terms, or all combinations of
+powers.
+
+After all the sample lines are completed the final surface fits are
+computed. The coefficients of the fits are written to the image header
+under the specified sky fit names and the fits are passed on to the
+detection phase. Note that if the input image is read only then the
+fit will not be written to the header but the task continues.
+
+UPDATED TO SKY DURING DETECTION
+
+
+DETECTION
+
+The detection of objects in an image is conceptually quite simple. Each
+pixel is compared against the expected sky at that point and if it is
+more that a specified number of sky sigma above the sky it is a candidate
+object pixels. Candidate object pixels are grouped into objects on the basis
+of being connected along the eight neighboring directions. The candidate
+object is then accepted if it satisfies the criteria of a minimum
+number of pixels, a sufficiently significant maximum pixel, and a sufficiently
+significant flux above sky.
+
+To detect faint objects where individual pixels are not significantly above
+the sky but all pixels taken together are significant a detection filter is
+applied. This consists of applying a convolution function to the image and
+performing the detection described in the previous paragraph on the
+convolved pixels with the sky sigma suitable adjusted for the convolution.
+The convolution acts as an optimizing filter for objects with shapes
+corresponding to the convolution weights. The remaining discussion
+is in terms of the convolved pixel values. The case of no convolution
+can be thought of as a convolution with a delta function though the
+implementation is not done as a convolution for efficiency.
+
+Two other options to the detection are to also find pixels that are
+significantly below sky (using an independent threshold to that used for
+detecting pixels above sky) and form them into "dark" objects and to
+take the remaining pixels that are not significantly above or below the
+sky and use them to define a sky sample for output or for updating the
+initial sky.
+
+We now go into more detail. The background sky and sky sigma against which
+the detection is performed is initially set as described earlier. If desired
+the sky pixels may be accumulated to update the sky. After updating the
+sky the detection step may be repeated using the new sky. This is
+discussed futher when we reach the end of the detection step description.
+
+The convolution is specified by the \fIconvolve\fR parameter. The values for
+this parameter and the definition of the convolution are given in the
+CONVOLUTION DETECTION FILTER section. The input pixel data is convolved
+and the sky sigma is appropriately adjusted.
+
+When the central pixel in the convolution is flagged as a bad pixel by the
+bad pixel mask (any non-zero value is a bad pixels) then the convolved
+value is considered to be a bad pixel. If an output object masks is
+specified the pixel will be marked with the value specified by the
+\fIbpval\fR parameter. The value may be set to not show the bad pixel in
+the object mask, to set all input bad pixels to some value, or to pass the
+input bad pixel value to the object mask. Note that bad pixel masks in the
+object mask must be between 1 and 10 to avoid confusion with the values
+used to identify objects. If other pixels in the convolution are flagged
+as bad pixels they are excluded from the convolution and the
+convolved sky sigma is adjusted but the convolution value is still used
+as a valid image pixel for detection.
+
+The sigma threshold for pixels to be detected as part of an object above
+sky is given by the \fIhsigma\fR. This number is multiplied by the sky
+sigma to get the deviation from sky. As noted earlier the sky sigma is
+for the convolved pixels and the
+
+
+CONVOLUTION DETECTION FILTER
+
+
+The convolution detection filter is specified with the \fIconvolve\fR
+parameter. There is only one convolution that can be specified and it applies
+to all input images in a list. If a null string ("") is specified
+then no convolution is performed. The task has been optimizations for
+this case to avoid treating this as a 1x1 convolution and to avoid extra
+memory allocations required when a convolution is done.
+
+The convolved value at pixel (i,j), denoted I(i,j), within an image of size
+CxL is defined by
+
+.nf
+ I_convolved(i,j) = sum_kl{I_unconvolved(m,n)*W(k,l)} / sum_kl{W(k,l)}
+.fi
+
+where I(m,n) is the unconvolved value at pixel (m,n), W(k,l) are the NX x
+NY (both must be odd) convolution weights, sum_kl is the double sum over k
+and l, and
+
+.nf
+ m' = i + k - (NX+1)/2 for k = 1 to NX
+ n' = j + l - (NY+1)/2 for l = 1 to NY
+
+ m = m' (1<=m'<=C) m = 1-m' (m'<1) m = 2C-m' (m'>C)
+ n = n' (1<=n'<=L) n = 1-n' (n'<1) n = 2L-n' (m'>L)
+.fi
+
+The last two lines represent boundary reflection at the edges of the image.
+
+The sky sigma of a convolved pixel is approximated by
+
+.nf
+ sigma_convolved(i,j) = sigma_unconvolved(i,j) / sum_kl{W(k,l)}
+.fi
+
+In the presence of bad pixels identified by a bad pixel mask the convolution
+weight applied to a bad pixel is set to zero. The sum of the weights
+used to normalize the convolution is then modified from the situation with
+no bad pixels. This will correct the convolved pixel value for the missing
+data and the estimated sky sigma is appropriately larger.
+
+A convolution can be computational slow, especially for larger sizes.
+The implementation of the convolution has been optimized to recognize
+bilinear symmetries or lines which are scaled versions of other lines.
+So if possible such symmetries should be used. The "block", "bilinear",
+and "gauss" special convolutions described below have such symmetries.
+
+There is also an overhead in checking for bad pixels. The convolution
+has an optimization to avoid such checks in the case where no bad pixel
+mask is specified.
+
+The \fIconvolve\fR parameter is a string which can take one of the
+following forms.
+
+.ls ""
+There is no convolution or, equivalently, NX=1, NY=1.
+.le
+.ls @[filename]
+The weights are given in the specified file. The format consists of lines
+of whitespace separated values. The number of values on each line must be
+the same and defines NX and the number of lines defines NY.
+.le
+.ls block [NX] [NY]
+The weights are all the same and the convolution size is given by the
+two numbers following the word "block".
+.le
+.ls bilinear [NX] [NY]
+The weights are the bilinear matrix product of triangular one dimensional
+matrices of sizes given by the two numbers following the word "bilinear".
+The weights are described by the matrix product relation
+
+.nf
+ [1 ... (NX+1)/2 ... 1] * Transpose{[1 ... (NY+2)/2 ... 1]}
+.fi
+
+For example for NX=5, and NY=3 the weights would be
+
+.nf
+ 1 2 3 2 1
+ 2 4 6 4 2
+ 1 2 3 2 1
+.fi
+.le
+.ls gauss [NX] [NY] [SX] [SY]
+The weights are bidimensional gaussian values on a grid of size NX by NY
+with sigma values SX and SY (real numbers) in units of pixel spacing.
+.le
+.ls [W(1,1)] ... [W(NX,1)], ..., [W(1,NY)] ... [W(NX,NY)]
+The weights are specified as a string of real values. The values are
+whitespace separated within each line and the lines are delimited by
+comma. For example
+
+.nf
+ 1 2 1
+ 1 2 1, 2 3 2, 1 2 1 ==> 2 3 2
+ 1 2 1
+.le
+
+When a logfile is defined the weights are included in the log output.
+
+
+OBJECT MASKS
+
+.ih
+EXAMPLES
+.ih
+REVISIONS
+.ih
+SEE ALSO
+.endhelp
diff --git a/noao/nproto/ace/doc/installation.hlp b/noao/nproto/ace/doc/installation.hlp
new file mode 100644
index 00000000..c399ad4c
--- /dev/null
+++ b/noao/nproto/ace/doc/installation.hlp
@@ -0,0 +1,208 @@
+.help installation Jan01 ace
+
+.ce
+\fBACE: Astronomical Cataloging Environment\fR
+.ce
+Release Notes and Installation Instructions
+
+.sh
+SUMMARY
+The ACE external package is used to catalog objects in images and manipulate
+the catalogs.
+
+.sh
+RELEASE INFORMATION
+The following summary only highlights the major changes. There will also
+be minor changes and bug fixes.
+
+.ls V0.2: January 27, 2001
+Alpha test version.
+.le
+.sh
+INSTALLATION INSTRUCTIONS
+Installation of this external package consists of obtaining the files,
+creating a directory containing the package, compiling the executables or
+installing precompiled executables, and defining the environment to load
+and run the package. The package may be
+installed for a site or as a personal installation. If you need help with
+these installation instructions contact iraf@noao.edu or call the IRAF
+HOTLINE at 520-318-8160.
+.ls [arch]
+In the following steps you will need to know the IRAF architecture
+identifier for your IRAF installation. This identifier is similar to the
+host operating system type. The identifiers are things like "ssun" for
+Solaris, "alpha" for Dec Alpha, and "linux" or "redhat" for most Linux
+systems. The IRAF architecture identifier is defined when you run IRAF.
+Start the CL and then type
+
+.nf
+ cl> show arch
+ .ssun
+.fi
+
+This is the value you need to know without the leading '.'; i.e. the
+IRAF architecture is "ssun" in the above example.
+.le
+.ls [1-site]
+If you are installing the package for site use, login as IRAF
+and edit the IRAF file defining the packages.
+
+.nf
+ % cd $hlib
+.fi
+
+Define the environment variable ace to be the pathname to
+the ace package root directory. The '$'
+character must be escaped in the VMS pathname and UNIX pathnames must be
+terminated with a '/'. Edit extern.pkg to include the following.
+
+.nf
+ reset ace = /local/ace/
+ task ace.pkg = ace$ace.cl
+.fi
+
+Near the end of the hlib$extern.pkg file, update the definition of
+helpdb so it includes the ace help database, copying the syntax
+already used in the string. Add this line before the line
+containing a closing quote:
+
+.nf
+ ,ace$lib/helpdb.mip\
+.fi
+.le
+.ls [1-personal]
+If you are installing the package for personal use define a host
+environment variable with the pathname of the directory where the package
+will be located (needed in order to build the package from the source
+code). Note that pathnames must end with '/'. For example:
+
+.nf
+ % setenv ace /local/ace/
+.fi
+
+In your login.cl or loginuser.cl file make the following definitions
+somewhere before the "keep" statement.
+
+.nf
+ reset ace = /local/ace/
+ task ace.pkg = ace$ace.cl
+ printf ("reset helpdb=%s,ace$lib/helpdb.mip\nkeep\n",
+ envget("helpdb")) | cl
+ flpr
+.fi
+
+If you will be compiling the package, as opposed to installing a binary
+distribution, then you need to define various environment variables.
+The following is for Unix/csh which is the main supported environment.
+
+.nf
+ # Example
+ % setenv iraf /iraf/iraf/ # Path to IRAF root (example)
+ % source $iraf/unix/hlib/irafuser.csh # Define rest of environment
+ % setenv IRAFARCH ssun # IRAF architecture
+.fi
+
+where you need to supply the appropriate path to the IRAF installation root
+in the first step and the IRAF architecture identifier for your machine
+in the last step.
+.le
+.ls [2]
+Login into IRAF. Create a directory to contain the package files and the
+instrument database files. These directory should be outside the standard
+IRAF directory tree.
+
+.nf
+ cl> mkdir ace$
+ cl> cd ace
+.fi
+.le
+.ls [3]
+The package is distributed as a tar archive for the
+sources and, as an optional convenience, a tar archive of the executables
+for select host computers. Note that IRAF includes a tar reader. The tar
+file(s) are most commonly obtained via anonymous ftp. Below is an example
+from a Unix machine where the compressed files have the ".Z" extension.
+Files with ".gz" or ".tgz" can be handled similarly.
+
+.nf
+ cl> ftp iraf.noao.edu (140.252.1.1)
+ login: anonymous
+ password: [your email address]
+ ftp> cd iraf/extern
+ ftp> get ace.readme
+ ftp> binary
+ ftp> get ace.tar.Z
+ ftp> get ace-bin.<arch>.Z (optional)
+ ftp> quit
+ cl> !uncompress ace.tar
+ cl> !uncompress ace-bin.<arch> (optional)
+.fi
+
+The readme file contains these instructions. The <arch> in the
+optional executable distribution is replaced by the IRAF architecture
+identification for your computer.
+
+Upon request the tar file(s) may be otained on tape for a service
+charge. In this case you would mount the tape use rtar to extract
+the tar files.
+.le
+.ls [4]
+Extract the source files from the tar archive using 'rtar".
+
+.nf
+ cl> softools
+ so> rtar -xrf ace.tar
+ so> bye
+.fi
+
+On some systems, an error message will appear ("Copy 'bin.generic'
+to './bin fails") which can be ignored.
+Sites should leave the symbolic link 'bin' in the package root
+directory pointing to 'bin.generic' but can delete any of the
+bin.<arch> directories that won't be used. If there is no binary
+directory for the system you are installing it will be created
+when the package is compiled later or when the binaries are installed.
+
+If the binary executables have been obtained these are now extracted
+into the appropriate bin.<arch> directory.
+
+.nf
+ # Example of sparc installation.
+ cl> cd ace
+ cl> rtar -xrf ace-bin.sparc # Creates bin.sparc directory
+.fi
+
+The various tar file can be deleted once they have been
+successfully installed.
+.ls [5]
+For a source installation you now have to build the package
+executable(s). The "tables" package must be installed first if not
+already available. First you configure the package for the particular
+architecture.
+
+.nf
+ cl> cd ace
+ cl> mkpkg <arch> # Substitute sparc, ssun, alpha, etc.
+.fi
+
+This will change the bin link from bin.generic to bin.<arch>. The binary
+directory will be created if not present. If an error occurs in setting
+the architecture then you may need to add an entry to the file "mkpkg".
+Just follow the examples in the file.
+
+To create the executables and move them to the binary directory
+
+.nf
+ cl> mkpkg -p ace # build executables
+ cl> mkpkg generic # optionally restore generic setting
+.fi
+
+Check for errors. If the executables are not moved to the binary directory
+then step [1] to define the path for the package was not done correctly.
+The last step restores the package to a generic configuration. This is not
+necessary if you will only have one architecture for the package.
+.le
+
+This should complete the installation. You can now load the package
+and begin testing and use.
+.endhelp
diff --git a/noao/nproto/ace/doc/objmasks.hlp b/noao/nproto/ace/doc/objmasks.hlp
new file mode 100644
index 00000000..1c35c4c9
--- /dev/null
+++ b/noao/nproto/ace/doc/objmasks.hlp
@@ -0,0 +1,710 @@
+.help objmasks Jan02 nproto
+.ih
+NAME
+objmasks -- detect objects in images and create masks and sky maps
+.ih
+SYNOPSIS
+.ih
+USAGE
+objmasks images objmasks skys
+.ih
+PARAMETERS
+.ls images
+List of images or multiextension files for which object masks are desired.
+.le
+.ls objmasks
+List of object masks to be created. This list must match the input list.
+Multiextension input files will produce multiextension mask files. If the
+input image is writable, the name of the created mask will recorded in the
+image header. Note that it is possible to specify a null image to
+not produce an output mask. This might be done if the background sky
+or sky sigma maps are desired or to just see the log information.
+.le
+
+.ls omtype = "numbers" (boolean|numbers|colors|all)
+The type of encoding for the object mask values. In all cases non-object pixels
+(that is background) have mask values of zero. The choices for the mask
+values are "boolean", "numbers", "colors", and "all". These are described
+in the \fIOutput Data\fR section.
+.le
+.ls skys = "", sigmas = ""
+Optional lists of input or output sky and sigma maps. Maps are either
+constant values or images which are interpolated to the size of the input
+images. If a list is given it must match the input \fIimages\fR list.
+If constant values or existing maps are specified then those are used
+without change. If a new filename is given then an output file is created
+with the values computed by the task. Multiextension input images create
+or apply the same extension names to the specified sky or sigma files.
+Constant input values apply to all extensions. The sigma values are
+per single input image pixel.
+.le
+.ls masks = "!BPM"
+List of bad pixel masks for the input images. Non-zero masks values are
+ignored in the object detection and are passed on to the output object
+masks based on the \fIomtype\fR parameter. An empty list applies no bad
+pixel mask, a single mask applies to all input images, and a matching
+list matches the masks with the input image. A mask is specified by a
+filename or by reference to a filename given by the value of a header
+keyword in the input image. A header keyword reference is made with the
+syntax "!<keyword>" where <keyword> is the desired keyword with case
+ignored. For multiextension files the input masks may be either a
+multiextension file with matching extension names or a directory of
+pixel list files with the extension names as filenames.
+.le
+.ls extnames = ""
+Extensions to select from multiextension files. A null string matches all
+extension names. Otherwise the parameter is a comma separated list of
+patterns that match the entire extension name. Thus, an explicit list of
+extension names may be specified or the pattern matching characters '?' for
+any character or '[]' for a set of characters may be used. The set may
+include ranges in ascii order by using hyphens; i.e. 1-3 matches the
+characters 1, 2, and 3.
+.le
+.ls logfiles = "STDOUT"
+List of output log files. If no list is given then no output log information
+will be produced. If only one file is specified it applies to all input
+images otherwise the list of files must match the images list. Note that
+the special name "STDOUT" corresponds to terminal output.
+.le
+
+.ls blkstep = 1
+The mean and sigma of the background or sky pixels are determined in a
+first pass through the image. If \fIblkstep\fR is one all lines are used.
+To skip lines in order to speed up this computation, the parameter may be
+set to a larger value to define the increment between lines. However, the
+task will enforce a preset minimum number to insure a sufficient sample.
+.le
+.ls blksize = -10
+The background mean sky and sky sigma are determined in a set of square
+blocks from which the values are linearly interpolated to each point in the
+input image. The size of the blocks may be specified as a number of blocks
+spanning the smaller image dimension by using a negative integer value.
+Or the size may be specified as the number of pixels across a block.
+The task will enforce a preset minimum number of pixels per block which may
+require using bigger blocks than specified. The background determination
+algorithm is described further in the "Background Determination" section.
+.le
+
+.ls convolve = "block 3 3"
+Convolution filter to be applied prior to threshold detection. The
+convolution filter is defined by a set of weights in a 2D array. These
+may be specified in files or with certain forms given by special strings.
+The options are described in the "Convolution Filter" section.
+.le
+.ls hsigma = 3., lsigma = 10.
+Object pixels are identified by sigma thresholds about the mean background
+based on the estimated background sigma at each point in the image.
+The sigma factors are specified in terms of the "per pixel" sigma before
+convolution. The \fIhsigma\fR value is the "high" or above background
+limit and the \fIlsigma\fR value is the "low" or below background limit.
+Typically detections are one-sided, such as detecting objects above
+the background, and so the thresholds need not be equal.
+.le
+.ls hdetect = yes, ldetect = no
+Identify objects as pixels which are above the background (\fIhdetect\fR)
+and below the background (\fIldetect\fR)? If objects are detected but the
+corresponding parameter is no then the output mask will not include those
+objects.
+.le
+.ls neighbors = "8" (8|4)
+The threshold selected pixels are associated with other neighboring pixels to
+form an object. The criterion for a neighbor being part of the
+same object is defined by this parameter. The choices are "8" for
+pixels touching in any of the 8 directions or "4" to identify neighbors
+as only horizontal or vertically adjacent.
+.le
+.ls minpix = 6
+The minimum number of neighboring pixels which define an acceptable object.
+.le
+.ls ngrow = 2, agrow = 2.
+After an object is identified as a set of threshold detected pixels,
+additional neighboring pixels may be added to the object. This allows
+expanding the object into the faint wings of the light distribution. The
+additional pixels are those which touch the boundary pixels. Pixels are
+added in multiple passes, each time extending the previous boundary. The
+parameter \fIngrow\fR (an integer value) defines the maximum number of
+boundary extensions. The parameter \fIagrow\fR (a real value) specifies
+the maximum increase in area (number of pixels) from the original
+detection.
+.le
+.ih
+DESCRIPTION
+\fBOBJMASKS\fR is a task for creating masks covering objects in images.
+An optional secondary product of this task is to produce background
+and sigma maps. Objects are identified by threshold sigma detection.
+These object masks may be used by other applications to exclude the object
+data or focus on the objects. The detection consists of determining a
+smooth, spatially variable mean background and background sigma (if no
+input maps are provided), convolving the data by an optional filter to
+optimize detection of faint sources, collecting pixels satisfying the
+detection thresholds, assigning neighboring pixels to a common object,
+applying a minimum number of pixels test to the objects, and growing
+objects to extend into the wings of the object light distribution.
+The last step is writing out the identified object pixels as a mask.
+
+1. Input Data
+
+The input data consists of one or more 2D images. The images are assumed
+to contain a moderately smooth background and multiple sources or
+objects. This task is most useful for images with large numbers of small
+sources rather than one large object such as a nearby galaxy. The input
+images, specified by the \fIimages\fR parameter, may be individual images
+(which includes images selected from multiextension files as explicit
+image extensions) or multiextension files specified by a root filename. In
+the latter case the image extension names selected by the \fIextnames\fR
+parameter are used.
+
+Background means and sigmas (specified per image pixels) may be specified
+by "maps". These may be constant numerical values or images. The map
+images will be linearly interpolated to the size of the input images.
+For multi-extension input data, constant map values apply to all extensions
+and maps are also multiextension files with map images having the same
+extension names.
+
+Bad pixel masks may be associated with the input images to
+exclude pixels from the background and object determinations. These
+bad pixels are also included in the output object masks. The bad pixel
+masks are specified by the \fImasks\fR parameter. This parameter may
+identify a mask by a filename or a keyword. A single mask may be
+specified to apply to all images or a matching list of masks may be
+given.
+
+The masks are in one of the supported mask formats. As of IRAF V2.12 this
+includes pixel list (.pl) files and FITS "type=mask" extensions. When the
+input files are multiextension files, the selected extension names are
+appended to the specified mask filename to select masks with the same
+extension name. If a mask file of the form "name[ext]" is not found
+the task will treat the filename as a directory of pixel list files and
+select the pixel list file with the extension name; i.e. "name/ext.pl".
+
+2. Output Data
+
+The output of this task are object masks, sky maps, sigma maps, and log
+information. The output object masks default to mask type extensions. If an
+extension name is not specified explicitly the default extension name
+"pl" is created. To select a pixel list output format an explicit ".pl"
+extension must be used.
+
+When the input data are multiextension files, the output masks, mean sky
+maps, and sky sigma maps will be multiextension files with the specified
+rootnames and the same extension name as the input.
+
+The output mask values identify non-object pixels with zero. The non-zero
+values are encoded as selected by the \fIomtype\fR parameter. The choices
+are:
+
+.ls "boolean"
+All object and bad pixels have a mask value of one; i.e. the output masks
+consists only of the values 0 and 1.
+.le
+.ls "numbers"
+Input bad pixels values between 1 and 10 preserve their value and all
+other input mask values are mapped to 10. The object mask pixels have
+object numbers starting with 11. The object numbers are assigned by
+the task (roughly in order from the first line to the last line) and
+all pixels from a single object have the same unique object number.
+.le
+.ls "colors"
+Input bad pixels are mapped to output values of one. The object numbers
+are modulo 8 plus 2; i.e. values between 2 and 9. The purpose of this
+numbering is to allow mapping to the nine standard display colors for an
+interesting overlay with the \fBdisplay\fR task and "ocolors='+203'".
+.le
+.ls "all"
+This is the same as "numbers" except that bits 24 to 27 in the mask values
+are used for various purposes. In particular bit 24 is set for the boundary
+pixels. This numbering will be used in the future by special tasks.
+.le
+
+Output mean sky and sky sigma maps consist of the mean and sigma values
+in blocks as described in the "Background Determination" section.
+Therefore, the size of the map images are smaller than the input data images.
+These maps need to be interpolated to the size of the input image
+to obtain the values used for particular pixels in the data images.
+This interpolation expansion is done automatically by some tasks such
+as \fBmscred.rmfringe\fR.
+
+The log output provides information about the files, the phase of the
+processing, some of the parameters, and the convolution filter weights.
+The output begins with the task identifier ACE. This is because this
+prototype task is a first release piece of a major package called ACE
+(Astronomical Cataloging Environment), which is under development.
+
+3. Background Determination
+
+Detection of sources in an image begins with determining the background.
+By this we mean estimating the probability distribution of the background
+pixel values at every pixel in the image. In practice we only estimate
+the central value and width and assume a normal distribution for evaluating
+the significance of deviations from the central value. Since we normally
+won't have a sample of values at each pixel the distribution is
+determined from a sample of nearby pixels.
+
+In this discussion the central value of a distribution is denoted by <I>.
+It is estimated by the mean or mode of the sample. The width of the
+distribution about <I> is denoted by <S> and is estimated by the absolute
+mean residual converted to the standard deviation of a normal distribution
+with the same absolute mean residual. The normal deviation of a value I
+from the distribution is defined as R = (I - <I>) / <S>.
+
+The background may be specified by input maps for one or both of the
+background quantities. The maps may be constant values which apply
+to all pixels or a grid of values given in an image which are linearly
+interpolated to the full size of the input data. For those quantities
+which are not input the following algorithm is used for computing
+a map. The maps may be output and used as a product of this task.
+
+The background and/or sigma are estimated in two initial passes through the
+data. The first pass algorithm fits linear functions to a subsample of
+lines using sigma clipping iteration to eliminate objects. The subsample
+is used to speed up the algorithm and is reasonable since only linear
+functions are used. Each sample line is block averaged in blocks of 10
+pixels and a linear function is fit by least squares to obtain an estimate
+for <I> along the line. The fitting weights are the number of good pixels
+in each block average after elimination of bad pixels specified by the
+user in a bad pixel mask. The absolute values of the residuals are also
+fit to produce a constant function for <S>.
+
+To exclude objects from affecting these estimates the fitting is iterated
+using sigma clipping rejection on the normal deviations R. In the
+first iteration the fitting function for <S> is a constant and in
+subsequent steps a linear fit is used. When the sigma clipping iteration
+rejects no more data, the remaining block averages, absolute residuals, and
+weights are used to fit a 2D plane for both <I> and <S>. The <S> surface
+is a constant in order to avoid potential negative sigma values.
+
+This first pass algorithm is fast and produces good estimates for the
+planar approximation to the background. The second pass divides the image
+into large, equal sized blocks, as specified by the \fIblksize\fR
+parameter, and estimates <I> and <S> in each block. The size of the blocks
+needs to be large enough to give good estimates of the statistics though
+small enough to handle the scale of variations in the sky. Each block is
+divided into four subblocks for independent estimates which are then
+combined into a final value for the block. As with the first pass, the
+second pass can be speeded up by using a subsample of lines (parameter
+\fBblkstep\fR) provided some minimum number of lines per subblock is
+maintained.
+
+The background estimates in each subblock are made using histograms of the
+normal deviations R computed relative to the first pass estimates of <I>
+and <S>. When pixels are added into the histogram the <I> and <S> used to
+compute R are accumulated into means of these quantities in order
+to convert estimates from the normalized deviation histogram back into data
+values. The histograms are truncated at +/-2.5 and have bin widths
+determined by requiring a specified average bin population based on the
+number of pixels in the block. Typically the bin population is of order
+500. The histogram truncation is essentially an object-background
+discrimination.
+
+When all the pixels in a subblock have been accumulated, new estimates of
+<I> and <S> are computed. If the number of pixels in the histogram is
+less than two-thirds of the subblock pixels the estimates are set to be
+indefinite. This flags the subblock as too contaminated by objects to be
+used. All subblock neighbors, which may cross the full block boundaries,
+are also rejected to minimize contamination by the wings of big galaxies
+and very bright stars.
+
+If the histogram has enough pixels, the bin populations are squared to
+emphasize the peak of the distribution and reduce the effects of the
+truncated edges of the histogram. Because of noise and the fine binning of
+the histogram, a simple mode cannot be used and squaring the bin numbers
+helps to approach the mode with a centroid. Squaring the bin values and
+then computing the centroid can also be thought of as a weighted centroid.
+
+Generally a mode is considered the best estimate to use for the central
+value <I> of the sky distribution. But it is unclear how to best estimate
+the mode without an infinite number of pixels. One could do something like
+fit a parabola to the histogram peak. But instead we use the empirical
+relation for a skewed distribution between the mean, mode, and median;
+<I>=mean-3*(mean-median). The mean is the weighted centroid and the median
+is obtained numerically from the histogram using linear interpolation to
+get a subbin value.
+
+The <S> values are obtained from the absolute mean residual of the
+unweighted histogram about the previously derived central value <I> of the
+histogram. The conversion to a standard deviation is made by computing the
+ratio between the standard deviation and mean absolute deviation of a
+Gaussian distribution. The standard value over the entire distribution
+cannot be used because the histogram is truncated. However, it is easy to
+numerically compute the ratio with the same truncation.
+
+Once <I> and <S> are obtained in bin numbers it is converted to data
+values by using the mean and sigma of the input pixel values used
+to create the histogram.
+
+The averages of the subblock <I> and <S> values which are not indeterminate
+in each block are computed. If any of the full blocks are indeterminate
+when all the subblocks have been eliminated as contaminated, values are
+obtained for them by interpolation from nearby blocks. The block values
+are then linearly interpolated to get background values for every
+pixel in the input image.
+
+Note that the background pixels used in the block algorithm before
+detection are derived by simple sigma clipping of the histogram values
+around the planar background. If an output map for either the mean
+values or the sigmas is specified then during the object detection stage
+the background and sigmas are updated using the detected sky pixels about
+the initial block sampled background. This is a more sensitive selection
+of sky pixels since convolution filtering can exclude pixels from faint
+objects and the wings of all objects. The new set of sky pixels are
+accumulated and used in the same way as described earlier.
+
+4. Convolution Filters
+
+In order to improve the detection of faint sources dominated by the
+background noise, the input data may be convolved to produce filtered
+values in which the noise has been suppressed. The threshold detection
+is then performed on the filtered data values.
+
+The convolution detection filter is specified with the \fIconvolve\fR
+parameter. There is only one convolution that can be specified and it
+applies to all input images in a list. If a null string ("") is specified
+then no convolution is performed. The task has been optimizations for this
+case to avoid treating this as a 1x1 convolution and to avoid extra memory
+allocations required when a convolution is done.
+
+The convolved value at pixel (i,j), denoted I'(i,j), is defined by
+
+.nf
+ I'(i,j) = sum_kl{I(m,n)*W(k,l)} / sum_kl{W(k,l)}
+.fi
+
+where I(m,n) is the unconvolved value at pixel (m,n), W(k,l) are the NX x
+NY (both must be odd) convolution weights, sum_kl is the double sum over k
+and l, and
+
+.nf
+ m' = i + k - (NX+1)/2 for k = 1 to NX
+ n' = j + l - (NY+1)/2 for l = 1 to NY
+
+ m = m' (1<=m'<=C) m = 1-m' (m'<1) m = 2C-m' (m'>C)
+ n = n' (1<=n'<=L) n = 1-n' (n'<1) n = 2L-n' (m'>L)
+.fi
+
+The size of the image is C x L. The last two lines represent boundary
+reflection at the edges of the image.
+
+The sky sigma of a convolved pixel is approximated by
+
+.nf
+ sigma'(i,j) = sigma(i,j) / sum_kl{W(k,l)}
+.fi
+
+In the presence of bad pixels specified in the bad pixel mask the
+convolution weight applied to a bad pixel is set to zero. If the central
+pixel is bad then the convolved value is also considered to be bad. The
+sum of the weights used to normalize the convolution is then modified from
+the situation with no bad pixels. This will correct the convolved pixel
+value for the missing data and the estimated sky sigma is appropriately
+larger. Since there is an overhead in checking for bad pixels the
+convolution has an optimization to avoid such checks in the case where no
+bad pixel mask is specified.
+
+A convolution can be computational slow, especially for larger convolution
+kernel sizes. The implementation of the convolution has been optimized to
+recognize bilinear symmetries or lines which are scaled versions of other
+lines. So if possible users should chose convolutions with such symmetries
+to be most efficient. The "block", "bilinear", and "gauss" special
+convolutions described below all have such symmetries.
+
+The \fIconvolve\fR parameter is a string with one of the following forms.
+
+.ls ""
+There is no convolution or, equivalently, NX=1, NY=1.
+.le
+.ls @[filename]
+The weights are given in the specified file. The format consists of lines
+of whitespace separated values. The number of values on each line must be
+the same and defines NX and the number of lines defines NY.
+.le
+.ls block [NX] [NY]
+The weights are all the same and the convolution size is given by the
+two numbers following the word "block". This is a moving block average
+filter.
+.le
+.ls bilinear [NX] [NY]
+The weights are the bilinear matrix product of triangular one dimensional
+matrices of sizes given by the two numbers following the word "bilinear".
+The weights are described by the matrix product relation
+
+.nf
+ [1 ... (NX+1)/2 ... 1] * Transpose{[1 ... (NY+2)/2 ... 1]}
+.fi
+
+For example for NX=5, and NY=3 the weights would be
+
+.nf
+ 1 2 3 2 1
+ 2 4 6 4 2
+ 1 2 3 2 1
+.fi
+.le
+.ls gauss [NX] [NY] [SX] [SY]
+The weights are bidimensional gaussian values on a grid of size NX by NY
+with sigma values SX and SY (real numbers) in units of pixel spacing.
+.le
+.ls [W(1,1)] ... [W(NX,1)], ..., [W(1,NY)] ... [W(NX,NY)]
+The weights are specified as a string of real values. The values are
+whitespace separated within each line and the lines are delimited by
+comma. For example
+
+.nf
+ 1 2 1
+ 1 2 1, 2 3 2, 1 2 1 ==> 2 3 2
+ 1 2 1
+.fi
+.le
+
+When a logfile is defined the convolution weights are included in the
+output.
+
+5. Object Detection
+
+The detection of objects in an image is conceptually quite simple once the
+background is known. If an input pixel, before any convolution, is
+identified in the bad pixel mask the output object mask pixel is also
+identified as bad. Otherwise the input data is convolved as described
+previously.
+
+Each convolved pixel is compared against the expected background at that
+point and, if it is more that a specified number of convolution adjusted
+background sigma above (\fIhsigma\fR) or below (\fIlsigma\fR) the
+background, it is identified as a candidate object pixel. Candidate object
+pixels, with the same sense of deviation, are grouped into objects on
+the basis of being connected along the four or eight neighboring directions
+as specified by the \fIneighbor\fR parameter. The candidate object is then
+accepted if it satisfies the minimum number of pixels (\fIminpix\fR) in
+an object and the \fIhdetect\fR or \fIldetect\fR parameter selects that
+type of object. The accepted objects are assigned sequential numbers
+beginning with 11. The object numbers are used, as described in the
+section on the output data, to set the output object mask values.
+
+If an output mean sky or sigma map is requested, the output is that
+updated by the sky pixels identified during the detection.
+
+6. Object Growing
+
+Astronomical objects do not have sharp edges but have light distributions
+that merge into the background. This is due not only to the nature of
+extended sources but to the atmospheric and instrument point spread function
+effects on unresolved sources. In order to include pixels which extend
+away from the threshold detection and contain some amount of light
+apart from the background, the task provides options to extend or grow
+the object boundaries. This is done by making multiple passes where
+pixels which have not been identified as object pixels but which neighbor
+object pixels are assigned to the object which they neighbor in any of
+the eight directions. Each pass can be thought of as adding a ring
+of new pixels following the boundary of the object from the previous
+pass.
+
+When a non-object pixel neighbors two or more object pixels it is
+assigned to the object with the greater "flux". The flux is the sum
+of the pixel value deviations from the background.
+
+The parameter \fIngrow\fR selects the maximum number of growing iterations.
+The parameter \fIagrow\fR selects the maximum fractional increase in
+the number of original detected object pixels. The number of pixels
+is called the "area" of the object. The growing of an object stops
+when either maximum is exceedd at the end of a growing iteration.
+.ih
+EXAMPLES
+1. The following is a test example with default parameters that can be run
+by anyone. An artificial galaxy field image is generated with the task
+\fBmkexample\fR (the \fBartdata\fR package is assumed to already be loaded)
+and a mask is created with \fBobjmasks\fR. The image is displayed with
+the object mask overlayed in colors.
+
+.nf
+ np> mkexample galfield galfield
+ Creating example galfield in image galfield ...
+ np> objmasks omtype=color
+ List of images or MEF files: galfield
+ List of output object masks: gfmask
+ ACE:
+ Image: galfield - Example artificial galaxy field
+ Set sky and sigma:
+ Determine sky and sigma by surface fits:
+ start line = 1, end line = 512, step = 51.1
+ xorder = 2, yorder = 2, xterms = half
+ hclip = 2., lclip = 3.
+ Determine sky and sigma by block statistics:
+ Number of blocks: 5 5
+ Number of pixels per block: 100 100
+ Number of subblocks: 10 10
+ Number of pixels per subblock: 50 50
+ Detect objects:
+ Convolution:
+ 1. 1. 1.
+ 1. 1. 1.
+ 1. 1. 1.
+ 422 objects detected
+ Grow objects: ngrow = 2, agrow = 2.
+ Write object mask: gfmask[pl,type=mask]
+ np> display galfield 1
+ z1=371.5644 z2=455.8792
+ np> display galfield 2 overlay=gfmask[pl] ocolors="+203"
+ z1=371.5644 z2=455.8792
+.fi
+
+2. In the first example there was no input mask. The next example
+creates a new object mask using the first object mask as an input
+"bad pixel mask". While this is not the usual usage of the bad pixel
+mask it does illustrate an interesting option. Note that the mask
+values in the input mask are mapped to an output value of 1 in the
+"colors" output. In this example the output is forced to be a pl
+file by using the explicit extension.
+
+.nf
+ np> objmasks omtype=colors mask=gfmask[pl]
+ List of images or MEF files (galfield):
+ List of output object masks (gfmask): gfmask1.pl
+ ACE:
+ Image: galfield - Example artificial galaxy field
+ Bad pixel mask: gfmask.pl
+ Set sky and sigma:
+ Determine sky and sigma by surface fits:
+ start line = 1, end line = 512, step = 51.1
+ xorder = 2, yorder = 2, xterms = half
+ hclip = 2., lclip = 3.
+ Determine sky and sigma by block statistics:
+ Number of blocks: 5 5
+ Number of pixels per block: 100 100
+ Number of subblocks: 10 10
+ Number of pixels per subblock: 50 50
+ Detect objects:
+ Convolution:
+ 1. 1. 1.
+ 1. 1. 1.
+ 1. 1. 1.
+ 44 objects detected
+ Grow objects: ngrow = 2, agrow = 2.
+ Write object mask: gfmask1.pl
+ np> display galfield 2 overlay=gfmask1 ocolors="+203"
+ z1=371.5644 z2=455.8792
+.fi
+
+3. The next example illustrates use with a multiextension file. The
+example is two realizations of the galfield artificial data.
+
+.nf
+ np> mkexamples galfield mef.fits[im1]
+ Creating example galfield in image mef[im1] ...
+ np> mkexamples galfield mef[im2,append] oseed=2
+ Creating example galfield in image mef[im2,append] ...
+ np> objmasks
+ List of images or MEF files (galfield): mef
+ List of output object masks (gfmask1.pl): mefmask
+ ACE:
+ Image: mef[im1] - Example artificial galaxy field
+ Set sky and sigma:
+ Determine sky and sigma by surface fits:
+ start line = 1, end line = 512, step = 51.1
+ xorder = 2, yorder = 2, xterms = half
+ hclip = 2., lclip = 3.
+ Determine sky and sigma by block statistics:
+ Number of blocks: 5 5
+ Number of pixels per block: 100 100
+ Number of subblocks: 10 10
+ Number of pixels per subblock: 50 50
+ Detect objects:
+ Convolution:
+ 1. 1. 1.
+ 1. 1. 1.
+ 1. 1. 1.
+ 422 objects detected
+ Grow objects: ngrow = 2, agrow = 2.
+ Write object mask: mefmask[im1,append,type=mask]
+ ACE:
+ Image: mef[im2] - Example artificial galaxy field
+ Set sky and sigma:
+ Determine sky and sigma by surface fits:
+ start line = 1, end line = 512, step = 51.1
+ xorder = 2, yorder = 2, xterms = half
+ hclip = 2., lclip = 3.
+ Determine sky and sigma by block statistics:
+ Number of blocks: 5 5
+ Number of pixels per block: 100 100
+ Number of subblocks: 10 10
+ Number of pixels per subblock: 50 50
+ Detect objects:
+ Convolution:
+ 1. 1. 1.
+ 1. 1. 1.
+ 1. 1. 1.
+ 410 objects detected
+ Grow objects: ngrow = 2, agrow = 2.
+ Write object mask: mefmask[im2,append,type=mask]
+ np> display mef[im1] 1 over=mefmask[im1]
+ z1=371.5644 z2=455.8792
+ np> display mef[im2] 2 over=mefmask[im2]
+ z1=371.5666 z2=455.7844
+.fi
+
+4. This example shows outputing the sky information.
+
+.nf
+ np> objmasks galfield gfmask2 sky=gfsky2
+ ACE:
+ Image: galfield - Example artificial galaxy field
+ Set sky and sigma:
+ Determine sky and sigma by surface fits:
+ start line = 1, end line = 512, step = 51.1
+ xorder = 2, yorder = 2, xterms = half
+ hclip = 2., lclip = 3.
+ Determine sky and sigma by block statistics:
+ Number of blocks: 5 5
+ Number of pixels per block: 100 100
+ Number of subblocks: 10 10
+ Number of pixels per subblock: 50 50
+ Write sky map: gfsky2
+ Detect objects:
+ Convolution:
+ 1. 1. 1.
+ 1. 1. 1.
+ 1. 1. 1.
+ 422 objects detected
+ Update sky map: gfsky2
+ Grow objects: ngrow = 2, agrow = 2.
+ Write object mask: gfmask2[pl,append,type=mask]
+ np> imstat gfsky2
+ # IMAGE NPIX MEAN STDDEV MIN MAX
+ gfsky2 25 401.1 0.4397 400.3 401.9
+.fi
+
+5. This examples shows specifying the sky information as constant values.
+In this case we already know that the artificial image has a
+constant background of 400 and a sigma of 10.
+
+.nf
+ np> objmasks galfield gfmask3 sky=400 sigma=10
+ ACE:
+ Image: galfield - Example artificial galaxy field
+ Set sky and sigma:
+ Use constant input sky: 400.
+ Use constant input sigma: 10.
+ Detect objects:
+ Convolution:
+ 1. 1. 1.
+ 1. 1. 1.
+ 1. 1. 1.
+ 432 objects detected
+ Grow objects: ngrow = 2, agrow = 2.
+ Write object mask: gfmask3[pl,append,type=mask]
+.fi
+
+.ih
+REVISIONS
+.le
+.ih
+SEE ALSO
+.endhelp
+266c266
+< fit to produce a function for <S>.
+---
+> fit to produce a constant function for <S>.
+273c273,274
+< weights are used to fit a 2D plane for both <I> and <S>.
+---
+> weights are used to fit a 2D plane for both <I> and <S>. The <S> surface
+> is a constant in order to avoid potential negative sigma values.
+
diff --git a/noao/nproto/ace/edgewts.xNEW b/noao/nproto/ace/edgewts.xNEW
new file mode 100644
index 00000000..fdd8d8dd
--- /dev/null
+++ b/noao/nproto/ace/edgewts.xNEW
@@ -0,0 +1,56 @@
+task test
+
+procedure test ()
+
+double dx, dy, r[11], w[11], clgetd()
+int i, nr
+
+begin
+ dx = clgetd ("dx")
+ dy = clgetd ("dy")
+ nr = 11
+
+ call edgewts (dx, dy, r, w, nr)
+ do i = 1, nr {
+ call eprintf ("%.2f %.4g\n")
+ call pargd (r[i])
+ call pargd (w[i])
+ }
+end
+
+procedure edgewts (dx, dy, r, w, nr)
+
+double dx #I Distance from aperture center to pixel center
+double dy #I Distance from aperture center to pixel center
+double r[nr] #O Aperture radii
+double w[nr] #O Weights
+int nr #O Number of aperture radius points
+
+int i, j, k, n
+double r2, rmin, rmax, dr, a, d, rap2, y2
+
+begin
+ rmin = sqrt ((max(0.,dx-0.6))**2+(max(0.,dy-0.6))**2)
+ rmax = sqrt ((dx+0.6)**2+(dy+0.6)**2)
+ dr = (rmax - rmin) / nr
+ rmin = rmin + dr / 2
+
+ n = 100
+ d = 1.0D0 / (2 * n + 1)
+ a = d * d
+
+ do k = 1, nr {
+ rap2 = (rmin + (k - 1) * dr) ** 2
+ r[k] = sqrt (rap2)
+ w[k] = 0.0D0
+ do j = -n, n {
+ y2 = (dy + j * d) ** 2
+ do i = -n, n {
+ r2 = y2 + (dx + i * d) ** 2
+ if (r2 > rap2)
+ break
+ w[k] = w[k] + a
+ }
+ }
+ }
+end
diff --git a/noao/nproto/ace/evaluate.h b/noao/nproto/ace/evaluate.h
new file mode 100644
index 00000000..e2ccf001
--- /dev/null
+++ b/noao/nproto/ace/evaluate.h
@@ -0,0 +1,6 @@
+# EVALUATE definitions
+
+define EVL_STRLEN 99 # Length of strings
+define EVL_LEN 50 # Parameters structure length
+
+define EVL_MAGZERO Memc[P2C($1+$2-1)] # Magnitude zero point
diff --git a/noao/nproto/ace/evaluate.par b/noao/nproto/ace/evaluate.par
new file mode 100644
index 00000000..0fda4d32
--- /dev/null
+++ b/noao/nproto/ace/evaluate.par
@@ -0,0 +1,32 @@
+# ACEEVALUATE
+
+images,f,a,,,,"List of images"
+incatalogs,s,a,"",,,"List of input catalogs"
+outcatalogs,s,a,"",,,"List of output catalogs"
+objmasks,s,h,"",,,"List of object masks"
+catdefs,s,h,"",,,"List of catalog definitions"
+skys,s,h,"",,,"List of sky maps"
+sigmas,s,h,"",,,"List of sigma maps"
+exps,s,h,"",,,"List of exposure maps"
+gains,s,h,"",,,"List of gain maps"
+logfiles,s,h,"STDOUT",,,"List of log files
+
+# Sky"
+skytype,s,h,"block","fit|block",,"Type of sky estimation
+
+# Sky Fitting"
+fitstep,i,h,100,1,,"Line step for sky sampling"
+fitblk1d,i,h,10,,,"Block average for line fitting"
+fithclip,r,h,2.,,,"High sky clipping during 1D sky estimation"
+fitlclip,r,h,3.,,,"Low sky clippling during 1D sky estimation"
+fitxorder,i,h,1,1,,"Sky fitting x order"
+fityorder,i,h,1,1,,"Sky fitting y order"
+fitxterms,s,h,"half","none|full|half",,"Sky fitting cross terms
+
+# Sky Blocks"
+blkstep,i,h,10,1,,"Line step for sky sampling"
+blksize,i,h,2,,,"Block size (+=pixels, -=blocks)"
+blknsubblks,i,h,3,1,,"Number of subblocks per axis
+
+# Evaluate"
+magzero,s,h,"INDEF",,,"Magnitude zero point"
diff --git a/noao/nproto/ace/evaluate.x b/noao/nproto/ace/evaluate.x
new file mode 100644
index 00000000..c3b5b608
--- /dev/null
+++ b/noao/nproto/ace/evaluate.x
@@ -0,0 +1,641 @@
+include <error.h>
+include <imhdr.h>
+include <pmset.h>
+include "ace.h"
+include "cat.h"
+include "objs.h"
+include "evaluate.h"
+
+
+# EVALUATE -- Evaluate object parameters.
+
+procedure evaluate (evl, cat, im, om, skymap, sigmap, gainmap, expmap, logfd)
+
+pointer evl #I Parameters
+pointer cat #I Catalog structure
+pointer im #I Image pointer
+pointer om #I Object mask pointer
+pointer skymap #I Sky map
+pointer sigmap #I Sigma map
+pointer gainmap #I Gain map
+pointer expmap #I Exposure map
+int logfd #I Logfile
+
+int i, n, c, l, nc, nl, c1, c2, nummax, num, nobjsap
+real x, x2, y, y2, s, s2, f, f2, val, sky, ssig, s2x, s2y
+pointer objs, obj, rlptr
+pointer data, skydata, ssigdata, gaindata, expdata, sigdata
+pointer sp, v, rl, sum_s2x, sum_s2y
+
+int andi(), ori(), ctor()
+real imgetr()
+bool pm_linenotempty()
+errchk salloc, calloc, malloc, evgdata
+
+begin
+ call smark (sp)
+ call salloc (v, PM_MAXDIM, TY_LONG)
+ call salloc (rl, 3+3*IM_LEN(im,1), TY_INT)
+
+ if (logfd != NULL)
+ call fprintf (logfd, " Evaluate objects:\n")
+
+ objs = CAT_OBJS(cat)
+ nummax = CAT_NUMMAX(cat)
+
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+
+ # Allocate work arrays.
+ call salloc (sigdata, nc, TY_REAL)
+ call salloc (sum_s2x, nummax, TY_REAL)
+ call salloc (sum_s2y, nummax, TY_REAL)
+ call aclrr (Memr[sum_s2x], nummax)
+ call aclrr (Memr[sum_s2y], nummax)
+
+ # Initialize isophotal quantities.
+ do i = NUMSTART-1, nummax-1 {
+ obj = Memi[objs+i]
+ if (obj == NULL)
+ next
+ OBJ_NPIX(obj) = 0
+ OBJ_SKY(obj) = 0.
+ OBJ_PEAK(obj) = 0.
+ OBJ_FLUX(obj) = 0.
+ OBJ_X1(obj) = 0.
+ OBJ_Y1(obj) = 0.
+ OBJ_X2(obj) = 0.
+ OBJ_Y2(obj) = 0.
+ OBJ_XY(obj) = 0.
+ OBJ_SIG(obj) = 0.
+ OBJ_ISIGAVG(obj) = 0.
+ OBJ_ISIGAVG2(obj) = INDEFR
+ OBJ_FLUXVAR(obj) = 0.
+ OBJ_XVAR(obj) = 0.
+ OBJ_YVAR(obj) = 0.
+ OBJ_XYCOV(obj) = 0.
+ }
+
+ # Initialize aperture photometry.
+ call evapinit (cat, nobjsap)
+
+ # Get magnitude zero.
+ if (EVL_MAGZERO(evl,1) == '!') {
+ iferr (CAT_MAGZERO(cat) = imgetr (im, EVL_MAGZERO(evl,2))) {
+ call erract (EA_WARN)
+ CAT_MAGZERO(cat) = INDEFR
+ }
+ } else {
+ i = 1
+ if (ctor (EVL_MAGZERO(evl,1), i, CAT_MAGZERO(cat)) == 0)
+ CAT_MAGZERO(cat) = INDEFR
+ }
+ call catputr (cat, "magzero", CAT_MAGZERO(cat))
+
+ # Go through the lines of the image accumulating the image data
+ # into the parameters. The data is read the first time it is
+ # required.
+ Memi[v] = 1
+ do l = 1, nl {
+ Memi[v+1] = l
+ data = NULL
+
+ # Do circular aperture photometry. Check nobjsap to avoid
+ # subroutine call.
+ if (nobjsap > 0)
+ call evapeval (l, im, skymap, sigmap, gainmap, expmap,
+ data, skydata, ssigdata, gaindata, expdata, sigdata)
+
+ # Accumulate object region quantities if there are object
+ # regions in the current line.
+ if (!pm_linenotempty (om, Memi[v]))
+ next
+ call pmglri (om, Memi[v], Memi[rl], 0, nc, 0)
+
+ # Go through each object region.
+ rlptr = rl
+ do i = 2, Memi[rl] {
+ rlptr = rlptr + 3
+ c1 = Memi[rlptr]
+ c2 = c1 + Memi[rlptr+1] - 1
+ num = MNUM(Memi[rlptr+2])
+
+ # Do all unevaluated objects and their parents.
+ while (num >= NUMSTART) {
+ if (data == NULL)
+ call evgdata (l, im, skymap, sigmap, gainmap, expmap,
+ data, skydata, ssigdata, gaindata, expdata, sigdata)
+
+ obj = Memi[objs+num-1]
+ if (obj == NULL)
+ break
+
+ if (OBJ_NPIX(obj) == 0) {
+ val = Memr[data+c1-1]
+ sky = Memr[skydata+c1-1]
+ ssig = Memr[ssigdata+c1-1]
+
+ OBJ_XMIN(obj) = c1
+ OBJ_XMAX(obj) = c1
+ OBJ_YMIN(obj) = l
+ OBJ_YMAX(obj) = l
+ OBJ_ISIGMAX(obj) = (val - sky) / ssig
+ }
+
+ s2x = Memr[sum_s2x+num-1]
+ s2y = Memr[sum_s2y+num-1]
+ do c = c1, c2 {
+ val = Memr[data+c-1]
+ sky = Memr[skydata+c-1]
+ ssig = Memr[ssigdata+c-1]
+ s = Memr[sigdata+c-1]
+
+ x = c - OBJ_XMIN(obj)
+ y = l - OBJ_YMIN(obj)
+ x2 = x * x
+ y2 = y * y
+ s2 = s * s
+
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ OBJ_SKY(obj) = OBJ_SKY(obj) + sky
+ OBJ_SIG(obj) = OBJ_SIG(obj) + ssig
+ val = val - sky
+ if (val > OBJ_PEAK(obj))
+ OBJ_PEAK(obj) = val
+ OBJ_FLUX(obj) = OBJ_FLUX(obj) + val
+ OBJ_FLUXVAR(obj) = OBJ_FLUXVAR(obj) + s2
+
+ OBJ_XMIN(obj) = min (OBJ_XMIN(obj), c)
+ OBJ_XMAX(obj) = max (OBJ_XMAX(obj), c)
+ OBJ_X1(obj) = OBJ_X1(obj) + x * val
+ OBJ_X2(obj) = OBJ_X2(obj) + x2 * val
+ OBJ_XVAR(obj) = OBJ_XVAR(obj) + x2 * s2
+ s2x = s2x + x * s2
+
+ OBJ_YMIN(obj) = min (OBJ_YMIN(obj), l)
+ OBJ_YMAX(obj) = max (OBJ_YMAX(obj), l)
+ OBJ_Y1(obj) = OBJ_Y1(obj) + y * val
+ OBJ_Y2(obj) = OBJ_Y2(obj) + y2 * val
+ OBJ_YVAR(obj) = OBJ_YVAR(obj) + y2 * s2
+ s2y = s2y + y * s2
+
+ OBJ_XY(obj) = OBJ_XY(obj) + x * y * val
+ OBJ_XYCOV(obj) = OBJ_XYCOV(obj) + x * y * s2
+
+ val = val / ssig
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) + val
+ OBJ_ISIGMAX(obj) = max (OBJ_ISIGMAX(obj), val)
+
+ }
+ Memr[sum_s2x+num-1] = s2x
+ Memr[sum_s2y+num-1] = s2y
+
+ num = OBJ_PNUM(obj)
+ }
+ }
+ }
+
+ # Finish up the evaluations.
+ do i = NUMSTART-1, nummax-1 {
+ obj = Memi[objs+i]
+ if (obj == NULL)
+ next
+ n = OBJ_NPIX(obj)
+ if (n > 0) {
+ OBJ_SKY(obj) = OBJ_SKY(obj) / n
+ f = OBJ_FLUX(obj)
+ if (f > 0.) {
+ f2 = f * f
+ x = OBJ_X1(obj) / f
+ s2x = Memr[sum_s2x+i]
+ s2y = Memr[sum_s2y+i]
+
+ OBJ_X1(obj) = x + OBJ_XMIN(obj)
+ OBJ_X2(obj) = OBJ_X2(obj) / f - x * x
+ OBJ_XVAR(obj) = (OBJ_XVAR(obj) - 2 * x * s2x +
+ x * x * OBJ_FLUXVAR(obj)) / f2
+
+ y = OBJ_Y1(obj) / f
+ OBJ_Y1(obj) = y + OBJ_YMIN(obj)
+ OBJ_Y2(obj) = OBJ_Y2(obj) / f - y * y
+ OBJ_YVAR(obj) = (OBJ_YVAR(obj) - 2 * y * s2y +
+ y * y * OBJ_FLUXVAR(obj)) / f2
+
+ OBJ_XY(obj) = OBJ_XY(obj) / f - x * y
+ OBJ_XYCOV(obj) = (OBJ_XYCOV(obj) - x * s2x -
+ y * s2y + x * y * OBJ_FLUXVAR(obj)) / f2
+
+ if (IS_INDEFR(OBJ_XAP(obj)))
+ OBJ_XAP(obj) = OBJ_X1(obj)
+ if (IS_INDEFR(OBJ_YAP(obj)))
+ OBJ_YAP(obj) = OBJ_Y1(obj)
+ } else {
+ OBJ_X1(obj) = INDEFR
+ OBJ_Y1(obj) = INDEFR
+ OBJ_X2(obj) = INDEFR
+ OBJ_Y2(obj) = INDEFR
+ OBJ_XY(obj) = INDEFR
+ OBJ_XVAR(obj) = INDEFR
+ OBJ_YVAR(obj) = INDEFR
+ OBJ_XYCOV(obj) = INDEFR
+ OBJ_FLUXVAR(obj) = INDEFR
+ }
+ if (OBJ_PEAK(obj) == 0.)
+ OBJ_PEAK(obj) = INDEFR
+ OBJ_SIG(obj) = OBJ_SIG(obj) / n
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) / sqrt(real(n))
+ }
+ SETFLAG (obj, OBJ_EVAL)
+ }
+
+ # Do aperture photometry if we had to wait for the aperture centers
+ # to be defined.
+ if (nobjsap == 0) {
+ call evapinit (cat, nobjsap)
+ if (nobjsap > 0) {
+ Memi[v] = 1
+ do l = 1, nl {
+ Memi[v+1] = l
+ data = NULL
+ call evapeval (l, im, skymap, sigmap, gainmap, expmap,
+ data, skydata, ssigdata, gaindata, expdata, sigdata)
+ }
+ }
+ }
+ call evapfree ()
+
+ # Set apportioned fluxes.
+ call evapportion (cat, Memr[sum_s2x])
+
+ # Set WCS coordinates.
+ call evalwcs (cat, im)
+
+ call sfree (sp)
+end
+
+
+# EVAPINIT -- Initialize aperture photometry. nobjsap will signal whether
+# there are any objects to evaluate.
+
+procedure evapinit (cat, nobjsap)
+
+pointer cat #I Catalog
+int nobjsap #O Number of objects for aperture evaluation
+
+int i, nummax
+pointer tbl, stp, sym, apflux, obj, sthead(), stnext()
+
+int ycompare()
+extern ycompare
+errchk calloc, malloc
+
+int nobjs # Number of objects to evaluate
+int naps # Number of apertures per object
+real rmax # Maximum aperture radius
+pointer r2aps # Array of aperture radii squared (ptr)
+pointer ysort # Array of Y sorted object number indices (ptr)
+int ystart # Index of first object to consider
+pointer objs # Array of object structure (ptr)
+common /evapcom/ nobjs, naps, rmax, r2aps, ysort, ystart, objs
+
+begin
+ nobjsap = 0
+ nobjs = 0
+ naps = 0
+ r2aps = NULL
+ ysort = NULL
+
+ tbl = CAT_OUTTBL(cat)
+ if (tbl == NULL)
+ return
+ stp = TBL_STP(tbl)
+
+ # Determine number of apertures.
+ naps = 0
+ for (sym=sthead(stp); sym!=NULL; sym=stnext(stp,sym)) {
+ if (ENTRY_ID(sym) != ID_APFLUX)
+ next
+ }
+ if (naps == 0)
+ return
+
+ objs = CAT_OBJS(cat)
+ nummax = CAT_NUMMAX(cat)
+
+ # Allocate memory.
+ call calloc (CAT_APFLUX(cat), nummax*naps, TY_REAL)
+ call malloc (r2aps, naps, TY_REAL)
+ call malloc (ysort, nummax, TY_INT)
+
+ # Get the maximum radius since that will define the line
+ # limits needed for each object. Compute array of radius squared
+ # for the apertures. Pixels are checked for being in the aperture
+ # in r^2 to avoid square roots.
+ rmax = 0.
+ naps = 0
+ for (sym=sthead(stp); sym!=NULL; sym=stnext(stp,sym)) {
+ if (ENTRY_ID(sym) != ID_APFLUX)
+ next
+ rmax = max (ENTRY_RAP(sym), rmax)
+ Memr[r2aps+naps] = ENTRY_RAP(sym) ** 2
+ naps = naps + 1
+ }
+
+ # Allocate regions of the apflux array to objects with
+ # defined aperture centers. For the objects create a sorted
+ # index array by YAP so that we can quickly find objects
+ # which include a particular line in their apertures.
+
+ apflux = CAT_APFLUX(cat)
+ do i = NUMSTART-1, nummax-1 {
+ obj = Memi[objs+i]
+ if (obj == NULL)
+ next
+ if (IS_INDEFR(OBJ_XAP(obj)) || IS_INDEFR(OBJ_YAP(obj)))
+ next
+ OBJ_APFLUX(obj) = apflux
+ apflux = apflux + naps
+ Memi[ysort+nobjsap] = i
+ nobjsap = nobjsap + 1
+ }
+
+ if (nobjsap > 1)
+ call gqsort (Memi[ysort], nobjsap, ycompare, objs)
+
+ if (nobjsap == 0) {
+ call mfree (CAT_APFLUX(cat), TY_REAL)
+ call evapfree ()
+ }
+end
+
+
+# EVAPFREE -- Free aperture photometry memory.
+
+procedure evapfree ()
+
+int nobjs # Number of objects to evaluate
+int naps # Number of apertures per object
+real rmax # Maximum aperture radius
+pointer r2aps # Array of aperture radii squared (ptr)
+pointer ysort # Array of Y sorted object number indices (ptr)
+int ystart # Index of first object to consider
+pointer objs # Array of object structure (ptr)
+common /evapcom/ nobjs, naps, rmax, r2aps, ysort, ystart, objs
+
+begin
+ call mfree (r2aps, TY_REAL)
+ call mfree (ysort, TY_INT)
+end
+
+
+# EVAPEVAL -- Do circular aperture photometry. Maintain i1 as the
+# first entry in the sorted index array to be considered. All
+# earlier entries will have all aperture lines less than the
+# current line. Break on the first object whose minimum aperture
+# line is greater than the current line.
+
+procedure evapeval (l, im, skymap, sigmap, gainmap, expmap, data, skydata,
+ ssigdata, gaindata, expdata, sigdata)
+
+int l #I Line
+pointer im #I Image
+pointer skymap #I Sky map
+pointer sigmap #I Sigma map
+pointer gainmap #I Gain map
+pointer expmap #I Exposure map
+pointer data #O Image data
+pointer skydata #O Sky data
+pointer ssigdata #O Sky sigma data
+pointer gaindata #O Gain data
+pointer expdata #O Exposure data
+pointer sigdata #O Total sigma data
+
+int i, j, nc, c
+real x, y, l2, r2, val, sky
+pointer obj, apflux
+
+int nobjs # Number of objects to evaluate
+int naps # Number of apertures per object
+real rmax # Maximum aperture radius
+pointer r2aps # Array of aperture radii squared (ptr)
+pointer ysort # Array of Y sorted object number indices (ptr)
+int ystart # Index of first object to consider
+pointer objs # Array of object structure (ptr)
+common /evapcom/ nobjs, naps, rmax, r2aps, ysort, ystart, objs
+
+begin
+ nc = IM_LEN(im,1)
+ do i = ystart, nobjs {
+ obj = Memi[objs+Memi[ysort+i-1]]
+ y = OBJ_YAP(obj)
+ if (y - rmax > l)
+ break
+ if (y + rmax < l) {
+ ystart = ystart + 1
+ next
+ }
+ x = OBJ_XAP(obj)
+ apflux = OBJ_APFLUX(obj)
+ if (data == NULL)
+ call evgdata (l, im, skymap, sigmap, gainmap, expmap,
+ data, skydata, ssigdata, gaindata, expdata, sigdata)
+
+ # Accumulate data within in the apertures using the r^2
+ # values. Currently partial pixels are not considered and
+ # errors are not evaluated.
+ # Note that bad pixels or object overlaps are not excluded
+ # in the apertures.
+ l2 = (l - y) ** 2
+ do c = max (0, int(x-rmax)), min (nc, int(x+rmax+1)) {
+ r2 = (c - x) ** 2 + l2
+ do j = 0, naps-1 {
+ if (r2 < Memr[r2aps+j]) {
+ val = Memr[data+c-1]
+ sky = Memr[skydata+c-1]
+ Memr[apflux+j] = Memr[apflux+j] + (val - sky)
+ }
+ }
+ }
+ }
+end
+
+
+# EVAPPORTION -- Compute apportioned fluxes after the object isophotoal
+# fluxes have been computed.
+
+procedure evapportion (cat, sum_flux)
+
+pointer cat #I Catalog
+real sum_flux[ARB] #I Work array of size NUMMAX
+
+int nummax, num, pnum, nindef
+pointer objs, obj, pobj
+
+begin
+ objs = CAT_OBJS(cat)
+ nummax = CAT_NUMMAX(cat)
+
+ call aclrr (sum_flux, nummax)
+ do num = NUMSTART, nummax {
+ obj = Memi[objs+num-1]
+ if (obj == NULL)
+ next
+ pnum = OBJ_PNUM(obj)
+ if (pnum == 0) {
+ OBJ_FRAC(obj) = 1.
+ OBJ_FRACFLUX(obj) = OBJ_FLUX(obj)
+ next
+ }
+
+ sum_flux[pnum] = sum_flux[pnum] + max (0., OBJ_FLUX(obj))
+ OBJ_FRACFLUX(obj) = INDEFR
+ }
+
+ nindef = 0
+ do num = NUMSTART, nummax {
+ obj = Memi[objs+num-1]
+ if (obj == NULL)
+ next
+ pnum = OBJ_PNUM(obj)
+ if (pnum == 0)
+ next
+ pobj = Memi[objs+pnum-1]
+
+ if (sum_flux[pnum] > 0.) {
+ OBJ_FRAC(obj) = max (0., OBJ_FLUX(obj)) / sum_flux[pnum]
+ if (IS_INDEFR(OBJ_FRACFLUX(pobj)))
+ nindef = nindef + 1
+ else
+ OBJ_FRACFLUX(obj) = OBJ_FRACFLUX(pobj) * OBJ_FRAC(obj)
+ } else {
+ OBJ_FRAC(obj) = INDEFR
+ OBJ_FRACFLUX(obj) = OBJ_FLUX(obj)
+ }
+ }
+
+ while (nindef > 0) {
+ nindef = 0
+ do num = NUMSTART, nummax {
+ obj = Memi[objs+num-1]
+ if (obj == NULL)
+ next
+ pnum = OBJ_PNUM(obj)
+ if (pnum == 0)
+ next
+
+ pobj = Memi[objs+pnum-1]
+ if (IS_INDEFR(OBJ_FRACFLUX(pobj)))
+ nindef = nindef + 1
+ else {
+ if (IS_INDEFR(OBJ_FRAC(obj)))
+ OBJ_FRACFLUX(obj) = OBJ_FLUX(obj)
+ else
+ OBJ_FRACFLUX(obj) = OBJ_FRACFLUX(pobj) * OBJ_FRAC(obj)
+ }
+ }
+ }
+end
+
+
+# EVALWCS -- Set WCS coordinates.
+
+procedure evalwcs (cat, im)
+
+pointer cat #I Catalog structure
+pointer im #I IMIO pointer
+
+int i
+pointer mw, ct, objs, obj, mw_openim(), mw_sctran()
+errchk mw_openim
+
+begin
+ mw = mw_openim (im)
+ ct = mw_sctran (mw, "logical", "world", 03B)
+
+ objs = CAT_OBJS(cat)
+ do i = NUMSTART-1, CAT_NUMMAX(cat)-1 {
+ obj = Memi[objs+i]
+ if (obj == NULL)
+ next
+ if (IS_INDEFR(OBJ_XAP(obj)) || IS_INDEFR(OBJ_YAP(obj))) {
+ OBJ_WX(obj) = INDEFD
+ OBJ_WY(obj) = INDEFD
+ } else
+ call mw_c2trand (ct, double(OBJ_XAP(obj)),
+ double(OBJ_YAP(obj)), OBJ_WX(obj), OBJ_WY(obj))
+ }
+
+ call mw_ctfree (ct)
+ call mw_close (mw)
+end
+
+
+# YCOMPARE -- Compare Y values of two objects for sorting.
+
+int procedure ycompare (objs, i1, i2)
+
+pointer objs #I Pointer to array of objects
+int i1 #I Index of first object to compare
+int i2 #I Index of second object to compare
+
+real y1, y2
+
+begin
+ y1 = OBJ_YAP(Memi[objs+i1])
+ y2 = OBJ_YAP(Memi[objs+i2])
+ if (y1 < y2)
+ return (-1)
+ else if (y1 > y2)
+ return (1)
+ else
+ return (0)
+end
+
+
+# EVGDATA -- Get evaluation data for an image line.
+
+procedure evgdata (l, im, skymap, sigmap, gainmap, expmap, data, skydata,
+ ssigdata, gaindata, expdata, sigdata)
+
+int l #I Line
+pointer im #I Image
+pointer skymap #I Sky map
+pointer sigmap #I Sigma map
+pointer gainmap #I Gain map
+pointer expmap #I Exposure map
+pointer data #O Image data
+pointer skydata #O Sky data
+pointer ssigdata #O Sky sigma data
+pointer gaindata #O Gain data
+pointer expdata #O Exposure data
+pointer sigdata #O Total sigma data
+
+int nc
+pointer imgl2r(), map_glr()
+errchk imgl2r, map_glr, noisemodel
+
+begin
+ nc = IM_LEN(im,1)
+ data = imgl2r (im, l)
+ skydata = map_glr (skymap, l, READ_ONLY)
+ ssigdata = map_glr (sigmap, l, READ_ONLY)
+ if (gainmap == NULL && expmap == NULL)
+ sigdata = ssigdata
+ else if (expmap == NULL) {
+ gaindata = map_glr (gainmap, l, READ_ONLY)
+ call noisemodel (Memr[data], Memr[skydata],
+ Memr[ssigdata], Memr[gaindata], INDEFR,
+ Memr[sigdata], nc)
+ } else if (gainmap == NULL) {
+ expdata = map_glr (expmap, l, READ_WRITE)
+ call noisemodel (Memr[data], Memr[skydata],
+ Memr[ssigdata], INDEFR, Memr[expdata],
+ Memr[sigdata], nc)
+ } else {
+ gaindata = map_glr (gainmap, l, READ_ONLY)
+ expdata = map_glr (expmap, l, READ_WRITE)
+ call noisemodel (Memr[data], Memr[skydata],
+ Memr[ssigdata], Memr[gaindata],
+ Memr[expdata], Memr[sigdata], nc)
+ }
+end
diff --git a/noao/nproto/ace/filter.h b/noao/nproto/ace/filter.h
new file mode 100644
index 00000000..c61382b2
--- /dev/null
+++ b/noao/nproto/ace/filter.h
@@ -0,0 +1,14 @@
+# Filter operand names.
+define FILT_NAMES "|id|number|x|y|wx|wy|npix|flux|peak|"
+define FILT_OBJID 1
+define FILT_NUM 2
+define FILT_X 3
+define FILT_Y 4
+define FILT_WX 5
+define FILT_WY 6
+define FILT_NPIX 7
+define FILT_FLUX 8
+define FILT_PEAK 9
+
+# Filter functions.
+define FILT_FUNCS "|dummy|"
diff --git a/noao/nproto/ace/filter.x b/noao/nproto/ace/filter.x
new file mode 100644
index 00000000..b43e3de0
--- /dev/null
+++ b/noao/nproto/ace/filter.x
@@ -0,0 +1,134 @@
+include <evvexpr.h>
+include "ace.h"
+include "objs.h"
+include "filter.h"
+
+
+procedure t_filter ()
+
+pointer catalog #I Catalog name
+pointer filt #I Filter
+
+pointer sp, cat, obj, cathead(), catnext()
+errchk catopen
+
+begin
+ call smark (sp)
+ call salloc (catalog, SZ_FNAME, TY_CHAR)
+ call salloc (filt, SZ_LINE, TY_CHAR)
+
+ call clgstr ("catalog", Memc[catalog], SZ_FNAME)
+ call clgstr ("filter", Memc[filt], SZ_FNAME)
+
+ call catopen (cat, Memc[catalog], Memc[catalog], "")
+
+ for (obj=cathead(cat); obj!=NULL; obj=catnext(cat,obj)) {
+ call printf ("%d\n")
+ call pargi (OBJ_ROW(obj))
+ }
+
+ call catclose (cat)
+
+ call sfree (sp)
+end
+
+
+bool procedure filter (obj, filt)
+
+pointer obj #I Object structure
+char filt[ARB] #I Filter string
+bool match #O Filter return value
+
+int type, locpr()
+pointer o, evvexpr()
+extern filt_op(), filt_func()
+errchk evvexpr
+
+begin
+ if (obj == NULL)
+ return (false)
+ if (filt[1] == EOS)
+ return (true)
+
+ # Evaluate filter.
+ o = evvexpr (filt, locpr (filt_op), obj, locpr (filt_func), obj, 0)
+ if (o == NULL)
+ return (false)
+
+ type = O_TYPE(o)
+ if (O_TYPE(o) == TY_BOOL)
+ match = (O_VALI(o) == YES)
+
+ call mfree (o, TY_STRUCT)
+ if (type != TY_BOOL)
+ call error (1, "Filter expression is not boolean")
+
+ return (match)
+end
+
+
+procedure filt_op (obj, name, o)
+
+pointer obj #I Object structure
+char name[ARB] #I Operand name
+pointer o #O Pointer to output operand
+
+char lname[SZ_FNAME]
+int i, strdic()
+
+begin
+ call strcpy (name, lname, SZ_FNAME)
+ call strlwr (lname)
+ i = strdic (lname, lname, SZ_FNAME, FILT_NAMES)
+ switch (i) {
+ case FILT_NUM:
+ call xvv_initop (o, 0, TY_INT)
+ O_VALI(o) = OBJ_NUM(obj)
+ case FILT_X:
+ call xvv_initop (o, 0, TY_DOUBLE)
+ O_VALD(o) = OBJ_XAP(obj)
+ case FILT_Y:
+ call xvv_initop (o, 0, TY_DOUBLE)
+ O_VALD(o) = OBJ_YAP(obj)
+ case FILT_WX:
+ call xvv_initop (o, 0, TY_DOUBLE)
+ O_VALD(o) = OBJ_WX(obj)
+ case FILT_WY:
+ call xvv_initop (o, 0, TY_DOUBLE)
+ O_VALD(o) = OBJ_WY(obj)
+ case FILT_NPIX:
+ call xvv_initop (o, 0, TY_INT)
+ O_VALI(o) = OBJ_NPIX(obj)
+ case FILT_FLUX:
+ call xvv_initop (o, 0, TY_DOUBLE)
+ O_VALD(o) = OBJ_FLUX(obj)
+ case FILT_PEAK:
+ call xvv_initop (o, 0, TY_DOUBLE)
+ O_VALD(o) = OBJ_PEAK(obj)
+ default:
+ call xvv_error1 ("quantity `%s' not found", name)
+ }
+end
+
+
+
+procedure filt_func (obj, func, args, nargs, o)
+
+pointer obj #I Object structure
+char func[ARB] #I Function
+pointer args[ARB] #I Arguments
+int nargs #I Number of arguments
+pointer o #O Function value operand
+
+int ifunc, strdic()
+pointer sp, buf
+bool strne()
+
+begin
+ call smark (sp)
+ call salloc (buf, SZ_LINE, TY_CHAR)
+
+ ifunc = strdic (func, Memc[buf], SZ_LINE, FILT_FUNCS)
+ if (ifunc == 0 || strne (func, Memc[buf]))
+ call xvv_error1 ("unknown function `%s'", func)
+end
diff --git a/noao/nproto/ace/grow.h b/noao/nproto/ace/grow.h
new file mode 100644
index 00000000..ff91542e
--- /dev/null
+++ b/noao/nproto/ace/grow.h
@@ -0,0 +1,6 @@
+# Grow parameter structure
+
+define GRW_LEN 2 # Length of parameter structure
+
+define GRW_NGROW Memi[$1] # Number of grow passes
+define GRW_AGROW Memr[P2R($1+1)] # Grow area factor
diff --git a/noao/nproto/ace/grow.x b/noao/nproto/ace/grow.x
new file mode 100644
index 00000000..a9c84cd2
--- /dev/null
+++ b/noao/nproto/ace/grow.x
@@ -0,0 +1,959 @@
+include <pmset.h>
+include "ace.h"
+include "cat.h"
+include "objs.h"
+include "grow.h"
+
+
+procedure grow (grw, cat, objmask, logfd)
+
+pointer grw #I Grow parameter structure
+pointer cat #I Catalog of objects
+pointer objmask #I Object mask
+int logfd #I Logfile
+
+int ngrow #I Number of pixels to grow
+real agrow #I Area factor grow
+
+int i, j, nc, nl, m, n
+pointer sp, v, bufs, obuf
+pointer buf1, buf2, buf3, obj
+
+int grow1(), grow2(), grow3(), andi(), ori(), noti()
+pointer cathead(), catnext()
+
+begin
+ call grw_pars ("open", "", grw)
+ ngrow = GRW_NGROW(grw)
+ agrow = GRW_AGROW(grw)
+
+ if (ngrow < 1 && agrow <= 1.)
+ return
+
+ if (logfd != NULL) {
+ call fprintf (logfd, " Grow objects: ngrow = %d, agrow = %g\n")
+ call pargi (ngrow)
+ call pargr (agrow)
+ }
+
+ call smark (sp)
+ call salloc (v, PM_MAXDIM, TY_LONG)
+
+ call pm_gsize (objmask, i, Meml[v], n)
+ nc = Meml[v]; nl = Meml[v+1]
+ Meml[v] = 1
+
+ call salloc (bufs, 3, TY_POINTER)
+ do i = 1, 3
+ call salloc (Memi[bufs+i-1], nc, TY_INT)
+ call salloc (obuf, nc, TY_INT)
+
+ for (obj=cathead(cat); obj!=NULL; obj=catnext(cat,obj)) {
+ if (GROWN(obj))
+ next
+ UNSETFLAG (obj, OBJ_EVAL)
+ OBJ_NDETECT(obj) = OBJ_NPIX(obj)
+ }
+
+ do j = 1, ngrow {
+ m = 0
+ buf2 = NULL; buf3 = NULL
+ do i = 1, nl {
+ buf1 = buf2
+ buf2 = buf3
+ buf3 = NULL
+
+ if (i != 1 && buf1 == NULL) {
+ Meml[v+1] = i - 1
+ buf1 = Memi[bufs+mod(Meml[v+1],3)]
+ call pmglpi (objmask, Meml[v], Memi[buf1], 0, nc, 0)
+ }
+ if (buf2 == NULL) {
+ Meml[v+1] = i
+ buf2 = Memi[bufs+mod(Meml[v+1],3)]
+ call pmglpi (objmask, Meml[v], Memi[buf2], 0, nc, 0)
+ }
+ if (i != nl && buf3 == NULL) {
+ Meml[v+1] = i+1
+ buf3 = Memi[bufs+mod(Meml[v+1],3)]
+ call pmglpi (objmask, Meml[v], Memi[buf3], 0, nc, 0)
+ }
+
+ if (i == 1)
+ n = grow1 (cat, i, Memi[buf2], Memi[buf3],
+ Memi[obuf], nc, nl)
+ else if (i == nl)
+ n = grow3 (cat, i, Memi[buf1], Memi[buf2],
+ Memi[obuf], nc, nl)
+ else
+ n = grow2 (cat, i, Memi[buf1], Memi[buf2], Memi[buf3],
+ Memi[obuf], nc, nl)
+
+ if (n > 0) {
+ Meml[v+1] = i
+ call pmplpi (objmask, Meml[v], Memi[obuf], 0, nc, PIX_SRC)
+ m = m + n
+ }
+ }
+
+ n = 0
+ for (obj=cathead(cat); obj!=NULL; obj=catnext(cat,obj)) {
+ if (GROWN(obj))
+ next
+ if (real (OBJ_NPIX(obj)) / OBJ_NDETECT(obj) >= agrow)
+ SETFLAG (obj, OBJ_GROW)
+ else
+ n = n + 1
+ }
+
+ if (n == 0 || m == 0)
+ break
+ }
+
+ if (n != 0) {
+ for (obj=cathead(cat); obj!=NULL; obj=catnext(cat,obj)) {
+ if (GROWN(obj))
+ next
+ SETFLAG (obj, OBJ_GROW)
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+int procedure grow1 (cat, line, in2, in3, out, nc, nl)
+
+pointer cat #I Catalog
+int line #I Line
+int in2[nc] #I Current line
+int in3[nc] #I Next line
+int out[nc] #I Output line
+int nc, nl #I Dimension of image
+
+int i, j, n, id, id0, id1, num1, andi()
+bool grow
+pointer objs, obj, obj1
+
+begin
+ objs = CAT_OBJS(cat) - 1
+ obj1 = NULL
+ n = 0
+ do i = 1, nc {
+ id0 = in2[i]
+ if (id0 != 0 && MNOTSPLIT(id0)) {
+ out[i] = id0
+ next
+ }
+
+ id = 0
+ j = i - 1
+ if (i > 1) {
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[i]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ j = i + 1
+ if (i < nc) {
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (id == 0)
+ out[i] = in2[i]
+ else {
+ out[i] = id
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ n = n + 1
+ }
+ }
+
+ return (n)
+end
+
+
+int procedure grow2 (cat, line, in1, in2, in3, out, nc, nl)
+
+pointer cat #I Catalog
+int line #I Line
+int in1[nc] #I Previous line
+int in2[nc] #I Current line
+int in3[nc] #I Next line
+int out[nc] #I Output line
+int nc, nl #I Dimension of image
+
+int i, j, n, id, id0, id1, num1, andi()
+bool grow
+pointer objs, obj, obj1
+
+begin
+ objs = CAT_OBJS(cat) - 1
+ obj1 = NULL
+ n = 0
+ do i = 2, nc-1 {
+ id0 = in2[i]
+ if (id0 != 0 && MNOTSPLIT(id0)) {
+ out[i] = id0
+ next
+ }
+
+ id = 0
+ j = i - 1
+ id1 = in1[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in1[i]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[i]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ j = i + 1
+ id1 = in1[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+
+ if (id == 0)
+ out[i] = in2[i]
+ else {
+ out[i] = id
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ n = n + 1
+ }
+ }
+
+ # First pixel
+ id0 = in2[1]
+ if (id0 != 0 && MNOTSPLIT(id0))
+ out[1] = id0
+ else {
+ id = 0
+ id1 = in1[1]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[1]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in1[2]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in2[2]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[2]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+
+ if (id == 0)
+ out[1] = in2[1]
+ else {
+ out[1] = id
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ n = n + 1
+ }
+ }
+
+ # Last pixel
+ id0 = in2[nc]
+ if (id0 != 0 && MNOTSPLIT(id0))
+ out[nc] = id0
+ else {
+ id = 0
+ j = nc - 1
+ id1 = in1[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in1[nc]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in3[nc]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+
+ if (id == 0)
+ out[nc] = in2[nc]
+ else {
+ out[nc] = id
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ n = n + 1
+ }
+ }
+
+ return (n)
+end
+
+
+int procedure grow3 (cat, line, in1, in2, out, nc, nl)
+
+pointer cat #I Catalog
+int line #I Line
+int in1[nc] #I Previous line
+int in2[nc] #I Current line
+int out[nc] #I Output line
+int nc, nl #I Dimension of image
+
+int i, j, n, id, id0, id1, num1, andi()
+bool grow
+pointer objs, obj, obj1
+
+begin
+ objs = CAT_OBJS(cat) - 1
+ obj1 = NULL
+ n = 0
+ do i = 1, nc {
+ id0 = in2[i]
+ if (id0 != 0 && MNOTSPLIT(id0)) {
+ out[i] = id0
+ next
+ }
+
+ id = 0
+ j = i - 1
+ if (i > 1) {
+ id1 = in1[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ }
+ id1 = in1[i]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ j = i + 1
+ if (i < nc) {
+ id1 = in1[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ id1 = in2[j]
+ num1 = MNUM(id1)
+ if (num1 >= NUMSTART) {
+ if (MNOTSPLIT(id1)) {
+ if (obj1 == NULL) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ } else if (OBJ_NUM(obj1) != num1) {
+ obj1 = Memi[objs+num1]
+ grow = NOTGROWN(obj1)
+ }
+ if (grow) {
+ if (id == 0) {
+ id = id1
+ obj = obj1
+ } else if (id != id1) {
+ if (OBJ_FLUX(obj) < OBJ_FLUX(obj1)) {
+ id = id1
+ obj = obj1
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (id == 0)
+ out[i] = in2[i]
+ else {
+ out[i] = id
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ n = n + 1
+ }
+ }
+
+ return (n)
+end
diff --git a/noao/nproto/ace/gwindow.h b/noao/nproto/ace/gwindow.h
new file mode 100644
index 00000000..ae91e2ea
--- /dev/null
+++ b/noao/nproto/ace/gwindow.h
@@ -0,0 +1,49 @@
+# Window descriptor structure.
+
+define LEN_WDES (210+(W_MAXWC+1)*LEN_WC)
+define LEN_WC 10 # 4=[XbXeYbYe]+2=tr_type[xy]
+define W_MAXWC 5 # max world coord systems
+define W_SZSTRING 99 # size of strings
+define W_SZIMSECT W_SZSTRING # image section string
+
+define W_DEVICE Memi[$1]
+define W_FRAME Memi[$1+1] # device frame number
+define W_XRES Memi[$1+2] # device resolution, x
+define W_YRES Memi[$1+3] # device resolution, y
+define W_BPDISP Memi[$1+4] # bad pixel display option
+define W_BPCOLORS Memi[$1+5] # overlay colors
+define W_OCOLORS Memi[$1+6] # badpixel colors
+define W_IMSECT Memc[P2C($1+10)] # image section
+define W_OVRLY Memc[P2C($1+60)] # overlay mask
+define W_BPM Memc[P2C($1+110)] # bad pixel mask
+define W_ZPM Memc[P2C($1+160)] # Z scaling pixel mask
+define W_WC ($1+$2*LEN_WC+210) # ptr to coord descriptor
+
+# Fields of the WC coordinate descriptor, a substructure of the window
+# descriptor. "W_XB(W_WC(w,0))" is the XB field of wc 0 of window W.
+
+define W_XS Memr[P2R($1)] # starting X value
+define W_XE Memr[P2R($1+1)] # ending X value
+define W_XT Memi[$1+2] # X transformation type
+define W_YS Memr[P2R($1+3)] # starting Y value
+define W_YE Memr[P2R($1+4)] # ending Y value
+define W_YT Memi[$1+5] # Y transformation type
+define W_ZS Memr[P2R($1+6)] # starting Z value (greyscale)
+define W_ZE Memr[P2R($1+7)] # ending Z value
+define W_ZT Memi[$1+8] # Z transformation type
+define W_UPTR Memi[$1+9] # LUT when ZT=USER
+
+# WC types.
+
+define W_NWIN 0 # Display window in NDC coordinates
+define W_DWIN 1 # Display window in image pixel coordinates
+define W_WWIN 2 # Display window in image world coordinates
+define W_IPIX 3 # Image pixel coordinates (in pixels)
+define W_DPIX 4 # Display pixel coordinates (in pixels)
+
+# Types of coordinate and greyscale transformations.
+
+define W_UNITARY 0 # values map without change
+define W_LINEAR 1 # linear mapping
+define W_LOG 2 # logarithmic mapping
+define W_USER 3 # user specifies transformation
diff --git a/noao/nproto/ace/mapio.x b/noao/nproto/ace/mapio.x
new file mode 100644
index 00000000..d8f9f9de
--- /dev/null
+++ b/noao/nproto/ace/mapio.x
@@ -0,0 +1,406 @@
+include <error.h>
+include <imhdr.h>
+
+define MAP_LENSTR 99 # Length of strings
+
+# Map data structure.
+define MAP_LEN 64 # Length of map structure
+define MAP_NAME Memc[P2C($1)] # Name of map
+define MAP_TYPE Memi[$1+51] # Type of map
+define MAP_MAP Memi[$1+52] # Structure pointer
+define MAP_COPY Memi[$1+53] # Data buffer for copy
+define MAP_NC Memi[$1+54] # Number of columns
+define MAP_NL Memi[$1+55] # Number of columns
+define MAP_SAMPLE Memi[$1+56] # Sample size for lines
+define MAP_BUF Memi[$1+57] # Data buffer for constant or sampling
+define MAP_BUF1 Memi[$1+58] # Data buffer for sampling
+define MAP_BUF2 Memi[$1+59] # Data buffer for sampling
+define MAP_LINE1 Memi[$1+60] # Sampling line number
+define MAP_LINE2 Memi[$1+61] # Sampling line number
+define MAP_LASTLINE Memi[$1+62] # Last line
+define MAP_LASTBUF Memi[$1+63] # Data buffer last returned
+
+# Types of maps allowed.
+define MAP_CONST 1 # Constant
+define MAP_IMAGE 2 # Image
+define MAP_GSURFIT 3 # GSURFIT
+
+
+# MAP_GLR -- Get a line of map data.
+
+pointer procedure map_glr (map, line, mode)
+
+pointer map #I Map pointer
+int line #I Line
+int mode #I Access mode (READ_ONLY, READ_WRITE)
+
+int i, nc, nl, sample, line1, line2
+real a, b
+pointer buf, buf1, buf2, mim_glr(), mgs_glr()
+errchk malloc, mim_glr, mgs_glr
+
+begin
+ # Check for repeated request.
+ if (line == MAP_LASTLINE(map)) {
+ buf = MAP_LASTBUF(map)
+ if (mode == READ_WRITE) {
+ nc = MAP_NC(map)
+ if (MAP_COPY(map) == NULL)
+ call malloc (MAP_COPY(map), nc, TY_REAL)
+ call amovr (Memr[buf], Memr[MAP_COPY(map)], nc)
+ buf = MAP_COPY(map)
+ }
+ return (buf)
+ }
+
+ nc = MAP_NC(map)
+ nl = MAP_NL(map)
+ sample = MAP_SAMPLE(map)
+
+ # Check for subsampling. A constant map will never be sampled.
+ if (sample > 1) {
+ if (MAP_BUF1(map) == NULL) {
+ call malloc (MAP_BUF(map), nc, TY_REAL)
+ call malloc (MAP_BUF1(map), nc, TY_REAL)
+ call malloc (MAP_BUF2(map), nc, TY_REAL)
+ }
+ line1 = (line-1) / sample * sample + 1
+ line2 = min (nl, line1 + sample)
+ buf1 = MAP_BUF1(map)
+ buf2 = MAP_BUF2(map)
+ if (line1 == MAP_LINE2(map)) {
+ MAP_BUF2(map) = buf1
+ MAP_BUF1(map) = buf2
+ MAP_LINE2(map) = MAP_LINE1(map)
+ MAP_LINE1(map) = line1
+ buf1 = MAP_BUF1(map)
+ buf2 = MAP_BUF2(map)
+ } else if (line2 == MAP_LINE1(map)) {
+ MAP_BUF1(map) = buf2
+ MAP_BUF2(map) = buf1
+ MAP_LINE1(map) = MAP_LINE2(map)
+ MAP_LINE2(map) = line2
+ buf1 = MAP_BUF1(map)
+ buf2 = MAP_BUF2(map)
+ }
+ if (line1 != MAP_LINE1(map)) {
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ buf = mim_glr (MAP_MAP(map), line1)
+ case MAP_GSURFIT:
+ buf = mgs_glr (MAP_MAP(map), line1)
+ }
+ call amovr (Memr[buf], Memr[buf1], nc)
+ MAP_LINE1(map) = line1
+ }
+ if (line2 != MAP_LINE2(map)) {
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ buf = mim_glr (MAP_MAP(map), line2)
+ case MAP_GSURFIT:
+ buf = mgs_glr (MAP_MAP(map), line2)
+ }
+ call amovr (Memr[buf], Memr[buf2], nc)
+ MAP_LINE2(map) = line2
+ }
+ if (line == line1)
+ buf = buf1
+ else if (line == line2)
+ buf = buf2
+ else {
+ buf = MAP_BUF(map)
+ b = real (line - line1) / sample
+ a = 1 - b
+ do i = 0, nc-1
+ Memr[buf+i] = a * Memr[buf1+i] + b * Memr[buf2+i]
+ }
+ } else {
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ buf = mim_glr (MAP_MAP(map), line)
+ case MAP_GSURFIT:
+ buf = mgs_glr (MAP_MAP(map), line)
+ case MAP_CONST:
+ buf = MAP_BUF(map)
+ }
+ }
+ MAP_LASTLINE(map) = line
+ MAP_LASTBUF(map) = buf
+
+ # Make a copy which might be modified by the caller.
+ if (mode == READ_WRITE) {
+ nc = MAP_NC(map)
+ if (MAP_COPY(map) == NULL)
+ call malloc (MAP_COPY(map), nc, TY_REAL)
+ call amovr (Memr[buf], Memr[MAP_COPY(map)], nc)
+ buf = MAP_COPY(map)
+ }
+
+ return (buf)
+end
+
+
+# MAP_OPEN -- Open map. Return NULL if no map is found.
+
+pointer procedure map_open (name, refim)
+
+char name[ARB] #I Name
+pointer refim #I Reference image
+pointer map #O Map pointer returned
+
+int i, nc, nl, nowhite(), ctor()
+real const
+pointer sp, mapstr, im, gs, immap(), mim_open(), mgs_open()
+errchk calloc, malloc, imgstr, mim_open, mgs_open
+
+begin
+ call smark (sp)
+ call salloc (mapstr, SZ_FNAME, TY_CHAR)
+
+ i = 1
+ nc = IM_LEN(refim,1)
+ nl = IM_LEN(refim,2)
+
+ call calloc (map, MAP_LEN, TY_STRUCT)
+ MAP_NC(map) = nc
+ MAP_NL(map) = nl
+
+ iferr {
+ # Check for missing map name, and keyword redirection.
+ if (nowhite (name, Memc[mapstr], SZ_FNAME) == 0)
+ call error (1, "No map specified")
+ if (Memc[mapstr] == '!')
+ call imgstr (refim, Memc[mapstr+1], Memc[mapstr], SZ_FNAME)
+ call strcpy (Memc[mapstr], MAP_NAME(map), MAP_LENSTR)
+
+ ifnoerr (im = immap (MAP_NAME(map), READ_ONLY, 0)) {
+ call imunmap (im)
+ MAP_TYPE(map) = MAP_IMAGE
+ MAP_MAP(map) = mim_open (MAP_NAME(map), refim)
+ } else ifnoerr (call mgs_ggs (refim, MAP_NAME(map), gs)) {
+ MAP_TYPE(map) = MAP_GSURFIT
+ MAP_MAP(map) = mgs_open (MAP_NAME(map), refim, gs)
+ } else if (ctor (MAP_NAME(map), i, const) > 0) {
+ MAP_TYPE(map) = MAP_CONST
+ call malloc (MAP_BUF(map), nc, TY_REAL)
+ call amovkr (const, Memr[MAP_BUF(map)], nc)
+ } else {
+ call mfree (map, TY_STRUCT)
+ call sprintf (Memc[mapstr], SZ_FNAME, "Can't open map (%s)")
+ call pargstr (name)
+ call error (2, Memc[mapstr])
+ }
+ } then {
+ call map_close (map)
+ call erract (EA_ERROR)
+ }
+
+ call sfree (sp)
+ return (map)
+end
+
+
+# MAP_OPENGS -- Open GSURFIT map given the GSURFIT pointer.
+
+pointer procedure map_opengs (gs, refim)
+
+pointer gs #I GSURFIT pointer
+pointer refim #I Reference image
+pointer map #O Map pointer returned
+
+pointer mgs_open()
+errchk calloc, mgs_open
+
+begin
+ iferr {
+ call calloc (map, MAP_LEN, TY_STRUCT)
+ MAP_NC(map) = IM_LEN(refim,1)
+ MAP_NL(map) = IM_LEN(refim,2)
+ MAP_TYPE(map) = MAP_GSURFIT
+ MAP_MAP(map) = mgs_open (MAP_NAME(map), refim, gs)
+ } then {
+ call map_close (map)
+ call erract (EA_ERROR)
+ }
+
+ return (map)
+end
+
+
+# MAP_CLOSE -- Unmap map structure.
+
+procedure map_close (map)
+
+pointer map #I Map pointer
+
+begin
+ if (map == NULL)
+ return
+
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ call mim_close (MAP_MAP(map))
+ case MAP_GSURFIT:
+ call mgs_close (MAP_MAP(map))
+ }
+
+ call mfree (MAP_COPY(map), TY_REAL)
+ call mfree (MAP_BUF(map), TY_REAL)
+ call mfree (MAP_BUF1(map), TY_REAL)
+ call mfree (MAP_BUF2(map), TY_REAL)
+ call mfree (map, TY_STRUCT)
+end
+
+
+# MAP_GETS -- Get string parameter.
+
+procedure map_gets (map, param, val, maxchar)
+
+pointer map #I Map pointer
+char param[ARB] #I Parameter
+char val[ARB] #O Parameter string value
+int maxchar #I Maximum number of characters to return
+
+bool streq()
+errchk mim_gets(), mgs_gets()
+
+begin
+ if (streq (param, "mapname"))
+ call strcpy (MAP_NAME(map), val, maxchar)
+ else {
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ call mim_gets (MAP_MAP(map), param, val, maxchar)
+ case MAP_GSURFIT:
+ call mgs_gets (MAP_MAP(map), param, val, maxchar)
+ default:
+ call error (1, "map_gets: unknown parameter")
+ }
+ }
+end
+
+
+# MAP_GETI -- Get integer parameter.
+
+procedure map_geti (map, param, val)
+
+pointer map #I Map pointer
+char param[ARB] #I Parameter
+int val #O Value
+
+errchk mim_geti(), mgs_geti()
+
+begin
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ call mim_geti (MAP_MAP(map), param, val)
+ case MAP_GSURFIT:
+ call mgs_geti (MAP_MAP(map), param, val)
+ default:
+ call error (1, "map_geti: unknown parameter")
+ }
+end
+
+
+# MAP_GETR -- Get real parameter.
+
+procedure map_getr (map, param, val)
+
+pointer map #I Map pointer
+char param[ARB] #I Parameter
+real val #O Value
+
+bool streq()
+errchk mim_getr(), mgs_getr()
+
+begin
+ if (streq (param, "constant")) {
+ if (MAP_TYPE(map) == MAP_CONST) {
+ val = Memr[MAP_BUF(map)]
+ return
+ } else
+ call error (1, "map_getr: map is not constant")
+ }
+
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ call mim_getr (MAP_MAP(map), param, val)
+ case MAP_GSURFIT:
+ call mgs_getr (MAP_MAP(map), param, val)
+ default:
+ call error (1, "map_getr: unknown parameter")
+ }
+end
+
+
+# MAP_SETI -- Set integer parameter.
+
+procedure map_seti (map, param, val)
+
+pointer map #I Map pointer
+char param[ARB] #I Parameter
+int val #I Value
+
+bool streq()
+errchk mim_seti(), mgs_seti
+
+begin
+ switch (MAP_TYPE(map)) {
+ case MAP_CONST:
+ ;
+ case MAP_IMAGE:
+ if (streq (param, "sample"))
+ MAP_SAMPLE(map) = max (1, val)
+ else
+ call mim_seti (MAP_MAP(map), param, val)
+ case MAP_GSURFIT:
+ if (streq (param, "sample"))
+ MAP_SAMPLE(map) = max (1, val)
+ else
+ call mgs_seti (MAP_MAP(map), param, val)
+ }
+end
+
+
+# MAP_SETR -- Set real parameter.
+
+procedure map_setr (map, param, val)
+
+pointer map #I Map pointer
+char param[ARB] #I Parameter
+real val #I Value
+
+errchk mim_setr(), mgs_setr
+
+begin
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ call mim_setr (MAP_MAP(map), param, val)
+ case MAP_GSURFIT:
+ call mgs_setr (MAP_MAP(map), param, val)
+ default:
+ call error (1, "map_setr: unknown parameter")
+ }
+end
+
+
+# MAP_SETS -- Set string parameter.
+
+procedure map_sets (map, param, val)
+
+pointer map #I Map pointer
+char param[ARB] #I Parameter
+char val[ARB] #I Value
+
+errchk mim_sets(), mgs_sets
+
+begin
+ switch (MAP_TYPE(map)) {
+ case MAP_IMAGE:
+ call mim_sets (MAP_MAP(map), param, val)
+ case MAP_GSURFIT:
+ call mgs_sets (MAP_MAP(map), param, val)
+ default:
+ call error (1, "map_sets: unknown parameter")
+ }
+end
diff --git a/noao/nproto/ace/maskcolor.x b/noao/nproto/ace/maskcolor.x
new file mode 100644
index 00000000..29e25e55
--- /dev/null
+++ b/noao/nproto/ace/maskcolor.x
@@ -0,0 +1,54 @@
+# MASKCOLOR -- A color for a mask value.
+
+procedure mcolors (colors, maskval, dataval)
+
+pointer colors #I Mask colormap object
+int maskval #I Mask value
+short dataval #U Data value to be set
+
+int i, j, offset, color
+
+begin
+ color = Memi[colors+2]
+ offset = Memi[colors+3]
+ do i = 2, Memi[colors] {
+ j = 4 * i - 4
+ if (maskval >= Memi[colors+j] && maskval <= Memi[colors+j+1]) {
+ color = Memi[colors+j+2]
+ offset = Memi[colors+j+3]
+ break
+ }
+ }
+
+ if (offset == YES)
+ color = maskval + color
+ if (color >= 0)
+ dataval = color
+end
+
+
+procedure mcolorr (colors, maskval, dataval)
+
+pointer colors #I Mask colormap object
+int maskval #I Mask value
+real dataval #U Data value to be set
+
+int i, j, offset, color
+
+begin
+ color = Memi[colors+2]
+ offset = Memi[colors+3]
+ do i = 2, Memi[colors] {
+ j = 4 * i - 4
+ if (maskval >= Memi[colors+j] && maskval <= Memi[colors+j+1]) {
+ color = Memi[colors+j+2]
+ offset = Memi[colors+j+3]
+ break
+ }
+ }
+
+ if (offset == YES)
+ color = maskval + color
+ if (color >= 0)
+ dataval = color
+end
diff --git a/noao/nproto/ace/mgs.x b/noao/nproto/ace/mgs.x
new file mode 100644
index 00000000..2e11cab7
--- /dev/null
+++ b/noao/nproto/ace/mgs.x
@@ -0,0 +1,321 @@
+include <error.h>
+include <imhdr.h>
+include <imio.h>
+include <math/gsurfit.h>
+
+# Data structure.
+define MGS_SZNAME 99 # Length of mgs name string
+define MGS_LEN 56 # Length of structure
+define MGS_GS Memi[$1] # GSURFIT pointer
+define MGS_X Memi[$1+1] # Pointer to line of x values
+define MGS_Y Memi[$1+2] # Pointer to line of y values
+define MGS_Z Memi[$1+3] # Pointer to line of z values
+define MGS_NC Memi[$1+4] # Number of columns
+define MGS_REFIM Memi[$1+5] # Reference image pointer
+define MGS_NAME Memc[P2C($1+6)] # Map name
+
+
+# MGS_GLR -- Get a line of data.
+
+pointer procedure mgs_glr (mgs, line)
+
+pointer mgs #I Map pointer
+int line #I Line
+
+int nc
+pointer x, y, z, gs
+
+begin
+ if (mgs == NULL)
+ call error (1, "Map is undefined")
+
+ gs = MGS_GS(mgs)
+ x = MGS_X(mgs)
+ y = MGS_Y(mgs)
+ z = MGS_Z(mgs)
+ nc = MGS_NC(mgs)
+
+ call amovkr (real(line), Memr[y], nc)
+ call gsvector (gs, Memr[x], Memr[y], Memr[z], nc)
+
+ return (z)
+end
+
+
+# MGS_OPEN -- Open mgs.
+
+pointer procedure mgs_open (name, refim, gsin)
+
+char name[ARB] #I Name
+pointer refim #I Reference image
+pointer gsin #I GSURFIT pointer
+pointer mgs #O Map pointer returned
+
+int i, nc, nl
+real gsgetr()
+pointer gs
+errchk mgs_ggs
+
+begin
+ nc = IM_LEN(refim,1)
+ nl = IM_LEN(refim,2)
+
+ call calloc (mgs, MGS_LEN, TY_STRUCT)
+ MGS_REFIM(mgs) = refim
+ call strcpy (name, MGS_NAME(mgs), MGS_SZNAME)
+ MGS_NC(mgs) = nc
+
+ iferr {
+ gs = gsin
+ if (gs == NULL) {
+ call mgs_ggs (refim, name, gs)
+ MGS_GS(mgs) = gs
+ }
+
+ if (1 < gsgetr (gs, GSXMIN) || nc > gsgetr (gs, GSXMAX) ||
+ 1 < gsgetr (gs, GSYMIN) || nl > gsgetr (gs, GSYMAX))
+ call error (2, "Map and data images have different sizes")
+
+ MGS_GS(mgs) = gs
+ call malloc (MGS_X(mgs), nc, TY_REAL)
+ call malloc (MGS_Y(mgs), nc, TY_REAL)
+ call malloc (MGS_Z(mgs), nc, TY_REAL)
+ do i = 1, nc
+ Memr[MGS_X(mgs)+i-1] = i
+ } then {
+ call mgs_close (mgs)
+ call erract (EA_ERROR)
+ }
+
+ return (mgs)
+end
+
+
+# MGS_CLOSE -- Close mgs.
+
+procedure mgs_close (mgs)
+
+pointer mgs #I Map pointer
+
+begin
+ if (mgs == NULL)
+ return
+
+ if (MGS_GS(mgs) != NULL)
+ call gsfree (MGS_GS(mgs))
+ call mfree (MGS_X(mgs), TY_REAL)
+ call mfree (MGS_Y(mgs), TY_REAL)
+ call mfree (MGS_Z(mgs), TY_REAL)
+ call mfree (mgs, TY_STRUCT)
+end
+
+
+# MGS_GETS -- Get string parameter.
+
+procedure mgs_gets (mgs, param, val, maxchar)
+
+pointer mgs #I Map pointer
+char param[ARB] #I Parameter
+char val[ARB] #O Parameter string value
+int maxchar #I Maximum number of characters to return
+
+begin
+ call error (1, "mgs_gets: unknown parameter")
+end
+
+
+# MGS_SETS -- Set string parameter.
+
+procedure mgs_sets (mgs, param, val)
+
+pointer mgs #I Map pointer
+char param[ARB] #I Parameter
+char val[ARB] #O Parameter string value
+
+begin
+ call error (1, "mgs_sets: unknown parameter")
+end
+
+
+# MGS_GETI -- Get integer parameter.
+
+procedure mgs_geti (mgs, param, val)
+
+pointer mgs #I Map pointer
+char param[ARB] #I Parameter
+int val #O Value
+
+bool streq()
+
+begin
+ if (streq (param, "gsurfit"))
+ val = MGS_GS(mgs)
+ else
+ call error (1, "mgs_geti: unknown parameter")
+end
+
+
+# MGS_SETI -- Set integer parameter.
+
+procedure mgs_seti (mgs, param, val)
+
+pointer mgs #I Map pointer
+char param[ARB] #I Parameter
+int val #I Value
+
+bool streq()
+
+begin
+ if (streq (param, "gsurfit")) {
+ call mgs_pgs (MGS_REFIM(mgs), MGS_NAME(mgs), val)
+ call gsfree (MGS_GS(mgs))
+ MGS_GS(mgs) = val
+ } else
+ call error (1, "mgs_seti: unknown parameter")
+end
+
+
+# MGS_GETR -- Get real parameter.
+
+procedure mgs_getr (mgs, param, val)
+
+pointer mgs #I Map pointer
+char param[ARB] #I Parameter
+real val #O Value
+
+begin
+ call error (1, "mgs_getr: unknown parameter")
+end
+
+
+# MGS_SETR -- Set real parameter.
+
+procedure mgs_setr (mgs, param, val)
+
+pointer mgs #I Map pointer
+char param[ARB] #I Parameter
+real val #I Value
+
+begin
+ call error (1, "mgs_setr: unknown parameter")
+end
+
+
+# MAP_PGS -- Put mgs surface fit.
+
+procedure mgs_pgs (im, key, gs)
+
+pointer im #I Image pointer
+char key[ARB] #I Keyword root
+pointer gs #I Surface fit pointer
+
+int i, nc, fd, gsgeti(), stropen()
+pointer sp, kw, card, coeffs, strbuf, cp, cp1, cp2
+
+begin
+ if (IM_SECTUSED(im) == YES)
+ return
+
+ call smark (sp)
+ call salloc (kw, 80, TY_CHAR)
+ call salloc (card, 68, TY_CHAR)
+
+ nc = gsgeti (gs, GSNSAVE)
+ call salloc (coeffs, nc, TY_REAL)
+ call gssave (gs, Memr[coeffs])
+
+ # Convert coeffs to a string. Last character will be space.
+ call salloc (strbuf, 20*nc, TY_CHAR)
+ call aclrc (Memc[strbuf], 20*nc)
+ fd = stropen (Memc[strbuf], 20*nc, WRITE_ONLY)
+ do i = 1, nc {
+ call fprintf (fd, "%g ")
+ call pargr (Memr[coeffs+i-1])
+ }
+ call close (fd)
+
+ i = 1
+ cp1 = strbuf
+ for (cp=cp1; Memc[cp] != EOS; cp=cp+1) {
+ if (Memc[cp] == ' ')
+ cp2 = cp
+ if (cp - cp1 + 1 == 68) {
+ call sprintf (Memc[kw], 8, "%.6s%02d")
+ call pargstr (key)
+ call pargi (i)
+ i = i + 1
+ Memc[cp2] = EOS
+ call imastr (im, Memc[kw], Memc[cp1])
+ cp1 = cp2 + 1
+ cp = cp1
+ }
+ }
+ if (cp - cp1 + 1 > 0) {
+ call sprintf (Memc[kw], 8, "%.6s%02d")
+ call pargstr (key)
+ call pargi (i)
+ i = i + 1
+ Memc[cp2] = EOS
+ call imastr (im, Memc[kw], Memc[cp1])
+ }
+ repeat {
+ call sprintf (Memc[kw], 8, "%.6s%02d")
+ call pargstr (key)
+ call pargi (i)
+ i = i + 1
+ iferr (call imdelf (im, Memc[kw]))
+ break
+ }
+
+ call sfree (sp)
+end
+
+
+# MAP_GGS -- Get mgs surface fit.
+
+procedure mgs_ggs (im, key, gs)
+
+pointer im #I Image pointer
+char key[ARB] #I Keyword root
+pointer gs #O Surface fit pointer
+
+int i, j, nc, ctor()
+pointer sp, kw, card, coeffs
+
+begin
+ if (IM_SECTUSED(im) == YES)
+ call error (1, "No surface fit with an image section")
+
+ call smark (sp)
+ call salloc (kw, 8, TY_CHAR)
+ call salloc (card, 68, TY_CHAR)
+
+ call malloc (coeffs, 100, TY_REAL)
+ iferr {
+ nc = 0
+ do i = 1, ARB {
+ call sprintf (Memc[kw], 8, "%.6s%02d")
+ call pargstr (key)
+ call pargi (i)
+ iferr (call imgstr (im, Memc[kw], Memc[card], 68))
+ break
+ j = 1
+ while (ctor (Memc[card], j, Memr[coeffs+nc]) != 0) {
+ nc = nc + 1
+ if (mod (nc, 100) == 0)
+ call realloc (coeffs, nc+100, TY_REAL)
+ }
+ }
+
+ if (nc == 0)
+ call error (1, "Surface fit not found")
+
+ call gsrestore (gs, Memr[coeffs])
+ call mfree (coeffs, TY_REAL)
+ } then {
+ call mfree (coeffs, TY_REAL)
+ call erract (EA_ERROR)
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/nproto/ace/mim.x b/noao/nproto/ace/mim.x
new file mode 100644
index 00000000..9a621e40
--- /dev/null
+++ b/noao/nproto/ace/mim.x
@@ -0,0 +1,544 @@
+# MIM (Match IMage) -- Match a 2D image to a 2D reference image.
+#
+# These routines provide an I/O interface to get data from a 2D image which
+# matches a line of a 2D reference image. The two common uses are to get a
+# subraster of the image which matches the reference image and to interpolate
+# an image which is blocked to a lower resolution than the reference image.
+# The matching is done in physical pixel coordinates. It is completely
+# general in allowing any linear transformation between the physical
+# coordinates. But in most cases the reference image and the input image
+# will be related either by an image section or some kind of blocking factor
+# without rotation. Any relative rotation of the two in physical pixels is
+# likely to be slow for large images (either the reference image or the mim
+# image). Interpolation (if any is required) is done with the MSI library.
+# Extrapolation outside of the input image uses the nearest edge value.
+#
+# mim = mim_open (input, refim)
+# buf = mim_glr (mim, refline)
+# mim_close (mim)
+#
+# Parameters may be queried and set by the following routines.
+#
+# mim_geti (mim, param, val)
+# mim_getr (mim, param, val)
+# mim_gets (mim, param, str, maxchar)
+# mim_seti (mim, param, val)
+# mim_setr (mim, param, val)
+# mim_sets (mim, param, str)
+#
+# The parameters are specified by strings as given below. The default values
+# are in parentheses. Currently there are only integer parameters.
+#
+# msitype - interpolation type defined by the MSI library
+# (II_BISPLINE3)
+# msiedge - number of additional lines at each edge to include
+# in interpolation (3)
+# msimax - maximum number of pixels to allow in MSIFIT calls (500000)
+
+
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include <math/iminterp.h>
+
+# Data structure.
+define MIM_LEN 18
+define MIM_INTERP Memi[$1] # Use interpolation?
+define MIM_ROTATE Memi[$1+1] # Is there any rotation?
+define MIM_IM Memi[$1+2] # IMIO mim pointer
+define MIM_MSI Memi[$1+3] # MSI interpolation pointer
+define MIM_NCREF Memi[$1+4] # Number of columns in ref image
+define MIM_NC Memi[$1+5] # Number of columns in input image
+define MIM_NL Memi[$1+6] # Number of lines in input image
+define MIM_LINE1 Memi[$1+7] # First line in msi fit
+define MIM_LINE2 Memi[$1+8] # Last line in msi fit
+define MIM_X Memi[$1+9] # Pointer to line of x values
+define MIM_Y Memi[$1+10] # Pointer to line of y values
+define MIM_Z Memi[$1+11] # Pointer to line of z values
+define MIM_MW Memi[$1+12] # MWCS pointer
+define MIM_CT Memi[$1+13] # CT from ref logical to input logical
+define MIM_MSITYPE Memi[$1+14] # MSI interpolation type
+define MIM_MSIEDGE Memi[$1+15] # Number of edge pixels to reserve
+define MIM_MSIMAX Memi[$1+16] # Maximum number of pixels in msi fit
+define MIM_DELETE Memi[$1+17] # Delete image after closing?
+
+# Defaults
+define MIM_MSITYPEDEF II_BISPLINE3
+define MIM_MSIEDGEDEF 3
+define MIM_MSIMAXDEF 500000
+
+
+# MIM_GL -- Get a line of data matching a line of the reference image.
+# A pointer to the data is returned. The data buffer is assumed to be
+# read-only and not to be modified by the calling routine.
+
+pointer procedure mim_glr (mim, line)
+
+pointer mim #I Map pointer
+int line #I Reference image line
+
+int i, j, nc, nl, ncref, line1, line2, nlines
+pointer msi, ct, x, y, z, imname, ptr
+real rnl, val
+
+real mw_c1tranr()
+pointer imgl2r(), imgs2r()
+
+errchk imgl2r, msiinit, msifit, imdelete
+
+begin
+ if (mim == NULL)
+ call error (1, "Map is undefined")
+
+ # If interpolation is not needed return the IMIO buffer.
+ if (MIM_INTERP(mim) == NO) {
+ ptr = imgl2r (MIM_IM(mim), line)
+ return (ptr)
+ }
+
+ nc = MIM_NC(mim)
+ nl = MIM_NL(mim)
+ ncref = MIM_NCREF(mim)
+ rnl = nl
+ msi = MIM_MSI(mim)
+ ct = MIM_CT(mim)
+ x = MIM_X(mim)
+ y = MIM_Y(mim)
+ z = MIM_Z(mim)
+
+ # Set the interpolation coordinates in the input image logical pixels.
+ # This is limited to be within the input image. Therefore, requests
+ # outside the input image will use the nearest edge value.
+ # Also set the minimum range of input lines required.
+
+ if (MIM_ROTATE(mim) == NO) {
+ val = mw_c1tranr (ct, real(line))
+ val = max (1., min (rnl, val))
+ call amovkr (val, Memr[y], ncref)
+ line1 = max (1., val - 1)
+ line2 = min (rnl, val + 1)
+ } else {
+ call amovkr (real(line), Memr[y], ncref)
+ call mw_v2tranr (ct, Memr[x], Memr[y], Memr[z], Memr[y], ncref)
+ x = z
+
+ # Limit the x range to within the input image.
+ ptr = x
+ val = nc
+ do i = 1, ncref {
+ Memr[ptr] = max (1., min (val, Memr[ptr]))
+ ptr = ptr + 1
+ }
+
+ # Limit the y range to within the input image and find the range
+ # of lines required.
+ j = nint (Memr[y])
+ line1 = max (1, min (nl, j))
+ line2 = line1
+ ptr = y
+ rnl = nl
+ do i = 1, ncref {
+ val = max (1., min (rnl, Memr[ptr]))
+ j = nint (val)
+ line1 = min (j, line1)
+ line2 = max (j, line2)
+ Memr[ptr] = val
+ ptr = ptr + 1
+ }
+ line1 = max (1, line1 - 1)
+ line2 = min (nl, line2 + 1)
+ }
+
+ # Set or reset image interpolator. For small input interpolation
+ # images read the entire image, fit the interpolator, and free the
+ # image. For larger input images determine the range of lines
+ # required including edge space and fit the interpolator to those
+ # lines. Providing the reference lines are requested sequentially
+ # this is about as efficient as we can make it.
+
+ if (line1 < MIM_LINE1(mim) || line2 > MIM_LINE2(mim)) {
+ if (msi != NULL)
+ call msifree (MIM_MSI(mim))
+ if (min (nc, nl) > 3)
+ call msiinit (MIM_MSI(mim), MIM_MSITYPE(mim))
+ else if (min (nc, nl) > 1)
+ call msiinit (MIM_MSI(mim), II_BILINEAR)
+ else
+ call msiinit (MIM_MSI(mim), II_BINEAREST)
+ msi = MIM_MSI(mim)
+ if (nc * nl <= MIM_MSIMAX(mim)) {
+ nlines = nl
+ line1 = 1
+ line2 = nlines
+ ptr = imgs2r (MIM_IM(mim), 1, nc, line1, line2)
+ call msifit (msi, Memr[ptr], nc, nlines, nc)
+ if (MIM_DELETE(mim) == YES) {
+ call malloc (imname, SZ_FNAME, TY_CHAR)
+ call imstats (MIM_IM(mim), IM_IMAGENAME, Memc[imname],
+ SZ_FNAME)
+ call imgimage (Memc[imname], Memc[imname], SZ_FNAME)
+ call imunmap (MIM_IM(mim))
+ call imdelete (Memc[imname])
+ call mfree (imname, TY_CHAR)
+ } else
+ call imunmap (MIM_IM(mim))
+ } else {
+ nlines = max (2*MIM_MSIEDGE(mim)+(line2-line1+1),
+ MIM_MSIMAX(mim) / nc)
+ line1 = max (1, min (nl, line1 - MIM_MSIEDGE(mim)))
+ line2 = max (1, min (nl, line1 + nlines - 1))
+ line1 = max (1, min (nl, line2 - nlines + 1))
+ nlines = line2 - line1 + 1
+ ptr = imgs2r (MIM_IM(mim), 1, nc, line1, line2)
+ call msifit (msi, Memr[ptr], nc, nlines, nc)
+ }
+ MIM_LINE1(mim) = line1
+ MIM_LINE2(mim) = line2
+ }
+
+ # Interpolate input image to a line in the reference image.
+ call msivector (msi, Memr[x], Memr[y], Memr[z], ncref)
+
+ return (z)
+end
+
+
+# MIM_OPEN -- Open an image matched to a reference image.
+#
+# Fitting of any interpolator is later. This allows calls to reset
+# the interpolation type, edge buffer, and maximum size to fit.
+
+pointer procedure mim_open (input, refim)
+
+char input[ARB] #I Input image name
+pointer refim #I Reference image
+pointer mim #O Map pointer returned
+
+bool interp, rotate
+int i, nc, nl, ncref, nlref, ilt[6]
+double lt[6], ltref[6], ltin[6]
+pointer sp, section, im, mw, ct, x, ptr
+
+int strlen(), btoi()
+pointer immap(), mw_openim(), mw_sctran()
+errchk calloc, malloc
+errchk immap
+errchk mw_openim, mw_invertd, mw_sctran
+
+begin
+ call smark (sp)
+ call salloc (section, SZ_FNAME, TY_CHAR)
+
+ iferr {
+ mim = NULL; im = NULL; mw = NULL
+
+ call calloc (mim, MIM_LEN, TY_STRUCT)
+ MIM_DELETE(mim) = NO
+
+ call imgimage (input, Memc[section], SZ_FNAME)
+ ptr = immap (Memc[section], READ_ONLY, 0); im = ptr
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ ncref = IM_LEN(refim,1)
+ nlref = IM_LEN(refim,2)
+
+ # Check relationship between reference and input images in physical
+ # coordinates.
+
+ ptr = mw_openim (refim); mw = ptr
+ call mw_gltermd (mw, lt, lt[5], 2)
+ call mw_close (mw)
+
+ mw = mw_openim (im)
+ call mw_gltermd (mw, ltin, ltin[5], 2)
+
+ # Combine lterms.
+ call mw_invertd (lt, ltref, 2)
+ call mw_mmuld (ltref, ltin, lt, 2)
+ call mw_vmuld (lt, lt[5], lt[5], 2)
+ lt[5] = ltin[5] - lt[5]
+ lt[6] = ltin[6] - lt[6]
+ do i = 1, 6
+ lt[i] = nint (1D6 * lt[i]) / 1D6
+
+ # Check if interpolation is required.
+ interp = false
+ do i = 1, 6 {
+ ilt[i] = nint (lt[i])
+ if (lt[i] - ilt[i] > 1D-3) {
+ interp = true
+ break
+ }
+ }
+ if (lt[2] != 0. || lt[3] != 0.)
+ rotate = true
+ else
+ rotate = false
+ if (!interp && rotate)
+ interp = true
+
+ if (interp) {
+ # Use IMIO to extract a smaller section if possible to
+ # minimize the requirements for the interpolation.
+ # This could be more general if we deal with a section
+ # of a rotated image.
+
+ if (!rotate) {
+ ilt[1] = lt[1] + lt[5]
+ ilt[2] = lt[1] * ncref + lt[5] + 0.999
+ ilt[3] = lt[3] + lt[4] + lt[6]
+ ilt[4] = lt[4] * nlref + lt[6] + 0.999
+ ilt[1] = max (1, min (nc, ilt[1]))
+ ilt[2] = max (1, min (nc, ilt[2]))
+ ilt[3] = max (1, min (nl, ilt[3]))
+ ilt[4] = max (1, min (nl, ilt[4]))
+ if (ilt[1]!=1 || ilt[2]!=nc ||ilt[1]!=1 || ilt[2]!=nl) {
+ i = strlen(Memc[section]) + 1
+ call sprintf (Memc[section+i-1], SZ_FNAME-i,
+ "[%d:%d,%d:%d]")
+ call pargi (ilt[1])
+ call pargi (ilt[2])
+ call pargi (ilt[3])
+ call pargi (ilt[4])
+ call imunmap (im)
+ im = immap (Memc[section], READ_ONLY, 0)
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ lt[5] = lt[5] - ilt[1] + 1
+ lt[6] = lt[6] - ilt[3] + 1
+ }
+ }
+
+ # Set reference logical to input logical transformation.
+ # The reference logical coordinates are the physical
+ # coordinates of the transformation.
+
+ call mw_sltermd (mw, lt, lt[5], 2)
+
+ # If there are cross terms set the x array to the reference
+ # logical coordinates (physical transformation coordinates).
+ # Otherwise we only need to evalute x array once in the
+ # input logical coordinates to be interpolated.
+
+ call malloc (x, ncref, TY_REAL)
+ do i = 1, ncref
+ Memr[x+i-1] = i
+ if (rotate)
+ ct = mw_sctran (mw, "physical", "logical", 3B)
+ else {
+ ct = mw_sctran (mw, "physical", "logical", 1B)
+ call mw_v1tranr (ct, Memr[x], Memr[x], ncref)
+ ptr = x
+ do i = 1, ncref {
+ Memr[ptr] = max (1., min (real(nc), Memr[ptr]))
+ ptr = ptr + 1
+ }
+ call mw_ctfree (ct)
+ ct = mw_sctran (mw, "physical", "logical", 2B)
+ }
+
+ MIM_X(mim) = x
+ call malloc (MIM_Y(mim), ncref, TY_REAL)
+ call malloc (MIM_Z(mim), ncref, TY_REAL)
+ MIM_MW(mim) = mw
+ MIM_CT(mim) = ct
+ MIM_MSITYPE(mim) = MIM_MSITYPEDEF
+ MIM_MSIEDGE(mim) = MIM_MSIEDGEDEF
+ MIM_MSIMAX(mim) = MIM_MSIMAXDEF
+
+ } else {
+ # If ref is a subraster of the input use IMIO section to match.
+ if (ilt[1]!=1 || ilt[4]!=1 || ilt[5]!=0 || ilt[6]!=0) {
+ i = strlen(Memc[section]) + 1
+ call sprintf (Memc[section+i-1], SZ_FNAME-i,
+ "[%d:%d:%d,%d:%d:%d]")
+ call pargi (ilt[1]+ilt[5])
+ call pargi (ilt[1]*ncref+ilt[5])
+ call pargi (ilt[1])
+ call pargi (ilt[4]+ilt[6])
+ call pargi (ilt[4]*nlref+ilt[6])
+ call pargi (ilt[4])
+ call imunmap (im)
+ im = immap (Memc[section], READ_ONLY, 0)
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ }
+ call mw_close (mw)
+ }
+
+ MIM_IM(mim) = im
+ MIM_INTERP(mim) = btoi (interp)
+ MIM_ROTATE(mim) = btoi (rotate)
+ MIM_NC(mim) = nc
+ MIM_NL(mim) = nl
+ MIM_NCREF(mim) = ncref
+ } then {
+ if (mw != NULL)
+ call mw_close (mw)
+ if (im != NULL)
+ call imunmap (im)
+ call mim_close (mim)
+ call sfree (sp)
+ call erract (EA_ERROR)
+ }
+
+ call sfree (sp)
+ return (mim)
+end
+
+
+# MIM_CLOSE -- Close mim structure.
+
+procedure mim_close (mim)
+
+pointer mim #I MIM pointer
+
+pointer imname
+errchk imdelete
+
+begin
+ if (mim == NULL)
+ return
+
+ if (MIM_IM(mim) != NULL) {
+ if (MIM_DELETE(mim) == YES) {
+ call malloc (imname, SZ_FNAME, TY_CHAR)
+ call imstats (MIM_IM(mim), IM_IMAGENAME, Memc[imname], SZ_FNAME)
+ call imgimage (Memc[imname], Memc[imname], SZ_FNAME)
+ call imunmap (MIM_IM(mim))
+ call imdelete (Memc[imname])
+ call mfree (imname, TY_CHAR)
+ } else
+ call imunmap (MIM_IM(mim))
+ }
+ if (MIM_MSI(mim) != NULL)
+ call msifree (MIM_MSI(mim))
+ if (MIM_MW(mim) != NULL)
+ call mw_close (MIM_MW(mim))
+ call mfree (MIM_X(mim), TY_REAL)
+ call mfree (MIM_Y(mim), TY_REAL)
+ call mfree (MIM_Z(mim), TY_REAL)
+ call mfree (mim, TY_STRUCT)
+end
+
+
+# MIM_GETS -- Get string parameter.
+
+procedure mim_gets (mim, param, val, maxchar)
+
+pointer mim #I MIM pointer
+char param[ARB] #I Parameter
+char val[ARB] #O Parameter string value
+int maxchar #I Maximum number of characters to return
+
+begin
+ call error (1, "mim_gets: unknown parameter")
+end
+
+
+# MIM_GETI -- Get integer parameter.
+
+procedure mim_geti (mim, param, val)
+
+pointer mim #I MIM pointer
+char param[ARB] #I Parameter
+int val #O Value
+
+bool streq()
+
+begin
+ if (streq (param, "msitype"))
+ val = MIM_MSITYPE(mim)
+ else if (streq (param, "msiedge"))
+ val = MIM_MSIEDGE(mim)
+ else if (streq (param, "msimax"))
+ val = MIM_MSIMAX(mim)
+ else if (streq (param, "delete"))
+ val = MIM_DELETE(mim)
+ else
+ call error (1, "mim_geti: unknown parameter")
+end
+
+
+# MIM_GETR -- Get real parameter.
+
+procedure mim_getr (mim, param, val)
+
+pointer mim #I MIM pointer
+char param[ARB] #I Parameter
+real val #O Value
+
+begin
+ call error (1, "mim_getr: unknown parameter")
+end
+
+
+# MIM_SETS -- Set string parameter.
+
+procedure mim_sets (mim, param, val)
+
+pointer mim #I MIM pointer
+char param[ARB] #I Parameter
+char val[ARB] #I Value
+
+begin
+ call error (1, "mim_sets: unknown parameter")
+end
+
+
+# MIM_SETI -- Set integer parameter.
+
+procedure mim_seti (mim, param, val)
+
+pointer mim #I MIM pointer
+char param[ARB] #I Parameter
+int val #I Value
+
+bool streq()
+
+begin
+ if (streq (param, "msitype")) {
+ if (val != MIM_MSITYPE(mim)) {
+ MIM_MSITYPE(mim) = val
+ if (MIM_MSI(mim) != NULL) {
+ call msifree (MIM_MSI(mim))
+ MIM_LINE1(mim) = 0
+ MIM_LINE2(mim) = 0
+ }
+ }
+ } else if (streq (param, "msiedge")) {
+ if (val != max (3, MIM_MSIEDGE(mim))) {
+ MIM_MSIEDGE(mim) = val
+ if (MIM_MSI(mim) != NULL) {
+ call msifree (MIM_MSI(mim))
+ MIM_LINE1(mim) = 0
+ MIM_LINE2(mim) = 0
+ }
+ }
+ } else if (streq (param, "msimax")) {
+ if (val != max (64000, MIM_MSIMAX(mim))) {
+ MIM_MSIMAX(mim) = val
+ if (MIM_MSI(mim) != NULL) {
+ call msifree (MIM_MSI(mim))
+ MIM_LINE1(mim) = 0
+ MIM_LINE2(mim) = 0
+ }
+ }
+ } else if (streq (param, "delete"))
+ MIM_DELETE(mim) = val
+ else
+ call error (1, "mim_setr: unknown parameter")
+end
+
+
+# MIM_SETR -- Set real parameter.
+
+procedure mim_setr (mim, param, val)
+
+pointer mim #I MIM pointer
+char param[ARB] #I Parameter
+real val #I Value
+
+begin
+ call error (1, "mim_setr: unknown parameter")
+end
diff --git a/noao/nproto/ace/mkpkg b/noao/nproto/ace/mkpkg
new file mode 100644
index 00000000..d385a296
--- /dev/null
+++ b/noao/nproto/ace/mkpkg
@@ -0,0 +1,60 @@
+# Make ACE.
+
+$call relink
+$exit
+
+update:
+ $call relink
+ $call install
+ ;
+
+relink:
+ $update libpkg.a
+ $omake x_ace.x
+ $link x_ace.o libpkg.a -lds -lgsurfit -lcurfit -lxtools -liminterp\
+ -ltbtables -o xx_ace.e
+ ;
+
+install:
+ $move xx_ace.e acebin$x_ace.e
+ ;
+
+libpkg.a:
+ aceoverlay.x ace.h <error.h> <imhdr.h> <imset.h> <pmset.h>
+ bndry.x ace.h <pmset.h>
+ catdefine.x ace.h cat.h objs.h
+ catio.x ace.h cat.h <imset.h> <math.h> objs.h
+ convolve.x <ctype.h> <imhdr.h>
+ detect.x ace.h cat.h detect.h <imhdr.h> <mach.h> objs.h\
+ <pmset.h> skyblock.h split.h
+ evaluate.x ace.h cat.h <error.h> evaluate.h <imhdr.h> objs.h\
+ <pmset.h>
+ filter.x ace.h <evvexpr.h> filter.h objs.h
+ grow.x ace.h cat.h grow.h objs.h <pmset.h>
+ mapio.x <error.h> <imhdr.h>
+ maskcolor.x
+ mgs.x <error.h> <imhdr.h> <imio.h> <math/gsurfit.h>
+ mim.x <error.h> <imhdr.h> <imset.h> <math/iminterp.h>
+ noisemodel.x
+ omwrite.x <imhdr.h> <pmset.h> ace.h
+ pars.x <ctype.h> detect.h evaluate.h grow.h <math/curfit.h>\
+ <math/gsurfit.h> skyblock.h skyfit.h sky.h split.h
+ skyblock.x <ctype.h> <error.h> <imhdr.h> <imset.h> <mach.h>\
+ skyblock.h
+ skyfit.x <imhdr.h> <math/curfit.h> <math/gsurfit.h> skyfit.h
+ skyimages.x <error.h> <imhdr.h>
+ sky.x <error.h> sky.h
+ split.x ace.h cat.h <mach.h> objs.h <pmset.h> split.h
+ tables.x
+ t_acedetect.x ace.h acedetect.h cat.h <error.h> <fset.h> <imhdr.h>\
+ <imset.h> <pmset.h>
+ t_acedisplay.x <ctype.h> display.h <error.h> gwindow.h <imhdr.h>\
+ <imhdr.h> <imset.h> <imset.h> <mach.h> <mach.h>\
+ <pmset.h>
+ t_imext.x <ctype.h> <error.h> <imhdr.h> <imset.h> <mach.h>
+ t_mscext.x <error.h> <imhdr.h> <imset.h>
+ x_ace.x
+ xtmaskname.x
+ xtpmmap.x <ctype.h> <error.h> <imhdr.h> <imset.h> <mach.h>\
+ <mwset.h> <pmset.h>
+ ;
diff --git a/noao/nproto/ace/noisemodel.x b/noao/nproto/ace/noisemodel.x
new file mode 100644
index 00000000..0503c4a1
--- /dev/null
+++ b/noao/nproto/ace/noisemodel.x
@@ -0,0 +1,102 @@
+# NOISEMODEL -- Compute noise model.
+#
+# var = (var(sky) + (image-sky)/gain) / sqrt (exposure)
+#
+# What is actually returned is the square root of the variance.
+# The variance of the sky and the effective gain are for a unit
+# exposure in the exposure map.
+
+procedure noisemodel (image, sky, sig, gain, exp, sigma, npix)
+
+real image[npix] #I Image
+real sky[npix] #I Sky
+real sig[npix] #I Sky sigma
+real gain[npix] #I Gain
+real exp[npix] #I Exposure
+real sigma[npix] #O Sigma
+int npix #I Number of pixels
+
+int i
+real e, elast, sqrte
+
+begin
+ if (IS_INDEFR(exp[1])) {
+ if (IS_INDEFR(gain[1]))
+ call amovr (sig, sigma, npix)
+ else {
+ do i = 1, npix
+ sigma[i] = sqrt (sig[i] * sig[1] +
+ (image[i] - sky[i]) / gain[i])
+ }
+ } else if (IS_INDEFR(gain[1])) {
+ elast = INDEFR
+ do i = 1, npix {
+ e = exp[i]
+ if (e == 0.) {
+ sigma[i] = sig[i]
+ next
+ }
+ if (e != elast) {
+ sqrte = sqrt (e)
+ elast = e
+ }
+ sigma[i] = sig[i] / sqrte
+ }
+ } else {
+ do i = 1, npix {
+ e = exp[i]
+ if (e == 0.) {
+ sigma[i] = sqrt (sig[i] * sig[i] +
+ (image[i] - sky[i]) / gain[i])
+ next
+ }
+ sigma[i] = sqrt ((sig[i] * sig[i] +
+ (image[i] - sky[i]) / gain[i]) / e)
+ }
+ }
+end
+
+
+# EXPSIGMA -- Apply exposure map to correct sky sigma.
+# Assume the exposure map has region of contiguous constant values so
+# that the number of square roots can be minimized. An exposure map
+# value of zero leaves the sigma unchanged.
+
+procedure expsigma (sigma, expmap, npix, mode)
+
+real sigma[npix] #U Sigma values
+real expmap[npix] #I Exposure map values
+int npix #I Number of pixels
+int mode #I 0=divide, 1=multiply
+
+int i
+real exp, lastexp, scale
+
+begin
+ switch (mode) {
+ case 0:
+ lastexp = INDEFR
+ do i = 1, npix {
+ exp = expmap[i]
+ if (exp == 0.)
+ next
+ if (exp != lastexp) {
+ scale = sqrt (exp)
+ lastexp = exp
+ }
+ sigma[i] = sigma[i] / scale
+ }
+ case 1:
+ lastexp = INDEFR
+ do i = 1, npix {
+ exp = expmap[i]
+ if (exp == 0.)
+ next
+ if (exp != lastexp) {
+ scale = sqrt (exp)
+ lastexp = exp
+ }
+ sigma[i] = sigma[i] * scale
+ }
+ }
+end
diff --git a/noao/nproto/ace/objmasks.cl b/noao/nproto/ace/objmasks.cl
new file mode 100644
index 00000000..2ff5e201
--- /dev/null
+++ b/noao/nproto/ace/objmasks.cl
@@ -0,0 +1,28 @@
+# OBJMASK -- Make object masks from image data.
+
+procedure objmasks ()
+
+begin
+ detect (images, objmasks=objmasks, masks=masks, omtype=omtype,
+ skys=skys, sigmas=sigmas,
+ extnames=extnames, logfiles=logfiles, blkstep=blkstep,
+ blksize=blksize, convolve=convolve, hsigma=hsigma,
+ lsigma=lsigma, hdetect=hdetect, ldetect=ldetect,
+ neighbors=neighbors, minpix=minpix, ngrow=ngrow, agrow=agrow,
+ exps=objmasks1.exps, gains=objmasks1.gains,
+ catalogs=objmasks1.catalogs, catdefs=objmasks1.catdefs,
+ dodetect=objmasks1.dodetect, dosplit=objmasks1.dosplit,
+ dogrow=objmasks1.dogrow, doevaluate=objmasks1.doevaluate,
+ skytype=objmasks1.skytype, fitstep=objmasks1.fitstep,
+ fitblk1d=objmasks1.fitblk1d, fithclip=objmasks1.fithclip,
+ fitlclip=objmasks1.fitlclip, fitxorder=objmasks1.fitxorder,
+ fityorder=objmasks1.fityorder, fitxterms=objmasks1.fitxterms,
+ blknsubblks=objmasks1.blknsubblks,
+ updatesky=objmasks1.updatesky, sigavg=objmasks1.sigavg,
+ sigmax=objmasks1.sigmax, bpval=objmasks1.bpval,
+ splitmax=objmasks1.splitmax, splitstep=objmasks1.splitstep,
+ splitthresh=objmasks1.splitthresh, sminpix=objmasks1.sminpix,
+ ssigavg=objmasks1.ssigavg, ssigmax=objmasks1.ssigmax,
+ magzero=objmasks1.magzero)
+
+end
diff --git a/noao/nproto/ace/objmasks.par b/noao/nproto/ace/objmasks.par
new file mode 100644
index 00000000..d77ceffe
--- /dev/null
+++ b/noao/nproto/ace/objmasks.par
@@ -0,0 +1,22 @@
+# OBJMASKS
+
+images,f,a,,,,"List of images or MEF files"
+objmasks,s,a,"",,,"List of output object masks"
+omtype,s,h,"numbers","boolean|numbers|colors|all",,"Object mask type"
+skys,s,h,"",,,"List of input/output sky maps"
+sigmas,s,h,"",,,"List of input/output sigma maps"
+masks,s,h,"!BPM",,,"List of input bad pixel masks"
+extnames,s,h,"",,,"Extension names"
+logfiles,s,h,"STDOUT",,,"List of log files
+"
+blkstep,i,h,1,1,,"Line step for sky sampling"
+blksize,i,h,-10,,,"Sky block size (+=pixels, -=blocks)"
+convolve,s,h,"block 3 3",,,"Convolution kernel"
+hsigma,r,h,3.,.1,,"Sigma threshold above sky"
+lsigma,r,h,10.,.1,,"Sigma threshold below sky"
+hdetect,b,h,yes,,,"Detect objects above sky?"
+ldetect,b,h,no,,,"Detect objects below sky?"
+neighbors,s,h,"8","4|8",,Neighbor type"
+minpix,i,h,6,1,,"Minimum number of pixels in detected objects"
+ngrow,i,h,2,0,,"Number of grow rings"
+agrow,r,h,2.,0,,"Area grow factor"
diff --git a/noao/nproto/ace/objmasks1.par b/noao/nproto/ace/objmasks1.par
new file mode 100644
index 00000000..9d822a88
--- /dev/null
+++ b/noao/nproto/ace/objmasks1.par
@@ -0,0 +1,30 @@
+# OBJMASKS1
+
+exps,s,h,"",,,"List of exposure maps"
+gains,s,h,"",,,"List of gain maps"
+catalogs,s,h,"",,,"List of catalogs"
+catdefs,s,h,"",,,"List of catalog definitions"
+dodetect,b,h,yes,,,"Detect objects?"
+dosplit,b,h,no,,,"Split merged objects?"
+dogrow,b,h,yes,,,"Grow object regions?"
+doevaluate,b,h,no,,,"Evaluate objects?"
+skytype,s,h,"block","fit|block",,"Type of sky estimation"
+fitstep,i,h,100,1,,"Line step for sky sampling"
+fitblk1d,i,h,10,,,"Block average for line fitting"
+fithclip,r,h,2.,,,"High sky clipping during 1D sky estimation"
+fitlclip,r,h,3.,,,"Low sky clippling during 1D sky estimation"
+fitxorder,i,h,2,1,,"Sky fitting x order"
+fityorder,i,h,2,1,,"Sky fitting y order"
+fitxterms,s,h,"half","none|full|half",,"Sky fitting cross terms"
+blknsubblks,i,h,2,1,,"Number of subblocks per axis"
+updatesky,b,h,yes,,,"Update sky during detection?"
+sigavg,r,h,4.,0.,,"Sigma of mean flux cutoff"
+sigmax,r,h,4.,0.,,"Sigma of maximum pixel"
+bpval,i,h,INDEF,,,"Output bad pixel value"
+splitmax,r,h,INDEF,,,"Maximum sigma above sky for splitting"
+splitstep,r,h,0.4,,,"Splitting steps in convolved sigma"
+splitthresh,r,h,5.,,,"Splitting threshold in sigma"
+sminpix,i,h,8,1,,"Minimum number of pixels in split objects"
+ssigavg,r,h,10.,0.,,"Sigma of mean flux cutoff"
+ssigmax,r,h,5.,0.,,"Sigma of maximum pixel"
+magzero,s,h,"INDEF",,,"Magnitude zero point"
diff --git a/noao/nproto/ace/objs.h b/noao/nproto/ace/objs.h
new file mode 100644
index 00000000..20dff95a
--- /dev/null
+++ b/noao/nproto/ace/objs.h
@@ -0,0 +1,139 @@
+# This file defines the object parameters.
+
+# The following are the parameter ids which are the offsets into the object
+# data structure. Note that the first group of parameters are those
+# determined during detection for potential objects. The second group
+# are parameters added after an object has been accepted.
+
+define ID_ROW 0 # i "" "" "Catalog row"
+define ID_NUM 1 # i "" "" "Object number"
+define ID_PNUM 2 # i "" "" "Parent number"
+define ID_XAP 3 # r pixels %.2f "X aperture coordinate"
+define ID_YAP 4 # r pixels %.2f "Y aperture coordinate"
+define ID_FLUX 5 # r counts "" "Isophotal flux (I - sky)"
+define ID_NPIX 6 # i pixels "" "Number of pixels"
+define ID_NDETECT 7 # i pixels "" "Number of detected pixels"
+define ID_ISIGAVG 8 # r sigma "" "Average (I - sky) / sig"
+define ID_ISIGMAX 9 # r sigma "" "Maximum (I - sky) / sig"
+define ID_ISIGAVG2 10 # r sigma "" "*Ref average (I - sky) / sig"
+define ID_FLAGS 11 # 8 "" "" "Flags"
+
+define ID_SKY 12 # r counts "" "Mean sky"
+define ID_SIG 13 # r counts "" "Sky sigma"
+define ID_PEAK 14 # r counts "" "Peak pixel value above sky"
+define ID_APFLUX 15 # r counts "" "Aperture fluxes"
+define ID_FRACFLUX 16 # r counts "" "Apportioned flux"
+define ID_FRAC 17 # r "" "" "Apportioned fraction"
+define ID_XMIN 18 # i pixels "" "Minimum X"
+define ID_XMAX 19 # i pixels "" "Maxium X"
+define ID_YMIN 20 # i pixels "" "Minimum Y"
+define ID_YMAX 21 # i pixels "" "Maxium Y"
+define ID_WX 22 # d pixels %.2f "X world coordinate"
+define ID_WY 24 # d pixels %.2f "Y world coordinate"
+define ID_X1 26 # r pixels %.2f "X centroid"
+define ID_Y1 27 # r pixels %.2f "Y centroid"
+define ID_X2 28 # r pixels "" "X 2nd moment"
+define ID_Y2 29 # r pixels "" "Y 2nd moment"
+define ID_XY 30 # r pixels "" "X 2nd cross moment"
+
+define ID_FLUXVAR 31 # r counts "" "*Variance in the flux"
+define ID_XVAR 32 # r pixels "" "*Variance in X centroid"
+define ID_YVAR 33 # r pixels "" "*Variance in Y centroid"
+define ID_XYCOV 34 # r pixels "" "*Covariance of X and Y"
+
+# The following are derived quantities which have ids above 1000.
+
+define ID_A 1001 # r pixels "" "Semimajor axis"
+define ID_B 1002 # r pixels "" "Semiminor axis"
+define ID_THETA 1003 # r degrees "" "Position angle"
+define ID_ELONG 1004 # r "" "" "Elongation = A/B"
+define ID_ELLIP 1005 # r "" "" "Ellipticity = 1 - B/A"
+define ID_R 1006 # r pixels "" "Second moment radius"
+define ID_CXX 1007 # r pixels "" "Second moment ellipse"
+define ID_CYY 1008 # r pixels "" "Second moment ellipse"
+define ID_CXY 1009 # r pixels "" "Second moment ellipse"
+
+define ID_FLUXERR 1011 # r counts "" "Error in flux"
+define ID_XERR 1012 # r pixels "" "Error in X centroid"
+define ID_YERR 1013 # r pixels "" "Error in Y centroid"
+define ID_AERR 1014 # r "" "" "Error in A"
+define ID_BERR 1015 # r "" "" "Error in B"
+define ID_THETAERR 1016 # r degrees "" "Error in THETA"
+define ID_CXXERR 1017 # r pixels "" "Error in CXX"
+define ID_CYYERR 1018 # r pixels "" "Error in CYY"
+define ID_CXYERR 1019 # r pixels "" "Error in CXY"
+
+
+# Reference to elements of the object data structure may be made with
+# the generic OBJ[IRDC] macros or with the individual structure macros.
+
+define OBJI Memi[$1+$2] # Reference integer parameter
+define OBJR Memr[P2R($1+$2)] # Reference real parameter
+define OBJD Memd[P2D($1+$2)] # Reference double parameter
+define OBJC Memc[P2C($1+$2)] # Reference char parameter
+
+define OBJ_DETLEN 12 # Length for candidate objects
+define OBJ_LEN 35 # Length for accepted objects
+
+# Detection pass parameters.
+define OBJ_ROW OBJI($1,ID_ROW) # Catalog row
+define OBJ_NUM OBJI($1,ID_NUM) # Object number
+define OBJ_PNUM OBJI($1,ID_PNUM) # Parent object number
+define OBJ_XAP OBJR($1,ID_XAP) # X aperture coordinate
+define OBJ_YAP OBJR($1,ID_YAP) # Y aperture coordinate
+define OBJ_NPIX OBJI($1,ID_NPIX) # Number of pixels
+define OBJ_NDETECT OBJI($1,ID_NDETECT) # Number of detected pixels
+define OBJ_ISIGAVG OBJR($1,ID_ISIGAVG) # Average (I - sky) / sig
+define OBJ_ISIGMAX OBJR($1,ID_ISIGMAX) # Maximum (I - sky) / sig
+define OBJ_ISIGAVG2 OBJR($1,ID_ISIGAVG2) # Ref average (I - sky) / sig
+define OBJ_FLAGS OBJI($1,ID_FLAGS) # Flags
+
+define OBJ_SKY OBJR($1,ID_SKY) # Mean sky
+define OBJ_SIG OBJR($1,ID_SIG) # Sky sigma
+define OBJ_PEAK OBJR($1,ID_PEAK) # Peak pixel value above sky
+define OBJ_FLUX OBJR($1,ID_FLUX) # Isophotal flux (I - sky)
+define OBJ_APFLUX OBJI($1,ID_APFLUX) # Array of aperture fluxes (ptr)
+define OBJ_FRACFLUX OBJR($1,ID_FRACFLUX) # Apportioned flux
+define OBJ_FRAC OBJR($1,ID_FRAC) # Approtioned fraction
+define OBJ_XMIN OBJI($1,ID_XMIN) # Minimum X
+define OBJ_XMAX OBJI($1,ID_XMAX) # Maxium X
+define OBJ_YMIN OBJI($1,ID_YMIN) # Minimum Y
+define OBJ_YMAX OBJI($1,ID_YMAX) # Maxium Y
+define OBJ_WX OBJD($1,ID_WX) # X world coordinate
+define OBJ_WY OBJD($1,ID_WY) # Y world coordinate
+define OBJ_X1 OBJR($1,ID_X1) # X centroid
+define OBJ_Y1 OBJR($1,ID_Y1) # Y centroid
+define OBJ_X2 OBJR($1,ID_X2) # X centroid
+define OBJ_Y2 OBJR($1,ID_Y2) # Y centroid
+define OBJ_XY OBJR($1,ID_XY) # X centroid
+
+define OBJ_FLUXVAR OBJR($1,ID_FLUXVAR) # Variance in flux
+define OBJ_XVAR OBJR($1,ID_XVAR) # Variance in X centroid
+define OBJ_YVAR OBJR($1,ID_YVAR) # Variance in Y centroid
+define OBJ_XYCOV OBJR($1,ID_XYCOV) # Covariance of X and Y centroid
+
+
+
+
+# Object flags.
+define OBJ_EVAL 001B # Object was evaluated
+define OBJ_GROW 002B # Object was grown
+define OBJ_SPLIT 004B # Object was split
+define OBJ_SINGLE 010B # Object was not split
+define OBJ_DARK 020B # Object was below sky
+
+define FLAGSET (andi(OBJ_FLAGS($1),$2)!=0)
+define FLAGNOTSET (andi(OBJ_FLAGS($1),$2)==0)
+define SETFLAG OBJ_FLAGS($1)=ori(OBJ_FLAGS($1),$2)
+define UNSETFLAG OBJ_FLAGS($1)=andi(OBJ_FLAGS($1),noti($2))
+
+define DARK (andi(OBJ_FLAGS($1),OBJ_DARK)!=0)
+define EVAL (andi(OBJ_FLAGS($1),OBJ_EVAL)!=0)
+define SPLIT (andi(OBJ_FLAGS($1),OBJ_SPLIT)!=0)
+define NOTSPLIT (andi(OBJ_FLAGS($1),OBJ_SPLIT)==0)
+define SINGLE (andi(OBJ_FLAGS($1),OBJ_SINGLE)!=0)
+define NOTSINGLE (andi(OBJ_FLAGS($1),OBJ_SINGLE)==0)
+define GROWN (andi(OBJ_FLAGS($1),OBJ_GROW)!=0)
+define NOTGROWN (andi(OBJ_FLAGS($1),OBJ_GROW)==0)
+
+define SZ_FLAGS 5 # Size of flag string
diff --git a/noao/nproto/ace/omwrite.x b/noao/nproto/ace/omwrite.x
new file mode 100644
index 00000000..83b96d2f
--- /dev/null
+++ b/noao/nproto/ace/omwrite.x
@@ -0,0 +1,98 @@
+include <imhdr.h>
+include <pmset.h>
+include "ace.h"
+
+
+procedure omwrite (pm, fname, omtype, refim, cat, catalog, objid, logfd)
+
+pointer pm #I Pixel mask pointer to save
+char fname[ARB] #I Filename
+int omtype #I Type of mask values
+pointer refim #I Reference image pointer
+pointer cat #I Catalog pointer
+char catalog[ARB] #I Catalog filename
+char objid[ARB] #I Object ID string
+int logfd #I Logfile
+
+int i, j, k, nc, nl, stridxs(), andi()
+long v[2]
+pointer sp, str, im, buf, immap(), impl2i()
+
+errchk immap
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Remove output only fields.
+ call strcpy (fname, Memc[str], SZ_LINE)
+ i = stridxs (",", fname)
+ if (i > 0) {
+ Memc[str+i-1] = ']'
+ Memc[str+i] = EOS
+ }
+
+ if (logfd != NULL) {
+ call fprintf (logfd, " Write object mask: %s\n")
+ call pargstr (Memc[str])
+ }
+
+ im = immap (fname, NEW_COPY, refim)
+ IM_PIXTYPE(im) = TY_INT
+
+ nc = IM_LEN(refim,1)
+ nl = IM_LEN(refim,2)
+
+ v[1] = 1
+ switch (omtype) {
+ case OM_BOOL:
+ do i = 1, nl {
+ v[2] = i
+ buf = impl2i (im, i)
+ call pmglpi (pm, v, Memi[buf], 0, nc, PIX_SRC)
+ call aminki (Memi[buf], 1, Memi[buf], nc)
+ }
+ case OM_ONUM:
+ do i = 1, nl {
+ v[2] = i
+ buf = impl2i (im, i)
+ call pmglpi (pm, v, Memi[buf], 0, nc, PIX_SRC)
+ do j = buf, buf+nc-1
+ Memi[j] = MNUM(Memi[j])
+ }
+ case OM_COLORS:
+ do i = 1, nl {
+ v[2] = i
+ buf = impl2i (im, i)
+ call pmglpi (pm, v, Memi[buf], 0, nc, PIX_SRC)
+ do j = buf, buf+nc-1 {
+ k = MNUM(Memi[j])
+ if (k > 0) {
+ if (k < NUMSTART)
+ k = 1
+ else
+ k = mod (k, 8) + 2
+ }
+ Memi[j] = k
+ }
+ }
+ default:
+ do i = 1, nl {
+ v[2] = i
+ call pmglpi (pm, v, Memi[impl2i(im,i)], 0, nc, PIX_SRC)
+ }
+ }
+
+ iferr (call imdelf (im, "DATASEC"))
+ ;
+ iferr (call imdelf (im, "TRIMSEC"))
+ ;
+ if (catalog[1] != EOS)
+ call imastr (im, "CATALOG", catalog)
+ if (objid[1] != EOS)
+ call imastr (im, "OBJID", objid)
+
+ call imastr (refim, "OBJMASK", Memc[str])
+
+ call imunmap (im)
+end
diff --git a/noao/nproto/ace/overlay.par b/noao/nproto/ace/overlay.par
new file mode 100644
index 00000000..a5fad3f5
--- /dev/null
+++ b/noao/nproto/ace/overlay.par
@@ -0,0 +1,30 @@
+# Parameter file for DISPLAY
+
+image,f,a,,,,image to be displayed
+frame,i,a,1,1,4,frame to be written into
+bpmask,f,h,"",,,bad pixel mask
+bpdisplay,s,h,"none","none|overlay|interpolate",,bad pixel display (none|overlay|interpolate)
+bpcolors,s,h,"red",,,bad pixel colors
+overlay,f,h,"!objmask",,,overlay mask
+ocolors,s,h,"1=red,green",,,overlay colors
+erase,b,h,yes,,,erase frame
+border_erase,b,h,no,,,erase unfilled area of window
+select_frame,b,h,yes,,,display frame being loaded
+repeat,b,h,no,,,repeat previous display parameters
+fill,b,h,no,,,scale image to fit display window
+zscale,b,h,yes,,,display range of greylevels near median
+contrast,r,h,0.25,,,contrast adjustment for zscale algorithm
+zrange,b,h,yes,,,display full image intensity range
+zmask,f,h,"",,,sample mask
+nsample,i,h,1000,100,,maximum number of sample pixels to use
+xcenter,r,h,0.5,0,1,display window horizontal center
+ycenter,r,h,0.5,0,1,display window vertical center
+xsize,r,h,1,0,1,display window horizontal size
+ysize,r,h,1,0,1,display window vertical size
+xmag,r,h,1.,,,display window horizontal magnification
+ymag,r,h,1.,,,display window vertical magnification
+order,i,h,0,0,1,"spatial interpolator order (0=replicate, 1=linear)"
+z1,r,h,,,,minimum greylevel to be displayed
+z2,r,h,,,,maximum greylevel to be displayed
+ztrans,s,h,linear,,,greylevel transformation (linear|log|none|user)
+lutfile,f,h,"",,,file containing user defined look up table
diff --git a/noao/nproto/ace/pars.x b/noao/nproto/ace/pars.x
new file mode 100644
index 00000000..516a8b7d
--- /dev/null
+++ b/noao/nproto/ace/pars.x
@@ -0,0 +1,375 @@
+include <ctype.h>
+include <math/curfit.h>
+include <math/gsurfit.h>
+include "sky.h"
+include "skyfit.h"
+include "skyblock.h"
+include "detect.h"
+include "split.h"
+include "grow.h"
+include "evaluate.h"
+
+
+
+# SKY_PARS -- Sky parameters.
+
+procedure sky_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+pointer pp
+
+int strdic()
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+ call calloc (pars, SKY_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+ call clgpseta (pp, "skytype", SKY_STR(pars), SKY_STRLEN)
+ SKY_TYPE(pars) = strdic (SKY_STR(pars), SKY_STR(pars), SKY_STRLEN,
+ SKY_TYPES)
+ call clcpset (pp)
+ case 'c':
+ if (pars != NULL) {
+ call skf_pars ("close", "", SKY_SKF(pars))
+ call skb_pars ("close", "", SKY_SKB(pars))
+ }
+ call mfree (pars, TY_STRUCT)
+ }
+end
+
+
+# SKF_PARS -- Sky fit parameters.
+
+procedure skf_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+pointer pp
+
+int clgpseti(), strdic()
+real clgpsetr()
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+ call calloc (pars, SKF_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+
+ SKF_STEP(pars) = clgpsetr (pp, "fitstep")
+ SKF_BLK1D(pars) = clgpseti (pp, "fitblk1d")
+ SKF_HCLIP(pars) = clgpsetr (pp, "fithclip")
+ SKF_LCLIP(pars) = clgpsetr (pp, "fitlclip")
+ SKF_XORDER(pars) = clgpseti (pp, "fitxorder")
+ SKF_YORDER(pars) = clgpseti (pp, "fityorder")
+
+ SKF_LMIN(pars) = SKFLMIN
+ SKF_FUNC1D(pars) = strdic (SKFFUNC1D, SKF_STR(pars),
+ SKF_STRLEN, CV_FUNCTIONS)
+ SKF_FUNC2D(pars) = strdic (SKFFUNC2D, SKF_STR(pars),
+ SKF_STRLEN, GS_FUNCTIONS)
+ SKF_XTERMS(pars) = strdic (SKFXTERMS, SKF_STR(pars),
+ SKF_STRLEN, GS_XTYPES) - 1
+ SKF_NITER(pars) = SKFNITER
+
+ call clcpset (pp)
+ case 'c':
+ call mfree (pars, TY_STRUCT)
+ }
+end
+
+
+# SKB_PARS -- Sky block parameters.
+
+procedure skb_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+pointer pp, cp
+double x, y, sum1, sum2
+
+int clgpseti()
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+
+ call calloc (pars, SKB_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+ SKB_BLKSTEP(pars) = clgpseti (pp, "blkstep")
+ SKB_BLKSIZE(pars) = clgpseti (pp, "blksize")
+ SKB_NSUBBLKS(pars) = max (1, clgpseti (pp, "blknsubblks"))
+
+ call strcpy (SKBCNV, Memc[SKB_CNV(pars)], SKB_STRLEN)
+ SKB_SKYMIN(pars) = SKBSKYMIN
+ SKB_FRAC(pars) = SKBFRAC
+ SKB_GROW(pars) = SKBGROW
+ SKB_SIGBIN(pars) = SKBSIGBIN
+ SKB_NMINPIX(pars) = SKBNMINPIX
+ SKB_NMINBINS(pars) = SKBNMINBINS
+ SKB_HISTWT(pars) = SKBHISTWT
+ #SKB_HISTWT(pars) = 1
+ SKB_A(pars) = 1. / SKBA
+ #SKB_A(pars) = 1. / .05
+ SKB_NBINS(pars) = nint (2 * SKB_SIGBIN(pars) * SKB_A(pars))
+ SKB_NBINS(pars) = SKB_NBINS(pars) + mod (SKB_NBINS(pars)+1, 2)
+ SKB_B(pars) = SKB_NBINS(pars) / 2. + 1
+
+ for (cp=SKB_CNV(pars); IS_WHITE(Memc[cp]); cp=cp+1)
+ ;
+ call strcpy (Memc[cp], Memc[SKB_CNV(pars)], SKB_STRLEN)
+
+ # Compute sigma correction factor from mean absolute deviation.
+ sum1 = 0.
+ sum2 = 0.
+ for (x=-SKB_SIGBIN(pars); x<=SKB_SIGBIN(pars); x=x+0.01) {
+ y = exp (-x*x/2.)
+ sum1 = sum1 + abs(x)*y
+ sum2 = sum2 + y
+ }
+ SKB_SIGFAC(pars) = sum2 / sum1
+
+ call clcpset (pp)
+ case 'c':
+ call mfree (pars, TY_STRUCT)
+ }
+end
+
+
+# DET_PARS -- Detect parameters.
+
+procedure det_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+pointer pp
+
+int i, j
+pointer cp, ptr
+bool clgpsetb()
+int clgpseti(), btoi()
+real clgpsetr()
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+ call calloc (pars, DET_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+
+ call clgpseta (pp, "convolve", Memc[DET_CNV(pars)], DET_STRLEN)
+ DET_HSIG(pars) = clgpsetr (pp, "hsigma")
+ DET_LSIG(pars) = clgpsetr (pp, "lsigma")
+ DET_HDETECT(pars) = btoi (clgpsetb (pp, "hdetect"))
+ DET_LDETECT(pars) = btoi (clgpsetb (pp, "ldetect"))
+ DET_NEIGHBORS(pars) = clgpseti (pp, "neighbors")
+ DET_MINPIX(pars) = clgpseti (pp, "minpix")
+ DET_SIGAVG(pars) = clgpsetr (pp, "sigavg")
+ DET_SIGPEAK(pars) = clgpsetr (pp, "sigmax")
+ DET_BPVAL(pars) = clgpseti (pp, "bpval")
+ if (clgpsetb (pp, "updatesky"))
+ call skb_pars ("open", pset, DET_SKB(pars))
+
+ # Check convolution kernel.
+ for (cp=DET_CNV(pars); IS_WHITE(Memc[cp]); cp=cp+1)
+ ;
+ call strcpy (Memc[cp], Memc[DET_CNV(pars)], DET_STRLEN)
+ if (Memc[DET_CNV(pars)] != EOS) {
+ call cnvparse (Memc[DET_CNV(pars)], ptr, i, j, NULL)
+ call mfree (ptr, TY_REAL)
+ if (i == 1 && j == 1)
+ Memc[DET_CNV(pars)] = EOS
+ }
+
+ call clcpset (pp)
+ case 'd':
+ if (pars != NULL)
+ return
+ call calloc (pars, DET_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+
+ call clgpseta (pp, "convolve", Memc[DET_CNV(pars)], DET_STRLEN)
+ DET_HSIG(pars) = clgpsetr (pp, "hsigma")
+ DET_LSIG(pars) = clgpsetr (pp, "lsigma")
+ DET_HDETECT(pars) = btoi (clgpsetb (pp, "hdetect"))
+ DET_LDETECT(pars) = btoi (clgpsetb (pp, "ldetect"))
+ DET_NEIGHBORS(pars) = clgpseti (pp, "neighbors")
+ DET_MINPIX(pars) = clgpseti (pp, "minpix")
+ DET_SIGAVG(pars) = clgpsetr (pp, "sigavg")
+ DET_SIGPEAK(pars) = clgpsetr (pp, "sigmax")
+ DET_BPVAL(pars) = clgpseti (pp, "bpval")
+ if (clgpsetb (pp, "updatesky"))
+ call skb_pars ("open", pset, DET_SKB(pars))
+
+ # Check convolution kernel.
+ for (cp=DET_CNV(pars); IS_WHITE(Memc[cp]); cp=cp+1)
+ ;
+ call strcpy (Memc[cp], Memc[DET_CNV(pars)], DET_STRLEN)
+ if (Memc[DET_CNV(pars)] != EOS) {
+ call cnvparse (Memc[DET_CNV(pars)], ptr, i, j, NULL)
+ call mfree (ptr, TY_REAL)
+ if (i == 1 && j == 1)
+ Memc[DET_CNV(pars)] = EOS
+ }
+
+ # The following are unique to diffdetect.
+ DET_FRAC2(pars) = clgpsetr (pp, "rfrac")
+
+ call clcpset (pp)
+ case 'c':
+ if (pars != NULL)
+ call skb_pars ("close", "", DET_SKB(pars))
+ call mfree (pars, TY_STRUCT)
+ }
+
+end
+
+
+# SPT_PARS -- Split parameters.
+
+procedure spt_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+pointer pp
+
+int clgpseti()
+real clgpsetr()
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+ call calloc (pars, SPT_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+
+ SPT_NEIGHBORS(pars) = clgpseti (pp, "neighbors")
+ SPT_SPLITMAX(pars) = clgpsetr (pp, "splitmax")
+ SPT_SPLITSTEP(pars) = clgpsetr (pp, "splitstep")
+ SPT_SPLITTHRESH(pars) = clgpsetr (pp, "splitthresh")
+ SPT_MINPIX(pars) = clgpseti (pp, "minpix")
+ SPT_SIGAVG(pars) = clgpsetr (pp, "sigavg")
+ SPT_SIGPEAK(pars) = clgpsetr (pp, "sigmax")
+ SPT_SMINPIX(pars) = clgpseti (pp, "sminpix")
+ SPT_SSIGAVG(pars) = clgpsetr (pp, "ssigavg")
+ SPT_SSIGPEAK(pars) = clgpsetr (pp, "ssigmax")
+
+ call clcpset (pp)
+ case 'c':
+ call mfree (pars, TY_STRUCT)
+ }
+
+end
+
+
+# GRW_PARS -- Grow parameters.
+
+procedure grw_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+pointer pp
+
+int clgpseti()
+real clgpsetr()
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+ call calloc (pars, GRW_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+ GRW_NGROW(pars) = clgpseti (pp, "ngrow")
+ GRW_AGROW(pars) = clgpsetr (pp, "agrow")
+ call clcpset (pp)
+ case 'c':
+ call mfree (pars, TY_STRUCT)
+ }
+
+end
+
+
+# EVL_PARS -- Evaluate parameters.
+
+procedure evl_pars (option, pset, pars)
+
+char option[ARB] #I Option
+char pset[ARB] #I Pset
+pointer pars #U Parameter structure
+
+int i, nowhite(), ctor()
+real magzero
+pointer pp
+
+pointer clopset()
+
+errchk calloc
+
+begin
+ switch (option[1]) {
+ case 'o':
+ if (pars != NULL)
+ return
+ call calloc (pars, EVL_LEN, TY_STRUCT)
+
+ pp = clopset (pset)
+ call clgpseta (pp, "magzero", EVL_MAGZERO(pars,1), EVL_STRLEN)
+ if (nowhite(EVL_MAGZERO(pars,1),EVL_MAGZERO(pars,1),EVL_STRLEN)==0)
+ call strcpy ("INDEF", EVL_MAGZERO(pars,1), EVL_STRLEN)
+ if (EVL_MAGZERO(pars,1) != '!') {
+ i = 1
+ if (ctor (EVL_MAGZERO(pars,1), i, magzero) == 0)
+ call error (1, "Magnitude zero point parameter syntax")
+ }
+ call clcpset (pp)
+ case 'c':
+ call mfree (pars, TY_STRUCT)
+ }
+end
diff --git a/noao/nproto/ace/reviewproto.cl b/noao/nproto/ace/reviewproto.cl
new file mode 100644
index 00000000..2af722de
--- /dev/null
+++ b/noao/nproto/ace/reviewproto.cl
@@ -0,0 +1,215 @@
+# REVIEWPROTO
+
+procedure reviewproto (catalog)
+
+string catalog {prompt="Catalog"}
+bool nooverlay = yes {prompt="Display image without overlays"}
+bool overlay = yes {prompt="Display image with overlays"}
+bool comparison = yes {prompt="Display comparison image"}
+file compimage = "" {prompt="Comparison image"}
+int box = 200 {prompt="Box size (pixels)"}
+string ocolors = "green" {prompt="Object mask color"}
+string lcolor = "red" {prompt="Label color"}
+
+struct *fd
+
+begin
+ file cat, im, mask, coords, compim, temp
+ int naxis1, naxis2, icolor, frame, nframe, x1, x2, y1, y2
+ real r, d, x, y, xt, yt
+ bool pause
+ string key, imsec
+ struct title
+
+ coords = mktemp ("tmp$iraf")
+ temp = mktemp ("tmp$iraf")
+
+ # Get query parameters.
+ cat = catalog
+
+ # Get header and coordinates.
+ tdump (cat, cdfile="", pfile=temp, datafile=coords,
+ columns="ra,dec", rows="", pwidth=80)
+ match ("IMAGE", temp, stop-) | scan (im, im, im)
+ match ("MASK", temp, stop-) | scan (mask, mask, mask)
+ delete (temp, verify-)
+
+ # Set image size.
+ sections (im, option="root") | scan (im)
+ hselect (im, "naxis1,naxis2", yes) | scan (naxis1, naxis2)
+
+ # Set comparison.
+ sections (compimage, option="root") | scan (compim)
+
+ # Translate color specification.
+ match (lcolor, "ace$colors.dat", stop-) | scan (lcolor, icolor)
+ if (nscan() != 2)
+ icolor = 200
+
+ # Number of frames.
+ nframe = 0
+ if (nooverlay)
+ nframe = nframe + 1
+ if (overlay)
+ nframe = nframe + 1
+ if (comparison && compim != "")
+ nframe = nframe + 1
+
+ # Loop through the list of catalog coordinates.
+ pause = NO
+ fd = coords
+ while (fscan (fd, r, d) != EOF) {
+ if (nscan() < 2)
+ next
+ if (r == INDEF ||d == INDEF)
+ next
+
+ # Pause with cursor read if there is more than one coordinate.
+ if (pause) {
+ printf ("q to quit any other key to continue...\n")
+ if (fscan (imcur, x, y, i, key) == EOF)
+ break
+ if (key == 'q')
+ break
+ pause = NO
+ }
+
+ # Display.
+ frame = nframe
+
+ if (comparison && compim != "") {
+ # Convert world coordinate to image section.
+ print (r, d) | wcsctran ("STDIN", "STDOUT", compim, "world",
+ "logical", columns="1 2", units="native native",
+ formats="", min_sigdigit=9, verbose=no) | scan (x, y)
+ x = nint (x); y = nint (y)
+ x1 = max (1, nint (x-box/2.))
+ x2 = min (naxis1, nint (x+box/2.))
+ y1 = max (1, nint (y-box/2.))
+ y2 = min (naxis2, nint (y+box/2.))
+ if (x2 > x1 && y2 > y1) {
+ # Display section.
+ printf ("%s[%d:%d,%d:%d]\n", compim, x1, x2, y1, y2) |
+ scan (imsec)
+ acedisplay (imsec, frame, bpmask="", bpdisplay="none",
+ bpcolors="red", overlay="", ocolors=ocolors,
+ erase=yes, border_erase=no, select_frame=yes,
+ repeat=no, fill=no, zscale=yes, contrast=0.25,
+ zrange=yes, zmask="", nsample=1000, xcenter=0.5,
+ ycenter=0.5, xsize=1., ysize=1., xmag=2., ymag=2.,
+ order=0, z1=0., z2=0., ztrans="linear", lutfile="",
+ >> "dev$null")
+
+ # Mark.
+ printf ("%g %g\n", x, y, >> temp)
+ tvmark (frame, temp, logfile="", autolog=no,
+ outimage="", deletions="", commands="",
+ mark="circle", radii="10", lengths="0",
+ font="raster", color=icolor, label=no,
+ number=no, nxoffset=0, nyoffset=0, pointsize=1,
+ txsize=1, tolerance=1.5, interactive=no)
+ delete (temp, verify-)
+
+ # Label.
+ xt = x1 + 10
+ yt = y2 + 10
+ printf ("%g %g '%.2H %.1h'\n", xt, yt, r, d, >> temp)
+ tvmark (frame, temp, logfile="", autolog=no,
+ outimage="", deletions="", commands="",
+ mark="none", radii="0", lengths="0",
+ font="raster", color=icolor, label=yes,
+ number=no, nxoffset=0, nyoffset=0, pointsize=1,
+ txsize=2, tolerance=1.5, interactive=no)
+ delete (temp, verify-)
+ }
+ frame = frame - 1
+ }
+
+ # Convert world coordinate to image section.
+ print (r, d) | wcsctran ("STDIN", "STDOUT", im, "world",
+ "logical", columns="1 2", units="native native",
+ formats="", min_sigdigit=9, verbose=no) | scan (x, y)
+ x = nint (x); y = nint (y)
+ x1 = max (1, nint (x-box/2.))
+ x2 = min (naxis1, nint (x+box/2.))
+ y1 = max (1, nint (y-box/2.))
+ y2 = min (naxis2, nint (y+box/2.))
+ if (x2 <= x1 || y2 <= y1)
+ next
+
+ # Display.
+ if (overlay) {
+ printf ("%s[%d:%d,%d:%d]\n", im, x1, x2, y1, y2) | scan (imsec)
+ acedisplay (imsec, frame, bpmask="", bpdisplay="none",
+ bpcolors="red", overlay=mask, ocolors=ocolors,
+ erase=yes, border_erase=no, select_frame=yes,
+ repeat=no, fill=no, zscale=yes, contrast=0.25,
+ zrange=yes, zmask="", nsample=1000, xcenter=0.5,
+ ycenter=0.5, xsize=1., ysize=1., xmag=2., ymag=2.,
+ order=0, z1=0., z2=0., ztrans="linear", lutfile="",
+ >> "dev$null")
+
+ # Mark
+ printf ("%g %g\n", x, y, >> temp)
+ tvmark (frame, temp, logfile="", autolog=no,
+ outimage="", deletions="", commands="",
+ mark="circle", radii="10", lengths="0",
+ font="raster", color=icolor, label=no,
+ number=no, nxoffset=0, nyoffset=0, pointsize=1,
+ txsize=1, tolerance=1.5, interactive=no)
+ delete (temp, verify-)
+
+ xt = x1 + 10
+ yt = y2 + 10
+ printf ("%g %g '%.2H %.1h'\n", xt, yt, r, d, >> temp)
+ tvmark (frame, temp, logfile="", autolog=no,
+ outimage="", deletions="", commands="",
+ mark="none", radii="0", lengths="0",
+ font="raster", color=icolor, label=yes,
+ number=no, nxoffset=0, nyoffset=0, pointsize=1,
+ txsize=2, tolerance=1.5, interactive=no)
+ delete (temp, verify-)
+
+ frame = frame - 1
+ }
+
+ # Display.
+ if (nooverlay) {
+ printf ("%s[%d:%d,%d:%d]\n", im, x1, x2, y1, y2) | scan (imsec)
+ acedisplay (imsec, frame, bpmask="", bpdisplay="none",
+ bpcolors="red", overlay="", ocolors=ocolors,
+ erase=yes, border_erase=no, select_frame=yes,
+ repeat=no, fill=no, zscale=yes, contrast=0.25,
+ zrange=yes, zmask="", nsample=1000, xcenter=0.5,
+ ycenter=0.5, xsize=1., ysize=1., xmag=2., ymag=2.,
+ order=0, z1=0., z2=0., ztrans="linear", lutfile="",
+ >> "dev$null")
+
+ # Mark
+ printf ("%g %g\n", x, y, >> temp)
+ tvmark (frame, temp, logfile="", autolog=no,
+ outimage="", deletions="", commands="",
+ mark="circle", radii="10", lengths="0",
+ font="raster", color=icolor, label=no,
+ number=no, nxoffset=0, nyoffset=0, pointsize=1,
+ txsize=1, tolerance=1.5, interactive=no)
+ delete (temp, verify-)
+
+ xt = x1 + 10
+ yt = y2 + 10
+ printf ("%g %g '%.2H %.1h'\n", xt, yt, r, d, >> temp)
+ tvmark (frame, temp, logfile="", autolog=no,
+ outimage="", deletions="", commands="",
+ mark="none", radii="0", lengths="0",
+ font="raster", color=icolor, label=yes,
+ number=no, nxoffset=0, nyoffset=0, pointsize=1,
+ txsize=2, tolerance=1.5, interactive=no)
+ delete (temp, verify-)
+
+ frame = frame - 1
+ }
+
+ pause = YES
+ }
+ fd = ""; delete (coords, verify-)
+end
diff --git a/noao/nproto/ace/sky.h b/noao/nproto/ace/sky.h
new file mode 100644
index 00000000..4aca7214
--- /dev/null
+++ b/noao/nproto/ace/sky.h
@@ -0,0 +1,14 @@
+# Grow parameter structure
+
+define SKY_LEN 8 # Length of parameter structure
+define SKY_STRLEN 9 # Length of string
+
+define SKY_TYPE Memi[$1] # Type of sky
+define SKY_SKF Memi[$1+1] # Sky fit parameters
+define SKY_SKB Memi[$1+2] # Sky block parameters
+define SKY_STR Memc[P2C($1+3)] # String
+
+
+define SKY_TYPES "|fit|block|"
+define SKY_FIT 1 # Sky fitting algorithm
+define SKY_BLOCK 2 # Sky block algorithm
diff --git a/noao/nproto/ace/sky.x b/noao/nproto/ace/sky.x
new file mode 100644
index 00000000..c713a437
--- /dev/null
+++ b/noao/nproto/ace/sky.x
@@ -0,0 +1,118 @@
+include <error.h>
+include "sky.h"
+
+
+# SKY -- Determine sky and sky sigma in an image.
+#
+# Get the sky and sigma map pointers. This is layered on the MAPIO routines
+# and lower level sky algorithms. The sky parameter structure will be
+# allocated if needed and must be freed by the calling program.
+#
+# If they are not defined compute an initial
+# sky and/or sky sigma surface fit using a subset of the input lines.
+# Whether the sky and/or the sigma are fit is determined by whether the input
+# sky and sky sigma pointers are NULL. The initial data for the surface fit
+# is measured at a subset of lines with any masked pixels excluded. Objects
+# are removed by fitting a 1D curve to each line, rejection points with large
+# residuals and iterating until only sky is left. The sky points are then
+# accumulated for a 2D surface fit and the residuals are added to a
+# histogram. The absolute deviations, scaled by 0.7979 to convert to an
+# gausian sigma, are accumulated for a sky sigma surface fit. After all the
+# sample lines are accumulated the surface fits are computed. The histogram
+# of residuals is then fit by a gaussian to estimate an offset from the sky
+# fit to the sky mode caused by unrejected object light. The offset is
+# applied to the sky surface.
+
+procedure sky (par, im, bpm, expmap, skyname, signame, skymap, sigmap,
+ dosky, dosig, logfd)
+
+pointer par #I Parameters
+pointer im #I Input image
+pointer bpm #I Input mask
+pointer expmap #I Exposure map
+char skyname[ARB] #I Sky map name
+char signame[ARB] #I Sigma map name
+pointer skymap #O Sky map
+pointer sigmap #O Sigma map
+bool dosky #O Sky computed?
+bool dosig #O Sigma computed?
+int logfd #I Verbose?
+
+real rval
+pointer sp, namesky, namesig
+
+int errcode()
+pointer map_open()
+errchk map_open, sky_fit, sky_block
+
+begin
+ call smark (sp)
+ call salloc (namesky, SZ_FNAME, TY_CHAR)
+ call salloc (namesig, SZ_FNAME, TY_CHAR)
+
+ if (logfd != NULL)
+ call fprintf (logfd, " Set sky and sigma:\n")
+
+ # Check whether to compute a sky.
+ skymap = NULL
+ if (skyname[1] != EOS) {
+ iferr (skymap = map_open (skyname, im)) {
+ skymap = NULL
+ if (errcode() != 2)
+ call erract (EA_ERROR)
+ }
+ if (logfd != NULL && skymap != NULL) {
+ ifnoerr (call map_getr (skymap, "constant", rval)) {
+ call fprintf (logfd, " Use constant input sky: %g\n")
+ call pargr (rval)
+ } else {
+ call fprintf (logfd, " Use input sky: %s\n")
+ call pargstr (skyname)
+ }
+ }
+ }
+ dosky = (skymap == NULL)
+
+ # Check whether to compute a sky sigma.
+ sigmap = NULL
+ if (signame[1] != EOS) {
+ iferr (sigmap = map_open (signame, im)) {
+ sigmap = NULL
+ if (errcode() != 2)
+ call erract (EA_ERROR)
+ }
+ if (logfd != NULL && sigmap != NULL) {
+ ifnoerr (call map_getr (sigmap, "constant", rval)) {
+ call fprintf (logfd, " Use constant input sigma: %g\n")
+ call pargr (rval)
+ } else {
+ call fprintf (logfd, " Use input sigma: %s\n")
+ call pargstr (signame)
+ }
+ }
+ }
+ dosig = (sigmap == NULL)
+
+ # Compute the sky.
+ if (dosky || dosig) {
+ # Set parameters.
+ call sky_pars ("open", "", par)
+
+ switch (SKY_TYPE(par)) {
+ case SKY_FIT:
+ call sky_fit (SKY_SKF(par), dosky, dosig, im, bpm, expmap,
+ skyname, signame, skymap, sigmap, logfd)
+ case SKY_BLOCK:
+ call sky_fit (SKY_SKF(par), dosky, dosig, im, bpm, expmap,
+ "", "", skymap, sigmap, logfd)
+ call map_seti (skymap, "sample", 5)
+ call map_seti (sigmap, "sample", 5)
+ call sky_block (SKY_SKB(par), dosky, dosig, im, bpm, expmap,
+ skyname, signame, skymap, sigmap, logfd)
+ default:
+ call error (1, "Unknown sky type")
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/nproto/ace/skyblock.h b/noao/nproto/ace/skyblock.h
new file mode 100644
index 00000000..40f5758d
--- /dev/null
+++ b/noao/nproto/ace/skyblock.h
@@ -0,0 +1,50 @@
+# Definitions for SKYBLOCK algorithm.
+
+define SKBSKYMIN 10000 # Minimum number of sky pixels in subblock
+define SKBFRAC 0.66 # Frac of sky pix in subblock for sky estimate
+define SKBGROW 1.5 # Grow for rejected subblocks
+define SKBSIGBIN 2.5 # Sigma width of histogram
+define SKBA 0.01 # Histogram resolution
+define SKBNMINPIX 50 # Minimum number of pixels/subblock/side
+define SKBNMINBINS 500 # Minimum average bin population
+define SKBHISTWT 2 # Default histogram weighting power
+define SKBCNV "" # Convolution
+
+define SKB_STRLEN 99 # String length in parameters
+define SKB_LEN 82 # Sky block structure length
+
+# The following apply to all images.
+define SKB_BLKSTEP Memi[$1] # Step size
+define SKB_BLKSIZE Memi[$1+1] # Number of pixels or blocks
+define SKB_NSUBBLKS Memi[$1+2] # Number of subblocks per block
+define SKB_SKYMIN Memi[$1+3] # Minimum number of sky pixels
+define SKB_NMINPIX Memi[$1+4] # Min pixels/subblock/side
+define SKB_SIGBIN Memr[P2R($1+5)] # Histogram sigma limit
+define SKB_A Memr[P2R($1+6)] # Histogram resolution
+define SKB_B Memr[P2R($1+7)] # Bin start
+define SKB_NBINS Memi[$1+8] # Number of bins
+define SKB_NMINBINS Memi[$1+9] # Min avg bin population
+define SKB_HISTWT Memi[$1+10] # Histogram weighting power
+define SKB_SIGFAC Memr[P2R($1+11)] # Sigma correction factor
+define SKB_FRAC Memr[P2R($1+12)] # Frac of sky pix in subblock
+define SKB_GROW Memr[P2R($1+13)] # Grow for rejected subblocks
+define SKB_CNV P2C($1+14) # Pointer to convolution string (99)
+
+# The following are set for each image.
+define SKB_NCBLK Memi[$1+65] # Number of blocks across image
+define SKB_NLBLK Memi[$1+66] # Number of blocks across image
+define SKB_NCPIX Memi[$1+67] # Number of pixels in blocks
+define SKB_NLPIX Memi[$1+68] # Number of pixels in blocks
+define SKB_NCSBLK Memi[$1+69] # Number of subblocks across image
+define SKB_NLSBLK Memi[$1+70] # Number of subblocks across image
+define SKB_NCSPIX Memi[$1+71] # Number of pixels in subblocks
+define SKB_NLSPIX Memi[$1+72] # Number of pixels in subblocks
+define SKB_NSKYMIN Memi[$1+73] # Minimum pixels to evaluate histogram
+define SKB_BINS Memi[$1+74] # Pointer to bins
+define SKB_NAV Memi[$1+75] # Number of bins to average for weights
+define SKB_NSKY Memi[$1+76] # Pointer to num sky pix
+define SKB_EXP Memi[$1+77] # Pointer to exposure values
+define SKB_SKYS Memi[$1+78] # Pointer to sky block values
+define SKB_SIGS Memi[$1+79] # Pointer to sigma block values
+define SKB_SKY Memi[$1+80] # Pointer to current sky block line
+define SKB_SIG Memi[$1+81] # Pointer to current sigma block line
diff --git a/noao/nproto/ace/skyblock.x b/noao/nproto/ace/skyblock.x
new file mode 100644
index 00000000..5e3eb5f9
--- /dev/null
+++ b/noao/nproto/ace/skyblock.x
@@ -0,0 +1,1039 @@
+include <error.h>
+include <ctype.h>
+include <imhdr.h>
+include <imset.h>
+include <mach.h>
+include "skyblock.h"
+
+
+# SKY_BLOCK - Determine sky and sky sigma in blocks.
+#
+# This is layered on MAPIO and CONVOLVE.
+
+procedure sky_block (skb, dosky, dosig, in, bpm, expmap, skyname, signame,
+ skymap, sigmap, logfd)
+
+pointer skb #U Sky block structure
+bool dosky #I Compute sky
+bool dosig #I Compute sigma
+pointer in #I Input image pointer
+pointer bpm #I Input mask
+pointer expmap #I Exposure map
+char skyname[ARB] #I Sky map name (if none then no output)
+char signame[ARB] #I Sigma map name (if none then no output)
+pointer skymap #U Sky map
+pointer sigmap #U Sigma map
+int logfd #I Verbose?
+
+int l, blkstep, nc, nl
+real cnvwt
+pointer sp, cnv, cnvdata, bp
+pointer im[2], indata, skydata, sigdata, expdata
+errchk skb_pars, skb_iminit, convolve, skb_accum, skb_update
+
+begin
+ if (!(dosky||dosig))
+ return
+
+ call smark (sp)
+
+ # Log operation.
+ if (logfd != NULL) {
+ if (dosky && dosig)
+ call fprintf (logfd,
+ " Determine sky and sigma by block statistics:\n")
+ else if (dosky)
+ call fprintf (logfd, " Determine sky by block statistics:\n")
+ else
+ call fprintf (logfd,
+ " Determine sigma by block statistics:\n")
+ }
+
+ # Set parameters if not set in a previous call or set externally.
+ if (skb == NULL)
+ call skb_pars ("open", "", skb)
+
+ # Set parameters for the image.
+ blkstep = SKB_BLKSTEP(skb)
+ call skb_iminit (skb, in, expmap, blkstep, logfd)
+
+ # Set maximum number of image columns and lines to use.
+ nc = SKB_NCSBLK(skb) * SKB_NCSPIX(skb)
+ nl = SKB_NLSBLK(skb) * SKB_NLSPIX(skb)
+
+ # Set up convolution. Note we can't use convolution with a blkstep.
+ cnv = SKB_CNV(skb)
+ if (Memc[cnv] != EOS) {
+ if (blkstep > 1) {
+ call salloc (cnv, 1, TY_CHAR)
+ Memc[cnv] = EOS
+ } else
+ call salloc (cnvdata, nc, TY_REAL)
+ }
+
+ # Setup bad pixel mask.
+ if (bpm == NULL) {
+ call salloc (bp, nc, TY_INT)
+ call aclri (Memi[bp], nc)
+ }
+
+ # Go through image creating low resolution sky blocks.
+ im[1] = in; im[2] = NULL
+ do l = 1, nl, blkstep {
+ call convolve (im, bpm, skymap, sigmap, expmap, 0,
+ 1., l, Memc[cnv], indata, bp, cnvdata, skydata,
+ sigdata, expdata, cnvwt, logfd)
+ call skb_accum (skb, l, blkstep, Memr[cnvdata], Memr[skydata],
+ Memr[sigdata], Memr[expdata], Memi[bp], nc, cnvwt)
+ }
+
+ # Free convolution memory.
+ call convolve (im, bpm, skymap, sigmap, expmap, 0,
+ 1., 0, Memc[cnv], indata, bp, cnvdata, skydata,
+ sigdata, expdata, cnvwt, logfd)
+
+ # Turn the sky blocks into sky maps.
+ call skb_update (skb, dosky, dosig, in, skyname, signame,
+ skymap, sigmap, logfd)
+
+ # Free memory.
+ call skb_imfree (skb)
+ call sfree (sp)
+end
+
+
+# SKB_IMINIT -- Initialize parameters and allocate memory for an image.
+
+procedure skb_iminit (skb, im, expmap, blkstep, logfd)
+
+pointer skb #U Sky block structure
+pointer im #I Image pointer
+pointer expmap #I Exposure map pointer
+int blkstep #U Line step for speed
+int logfd #I Log file descriptor
+
+int nc, nl
+
+begin
+ # Number of pixels per subblock.
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ if (SKB_BLKSIZE(skb) < 0) {
+ if (nc < nl) {
+ SKB_NCSPIX(skb) = max (SKB_NMINPIX(skb),
+ nc / (SKB_NSUBBLKS(skb) * max(1,-SKB_BLKSIZE(skb))))
+ SKB_NLSPIX(skb) = SKB_NCSPIX(skb)
+ } else {
+ SKB_NLSPIX(skb) = max (SKB_NMINPIX(skb),
+ nl / (SKB_NSUBBLKS(skb) * max(1,-SKB_BLKSIZE(skb))))
+ SKB_NCSPIX(skb) = SKB_NLSPIX(skb)
+ }
+ } else {
+ SKB_NCSPIX(skb) = max (SKB_NMINPIX(skb),
+ min (nc, SKB_BLKSIZE(skb)) / SKB_NSUBBLKS(skb))
+ SKB_NLSPIX(skb) = max (SKB_NMINPIX(skb),
+ min (nl, SKB_BLKSIZE(skb)) / SKB_NSUBBLKS(skb))
+ }
+
+ # Number of subblocks, blocks, and number of pixels per block.
+ SKB_NCSBLK(skb) = max (1, nc / SKB_NCSPIX(skb))
+ SKB_NLSBLK(skb) = max (1, nl / SKB_NLSPIX(skb))
+ SKB_NCBLK(skb) = (SKB_NCSBLK(skb)+SKB_NSUBBLKS(skb)-1)/SKB_NSUBBLKS(skb)
+ SKB_NLBLK(skb) = (SKB_NLSBLK(skb)+SKB_NSUBBLKS(skb)-1)/SKB_NSUBBLKS(skb)
+ SKB_NCPIX(skb) = SKB_NCSPIX(skb) * SKB_NSUBBLKS(skb)
+ SKB_NLPIX(skb) = SKB_NLSPIX(skb) * SKB_NSUBBLKS(skb)
+
+ # Each subblock must have at least SKYMIN or FRAC sky pixels.
+ SKB_NSKYMIN(skb) = min (SKB_SKYMIN(skb),
+ nint (SKB_FRAC(skb) * SKB_NCSPIX(skb) * SKB_NLSPIX(skb)))
+
+ # Histogram parameters.
+ SKB_NAV(skb) = nint (real(SKB_NBINS(skb)) / (min (SKB_NBINS(skb),
+ SKB_NCSPIX(skb) * SKB_NLSPIX(skb) / SKB_NMINBINS(skb))))
+ SKB_NAV(skb) = SKB_NAV(skb) + mod (SKB_NAV(skb)+1, 2)
+ #SKB_NAV(skb) = 1
+
+ # Set line subsampling for speed.
+ if (blkstep > 1) {
+ blkstep = min (1 + SKB_NLSPIX(skb) / 30, blkstep)
+ SKB_NSKYMIN(skb) = SKB_NSKYMIN(skb) / blkstep
+ }
+
+ # Allocate and initialize memory.
+ call calloc (SKB_BINS(skb), SKB_NBINS(skb)*(SKB_NCSBLK(skb)+1), TY_INT)
+ call calloc (SKB_NSKY(skb), SKB_NCSBLK(skb), TY_INT)
+ call calloc (SKB_SKYS(skb), SKB_NCSBLK(skb)*SKB_NLSBLK(skb), TY_REAL)
+ call calloc (SKB_SIGS(skb), SKB_NCSBLK(skb)*SKB_NLSBLK(skb), TY_REAL)
+ if (expmap == NULL) {
+ call malloc (SKB_EXP(skb), 1, TY_REAL)
+ Memr[SKB_EXP(skb)] = INDEFR
+ } else
+ call calloc (SKB_EXP(skb), SKB_NCSBLK(skb), TY_REAL)
+
+ # Set pointers to first line of blocks.
+ SKB_SKY(skb) = SKB_SKYS(skb)
+ SKB_SIG(skb) = SKB_SIGS(skb)
+
+ if (logfd != NULL) {
+ call fprintf (logfd, " Number of blocks: %d %d\n")
+ call pargi (SKB_NCBLK(skb))
+ call pargi (SKB_NLBLK(skb))
+ call fprintf (logfd, " Number of pixels per block: %d %d\n")
+ call pargi (SKB_NCPIX(skb))
+ call pargi (SKB_NLPIX(skb))
+ call fprintf (logfd, " Number of subblocks: %d %d\n")
+ call pargi (SKB_NCSBLK(skb))
+ call pargi (SKB_NLSBLK(skb))
+ call fprintf (logfd, " Number of pixels per subblock: %d %d\n")
+ call pargi (SKB_NCSPIX(skb))
+ call pargi (SKB_NLSPIX(skb))
+ if (blkstep > 1) {
+ call fprintf (logfd, " Line sampling step: %d\n")
+ call pargi (blkstep)
+ }
+ }
+end
+
+
+# SKB_IMFREE -- Free memory for an image.
+
+procedure skb_imfree (skb)
+
+pointer skb #I Sky block structure
+
+begin
+ call mfree (SKB_BINS(skb), TY_INT)
+ call mfree (SKB_NSKY(skb), TY_INT)
+ call mfree (SKB_SKYS(skb), TY_REAL)
+ call mfree (SKB_SIGS(skb), TY_REAL)
+ call mfree (SKB_EXP(skb), TY_REAL)
+end
+
+
+# SKB_ACCUM -- Accumulate sky pixels in block histograms.
+# Evaluate histograms when a block is complete.
+
+procedure skb_accum (skb, line, blkstep, cnv, sky, sig, exp, bp, nc, cnvwt)
+
+pointer skb #I Sky block structure
+int line #I Line
+int blkstep #I Line step
+real cnv[nc] #I Convolved image data
+real sky[nc] #I Sky data
+real sig[nc] #I Sky sigma data
+real exp[nc] #I Exposure data
+int bp[nc] #I Bad pixel values
+int nc #I Number of columns
+real cnvwt #I Sigma weight
+
+real a, b, s, t, rcnv, tcnv
+int c, n, ncmax, nbins, bin, csky
+pointer bins, skys, sigs, exps, nsky
+
+begin
+ if (line > SKB_NLSBLK(skb) * SKB_NLSPIX(skb))
+ return
+ ncmax = min (nc, SKB_NCSBLK(skb) * SKB_NCSPIX(skb))
+
+ a = SKB_A(skb)
+ b = SKB_B(skb)
+ n = SKB_NCSPIX(skb)
+ nbins = SKB_NBINS(skb)
+ bins = SKB_BINS(skb)
+ skys = SKB_SKY(skb)
+ sigs = SKB_SIG(skb)
+ exps = SKB_EXP(skb)
+ nsky = SKB_NSKY(skb)
+
+ if (IS_INDEFR(Memr[exps])) {
+ do c = 1, ncmax {
+ if (bp[c] != 0)
+ next
+
+ s = sky[c]
+ t = sig[c]
+ rcnv = cnv[c] - s
+ tcnv = t / cnvwt
+ bin = a * rcnv / tcnv + b
+ if (bin < 1 || bin > nbins)
+ next
+
+ csky = (c-1) / n
+ bin = bins + csky * nbins + bin - 1
+ Memi[bin] = Memi[bin] + 1
+ Memr[skys+csky] = Memr[skys+csky] + s
+ Memr[sigs+csky] = Memr[sigs+csky] + t
+ Memi[nsky+csky] = Memi[nsky+csky] + 1
+ }
+ } else {
+ do c = 1, ncmax {
+ if (bp[c] != 0)
+ next
+
+ s = sky[c]
+ t = sig[c]
+ rcnv = cnv[c] - s
+ tcnv = t / cnvwt
+ bin = a * rcnv / tcnv + b
+ if (bin < 1 || bin > nbins)
+ next
+
+ csky = (c-1) / n
+ bin = bins + csky * nbins + bin - 1
+ Memi[bin] = Memi[bin] + 1
+ Memr[skys+csky] = Memr[skys+csky] + s
+ Memr[sigs+csky] = Memr[sigs+csky] + t
+ Memr[exps+csky] = Memr[exps+csky] + exp[c]
+ Memi[nsky+csky] = Memi[nsky+csky] + 1
+ }
+ }
+
+ # Evaluate histogram sky values if all lines have been accumulated.
+ n = mod (line, SKB_NLSPIX(skb))
+ if (n == 0 || n + blkstep > SKB_NLSPIX(skb)) {
+ n = SKB_NCSBLK(skb)
+ call skb_blkeval (Memi[bins], nbins, a, b, Memr[skys], Memr[sigs],
+ Memr[exps], Memi[nsky], n, SKB_NSKYMIN(skb), SKB_NAV(skb),
+ SKB_HISTWT(skb), SKB_SIGFAC(skb))
+
+ # Initialize for accumulation of next line of blocks.
+ SKB_SKY(skb) = skys + n
+ SKB_SIG(skb) = sigs + n
+ if (!IS_INDEFR(Memr[exps]))
+ call aclrr (Memr[exps], n)
+ call aclri (Memi[nsky], n)
+ call aclri (Memi[bins], n*nbins)
+ }
+end
+
+
+# SKB_BLKEVAL -- Evaluate sky and sigma for each histogram in line of blocks.
+# Set to INDEF if there are not enough pixels in the histogram.
+
+procedure skb_blkeval (bins, nbins, a, b, skys, sigs, exps, nsky, ncsblk,
+ nskymin, nav, histwt, sigfac)
+
+int bins[nbins,ncsblk] #I Sky subblock bins
+int nbins #I Number of bins
+real a, b #I Binning coefficients
+real skys[ncsblk] #U Sky sum in, sky estimate out
+real sigs[ncsblk] #U Sigma sum in, sigma estimate out
+real exps[ncsblk] #I Exposure sum
+int nsky[ncsblk] #I Number of values in bin
+int ncsblk #I Number of sky pixels per subblock
+int nskymin #I Minimum number of sky pixels for good sky
+int nav #I Number of bins to average
+int histwt #I Histogram weighting power
+real sigfac #I Sigma conversion factor from mean abs dev.
+
+int i, j, k, l, m, n
+double sky, sig, exp, x, wt, skymean, skymed, skybin, sigbin
+double sum1, sum2, sum3
+
+begin
+# do i = 1, ncsblk {
+# do j = 1, nbins {
+# call printf ("%d\n")
+# call pargi (bins[j,i])
+# }
+# }
+ m = nav / 2
+ do i = 1, ncsblk {
+ n = nsky[i]
+ if (n < nskymin) {
+ skys[i] = INDEFR
+ sigs[i] = INDEFR
+ next
+ }
+
+ sky = skys[i] / n
+ sig = sigs[i] / n
+ if (!IS_INDEFR(exps[1])) {
+ exp = exps[i] / n
+ exps[i] = exp
+ } else
+ exp = 1
+
+ # Compute mean and median using a power weighting of the histogram.
+ sum1 = 0.
+ sum2 = 0.
+ sum3 = 0.
+ k = ncsblk + 1
+ call aclri (bins[1,k], nbins)
+ do j = 1, nbins {
+ n = bins[j,i]
+ do l = max(1,j-m), min (nbins,j+m)
+ bins[l,k] = bins[l,k] + n
+ }
+ n = nsky[i]
+ switch (histwt) {
+ case 1:
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ x = j
+ sum1 = sum1 + wt * x
+ sum2 = sum2 + wt
+ }
+ sum2 = sum2
+ x = 0
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ sum3 = sum3 + wt + x
+ if (sum3 >= sum2)
+ break
+ x = wt
+ }
+ case 2:
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt * wt
+ x = j
+ sum1 = sum1 + wt * x
+ sum2 = sum2 + wt
+ }
+ sum2 = sum2
+ x = 0
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt * wt
+ sum3 = sum3 + wt + x
+ if (sum3 >= sum2)
+ break
+ x = wt
+ }
+ case 3:
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt * wt * wt
+ x = j
+ sum1 = sum1 + wt * x
+ sum2 = sum2 + wt
+ }
+ sum2 = sum2
+ x = 0
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt * wt * wt
+ sum3 = sum3 + wt + x
+ if (sum3 >= sum2)
+ break
+ x = wt
+ }
+ case 4:
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt * wt
+ wt = wt * wt
+ x = j
+ sum1 = sum1 + wt * x
+ sum2 = sum2 + wt
+ }
+ sum2 = sum2
+ x = 0
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt * wt
+ wt = wt * wt
+ sum3 = sum3 + wt + x
+ if (sum3 >= sum2)
+ break
+ x = wt
+ }
+ default:
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt ** histwt
+ x = j
+ sum1 = sum1 + wt * x
+ sum2 = sum2 + wt
+ }
+ sum2 = sum2
+ x = 0
+ do j = 1, nbins {
+ wt = real (bins[j,k]) / n
+ wt = wt ** histwt
+ sum3 = sum3 + wt + x
+ if (sum3 >= sum2)
+ break
+ x = wt
+ }
+ }
+ skymean = sum1 / sum2
+ skymed = j - (sum3 - sum2) / (wt + x)
+ #skybin = skymean - max (0D0, 3 * (skymean - skymed))
+ skybin = skymean - 3 * (skymean - skymed)
+ #skybin = skymean
+ skys[i] = ((skybin + 0.5 - b) / a) * sig + sky
+
+ sum1 = 0.
+ sum2 = 0.
+ do j = 1, nbins {
+ wt = bins[j,k]
+ x = abs (j - skybin)
+ sum1 = sum1 + wt * x
+ sum2 = sum2 + wt
+ }
+ sigbin = sum1 / sum2
+ sigs[i] = sigbin / a * sig * sqrt (exp) * sigfac
+ }
+end
+
+
+# SKB_UPDATE -- Update the sky and sigma maps using the block values.
+
+procedure skb_update (skb, dosky, dosig, im, skyname, signame,
+ skymap, sigmap, logfd)
+
+pointer skb #I Sky block structure
+bool dosky #I Compute sky
+bool dosig #I Compute sigma
+pointer im #I Image pointer
+char skyname[ARB] #I Output sky map name
+char signame[ARB] #I Output sigma map name
+pointer skymap #U Sky map pointer
+pointer sigmap #U Sigma map pointer
+int logfd #I Log file descriptor
+
+bool skydebug, sigdebug
+pointer sp, fname, tmp, map_open()
+errchk skb_wmap, skb_grow, skb_merge, skb_wmap, map_close, map_open
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+
+ if (dosky) {
+ skydebug = false
+ if (skydebug)
+ call skb_wmap ("skydebug.fits", im, SKB_SKYS(skb),
+ SKB_NCSBLK(skb), SKB_NLSBLK(skb), SKB_NCSPIX(skb),
+ SKB_NLSPIX(skb), 0., NULL)
+
+ # Grow subblocks contaminated by large objects.
+ call skb_grow (SKB_SKYS(skb), SKB_NCSBLK(skb), SKB_NLSBLK(skb),
+ SKB_GROW(skb))
+
+ if (skydebug)
+ call skb_wmap ("skydebug.fits", im, SKB_SKYS(skb),
+ SKB_NCSBLK(skb), SKB_NLSBLK(skb), SKB_NCSPIX(skb),
+ SKB_NLSPIX(skb), 0., NULL)
+
+ # Merge sky from subblocks and interpolate missing regions.
+ call skb_merge (Memr[SKB_SKYS(skb)], SKB_NCSBLK(skb),
+ SKB_NLSBLK(skb), Memr[SKB_SKYS(skb)], SKB_NCBLK(skb),
+ SKB_NLBLK(skb))
+
+ # Write block maps and map them with the MAPIO interface.
+ # If no name is given then use a temporary image.
+ if (skyname[1] == EOS) {
+ call mktemp ("tmpsky", Memc[fname], SZ_FNAME)
+ call skb_wmap (Memc[fname], im, SKB_SKYS(skb),
+ SKB_NCBLK(skb), SKB_NLBLK(skb), SKB_NCPIX(skb),
+ SKB_NLPIX(skb), INDEFR, NULL)
+ } else {
+ call strcpy (skyname, Memc[fname], SZ_FNAME)
+ call skb_wmap (Memc[fname], im, SKB_SKYS(skb),
+ SKB_NCBLK(skb), SKB_NLBLK(skb), SKB_NCPIX(skb),
+ SKB_NLPIX(skb), INDEFR, logfd)
+ }
+ tmp = skymap
+ iferr (skymap = map_open (Memc[fname], im))
+ skymap = NULL
+ if (skymap == NULL) {
+ skymap = tmp
+ call error (1, "Could not update sky")
+ }
+ call map_close (tmp)
+ if (skyname[1] == EOS)
+ call map_seti (skymap, "delete", YES)
+ }
+
+ if (dosig) {
+ sigdebug = false
+ if (sigdebug)
+ call skb_wmap ("sigdebug.fits", im, SKB_SIGS(skb),
+ SKB_NCSBLK(skb), SKB_NLSBLK(skb), SKB_NCSPIX(skb),
+ SKB_NLSPIX(skb), 0., NULL)
+
+ # Grow subblocks contaminated by large objects.
+ call skb_grow (SKB_SIGS(skb), SKB_NCSBLK(skb), SKB_NLSBLK(skb),
+ SKB_GROW(skb))
+
+ if (sigdebug)
+ call skb_wmap ("sigdebug.fits", im, SKB_SIGS(skb),
+ SKB_NCSBLK(skb), SKB_NLSBLK(skb), SKB_NCSPIX(skb),
+ SKB_NLSPIX(skb), 0., NULL)
+
+ # Merge sky sigma from subblocks and interpolate missing regions.
+ call skb_merge (Memr[SKB_SIGS(skb)], SKB_NCSBLK(skb),
+ SKB_NLSBLK(skb), Memr[SKB_SIGS(skb)], SKB_NCBLK(skb),
+ SKB_NLBLK(skb))
+
+ # Write block maps and map them with the MAPIO interface.
+ # If no name is given then use a temporary image.
+ if (signame[1] == EOS) {
+ call mktemp ("tmpsig", Memc[fname], SZ_FNAME)
+ call skb_wmap (Memc[fname], im, SKB_SIGS(skb),
+ SKB_NCBLK(skb), SKB_NLBLK(skb), SKB_NCPIX(skb),
+ SKB_NLPIX(skb), INDEFR, NULL)
+ } else {
+ call strcpy (signame, Memc[fname], SZ_FNAME)
+ call skb_wmap (Memc[fname], im, SKB_SIGS(skb),
+ SKB_NCBLK(skb), SKB_NLBLK(skb), SKB_NCPIX(skb),
+ SKB_NLPIX(skb), INDEFR, logfd)
+ }
+ tmp = sigmap
+ iferr (sigmap = map_open (Memc[fname], im))
+ sigmap = NULL
+ if (sigmap == NULL) {
+ sigmap = tmp
+ call error (1, "Could not update sky sigma")
+ }
+ call map_close (tmp)
+ if (signame[1] == EOS)
+ call map_seti (sigmap, "delete", YES)
+ }
+
+ call sfree (sp)
+end
+
+
+# SKB_GROW -- Grow around subblocks with insufficient data.
+
+procedure skb_grow (sky, nc, nl, grow)
+
+pointer sky # Pointer to real sky array to be grown
+int nc, nl # Size of sky array
+real grow # Grow radius
+
+int i, j, k, l1, l2, ngrow, nbufs
+real grow2, val1, val2, y2
+pointer buf, buf1, buf2, ptr
+errchk calloc
+
+begin
+ # Initialize.
+ ngrow = int (grow)
+ grow2 = grow * grow
+ nbufs = min (1 + 2 * ngrow, nl)
+ call calloc (buf, nc*nbufs, TY_REAL)
+
+ l1 = 1; l2 = 1
+ while (l1 <= nl) {
+ buf1 = sky + (l1 - 1) * nc
+ buf2 = buf + mod (l1, nbufs) * nc
+ do i = 1, nc {
+ val1 = Memr[buf1]
+ val2 = Memr[buf2]
+ if (IS_INDEFR(val1)) {
+ do j = max(1,l1-ngrow), min (nl,l1+ngrow) {
+ ptr = buf + mod (j, nbufs) * nc - 1
+ y2 = (j - l1) ** 2
+ do k = max(1,i-ngrow), min (nc,i+ngrow) {
+ if ((k-i)**2 + y2 > grow2)
+ next
+ Memr[ptr+k] = INDEFR
+ }
+ }
+ } else if (!IS_INDEFR(val2))
+ Memr[buf2] = val1
+ buf1 = buf1 + 1
+ buf2 = buf2 + 1
+ }
+
+ if (l1 > ngrow) {
+ while (l2 <= nl) {
+ buf1 = sky + (l2 - 1) * nc
+ buf2 = buf + mod (l2, nbufs) * nc
+ do i = 1, nc {
+ Memr[buf1] = Memr[buf2]
+ Memr[buf2] = 0
+ buf1 = buf1 + 1
+ buf2 = buf2 + 1
+ }
+ l2 = l2 + 1
+ if (l1 != nl)
+ break
+ }
+ }
+ l1 = l1 + 1
+ }
+
+ call mfree (buf, TY_REAL)
+end
+
+
+# SKB_MERGE -- Merge subblock into blocks.
+# Use average of subblocks with minimum and maximum excluded.
+
+procedure skb_merge (in, ncin, nlin, out, ncout, nlout)
+
+real in[ncin,nlin]
+int ncin, nlin
+real out[ncout,nlout]
+int ncout, nlout
+
+int ncs, nls
+int i, i1, i2, iout, j, j1, j2, jout, n, nindef
+real val, sum, minval, maxval
+pointer work
+
+begin
+ # Number of input subblocks per output block.
+ ncs = nint (real (ncin) / ncout)
+ nls = nint (real (nlin) / nlout)
+
+ nindef = 0
+ j2 = 0; jout = 0
+ do j1 = 1, nlin, nls {
+ jout = jout + 1
+ j2 = min (nlin, j2 + nls)
+ i2 = 0; iout = 0
+ do i1 = 1, ncin, ncs {
+ iout = iout + 1
+ i2 = min (ncin, i2 + ncs)
+
+ n = 0
+ sum = 0.
+ minval = MAX_REAL
+ maxval = -MAX_REAL
+ do j = j1, j2 {
+ do i = i1, i2 {
+ if (IS_INDEFR(in[i,j]))
+ next
+ val = in[i,j]
+ sum = sum + val
+ minval = min (val, minval)
+ maxval = max (val, maxval)
+ n = n + 1
+ }
+ }
+ if (n > 2)
+ out[iout,jout] = (sum - minval - maxval) / (n - 2)
+ else if (n >= min (ncs, nls))
+ out[iout,jout] = sum / n
+ else {
+ out[iout,jout] = INDEFR
+ nindef = nindef + 1
+ }
+ }
+ }
+
+ # Interpolate to fill in blocks with no sky data.
+ if (nindef > 0) {
+ call malloc (work, ncout*nlout, TY_REAL)
+ call interp2 (out, Memr[work], ncout, nlout)
+ call amovr (Memr[work], out, ncout*nlout)
+ call mfree (work, TY_REAL)
+ }
+end
+
+
+## SKB_ESTIMATE -- Estimate of sky in block from subblocks.
+## Use order selection.
+#
+#procedure skb_merge (in, ncin, nlin, out, ncout, nlout, select)
+#
+#real in[ncin,nlin]
+#int ncin, nlin
+#real out[ncout,nlout]
+#int ncout, nlout
+#real select # Selection fraction
+#
+#int ncs, nls
+#int i, i1, i2, iout, j, j1, j2, jout, n, nindef, nselect
+#pointer sp, work, ptr
+#real asokr()
+#
+#begin
+# # Number of input subblocks per output block.
+# ncs = nint (real (ncin) / ncout)
+# nls = nint (real (nlin) / nlout)
+#
+# call smark (sp)
+# call salloc (work, ncs*nls, TY_REAL)
+#
+# nindef = 0
+# j2 = 0; jout = 0
+# do j1 = 1, nlin, nls {
+# jout = jout + 1
+# j2 = min (nlin, j2 + nls)
+# i2 = 0; iout = 0
+# do i1 = 1, ncin, ncs {
+# iout = iout + 1
+# i2 = min (ncin, i2 + ncs)
+# ptr = work
+# do j = j1, j2 {
+# do i = i1, i2 {
+# if (IS_INDEFR(in[i,j]))
+# next
+# Memr[ptr] = in[i,j]
+# ptr = ptr + 1
+# }
+# }
+# n = ptr - work
+# if (n >= min (ncs, nls)) {
+# nselect = nint (select * (n - 1)) + 1
+# out[iout,jout] = asokr (Memr[work], n, nselect)
+# } else {
+# out[iout,jout] = INDEFR
+# nindef = nindef + 1
+# }
+# }
+# }
+#
+# # Interpolate to fill in blocks with no sky data.
+# if (nindef > 0) {
+# call salloc (work, ncout*nlout, TY_REAL)
+# call interp2 (out, Memr[work], ncout, nlout)
+# call amovr (Memr[work], out, ncout*nlout)
+# }
+#
+# call sfree (sp)
+#end
+
+
+# SKB_WMAP -- Write map from block data.
+
+procedure skb_wmap (name, imref, data, ncblk, nlblk, ncpix, nlpix, blank, logfd)
+
+char name[ARB] #I Output name
+pointer imref #I Reference image pointer
+pointer data #I Block image data
+int ncblk, nlblk #I Block image dimensions
+int ncpix, nlpix #I Number of reference image pixels per block
+real blank #I Blank value
+int logfd #I Log file descriptor
+
+bool strne()
+int i, j, imaccess(), strlen(), stridxs()
+real a[2]
+pointer sp, title, str
+pointer im, mw, buf, immap(), impl2r(), mw_openim()
+errchk immap, imrename
+
+begin
+ call smark (sp)
+ call salloc (title, SZ_IMTITLE, TY_CHAR)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Create title for new image or to check for updating.
+ call sprintf (Memc[title], SZ_IMTITLE, "Sky for ")
+ i = strlen (Memc[title])
+ call imstats (imref, IM_IMAGENAME, Memc[title+i], SZ_IMTITLE-i)
+
+ iferr {
+ im = NULL; mw = NULL
+
+# # Check for existing image and rename.
+# if (imaccess (name, 0) == YES) {
+# j = strlen (name)
+# call malloc (fname, j+SZ_FNAME, TY_CHAR)
+# i = strldxs (".", name) - 1
+# if (i < 0)
+# i = j
+# do j = 1, ARB {
+# call strcpy (name, Memc[fname], i)
+# call sprintf (Memc[fname+i], SZ_FNAME, "%d%s")
+# call pargi (j)
+# call pargstr (name[i+1])
+# if (imaccess (Memc[fname], 0) == YES)
+# next
+# call imrename (name, Memc[fname])
+# break
+# }
+# call mfree (fname, TY_CHAR)
+# }
+
+ if (imaccess (name, 0) == NO) {
+ if (logfd != NULL) {
+ call strcpy (name, Memc[str], SZ_FNAME)
+ i = stridxs (",", Memc[str])
+ if (i > 0) {
+ Memc[str+i-1] = ']'
+ Memc[str+i] = EOS
+ }
+ call fprintf (logfd, " Write sky map: %s\n")
+ call pargstr (Memc[str])
+ }
+ buf = immap (name, NEW_COPY, imref); im = buf
+ IM_PIXTYPE(im) = TY_REAL
+ IM_LEN(im,1) = ncblk
+ IM_LEN(im,2) = nlblk
+ call strcpy (Memc[title], IM_TITLE(im), SZ_IMTITLE)
+ iferr (call imdelf (im, "BPM"))
+ ;
+ iferr (call imdelf (im, "DATASEC"))
+ ;
+ iferr (call imdelf (im, "TRIMSEC"))
+ ;
+
+ do i = 1, nlblk {
+ buf = impl2r(im,i)
+ call amovr (Memr[data+(i-1)*ncblk], Memr[buf], ncblk)
+ if (!IS_INDEFR(blank)) {
+ do j = 1, ncblk
+ if (IS_INDEFR(Memr[buf+j-1]))
+ Memr[buf+j-1] = blank
+ }
+ }
+
+ # Update the WCS.
+ mw = mw_openim (imref)
+ a[1] = 1. / ncpix
+ a[2] = 1. / nlpix
+ call mw_scale (mw, a, 3)
+ a[1] = 0.5
+ a[2] = 0.5
+ call mw_shift (mw, a, 3)
+ call mw_saveim (mw, im)
+ } else {
+ if (logfd != NULL) {
+ call strcpy (name, Memc[str], SZ_FNAME)
+ i = stridxs (",", Memc[str])
+ if (i > 0) {
+ Memc[str+i-1] = ']'
+ Memc[str+i] = EOS
+ }
+ call fprintf (logfd, " Update sky map: %s\n")
+ call pargstr (Memc[str])
+ }
+ buf = immap (name, READ_WRITE, 0); im = buf
+ if (strne (IM_TITLE(im), Memc[title]) ||
+ IM_LEN(im,1) != ncblk || IM_LEN(im,2) != nlblk)
+ call error (1, "Cannot update sky map")
+
+ do i = 1, nlblk {
+ buf = impl2r(im,i)
+ call amovr (Memr[data+(i-1)*ncblk], Memr[buf], ncblk)
+ if (!IS_INDEFR(blank)) {
+ do j = 1, ncblk
+ if (IS_INDEFR(Memr[buf+j-1]))
+ Memr[buf+j-1] = blank
+ }
+ }
+ }
+ } then
+ call erract (EA_WARN)
+
+ if (mw != NULL)
+ call mw_close (mw)
+ if (im != NULL)
+ call imunmap (im)
+ call sfree (sp)
+end
+
+
+# INTERP2 -- Interpolate 2D array by averaging 1D interpolations along lines
+# and columns. It is an error if there is no data to interpolate.
+
+procedure interp2 (in, out, nc, nl)
+
+real in[nc,nl] # Input data
+real out[nc,nl] # Output data (not the same as input)
+int nc, nl # Size of data
+
+int i, j, k1, k2, nerr
+pointer sp, flags, buf
+
+begin
+ call smark (sp)
+ call salloc (flags, nl, TY_INT)
+ call salloc (buf, nl, TY_REAL)
+
+ call amovki (OK, Memi[flags], nl)
+
+ # Interpolate along lines. Flag lines with no data.
+ nerr = 0
+ do i = 1, nl
+ iferr (call interp1 (in[1,i], out[1,i], nc)) {
+ Memi[flags+i-1] = ERR
+ nerr = nerr + 1
+ }
+
+ if (nerr == nl)
+ call error (1, "No data to interpolate")
+
+ # Interpolate along columns. Check for columns and lines with no data.
+ do j = 1, nc {
+ do i = 1, nl
+ Memr[buf+i-1] = in[j,i]
+
+ ifnoerr (call interp1 (Memr[buf], Memr[buf], nl)) {
+ do i = 1, nl {
+ if (Memi[flags+i-1] == OK)
+ out[j,i] = (out[j,i] + Memr[buf+i-1]) / 2.
+ else
+ out[j,i] = Memr[buf+i-1]
+ }
+ } else {
+ do i = 1, nl {
+ if (Memi[flags+i-1] == ERR) {
+ # Find nearest line with good data.
+ do k1 = i-1, 1, -1
+ if (Memi[flags+k1-1] == OK)
+ break
+ do k2 = i+1, nl
+ if (Memi[flags+k2-1] == OK)
+ break
+ if (k1 >= 1 & k2 <= nl) {
+ if (i - k1 < k2 - i)
+ out[j,i] = out[j,k1]
+ else
+ out[j,i] = out[j,k2]
+ } else if (k1 >= 1)
+ out[j,i] = out[j,k1]
+ else if (k2 <= nl)
+ out[j,i] = out[j,k2]
+ }
+ }
+ }
+ }
+ call sfree (sp)
+end
+
+
+# INTERP1 -- Interpolate 1D vectors.
+# An error is generated if there is no data to interpolate.
+
+procedure interp1 (in, out, npts)
+
+real in[npts] # Input line
+real out[npts] # Output line (may be the same as input)
+int npts # Number of points in line
+
+int i, i1, i2, j
+real v, v1, dv
+
+begin
+ i1 = 0
+ i2 = 1
+ do i = 1, npts {
+ v = in[i]
+ if (IS_INDEFR(v))
+ next
+ if (i > i2) {
+ if (i1 > 0) {
+ dv = (v - v1) / (i - i1)
+ do j = i2, i-1
+ out[j] = v + dv * (j - i)
+ } else {
+ do j = i2, i-1
+ out[j] = v
+ }
+ }
+ out[i] = v
+ v1 = v
+ i1 = i
+ i2 = i1+1
+ }
+
+ if (i1 == 0)
+ call error (1, "No data to interpolate")
+ else if (i2 <= npts) {
+ do j = i2, npts
+ out[j] = v1
+ }
+
+end
diff --git a/noao/nproto/ace/skyfit.h b/noao/nproto/ace/skyfit.h
new file mode 100644
index 00000000..585d1c95
--- /dev/null
+++ b/noao/nproto/ace/skyfit.h
@@ -0,0 +1,24 @@
+# Sky surface algorithm definitions.
+
+define SKF_LEN 16 # Length of parameter structure
+define SKF_STRLEN 9 # Length of string
+
+define SKF_STEP Memr[P2R($1)] # Number of sky lines to sample
+define SKF_LMIN Memr[P2R($1+1)] # Minimum number of lines to fit
+define SKF_FUNC1D Memi[$1+2] # 1D Fitting function
+define SKF_FUNC2D Memi[$1+3] # 2D Fitting function
+define SKF_XORDER Memi[$1+4] # Sky fitting x order
+define SKF_YORDER Memi[$1+5] # Sky fitting y order
+define SKF_XTERMS Memi[$1+6] # Sky fitting cross terms
+define SKF_BLK1D Memi[$1+7] # Sky block size for 1D averages
+define SKF_HCLIP Memr[P2R($1+8)] # Sky fitting high sigma clip
+define SKF_LCLIP Memr[P2R($1+9)] # Sky fitting low sigma clip
+define SKF_NITER Memi[$1+10] # Number of iterations
+define SKF_STR Memc[P2C($1+11)] # String
+
+
+define SKFLMIN 10 # Minimum number of lines to fit
+define SKFFUNC1D "chebyshev" # 1D fitting function
+define SKFFUNC2D "chebyshev" # 2D fitting function
+define SKFXTERMS "half" # Cross terms
+define SKFNITER 5 # Number of iterations
diff --git a/noao/nproto/ace/skyfit.x b/noao/nproto/ace/skyfit.x
new file mode 100644
index 00000000..0b295e8e
--- /dev/null
+++ b/noao/nproto/ace/skyfit.x
@@ -0,0 +1,393 @@
+include <imhdr.h>
+include <math/curfit.h>
+include <math/gsurfit.h>
+include "skyfit.h"
+
+
+# SKY_FIT -- Fit sky surface.
+#
+# Compute a sky and/or sky sigma surface fit using a subset of the input
+# lines. the input sky and sky sigma pointers are NULL. The initial data
+# for the surface fit is measured at a subset of lines with any masked
+# pixels excluded. Objects are removed by fitting a 1D curve to each line,
+# rejection points with large residuals and iterating until only sky is left.
+# The sky points are then accumulated for a 2D surface fit and the residuals
+# are added to a histogram. The absolute deviations, scaled by 0.7979 to
+# convert to an gausian sigma, are accumulated for a sky sigma surface fit.
+# After all the sample lines are accumulated the surface fits are computed.
+# The histogram of residuals is then fit by a gaussian to estimate an
+# offset from the sky fit to the sky mode caused by unrejected object light.
+# The offset is applied to the sky surface.
+
+procedure sky_fit (par, dosky, dosig, im, bpm, expmap, skyname, signame,
+ skymap, sigmap, logfd)
+
+pointer par #U Sky parameters
+bool dosky #I Compute sky
+bool dosig #I Compute sigma
+pointer im #I Input image
+pointer bpm #I Input mask
+pointer expmap #I Exposure map
+char skyname[ARB] #I Sky map name
+char signame[ARB] #I Sigma map name
+pointer skymap #U Sky map
+pointer sigmap #U Sigma map
+int logfd #I Verbose?
+
+# Parameters
+real step # Line sample step
+int lmin # Minimum number of lines to fit
+int func1d # 1D fitting function
+int func2d # 2D fitting function
+int xorder # Sky fitting x order
+int yorder # Sky fitting y order
+int xterms # Sky fitting cross terms
+int blk1d # Block average
+real hclip # Sky fitting high sigma clip
+real lclip # Sky fitting low sigma clip
+int niter # Number of clipping iterations
+
+int l1, l2
+int i, j, c, l, n, nc, nl, nskyblk, ier
+real res, sigma
+pointer sp, x, y, z, r, a, x1, w1, w2, skydata, sigdata, expdata, w, ptr
+pointer cvsky, cvsig, gssky, gssig
+
+pointer imgl2r(), imgl2i(), map_opengs(), map_glr()
+bool im_pmlne2()
+real amedr()
+errchk map_opengs, map_glr
+
+begin
+ if (!(dosky||dosig))
+ return
+
+ # Set parameters.
+ if (par == NULL)
+ call skf_pars ("open", "", par)
+ step = SKF_STEP(par)
+ lmin = SKF_LMIN(par)
+ xorder = SKF_XORDER(par)
+ yorder = SKF_YORDER(par)
+ xterms = SKF_XTERMS(par)
+ blk1d = SKF_BLK1D(par)
+ hclip = SKF_HCLIP(par)
+ lclip = SKF_LCLIP(par)
+ func1d = SKF_FUNC1D(par)
+ func2d = SKF_FUNC2D(par)
+ niter = SKF_NITER(par)
+
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ l1 = 1 + step / 2
+ l2 = nl - step / 2
+ step = real (l2-l1) / max (nint((l2-l1)/step),xorder+2,lmin)
+
+ if (logfd != NULL) {
+ if (dosky && dosig)
+ call fprintf (logfd,
+ " Determine sky and sigma by surface fits:\n")
+ else if (dosky)
+ call fprintf (logfd, " Determine sky by surface fit:\n")
+ else
+ call fprintf (logfd, " Determine sigma by surface fit:\n")
+ call fprintf (logfd,
+ " start line = %d, end line = %d, step = %.1f\n")
+ call pargi (l1)
+ call pargi (l2)
+ call pargr (step)
+ call fprintf (logfd,
+ " xorder = %d, yorder = %d, xterms = %s\n")
+ call pargi (xorder)
+ call pargi (yorder)
+ switch (xterms) {
+ case GS_XNONE:
+ call pargstr ("none")
+ case GS_XFULL:
+ call pargstr ("full")
+ case GS_XHALF:
+ call pargstr ("half")
+ }
+ call fprintf (logfd, " hclip = %g, lclip = %g\n")
+ call pargr (hclip)
+ call pargr (lclip)
+ }
+
+ # Allocate memory and initialize.
+ call smark (sp)
+ call salloc (x1, nc, TY_REAL)
+ call salloc (w1, nc, TY_REAL)
+ call salloc (w2, nc, TY_REAL)
+
+ nskyblk = nc / blk1d
+ call salloc (x, nskyblk, TY_REAL)
+ call salloc (y, nskyblk, TY_REAL)
+ call salloc (z, nskyblk, TY_REAL)
+ call salloc (r, nskyblk, TY_REAL)
+ call salloc (a, nskyblk, TY_REAL)
+ call salloc (skydata, nskyblk, TY_REAL)
+ call salloc (sigdata, nskyblk, TY_REAL)
+ if (expmap != NULL)
+ call salloc (expdata, nskyblk, TY_REAL)
+
+ do c = 1, nc
+ Memr[x1+c-1] = c
+ call amovkr (1., Memr[w1], nc)
+
+ # Initialize the 1D and 2D fitting pointers as needed.
+ if (dosky) {
+ call cvinit (cvsky, func1d, xorder, Memr[x1],
+ Memr[x1+nc-1])
+ call gsinit (gssky, func2d, xorder, yorder,
+ xterms, 1., real(nc), 1., real(nl))
+ }
+ if (dosig) {
+ call cvinit (cvsig, CHEBYSHEV, 1, Memr[x1], Memr[x1+nc-1])
+ call gsinit (gssig, GS_CHEBYSHEV, 1, 1, xterms,
+ 1., real(nc), 1., real(nl))
+ }
+
+ # For each sample line find sky points by 1D fitting and sigma
+ # rejection and then accumulate 2D surface fitting points.
+ do j = 0, ARB {
+ l = nint (l1 + j * step)
+ if (l > l2)
+ break
+
+ # Get input data and block average.
+ if (bpm == NULL)
+ w = w1
+ else if (!im_pmlne2 (bpm, l))
+ w = w1
+ else {
+ w = imgl2i (bpm, l)
+ n = nc
+ do c = 0, nc-1 {
+ if (Memi[w+c] != 0) {
+ Memr[w2+c] = 0
+ n = n - 1
+ } else
+ Memr[w2+c] = Memr[w1+c]
+ }
+ w = w2
+ if (n < 10)
+ next
+ }
+
+ # Block average.
+ if (skymap != NULL) {
+ ptr = map_glr (skymap, l, READ_ONLY)
+ call blkavg1 (Memr[ptr], Memr[w], nc, Memr[skydata],
+ nskyblk, blk1d)
+ }
+ if (expmap != NULL) {
+ ptr = map_glr (expmap, l, READ_ONLY)
+ call blkavg1 (Memr[ptr], Memr[w], nc, Memr[expdata],
+ nskyblk, blk1d)
+ }
+ if (sigmap != NULL) {
+ ptr = map_glr (sigmap, l, READ_ONLY)
+ call blkavg1 (Memr[ptr], Memr[w], nc, Memr[sigdata],
+ nskyblk, blk1d)
+ call adivkr (Memr[sigdata], sqrt(real(blk1d)), Memr[sigdata],
+ nskyblk)
+ if (expmap != NULL)
+ call expsigma (Memr[sigdata], Memr[expdata], nskyblk, 0)
+ }
+ call blkavg (Memr[x1], Memr[imgl2r(im,l)], Memr[w], nc,
+ Memr[x], Memr[z], Memr[w2], nskyblk, blk1d)
+ w = w2
+
+ # Iterate using line fitting.
+ do i = 1, niter {
+
+ # Fit sky.
+ if (dosky) {
+ call cvfit (cvsky, Memr[x], Memr[z], Memr[w], nskyblk,
+ WTS_USER, ier)
+ if (ier == NO_DEG_FREEDOM)
+ call error (1, "Fitting error")
+ call cvvector (cvsky, Memr[x], Memr[skydata], nskyblk)
+ }
+
+ # Compute residuals.
+ call asubr (Memr[z], Memr[skydata], Memr[r], nskyblk)
+
+ # Fit sky sigma.
+ if (dosig) {
+ do c = 0, nskyblk-1
+ Memr[a+c] = abs(Memr[r+c]) / 0.7979
+ if (expmap != NULL)
+ call expsigma (Memr[a], Memr[expdata], nskyblk, 1)
+ if (i == 1)
+ call amovkr (amedr(Memr[a],nskyblk), Memr[sigdata],
+ nskyblk)
+ else {
+ call cvfit (cvsig, Memr[x], Memr[a], Memr[w], nskyblk,
+ WTS_USER, ier)
+ if (ier == NO_DEG_FREEDOM)
+ call error (1, "Fitting error")
+ call cvvector (cvsig, Memr[x], Memr[sigdata], nskyblk)
+ }
+ if (expmap != NULL)
+ call expsigma (Memr[sigdata], Memr[expdata], nskyblk, 0)
+ }
+
+ # Reject deviant points.
+ n = 0
+ do c = 0, nskyblk-1 {
+ if (Memr[w+c] == 0.)
+ next
+ res = Memr[r+c]
+ sigma = Memr[sigdata+c]
+ if (res > hclip * sigma || res < -lclip * sigma) {
+ Memr[w+c] = 0.
+ n = n + 1
+ }
+ }
+ if (n == 0) {
+ if (i == 1 && dosig) {
+ call cvfit (cvsig, Memr[x], Memr[a], Memr[w], nskyblk,
+ WTS_USER, ier)
+ if (ier == NO_DEG_FREEDOM)
+ call error (1, "Fitting error")
+ }
+ break
+ }
+ }
+
+ # Accumulate the sky data for the line.
+ call amovkr (real(l), Memr[y], nskyblk)
+ if (dosky && dosig) {
+ call amulkr (Memr[a], sqrt(real(blk1d)), Memr[a], nskyblk)
+ call gsacpts (gssky, Memr[x], Memr[y], Memr[z], Memr[w],
+ nskyblk, WTS_USER)
+ call gsacpts (gssig, Memr[x], Memr[y], Memr[a], Memr[w],
+ nskyblk, WTS_USER)
+ } else if (dosky) {
+ call gsacpts (gssky, Memr[x], Memr[y], Memr[z], Memr[w],
+ nskyblk, WTS_USER)
+ } else {
+ call amulkr (Memr[a], sqrt(real(blk1d)), Memr[a], nskyblk)
+ call gsacpts (gssig, Memr[x], Memr[y], Memr[a],
+ Memr[w], nskyblk, WTS_USER)
+ }
+ }
+
+ # Compute the surface fits, store in header, and set output pointers.
+ if (dosky) {
+ if (skymap != NULL)
+ call map_close (skymap)
+ call cvfree (cvsky)
+ call gssolve (gssky, ier)
+ if (ier == NO_DEG_FREEDOM)
+ call error (1, "Fitting error")
+ if (skyname[1] != EOS)
+ call mgs_pgs (im, skyname, gssky)
+ skydata = map_opengs (gssky, im); skymap = skydata
+ }
+ if (dosig) {
+ if (sigmap != NULL)
+ call map_close (sigmap)
+ call cvfree (cvsig)
+ call gssolve (gssig, ier)
+ if (ier == NO_DEG_FREEDOM)
+ call error (1, "Fitting error")
+ if (signame[1] != EOS)
+ call mgs_pgs (im, signame, gssig)
+ sigdata = map_opengs (gssig, im); sigmap = sigdata
+ }
+
+ call sfree (sp)
+end
+
+
+procedure blkavg (xin, yin, win, nin, xout, yout, wout, nout, blksize)
+
+real xin[nin] #I Input values
+real yin[nin] #I Input values
+real win[nin] #I Input weights
+int nin #I Number of input values
+real xout[nout] #O Output values
+real yout[nout] #O Output values
+real wout[nout] #O Output weights
+int nout #O Number of output values
+int blksize #I Block size
+
+int i, j, n, imax
+real xavg, yavg, wsum, w
+
+begin
+ if (blksize == 1) {
+ nout = nin
+ call amovr (xin, xout, nout)
+ call amovr (yin, yout, nout)
+ call amovr (win, wout, nout)
+ return
+ }
+
+ n = blksize
+ imax = nin - 2 * blksize + 1
+ nout = 0
+ for (i=1; i<=nin; ) {
+ if (i > imax)
+ n = nin - i + 1
+ xavg = 0.
+ yavg = 0.
+ wsum = 0.
+ do j = 1, n {
+ w = win[i]
+ xavg = xavg + w * xin[i]
+ yavg = yavg + w * yin[i]
+ wsum = wsum + w
+ i = i + 1
+ }
+ if (wsum > 0.) {
+ nout = nout + 1
+ xout[nout] = xavg / wsum
+ yout[nout] = yavg / wsum
+ wout[nout] = wsum
+ }
+ }
+end
+
+
+procedure blkavg1 (in, win, nin, out, nout, blksize)
+
+real in[nin] #I Input values
+real win[nin] #I Input weights
+int nin #I Number of input values
+real out[nout] #O Output values
+int nout #O Number of output values
+int blksize #I Block size
+
+int i, j, n, imax
+real avg, wsum, w
+
+begin
+ if (blksize == 1) {
+ nout = nin
+ call amovr (in, out, nout)
+ return
+ }
+
+ n = blksize
+ imax = nin - 2 * blksize + 1
+ nout = 0
+ for (i=1; i<=nin; ) {
+ if (i > imax)
+ n = nin - i + 1
+ avg = 0.
+ wsum = 0.
+ do j = 1, n {
+ w = win[i]
+ avg = avg + w * in[i]
+ wsum = wsum + w
+ i = i + 1
+ }
+ if (wsum > 0.) {
+ nout = nout + 1
+ out[nout] = avg / wsum
+ }
+ }
+end
diff --git a/noao/nproto/ace/skygrow.xNEW b/noao/nproto/ace/skygrow.xNEW
new file mode 100644
index 00000000..8c78a4bc
--- /dev/null
+++ b/noao/nproto/ace/skygrow.xNEW
@@ -0,0 +1,89 @@
+include <imhdr.h>
+
+task skygrow = t_skygrow
+
+procedure t_skygrow ()
+
+int nc, nl
+pointer im, sky, immap(), imps2r(), imgs2r()
+
+begin
+ im = immap ("skyblk", READ_WRITE, 0)
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ sky = imps2r (im, 1, nc, 1, nl)
+ call amovr (Memr[imgs2r(im,1,nc,1,nl)], Memr[sky], nc*nl)
+ call skygrow (sky, nc, nl, 1.5, 0.)
+ call imunmap (im)
+end
+
+
+procedure skygrow (sky, nc, nl, grow, growval)
+
+pointer sky # Pointer tor eal sky array to be grown
+int nc, nl # Size of sky array
+real grow # Grow radius
+real growval # Value to be grown
+
+int i, j, k, l1, l2, ngrow, nbufs
+real grow2, growval1, val1, val2, y2
+pointer buf, buf1, buf2, ptr
+errchk calloc
+
+begin
+ # Initialize.
+ ngrow = int (grow)
+ grow2 = grow * grow
+ nbufs = min (1 + 2 * ngrow, nl)
+ if (growval == 0.) {
+ growval1 = 1.
+ call malloc (buf, nc*nbufs, TY_REAL)
+ call amovkr (growval1, Memr[buf], nc*nbufs)
+ } else {
+ growval1 = 0.
+ call calloc (buf, nc*nbufs, TY_REAL)
+ }
+
+ l1 = 1; l2 = 1
+ while (l1 <= nl) {
+ buf1 = sky + (l1 - 1) * nc
+ buf2 = buf + mod (l1, nbufs) * nc
+ do i = 1, nc {
+ val1 = Memr[buf1]
+ val2 = Memr[buf2]
+ if (val1 == growval) {
+ do j = max(1,l1-ngrow), min (nl,l1+ngrow) {
+ ptr = buf + mod (j, nbufs) * nc - 1
+ y2 = (j - l1) ** 2
+ do k = max(1,i-ngrow), min (nc,i+ngrow) {
+ if ((k-i)**2 + y2 > grow2)
+ next
+ Memr[ptr+k] = growval
+ }
+ }
+ } else if (val2 != growval)
+ Memr[buf2] = val1
+ buf1 = buf1 + 1
+ buf2 = buf2 + 1
+ }
+
+ if (l1 > ngrow) {
+ while (l2 <= nl) {
+ buf1 = sky + (l2 - 1) * nc
+ buf2 = buf + mod (l2, nbufs) * nc
+ do i = 1, nc {
+ Memr[buf1] = Memr[buf2]
+ Memr[buf2] = growval1
+ buf1 = buf1 + 1
+ buf2 = buf2 + 1
+ }
+ l2 = l2 + 1
+ if (l1 != nl)
+ break
+ }
+ }
+ l1 = l1 + 1
+ }
+
+ call mfree (buf, TY_REAL)
+end
diff --git a/noao/nproto/ace/skyimages.par b/noao/nproto/ace/skyimages.par
new file mode 100644
index 00000000..a1458d39
--- /dev/null
+++ b/noao/nproto/ace/skyimages.par
@@ -0,0 +1,10 @@
+# SKYIMAGES
+
+images,f,a,,,,"List of images"
+skyimages,f,a,,,,"List of output sky images"
+sigmaimages,f,a,,,,"List of output sigma images"
+skys,s,h,"",,,"List of sky maps"
+sigmas,s,h,"",,,"List of sigma maps"
+exps,s,h,"",,,"List of exposure maps"
+gains,s,h,"",,,"List of gain maps"
+logfiles,s,h,"STDOUT",,,"List of log files"
diff --git a/noao/nproto/ace/skyimages.x b/noao/nproto/ace/skyimages.x
new file mode 100644
index 00000000..899fc5da
--- /dev/null
+++ b/noao/nproto/ace/skyimages.x
@@ -0,0 +1,120 @@
+include <error.h>
+include <imhdr.h>
+
+
+# SKYIMAGES -- Write out sky images.
+
+procedure skyimages (outsky, outsig, im, skymap, sigmap, gainmap, expmap, logfd)
+
+char outsky[ARB] #I Output sky image name
+char outsig[ARB] #I Output sigma image name
+pointer im #I Image pointer
+pointer skymap #I Sky map
+pointer sigmap #I Sigma map
+pointer gainmap #I Gain map
+pointer expmap #I Exposure map
+int logfd #I Logfile
+
+int l, nc, nl
+pointer skyim, sigim, data, skydata, ssigdata, gaindata, expdata, sigdata, ptr
+
+pointer immap(), imgl2r(), impl2r(), map_glr()
+errchk immap, map_glr
+
+begin
+ # Return no output is needed.
+ if (outsky[1] == EOS && outsig[1] == EOS)
+ return
+
+ # Write log information.
+ if (logfd != NULL) {
+ call fprintf (logfd, " Output sky images:")
+ if (outsky[1] != EOS) {
+ call fprintf (logfd, " sky = %s")
+ call pargstr (outsky)
+ }
+ if (outsig[1] != EOS) {
+ call fprintf (logfd, " sigma = %s")
+ call pargstr (outsig)
+ }
+ call fprintf (logfd, "\n")
+ }
+
+ iferr {
+ skyim = NULL; sigim = NULL
+
+ # Map output image(s)
+ if (outsky[1] != EOS) {
+ ptr = immap (outsky, NEW_COPY, im)
+ skyim = ptr
+ }
+ if (outsig[1] != EOS) {
+ ptr = immap (outsig, NEW_COPY, im)
+ sigim = ptr
+ }
+
+ # Output the sky image data.
+ nc = IM_LEN(im,1)
+ nl = IM_LEN(im,2)
+ do l = 1, nl {
+ data = NULL
+ skydata = NULL
+ if (skyim != NULL) {
+ skydata = map_glr (skymap, l, READ_ONLY)
+ call amovr (Memr[skydata], Memr[impl2r(skyim,l)], nc)
+ }
+ if (sigim != NULL) {
+ ssigdata = map_glr (sigmap, l, READ_ONLY)
+ if (gainmap == NULL && expmap == NULL)
+ sigdata = ssigdata
+ else if (expmap == NULL) {
+ if (data == NULL)
+ data = imgl2r (im, l)
+ if (skydata == NULL)
+ skydata = map_glr (skymap, l, READ_ONLY)
+ gaindata = map_glr (gainmap, l, READ_ONLY)
+ call noisemodel (Memr[data], Memr[skydata],
+ Memr[ssigdata], Memr[gaindata], INDEFR,
+ Memr[sigdata], nc)
+ } else if (gainmap == NULL) {
+ expdata = map_glr (expmap, l, READ_WRITE)
+ call noisemodel (Memr[expdata], Memr[expdata],
+ Memr[ssigdata], INDEFR, Memr[expdata],
+ Memr[sigdata], nc)
+ } else {
+ if (data == NULL)
+ data = imgl2r (im, l)
+ if (skydata == NULL)
+ skydata = map_glr (skymap, l, READ_ONLY)
+ gaindata = map_glr (gainmap, l, READ_ONLY)
+ expdata = map_glr (expmap, l, READ_WRITE)
+ call noisemodel (Memr[data], Memr[skydata],
+ Memr[ssigdata], Memr[gaindata],
+ Memr[expdata], Memr[sigdata], nc)
+ }
+ if (skyim != NULL)
+ call amovr (Memr[sigdata], Memr[impl2r(sigim,l)], nc)
+ }
+ }
+
+ # Finish up.
+ if (skyim != NULL)
+ call imunmap (skyim)
+ if (sigim != NULL)
+ call imunmap (sigim)
+ } then {
+ call erract (EA_WARN)
+
+ # Close and delete output images on an errror.
+ if (skyim != NULL) {
+ call imunmap (skyim)
+ iferr (call imdelete (outsky))
+ ;
+ }
+ if (sigim != NULL) {
+ call imunmap (sigim)
+ iferr (call imdelete (outsig))
+ ;
+ }
+ }
+end
diff --git a/noao/nproto/ace/split.h b/noao/nproto/ace/split.h
new file mode 100644
index 00000000..db9589a1
--- /dev/null
+++ b/noao/nproto/ace/split.h
@@ -0,0 +1,13 @@
+# Detection parameter structure.
+define SPT_LEN 10 # Length of parameter structure
+
+define SPT_NEIGHBORS Memi[$1] # Neighbor type
+define SPT_SPLITMAX Memr[P2R($1+1)] # Maximum convolved sigma for splitting
+define SPT_SPLITSTEP Memr[P2R($1+2)] # Minimum split step in convolved sigma
+define SPT_SPLITTHRESH Memr[P2R($1+3)] # Transition convolved sigma
+define SPT_MINPIX Memi[$1+4] # Minimum number of pixels
+define SPT_SIGAVG Memr[P2R($1+5)] # Minimum average above sky in sigma
+define SPT_SIGPEAK Memr[P2R($1+6)] # Minimum peak above sky in sigma
+define SPT_SMINPIX Memi[$1+7] # Minimum number of split pixels
+define SPT_SSIGAVG Memr[P2R($1+8)] # Minimum split avg above sky in sigma
+define SPT_SSIGPEAK Memr[P2R($1+9)] # Minimum split peak above sky in sigma
diff --git a/noao/nproto/ace/split.x b/noao/nproto/ace/split.x
new file mode 100644
index 00000000..a0d564e4
--- /dev/null
+++ b/noao/nproto/ace/split.x
@@ -0,0 +1,625 @@
+include <pmset.h>
+include <mach.h>
+include "ace.h"
+include "cat.h"
+include "objs.h"
+include "split.h"
+
+
+# SPLIT - Split detected objects.
+#
+# Note that the sigma level map is modified and will be empty when done.
+
+procedure split (spt, cat, objmask, siglevel, siglevels, logfd)
+
+pointer spt #I Split parameters
+pointer cat #U Catalog structure
+pointer objmask #I Input and modified object mask
+pointer siglevel #I Sigma level mask.
+real siglevels[ARB] #I Sigma levels
+int logfd #I Logfile
+
+int neighbors # Neighbor type
+int dminpix # Minimum number of pixels for split object
+int sminpix # Minimum number of split pixels
+real sigavg # Minimum average above sky in sigma
+real sigmax # Minimum peak above sky in sigma
+real ssigavg # Minimum split average above sky in sigma
+real ssigmax # Minimum split peak above sky in sigma
+real splitmax # Maximum convolved sigma for splitting
+real splitstep # Minimum split step in convolved sigma
+real splitthresh # Transition convolved sigma
+
+int i, c, c1, c2, cs, clast, l, nc, nc1, nl
+int level, nsobjs, navail, nalloc, nummax, val, num, pnum, oval, sval
+long v[PM_MAXDIM]
+real threshold
+pointer sp, pnums, buf1, buf2, irl, orl, srl, outbuf, lastbuf
+pointer objs, obj, splitmask, irlptr, orlptr, srlptr
+pointer flags, ids, sobjs, links
+
+int andi(), ori()
+bool pm_linenotempty()
+pointer pm_create()
+
+begin
+ # Check for splitting map.
+ if (siglevel == NULL)
+ return
+
+ # Set parameters.
+ call spt_pars ("open", "", spt)
+
+ neighbors = SPT_NEIGHBORS(spt)
+ dminpix = SPT_MINPIX(spt)
+ sminpix = SPT_SMINPIX(spt)
+ sigavg = SPT_SIGAVG(spt)
+ sigmax = SPT_SIGPEAK(spt)
+ ssigavg = SPT_SSIGAVG(spt)
+ ssigmax = SPT_SSIGPEAK(spt)
+ splitmax = SPT_SPLITMAX(spt)
+ splitstep = SPT_SPLITSTEP(spt)
+ splitthresh = SPT_SPLITTHRESH(spt)
+
+ if (logfd != NULL) {
+ call fprintf (logfd, " Split objects: sminpix = %d\n")
+ call pargi (sminpix)
+ }
+
+ if (IS_INDEFR(splitmax))
+ splitmax = MAX_REAL
+
+ call pm_gsize (objmask, c, v, l)
+ splitmask = pm_create (c, v, l)
+ nc = v[1]
+ nl = v[2]
+
+ call smark (sp)
+ call salloc (pnums, nc, TY_INT)
+ call salloc (buf1, nc+2, TY_INT)
+ call salloc (buf2, nc+2, TY_INT)
+ call salloc (irl, 3+3*nc, TY_INT)
+ call salloc (orl, 3+3*nc, TY_INT)
+ call salloc (srl, 3+3*nc, TY_INT)
+
+ navail = 2 * CAT_NUMMAX(cat)
+ call calloc (ids, navail, TY_INT)
+ call calloc (links, navail, TY_INT)
+ call calloc (sobjs, navail, TY_POINTER)
+ nalloc = 0
+
+ # Go through sigma levels.
+ do level = 1, ARB {
+
+ # Check if sigma value is in splitting range.
+ threshold = siglevels[level]
+ if (threshold == 0.)
+ next
+ if (threshold > splitmax)
+ break
+
+ # Initialize flags.
+ nummax = CAT_NUMMAX(cat)
+ objs = CAT_OBJS(cat)
+ call calloc (flags, nummax+1, TY_SHORT)
+ do l = NUMSTART, nummax {
+ obj = Memi[objs+l-1]
+ if (obj == NULL)
+ next
+ if (SPLIT(obj) || SINGLE(obj))
+ next
+ if (OBJ_NPIX(obj) < 2 * sminpix) {
+ SETFLAG (obj, OBJ_SINGLE)
+ next
+ }
+ Mems[flags+l] = 1
+ }
+
+ # Clear the mask.
+ call pm_clear (splitmask)
+
+ outbuf = NULL
+ nsobjs = NUMSTART - 1
+ do l = 1, nl {
+ v[1] = 1
+ v[2] = l
+ if (!pm_linenotempty (siglevel, v)) {
+ outbuf = NULL
+ next
+ }
+
+ lastbuf = outbuf
+ if (lastbuf == buf1)
+ outbuf = buf2
+ else
+ outbuf = buf1
+
+ # Get sigma level mask.
+ call pmglri (siglevel, v, Memi[irl], 0, nc, 0)
+
+ # Get parent object mask. Skip end regions not in siglev mask.
+ i = Memi[irl] - 1
+ cs = Memi[irl+3]
+ nc1 = Memi[irl+3*i] + Memi[irl+3*i+1] - cs
+ v[1] = cs
+ call pmglpi (objmask, v, Memi[pnums], 0, nc1, 0)
+ v[1] = 1
+
+ # Initialize output range lists.
+ orlptr = orl; Memi[orlptr] = 0
+ srlptr = srl + 3; sval = 0
+ clast = 0
+
+ call aclri (Memi[outbuf], nc+2)
+ irlptr = irl
+ do i = 2, Memi[irl] {
+ irlptr = irlptr + 3
+ val = Memi[irlptr+2]
+ if (val < level)
+ next
+ c1 = Memi[irlptr]
+ c2 = c1 + Memi[irlptr+1] - 1
+ do c = c1, c2 {
+ pnum = Memi[pnums+c-cs]
+ if (MSPLIT(pnum))
+ next
+ pnum = MNUM (pnum)
+ if (Mems[flags+pnum] == 0)
+ next
+
+ if (lastbuf == NULL)
+ call sadd (c+1, l, Memi[outbuf], INDEFI, nc+2,
+ Memi[ids], Memi[links], Memi[sobjs],
+ nsobjs, nalloc, pnum, siglevels[val],
+ threshold, neighbors, num)
+ else
+ call sadd (c+1, l, Memi[outbuf], Memi[lastbuf],
+ nc+2, Memi[ids], Memi[links], Memi[sobjs],
+ nsobjs, nalloc, pnum, siglevels[val],
+ threshold, neighbors, num)
+
+ if (nalloc == navail) {
+ navail = max (100*nalloc*(nl+1)/l/100, nalloc+10000)
+ call realloc (ids, navail, TY_INT)
+ call realloc (links, navail, TY_INT)
+ call realloc (sobjs, navail, TY_POINTER)
+ }
+
+ # Update split object mask.
+ if (num != oval || c != clast) {
+ Memi[orlptr+1] = clast - Memi[orlptr]
+ orlptr = orlptr + 3
+
+ oval = num
+ Memi[orlptr] = c
+ Memi[orlptr+2] = oval
+ }
+
+ # Update sigma level mask.
+ if (val != sval || c != clast) {
+ if (sval > level) {
+ Memi[srlptr+1] = clast - Memi[srlptr]
+ srlptr = srlptr + 3
+ }
+
+ sval = val
+ if (sval > level) {
+ Memi[srlptr] = c
+ Memi[srlptr+2] = sval
+ }
+ }
+
+ clast = c + 1
+ }
+ }
+
+ # Update masks.
+ i = 1 + (orlptr - orl) / 3
+ if (i > 1) {
+ Memi[orlptr+1] = clast - Memi[orlptr]
+ Memi[orl] = i
+ Memi[orl+1] = nc
+ call pmplri (splitmask, v, Memi[orl], 0, nc, PIX_SRC)
+ }
+
+ if (sval > level) {
+ Memi[srlptr+1] = clast - Memi[srlptr]
+ Memi[srl] = 1 + (srlptr - srl) / 3
+ } else
+ Memi[srl] = (srlptr - srl) / 3
+ Memi[srl+1] = nc
+ call pmplri (siglevel, v, Memi[srl], 0, nc, PIX_SRC)
+ }
+ if (nsobjs < NUMSTART)
+ break
+
+ if (threshold <= splitthresh)
+ call srenum (cat, objmask, splitmask, Memi[ids], Memi[sobjs],
+ nsobjs, dminpix, sigavg, sigmax)
+ else
+ call srenum (cat, objmask, splitmask, Memi[ids], Memi[sobjs],
+ nsobjs, sminpix, ssigavg, ssigmax)
+
+ # Reuse object structures.
+ nsobjs = nalloc
+ nalloc = NUMSTART-1
+ do i = NUMSTART-1, nsobjs-1 {
+ obj = Memi[sobjs+i]
+ if (obj != NULL) {
+ Memi[sobjs+nalloc] = Memi[sobjs+i]
+ nalloc = nalloc + 1
+ }
+ }
+
+ call mfree (flags, TY_SHORT)
+ }
+
+ do i = 0, nalloc-1
+ call mfree (Memi[sobjs+i], TY_POINTER)
+ call mfree (ids, TY_INT)
+ call mfree (links, TY_INT)
+ call mfree (sobjs, TY_POINTER)
+
+ call pm_close (splitmask)
+
+ call sfree (sp)
+end
+
+
+# SPLITADD -- Add a pixel to the object list and set the mask value.
+
+procedure sadd (c, l, z, zlast, nc, ids, links, objs, nobjs, nalloc,
+ pnum, data, threshold, neighbors, num)
+
+int c, l #I Pixel coordinate
+int z[nc] #I Pixel values for current line
+int zlast[nc] #I Pixel values for last line
+int nc #I Number of pixels in a line
+int ids[ARB] #I Mask ids
+int links[ARB] #I Link to other mask ids with same number
+int objs[ARB] #I Object numbers
+int nobjs #U Number of objects
+int nalloc #U Number of allocated objects
+int pnum #I Parent number
+real data #I Approximate (I(convolved) - sky) / sigma(convolved)
+real threshold #I Threshold above sky in sigma units
+int neighbors #I Neighbor type
+int num #O Assigned mask value.
+
+int i, num1, c1, c2
+real val
+bool merge
+pointer obj, obj1
+
+begin
+ # Inherit number of a neighboring pixel.
+ num = INDEFI
+ merge = false
+ if (neighbors == 4) {
+ c1 = c - 1
+ c2 = c
+ if (IS_INDEFI(zlast[1])) {
+ if (z[c1] >= NUMSTART)
+ num = z[c1]
+ } else {
+ if (z[c1] >= NUMSTART) {
+ num = z[c1]
+ merge = true
+ } else if (zlast[c] >= NUMSTART)
+ num = ids[zlast[c]]
+ }
+ } else {
+ c1 = c - 1
+ c2 = c + 1
+ if (IS_INDEFI(zlast[1])) {
+ if (z[c1] >= NUMSTART)
+ num = z[c1]
+ } else {
+ if (z[c1] >= NUMSTART) {
+ num = z[c1]
+ merge = true
+ } else if (zlast[c1] >= NUMSTART)
+ num = ids[zlast[c1]]
+ else if (zlast[c] >= NUMSTART)
+ num = ids[zlast[c]]
+ else if (zlast[c2] >= NUMSTART)
+ num = ids[zlast[c2]]
+ }
+ }
+
+ # If no number assign a new number.
+ if (num == INDEFI) {
+ nobjs = nobjs + 1
+ num = nobjs
+ ids[num] = num
+ links[num] = 0
+ if (nalloc < nobjs) {
+ call malloc (objs[num], OBJ_DETLEN, TY_STRUCT)
+ nalloc = nobjs
+ OBJ_FLAGS(objs[num]) = 0
+ }
+ obj = objs[num]
+ OBJ_PNUM(obj) = pnum
+ OBJ_XAP(obj) = 0.
+ OBJ_YAP(obj) = 0.
+ OBJ_FLUX(obj) = 0.
+ OBJ_NPIX(obj) = 0
+ OBJ_ISIGAVG(obj) = 0.
+ OBJ_ISIGMAX(obj) = 0.
+ }
+ obj = objs[num]
+
+ # Merge overlapping objects from previous line.
+ if (merge) {
+ i = zlast[c2]
+ if (i >= NUMSTART && num != ids[i]) {
+ num1 = ids[i]
+
+ obj1 = objs[num1]
+ OBJ_XAP(obj) = OBJ_XAP(obj) + OBJ_XAP(obj1)
+ OBJ_YAP(obj) = OBJ_YAP(obj) + OBJ_YAP(obj1)
+ OBJ_FLUX(obj) = OBJ_FLUX(obj) + OBJ_FLUX(obj1)
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + OBJ_NPIX(obj1)
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) + OBJ_ISIGAVG(obj1)
+ OBJ_ISIGMAX(obj) = max (OBJ_ISIGMAX(obj), OBJ_ISIGMAX(obj1))
+
+ i = num
+ while (links[i] != 0)
+ i = links[i]
+ links[i] = num1
+ repeat {
+ i = links[i]
+ ids[i] = num
+ } until (links[i] == 0)
+
+ nalloc = nalloc + 1
+ objs[nalloc] = obj1
+ objs[num1] = NULL
+ }
+ }
+
+ z[c] = num
+ OBJ_NPIX(obj) = OBJ_NPIX(obj) + 1
+ val = data - threshold
+ OBJ_XAP(obj) = OBJ_XAP(obj) + val * c1
+ OBJ_YAP(obj) = OBJ_YAP(obj) + val * l
+ OBJ_FLUX(obj) = OBJ_FLUX(obj) + val
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) + val
+ OBJ_ISIGMAX(obj) = max (OBJ_ISIGMAX(obj), val)
+end
+
+
+# SRENUM -- Find detected pieces with a common parent and add to the
+# catalog and the object mask.
+
+procedure srenum (cat, om, sm, ids, sobjs, nsobjs, minpix,
+ sigavg, sigmax)
+
+pointer cat #I Catalog structure
+pointer om #I Object mask
+pointer sm #I Split mask
+int ids[nsobjs] #I Mask IDs
+pointer sobjs[nsobjs] #U Input and output object list
+int nsobjs #U Number of objects
+int minpix #I Minimum number of pixels
+real sigavg #I Cutoff of SIGAVG
+real sigmax #I Cutoff of SIGMAX
+
+int i, j, n, nummax, nc, nl
+real rval
+pointer sp, nsplit, v, irl, srl, orl
+pointer objs, obj, pobj
+int ori()
+
+begin
+ nummax = CAT_NUMMAX(cat)
+ objs = CAT_OBJS(cat)
+
+ call smark (sp)
+ call salloc (nsplit, nummax, TY_INT)
+ call aclri (Memi[nsplit], nummax)
+
+ # Eliminate objects, by setting ids to zero, which don't satisfy
+ # the selection criteria (size, peak value, etc). Find objects
+ # that have split by counting, in the nsplit array, how many pieces
+ # belong to each parent.
+
+ do i = NUMSTART, nsobjs {
+ obj = sobjs[i]
+ if (obj == NULL)
+ next
+
+ n = OBJ_NPIX(obj)
+ rval = sqrt (real(n))
+ OBJ_ISIGAVG(obj) = OBJ_ISIGAVG(obj) / rval
+ if (n < minpix ||
+ (OBJ_ISIGMAX(obj) < sigmax && OBJ_ISIGAVG(obj) < sigavg)) {
+ ids[i] = 0
+ next
+ }
+
+ rval = OBJ_FLUX(obj)
+ if (rval > 0.) {
+ OBJ_XAP(obj) = OBJ_XAP(obj) / rval
+ OBJ_YAP(obj) = OBJ_YAP(obj) / rval
+ } else {
+ OBJ_XAP(obj) = INDEFR
+ OBJ_YAP(obj) = INDEFR
+ }
+
+ n = OBJ_PNUM(obj)
+ Memi[nsplit+n-1] = Memi[nsplit+n-1] + 1
+ }
+
+ # Count objects that have a common parent (nsplit > 1) and assign
+ # new object numbers. Those not split are eliminated by setting
+ # ids to zero. Mark those unsplit objects whose parent objects
+ # are too small at the current size threshold as single to eliminate
+ # them from future attempts to split.
+
+ j = nummax
+ do i = NUMSTART, nsobjs {
+ obj = sobjs[i]
+ if (obj == NULL || ids[i] == 0)
+ next
+
+ n = OBJ_PNUM(obj)
+ if (Memi[nsplit+n-1] < 2) {
+ pobj = Memi[objs+n-1]
+ if (pobj != NULL) {
+ if (OBJ_NPIX(obj) < 2 * minpix)
+ SETFLAG (pobj, OBJ_SINGLE)
+ }
+ ids[i] = 0
+ } else {
+ j = j + 1
+ OBJ_NUM(obj) = j
+ nummax = nummax + 1
+ }
+ }
+
+ # If there are no split objects return.
+ if (nummax == CAT_NUMMAX(cat)) {
+ call sfree (sp)
+ return
+ }
+
+ # Update the object mask for the split objects.
+ call salloc (v, PM_MAXDIM, TY_LONG)
+ call pm_gsize (om, i, Meml[v], j)
+ nc = Meml[v]; nl = Meml[v+1]
+ call salloc (irl, 3+3*nc, TY_INT)
+ call salloc (srl, 3+3*nc, TY_INT)
+ call salloc (orl, 3+3*nc, TY_INT)
+
+ call srenum1 (om, sm, nc, nl, ids, sobjs, Memi[nsplit],
+ Meml[v], Memi[irl], Memi[srl], Memi[orl])
+
+ # Add split objects to catalog. Expand object structure.
+ call realloc (objs, nummax, TY_POINTER)
+ j = CAT_NUMMAX(cat)
+ do i = NUMSTART, nsobjs {
+ obj = sobjs[i]
+ if (obj == NULL || ids[i] == 0)
+ next
+
+ call newobj (obj)
+
+ sobjs[i] = NULL
+ Memi[objs+j] = obj
+ j = j + 1
+ }
+
+ # Set split flags for the split parent objects.
+ do i = NUMSTART, CAT_NUMMAX(cat)-1 {
+ obj = Memi[objs+i-1]
+ if (obj == NULL)
+ next
+ if (Memi[nsplit+i-1] > 1)
+ SETFLAG (obj, OBJ_SPLIT)
+ }
+
+ # Update catalog info.
+ CAT_NOBJS(cat) = nummax
+ CAT_NUMMAX(cat) = nummax
+ CAT_OBJS(cat) = objs
+
+ call sfree (sp)
+end
+
+
+procedure srenum1 (om, sm, nc, nl, ids, objs, nsplit, v, irl, srl, orl)
+
+pointer om #I Object mask pointer
+pointer sm #I Split mask pointer
+int nc, nl #I Dimensions
+int ids[ARB] #I Mask IDs
+pointer objs[ARB] #I Split objects
+int nsplit[ARB] #I Number of split pieces
+long v[PM_MAXDIM] #I Work array for line index
+int irl[3,nc] #I Work array for input range list
+int srl[3,nc] #I Work array for split range list
+int orl[3,nc] #I Work array for output range list
+
+int i, j, k, l, n, c1, c2, sc1, id, sid, andi(), ori()
+
+begin
+ v[1] = 1
+ do l = 1, nl {
+ v[2] = l
+ call pmglri (om, v, irl, 0, nc, 0)
+ call pmglri (sm, v, srl, 0, nc, 0)
+
+ srl[1,srl[1,1]+1] = nc + 1
+ sc1 = srl[1,2]
+
+ j = 1
+ k = 2
+ do i = 2, irl[1,1] {
+ sid = irl[3,i]
+ id = MNUM(sid)
+
+ # Unsplit object.
+ if (id < NUMSTART || nsplit[id] < 2) {
+ j = j + 1
+ orl[1,j] = irl[1,i]
+ orl[2,j] = irl[2,i]
+ orl[3,j] = sid
+ next
+ }
+
+ c1 = irl[1,i]
+ c2 = c1 + irl[2,i] - 1
+ id = MSETFLAG (id, MASK_SPLIT)
+
+ while (sc1 < c1) {
+ k = k + 1
+ sc1 = srl[1,k]
+ }
+
+ while (sc1 <= c2) {
+ sid = ids[srl[3,k]]
+
+ # Check for split piece that was eliminated.
+ if (sid == 0) {
+ k = k + 1
+ sc1 = srl[1,k]
+ next
+ }
+ sid = ids[sid]
+ if (sid == 0) {
+ k = k + 1
+ sc1 = srl[1,k]
+ next
+ }
+
+ # Add split piece to output.
+ if (sc1 > c1) {
+ j = j + 1
+ orl[1,j] = c1
+ orl[2,j] = sc1 - c1
+ orl[3,j] = id
+ }
+ n = srl[2,k]
+ j = j + 1
+ orl[1,j] = sc1
+ orl[2,j] = n
+ orl[3,j] = OBJ_NUM(objs[sid])
+ c1 = sc1 + n
+
+ k = k + 1
+ sc1 = srl[1,k]
+ }
+
+ if (c1 <= c2) {
+ j = j + 1
+ orl[1,j] = c1
+ orl[2,j] = c2 - c1 + 1
+ orl[3,j] = id
+ }
+ }
+ orl[1,1] = j
+ orl[2,1] = nc
+ call pmplri (om, v, orl, 0, nc, PIX_SRC)
+ }
+end
diff --git a/noao/nproto/ace/t_acedetect.x b/noao/nproto/ace/t_acedetect.x
new file mode 100644
index 00000000..8c211cef
--- /dev/null
+++ b/noao/nproto/ace/t_acedetect.x
@@ -0,0 +1,1195 @@
+include <error.h>
+include <fset.h>
+include <imset.h>
+include <pmset.h>
+include <imhdr.h>
+include "ace.h"
+include "acedetect.h"
+include "cat.h"
+
+
+# T_ACEDETECT -- Detect objects in images.
+# This entry procedure simply sets up the parameters.
+
+procedure t_acedetect ()
+
+pointer par # Parameters
+
+pointer sp, str
+
+bool clgetb()
+int clgwrd(), imtopenp(), imtopen(), clpopnu(), fntopnb()
+
+begin
+ call smark (sp)
+ call salloc (par, PAR_LEN, TY_STRUCT)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call aclri (Memi[par], PAR_LEN)
+
+ # Get list parameters.
+ PAR_IMLIST(par,1) = imtopenp ("images")
+ PAR_BPMLIST(par,1) = imtopenp ("masks")
+ PAR_SKYLIST(par,1) = imtopenp ("skys")
+ PAR_SIGLIST(par,1) = imtopenp ("sigmas")
+ PAR_EXPLIST(par,1) = imtopenp ("exps")
+ PAR_GAINLIST(par,1) = imtopenp ("gains")
+ PAR_SCALELIST(par,1) = fntopnb ("", NO)
+
+ PAR_IMLIST(par,2) = imtopen ("")
+ PAR_BPMLIST(par,2) = imtopen ("")
+ PAR_SKYLIST(par,2) = imtopen ("")
+ PAR_SIGLIST(par,2) = imtopen ("")
+ PAR_EXPLIST(par,2) = imtopen ("")
+ PAR_GAINLIST(par,2) = imtopen ("")
+ PAR_SCALELIST(par,2) = fntopnb ("", NO)
+
+ PAR_OMLIST(par) = imtopenp ("objmasks")
+ PAR_OMTYPE(par) = clgwrd ("omtype", Memc[str], SZ_LINE, OM_TYPES)
+ PAR_INCATLIST(par) = imtopen ("")
+ PAR_OUTCATLIST(par) = imtopenp ("catalogs")
+ PAR_CATDEFLIST(par) = clpopnu ("catdefs")
+ PAR_LOGLIST(par) = clpopnu ("logfiles")
+
+ PAR_OUTSKYLIST(par) = imtopen ("")
+ PAR_OUTSIGLIST(par) = imtopen ("")
+
+ call clgstr ("extnames", PAR_EXTNAMES(par), PAR_SZSTR)
+
+ # Get other parameters.
+ # The parameter structures flag whether an operation is requested.
+ #if (clgetb ("dosky"))
+ call sky_pars ("open", "", PAR_SKY(par))
+ if (clgetb ("dodetect"))
+ call det_pars ("open", "", PAR_DET(par))
+ if (clgetb ("dosplit"))
+ call spt_pars ("open", "", PAR_SPT(par))
+ if (clgetb ("dogrow"))
+ call grw_pars ("open", "", PAR_GRW(par))
+ if (clgetb ("doevaluate"))
+ call evl_pars ("open", "", PAR_EVL(par))
+
+ # Do the detection.
+ call aceall (par)
+
+ # Finish up.
+ call sky_pars ("close", "", PAR_SKY(par))
+ call det_pars ("close", "", PAR_DET(par))
+ call spt_pars ("close", "", PAR_SPT(par))
+ call grw_pars ("close", "", PAR_GRW(par))
+ call evl_pars ("close", "", PAR_EVL(par))
+
+ call imtclose (PAR_OUTSIGLIST(par))
+ call imtclose (PAR_OUTSKYLIST(par))
+
+ call clpcls (PAR_LOGLIST(par))
+ call imtclose (PAR_OMLIST(par))
+ call clpcls (PAR_CATDEFLIST(par))
+ call imtclose (PAR_OUTCATLIST(par))
+ call imtclose (PAR_INCATLIST(par))
+
+ call clpcls (PAR_SCALELIST(par,2))
+ call imtclose (PAR_GAINLIST(par,2))
+ call imtclose (PAR_EXPLIST(par,2))
+ call imtclose (PAR_SIGLIST(par,2))
+ call imtclose (PAR_SKYLIST(par,2))
+ call imtclose (PAR_BPMLIST(par,2))
+ call imtclose (PAR_IMLIST(par,2))
+
+ call clpcls (PAR_SCALELIST(par,1))
+ call imtclose (PAR_GAINLIST(par,1))
+ call imtclose (PAR_EXPLIST(par,1))
+ call imtclose (PAR_SIGLIST(par,1))
+ call imtclose (PAR_SKYLIST(par,1))
+ call imtclose (PAR_BPMLIST(par,1))
+ call imtclose (PAR_IMLIST(par,1))
+
+ call sfree (sp)
+end
+
+
+# T_ACEEVALUATE -- Evaluate objects.
+# This entry procedure simply sets up the parameters.
+
+procedure t_aceevaluate ()
+
+pointer par # Parameters
+
+pointer sp, str
+
+int imtopenp(), imtopen(), clpopnu(), fntopnb()
+
+begin
+ call smark (sp)
+ call salloc (par, PAR_LEN, TY_STRUCT)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call aclri (Memi[par], PAR_LEN)
+
+ # Get list parameters.
+ PAR_IMLIST(par,1) = imtopenp ("images")
+ PAR_BPMLIST(par,1) = imtopen ("")
+ PAR_SKYLIST(par,1) = imtopenp ("skys")
+ PAR_SIGLIST(par,1) = imtopenp ("sigmas")
+ PAR_EXPLIST(par,1) = imtopenp ("exps")
+ PAR_GAINLIST(par,1) = imtopenp ("gains")
+ PAR_SCALELIST(par,1) = fntopnb ("", NO)
+
+ PAR_IMLIST(par,2) = imtopen ("")
+ PAR_BPMLIST(par,2) = imtopen ("")
+ PAR_SKYLIST(par,2) = imtopen ("")
+ PAR_SIGLIST(par,2) = imtopen ("")
+ PAR_EXPLIST(par,2) = imtopen ("")
+ PAR_GAINLIST(par,2) = imtopen ("")
+ PAR_SCALELIST(par,2) = fntopnb ("", NO)
+
+ PAR_OMLIST(par) = imtopenp ("objmasks")
+ PAR_OMTYPE(par) = OM_ALL
+ PAR_INCATLIST(par) = imtopenp ("incatalogs")
+ PAR_OUTCATLIST(par) = imtopenp ("outcatalogs")
+ PAR_CATDEFLIST(par) = clpopnu ("catdefs")
+ PAR_LOGLIST(par) = clpopnu ("logfiles")
+
+ PAR_OUTSKYLIST(par) = imtopen ("")
+ PAR_OUTSIGLIST(par) = imtopen ("")
+
+ # Get other parameters.
+ # The parameter structures flag whether an operation is requested.
+ call sky_pars ("open", "", PAR_SKY(par))
+ call evl_pars ("open", "", PAR_EVL(par))
+
+ # Do the detection.
+ call aceall (par)
+
+ # Finish up.
+ call sky_pars ("close", "", PAR_SKY(par))
+ call det_pars ("close", "", PAR_DET(par))
+ call spt_pars ("close", "", PAR_SPT(par))
+ call grw_pars ("close", "", PAR_GRW(par))
+ call evl_pars ("close", "", PAR_EVL(par))
+
+ call imtclose (PAR_OUTSIGLIST(par))
+ call imtclose (PAR_OUTSKYLIST(par))
+
+ call clpcls (PAR_LOGLIST(par))
+ call imtclose (PAR_OMLIST(par))
+ call clpcls (PAR_CATDEFLIST(par))
+ call imtclose (PAR_INCATLIST(par))
+ call imtclose (PAR_OUTCATLIST(par))
+
+ call clpcls (PAR_SCALELIST(par,2))
+ call imtclose (PAR_GAINLIST(par,2))
+ call imtclose (PAR_EXPLIST(par,2))
+ call imtclose (PAR_SIGLIST(par,2))
+ call imtclose (PAR_SKYLIST(par,2))
+ call imtclose (PAR_BPMLIST(par,2))
+ call imtclose (PAR_IMLIST(par,2))
+
+ call clpcls (PAR_SCALELIST(par,1))
+ call imtclose (PAR_GAINLIST(par,1))
+ call imtclose (PAR_EXPLIST(par,1))
+ call imtclose (PAR_SIGLIST(par,1))
+ call imtclose (PAR_SKYLIST(par,1))
+ call imtclose (PAR_BPMLIST(par,1))
+ call imtclose (PAR_IMLIST(par,1))
+
+ call sfree (sp)
+end
+
+
+# T_ACESKY -- Output sky images.
+# This entry procedure simply sets up the parameters.
+
+procedure t_acesky ()
+
+pointer par # Parameters
+
+pointer sp, str
+
+int imtopenp(), imtopen(), clpopnu(), fntopnb()
+
+begin
+ call smark (sp)
+ call salloc (par, PAR_LEN, TY_STRUCT)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call aclri (Memi[par], PAR_LEN)
+
+ # Get list parameters.
+ PAR_IMLIST(par,1) = imtopenp ("images")
+ PAR_OUTSKYLIST(par) = imtopenp ("skyimages")
+ PAR_OUTSIGLIST(par) = imtopenp ("sigmaimages")
+ PAR_BPMLIST(par,1) = imtopen ("")
+ PAR_SKYLIST(par,1) = imtopenp ("skys")
+ PAR_SIGLIST(par,1) = imtopenp ("sigmas")
+ PAR_EXPLIST(par,1) = imtopenp ("exps")
+ PAR_GAINLIST(par,1) = imtopenp ("gains")
+ PAR_SCALELIST(par,1) = fntopnb ("", NO)
+
+ PAR_IMLIST(par,2) = imtopen ("")
+ PAR_BPMLIST(par,2) = imtopen ("")
+ PAR_SKYLIST(par,2) = imtopen ("")
+ PAR_SIGLIST(par,2) = imtopen ("")
+ PAR_EXPLIST(par,2) = imtopen ("")
+ PAR_GAINLIST(par,2) = imtopen ("")
+ PAR_SCALELIST(par,2) = fntopnb ("", NO)
+
+ PAR_OMLIST(par) = imtopen ("")
+ PAR_OMTYPE(par) = OM_ALL
+ PAR_INCATLIST(par) = imtopen ("")
+ PAR_OUTCATLIST(par) = imtopen ("")
+ PAR_CATDEFLIST(par) = fntopnb ("", NO)
+ PAR_LOGLIST(par) = clpopnu ("logfiles")
+
+ # Do the detection.
+ call aceall (par)
+
+ # Finish up.
+ call sky_pars ("close", "", PAR_SKY(par))
+ call det_pars ("close", "", PAR_DET(par))
+ call spt_pars ("close", "", PAR_SPT(par))
+ call grw_pars ("close", "", PAR_GRW(par))
+ call evl_pars ("close", "", PAR_EVL(par))
+
+ call imtclose (PAR_OUTSIGLIST(par))
+ call imtclose (PAR_OUTSKYLIST(par))
+
+ call clpcls (PAR_LOGLIST(par))
+ call imtclose (PAR_OMLIST(par))
+ call clpcls (PAR_CATDEFLIST(par))
+ call imtclose (PAR_INCATLIST(par))
+ call imtclose (PAR_OUTCATLIST(par))
+
+ call clpcls (PAR_SCALELIST(par,2))
+ call imtclose (PAR_GAINLIST(par,2))
+ call imtclose (PAR_EXPLIST(par,2))
+ call imtclose (PAR_SIGLIST(par,2))
+ call imtclose (PAR_SKYLIST(par,2))
+ call imtclose (PAR_BPMLIST(par,2))
+ call imtclose (PAR_IMLIST(par,2))
+
+ call clpcls (PAR_SCALELIST(par,1))
+ call imtclose (PAR_GAINLIST(par,1))
+ call imtclose (PAR_EXPLIST(par,1))
+ call imtclose (PAR_SIGLIST(par,1))
+ call imtclose (PAR_SKYLIST(par,1))
+ call imtclose (PAR_BPMLIST(par,1))
+ call imtclose (PAR_IMLIST(par,1))
+
+ call sfree (sp)
+end
+
+
+# T_DIFFDETECT -- Detect objects in the difference of images.
+
+procedure t_diffdetect ()
+
+pointer par # Parameters
+
+pointer sp, str
+
+int imtopenp(), imtopen(), clpopnu()
+
+begin
+ call smark (sp)
+ call salloc (par, PAR_LEN, TY_STRUCT)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call aclri (Memi[par], PAR_LEN)
+
+ # Get list parameters.
+ PAR_IMLIST(par,1) = imtopenp ("images")
+ PAR_BPMLIST(par,1) = imtopenp ("masks")
+ PAR_SKYLIST(par,1) = imtopenp ("skys")
+ PAR_SIGLIST(par,1) = imtopenp ("sigmas")
+ PAR_EXPLIST(par,1) = imtopenp ("exps")
+ PAR_GAINLIST(par,1) = imtopenp ("gains")
+ PAR_SCALELIST(par,1) = clpopnu ("scales")
+
+ PAR_IMLIST(par,2) = imtopenp ("rimages")
+ PAR_BPMLIST(par,2) = imtopenp ("rmasks")
+ PAR_SKYLIST(par,2) = imtopenp ("rskys")
+ PAR_SIGLIST(par,2) = imtopenp ("rsigmas")
+ PAR_EXPLIST(par,2) = imtopenp ("rexps")
+ PAR_GAINLIST(par,2) = imtopen ("")
+ PAR_SCALELIST(par,2) = clpopnu ("rscales")
+
+ PAR_OMLIST(par) = imtopenp ("objmasks")
+ PAR_OMTYPE(par) = OM_ALL
+ PAR_INCATLIST(par) = imtopen ("")
+ PAR_OUTCATLIST(par) = imtopenp ("catalogs")
+ PAR_CATDEFLIST(par) = clpopnu ("catdefs")
+ PAR_LOGLIST(par) = clpopnu ("logfiles")
+
+ PAR_OUTSKYLIST(par) = imtopen ("")
+ PAR_OUTSIGLIST(par) = imtopen ("")
+
+ # Get other parameters.
+ call sky_pars ("open", "", PAR_SKY(par))
+ call det_pars ("diff", "", PAR_DET(par))
+ call grw_pars ("open", "", PAR_GRW(par))
+ call evl_pars ("open", "", PAR_EVL(par))
+
+ # Do the detection.
+ call aceall (par)
+
+ # Finish up.
+ call sky_pars ("close", "", PAR_SKY(par))
+ call det_pars ("close", "", PAR_DET(par))
+ call spt_pars ("close", "", PAR_SPT(par))
+ call grw_pars ("close", "", PAR_GRW(par))
+ call evl_pars ("close", "", PAR_EVL(par))
+
+ call imtclose (PAR_OUTSIGLIST(par))
+ call imtclose (PAR_OUTSKYLIST(par))
+
+ call clpcls (PAR_LOGLIST(par))
+ call imtclose (PAR_OMLIST(par))
+ call clpcls (PAR_CATDEFLIST(par))
+ call imtclose (PAR_INCATLIST(par))
+ call imtclose (PAR_OUTCATLIST(par))
+
+ call clpcls (PAR_SCALELIST(par,2))
+ call imtclose (PAR_GAINLIST(par,2))
+ call imtclose (PAR_EXPLIST(par,2))
+ call imtclose (PAR_SIGLIST(par,2))
+ call imtclose (PAR_SKYLIST(par,2))
+ call imtclose (PAR_BPMLIST(par,2))
+ call imtclose (PAR_IMLIST(par,2))
+
+ call clpcls (PAR_SCALELIST(par,1))
+ call imtclose (PAR_GAINLIST(par,1))
+ call imtclose (PAR_EXPLIST(par,1))
+ call imtclose (PAR_SIGLIST(par,1))
+ call imtclose (PAR_SKYLIST(par,1))
+ call imtclose (PAR_BPMLIST(par,1))
+ call imtclose (PAR_IMLIST(par,1))
+
+ call sfree (sp)
+end
+
+
+
+# ACEALL -- Expand input list and set filenames.
+# This calls ACE for each image to be analyzed.
+
+procedure aceall (par)
+
+pointer par #I Parameters
+
+int i, j, k, list, imext
+pointer sp, str
+pointer image[4], bpmask[4], skyname[4], signame[4], expname[4], gainname[4]
+pointer incat[2], outcat[2], objmask[2], outsky[2], outsig[2], scalestr[2]
+pointer catdef, logfile
+pointer im, ptr
+
+int nowhite(), mscextensions(), strldxs(), strlen()
+int imtlen(), imtgetim(), clplen(), clgfil()
+pointer immap()
+errchk immap
+
+begin
+ call smark (sp)
+
+ # Allocate memory for all the file names. The first half of each
+ # array of names is for image names including extensions and the
+ # second half is for cluster names. The names are initialized
+ # to EOS and are only filled in if specified.
+
+ do j = 1, 4 {
+ call salloc (image[j], SZ_FNAME, TY_CHAR)
+ call salloc (bpmask[j], SZ_FNAME, TY_CHAR)
+ call salloc (skyname[j], SZ_FNAME, TY_CHAR)
+ call salloc (signame[j], SZ_FNAME, TY_CHAR)
+ call salloc (expname[j], SZ_FNAME, TY_CHAR)
+ call salloc (gainname[j], SZ_FNAME, TY_CHAR)
+ Memc[image[j]] = EOS
+ Memc[bpmask[j]] = EOS
+ Memc[skyname[j]] = EOS
+ Memc[signame[j]] = EOS
+ Memc[expname[j]] = EOS
+ Memc[gainname[j]] = EOS
+ }
+ do j = 1, 2 {
+ call salloc (objmask[j], SZ_FNAME, TY_CHAR)
+ call salloc (incat[j], SZ_FNAME, TY_CHAR)
+ call salloc (outcat[j], SZ_FNAME, TY_CHAR)
+ call salloc (outsky[j], SZ_FNAME, TY_CHAR)
+ call salloc (outsig[j], SZ_FNAME, TY_CHAR)
+ call salloc (scalestr[j], SZ_FNAME, TY_CHAR)
+ Memc[objmask[j]] = EOS
+ Memc[incat[j]] = EOS
+ Memc[outcat[j]] = EOS
+ Memc[outsky[j]] = EOS
+ Memc[outsig[j]] = EOS
+ Memc[scalestr[j]] = EOS
+ }
+ call salloc (catdef, SZ_FNAME, TY_CHAR)
+ call salloc (logfile, SZ_FNAME, TY_CHAR)
+ Memc[catdef] = EOS
+ Memc[logfile] = EOS
+
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Check lists match.
+ j = imtlen (PAR_IMLIST(par,1))
+ i = imtlen (PAR_BPMLIST(par,1))
+ if (i > 1 && i != j)
+ call error (1,
+ "Image and bad pixel mask lists do not match")
+ i = imtlen (PAR_SKYLIST(par,1))
+ if (i > 1 && i != j)
+ call error (1,
+ "Image and sky lists do not match")
+ i = imtlen (PAR_SIGLIST(par,1))
+ if (i > 1 && i != j)
+ call error (1,
+ "Image and sky sigma lists do not match")
+ i = imtlen (PAR_EXPLIST(par,1))
+ if (i > 1 && i != j)
+ call error (1,
+ "Image and exposure map lists do not match")
+ i = imtlen (PAR_GAINLIST(par,1))
+ if (i > 1 && i != j)
+ call error (1,
+ "Image and measurement gain lists do not match")
+ i = clplen (PAR_SCALELIST(par,1))
+ if (i > 1 && i != j)
+ call error (1,
+ "Image and scale lists do not match")
+
+ k = imtlen (PAR_IMLIST(par,2))
+ if (k > 1 && i != j)
+ call error (1,
+ "Image and reference lists do not match")
+ i = imtlen (PAR_BPMLIST(par,2))
+ if (i > 1 && i != k)
+ call error (1,
+ "Reference image bad pixel mask lists do not match")
+ i = imtlen (PAR_SKYLIST(par,2))
+ if (i > 1 && i != k)
+ call error (1,
+ "Reference image and sky lists do not match")
+ i = imtlen (PAR_SIGLIST(par,2))
+ if (i > 1 && i != k)
+ call error (1,
+ "Reference image and sky sigma lists do not match")
+ i = imtlen (PAR_EXPLIST(par,2))
+ if (i > 1 && i != k)
+ call error (1,
+ "Reference image and exposure map lists do not match")
+ i = imtlen (PAR_GAINLIST(par,2))
+ if (i > 1 && i != j)
+ call error (1,
+ "Reference image and measurement gain lists do not match")
+ i = clplen (PAR_SCALELIST(par,2))
+ if (i > 1 && i != k)
+ call error (1,
+ "Reference image and scale lists do not match")
+
+ i = clplen (PAR_INCATLIST(par))
+ if (i > 0 && i != j)
+ call error (1,
+ "Input image and input catalog lists do not match")
+ i = clplen (PAR_OUTCATLIST(par))
+ if (i > 0 && i != j)
+ call error (1,
+ "Input image and output catalog lists do not match")
+ i = clplen (PAR_CATDEFLIST(par))
+ if (i > 1 && i != j)
+ call error (1,
+ "Input image and catalog definition lists do not match")
+ i = imtlen (PAR_OMLIST(par))
+ if (i > 0 && i != j)
+ call error (1,
+ "Input image and object mask lists do not match")
+ i = clplen (PAR_LOGLIST(par))
+ if (i > 1 && i != j)
+ call error (1,
+ "Input image and logfile lists do not match")
+ i = imtlen (PAR_OUTSKYLIST(par))
+ if (i > 0 && i != j)
+ call error (1,
+ "Input image and output sky lists do not match")
+ i = imtlen (PAR_OUTSIGLIST(par))
+ if (i > 0 && i != j)
+ call error (1,
+ "Input image and output sigma lists do not match")
+
+ # Do each input image cluster.
+ while (imtgetim (PAR_IMLIST(par,1), Memc[image[1]], SZ_FNAME) != EOF) {
+ if (imtgetim (PAR_IMLIST(par,2), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[image[2]], SZ_FNAME)
+
+ # Get associated cluster names.
+ # Initialize image names to the cluster names.
+ # Strip whitespace to check for no name.
+ do j = 1, 2 {
+ if (imtgetim (PAR_BPMLIST(par,j), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[bpmask[j]], SZ_FNAME)
+ if (imtgetim (PAR_SKYLIST(par,j), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[skyname[j]], SZ_FNAME)
+ if (imtgetim (PAR_SIGLIST(par,j), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[signame[j]], SZ_FNAME)
+ if (imtgetim (PAR_EXPLIST(par,j), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[expname[j]], SZ_FNAME)
+ if (imtgetim (PAR_GAINLIST(par,j), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[gainname[j]], SZ_FNAME)
+ if (clgfil (PAR_SCALELIST(par,j), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[scalestr[j]], SZ_FNAME)
+
+ i = nowhite (Memc[bpmask[j]], Memc[bpmask[j]], SZ_FNAME)
+ i = nowhite (Memc[skyname[j]], Memc[skyname[j]], SZ_FNAME)
+ i = nowhite (Memc[signame[j]], Memc[signame[j]], SZ_FNAME)
+ i = nowhite (Memc[expname[j]], Memc[expname[j]], SZ_FNAME)
+ i = nowhite (Memc[gainname[j]], Memc[gainname[j]], SZ_FNAME)
+ i = nowhite (Memc[scalestr[j]], Memc[scalestr[j]], SZ_FNAME)
+ }
+
+ if (clgfil (PAR_INCATLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[incat[1]], SZ_FNAME)
+ if (clgfil (PAR_OUTCATLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[outcat[1]], SZ_FNAME)
+ if (imtgetim (PAR_OMLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[objmask[1]], SZ_FNAME)
+ if (imtgetim (PAR_OUTSKYLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[outsky[1]], SZ_FNAME)
+ if (imtgetim (PAR_OUTSIGLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[outsig[1]], SZ_FNAME)
+ if (clgfil (PAR_CATDEFLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[catdef], SZ_FNAME)
+ if (clgfil (PAR_LOGLIST(par), Memc[str], SZ_LINE) != EOF)
+ call strcpy (Memc[str], Memc[logfile], SZ_FNAME)
+
+ i = nowhite (Memc[incat[1]], Memc[incat[1]], SZ_FNAME)
+ i = nowhite (Memc[outcat[1]], Memc[outcat[1]], SZ_FNAME)
+ i = nowhite (Memc[objmask[1]], Memc[objmask[1]], SZ_FNAME)
+ i = nowhite (Memc[outsky[1]], Memc[outsky[1]], SZ_FNAME)
+ i = nowhite (Memc[outsig[1]], Memc[outsig[1]], SZ_FNAME)
+ i = nowhite (Memc[catdef], Memc[catdef], SZ_FNAME)
+ i = nowhite (Memc[logfile], Memc[logfile], SZ_FNAME)
+
+ # Expand clusters to images. As a special case, if the input is
+ # an explicit extension image then don't treat the filenames as MEF.
+ list = mscextensions (Memc[image[1]], "0-", PAR_EXTNAMES(par),
+ "", NO, YES, NO, "", NO, imext)
+ if (strldxs ("[", Memc[image[1]]) != 0)
+ imext = NO
+ while (imtgetim (list, Memc[image[3]], SZ_FNAME) != EOF) {
+ call strcpy (Memc[image[2]], Memc[image[4]], SZ_FNAME)
+ do j = 1, 2 {
+ call strcpy (Memc[bpmask[j]], Memc[bpmask[j+2]], SZ_FNAME)
+ call strcpy (Memc[skyname[j]], Memc[skyname[j+2]], SZ_FNAME)
+ call strcpy (Memc[signame[j]], Memc[signame[j+2]], SZ_FNAME)
+ call strcpy (Memc[expname[j]], Memc[expname[j+2]], SZ_FNAME)
+ call strcpy (Memc[gainname[j]],Memc[gainname[j+2]],SZ_FNAME)
+ }
+ call strcpy (Memc[incat[1]], Memc[incat[2]], SZ_FNAME)
+ call strcpy (Memc[outcat[1]], Memc[outcat[2]], SZ_FNAME)
+ call strcpy (Memc[objmask[1]], Memc[objmask[2]], SZ_FNAME)
+ call strcpy (Memc[outsky[1]], Memc[outsky[2]], SZ_FNAME)
+ call strcpy (Memc[outsig[1]], Memc[outsig[2]], SZ_FNAME)
+
+ # Add extensions if needed.
+ i = strldxs ("[", Memc[image[3]])
+ if (imext == YES && i > 0) {
+ i = image[3]+i-1
+ call strcpy (Memc[i], Memc[str], SZ_LINE)
+ Memc[str+strldxs ("]", Memc[str])-1] = EOS
+ call strcat (",append]", Memc[str], SZ_LINE)
+
+ if (Memc[image[2]]!=EOS &&
+ strldxs ("[", Memc[image[2]]) == 0)
+ call strcat (Memc[i], Memc[image[4]], SZ_FNAME)
+ do j = 1, 2 {
+ if (Memc[bpmask[j]]!=EOS && Memc[bpmask[j]]!='!' &&
+ strldxs ("[", Memc[bpmask[j]]) == 0)
+ call strcat (Memc[i], Memc[bpmask[j+2]], SZ_FNAME)
+ if (Memc[skyname[j]]!=EOS && Memc[skyname[j]]!='!' &&
+ strldxs ("[", Memc[skyname[j]]) == 0)
+ call strcat (Memc[str], Memc[skyname[j+2]],
+ SZ_FNAME)
+ if (Memc[signame[j]]!=EOS && Memc[signame[j]]!='!' &&
+ strldxs ("[", Memc[signame[j]]) == 0)
+ call strcat (Memc[str], Memc[signame[j+2]],
+ SZ_FNAME)
+ if (Memc[expname[j]]!=EOS && Memc[expname[j]]!='!' &&
+ strldxs ("[", Memc[expname[j]]) == 0)
+ call strcat (Memc[i], Memc[expname[j+2]], SZ_FNAME)
+ if (Memc[gainname[j]]!=EOS && Memc[gainname[j]]!='!' &&
+ strldxs ("[", Memc[gainname[j]]) == 0)
+ call strcat (Memc[i], Memc[gainname[j+2]], SZ_FNAME)
+ }
+ if (Memc[incat[1]]!=EOS && Memc[incat[1]]!='!' &&
+ strldxs ("[", Memc[incat[1]]) == 0)
+ call strcat (Memc[i], Memc[incat[2]], SZ_FNAME)
+ if (Memc[outcat[1]]!=EOS && Memc[outcat[1]]!='!' &&
+ strldxs ("[", Memc[outcat[1]]) == 0)
+ call strcat (Memc[i], Memc[outcat[2]], SZ_FNAME)
+ if (Memc[outsky[1]]!=EOS && Memc[outsky[1]]!='!' &&
+ strldxs ("[", Memc[outsky[1]]) == 0)
+ call strcat (Memc[str], Memc[outsky[2]], SZ_FNAME)
+ if (Memc[outsig[1]]!=EOS && Memc[outsig[1]]!='!' &&
+ strldxs ("[", Memc[outsig[1]]) == 0)
+ call strcat (Memc[str], Memc[outsig[2]], SZ_FNAME)
+ if (Memc[objmask[1]]!=EOS && Memc[objmask[1]]!='!' &&
+ strldxs ("[", Memc[objmask[1]]) == 0)
+ call strcat (Memc[str], Memc[objmask[2]], SZ_FNAME)
+ }
+
+ # Append DATASEC.
+ do i = 3, 4 {
+ if (Memc[image[i]] == EOS)
+ next
+ iferr {
+ im = NULL
+ ptr = immap (Memc[image[i]], READ_ONLY, 0); im = ptr
+ j = strlen (Memc[image[i]])
+ call imgstr (im, "DATASEC", Memc[image[i]+j],
+ SZ_FNAME-j)
+ } then
+ ;
+ if (im != NULL)
+ call imunmap (im)
+ }
+
+ # Process the image.
+ call ace (par, image[3], bpmask[3], skyname[3], signame[3],
+ expname[3], gainname[3], scalestr, Memc[incat[2]],
+ Memc[outcat[2]], Memc[objmask[2]], Memc[outsky[2]],
+ Memc[outsig[2]], Memc[catdef], Memc[logfile])
+
+ }
+ call imtclose (list)
+ }
+
+ call sfree (sp)
+end
+
+
+# ACE -- Do all the primary steps for a single input image/catalog.
+
+procedure ace (par, image, bpmask, skyname, signame, expname, gainname,
+ scalestr, incat, outcat, objmask, outsky, outsig, catdef, logfile)
+
+pointer par #I Parameters
+pointer image[2], bpmask[2], skyname[2], signame[2], expname[2]
+pointer gainname[2], scalestr[2]
+char incat[ARB], outcat[ARB], objmask[ARB], outsky[ARB], outsig[ARB]
+char catdef[ARB], logfile[ARB]
+
+bool dosky[2], dosig[2]
+int i, j, logfd, offset[2,2]
+real scale[2]
+pointer sp, bpname[2], str
+pointer im[2], bpm[2], skymap[2], sigmap[2], expmap[2], gainmap[2]
+pointer ptr, cat, om, omim, siglevmap, siglevels
+
+bool strne()
+real imgetr()
+int ctor(), strdic(), fnextn(), imstati()
+int open(), access(), imaccess()
+pointer immap(), xt_pmmap(), pm_open(), map_open()
+
+errchk open, immap, xt_pmmap, pm_newmask
+errchk cnvparse, sky, detect, split, grow, evaluate, map_open
+errchk catdefine, catopen, catgets
+
+#pointer bpm1, im_pmmapo()
+
+begin
+ call smark (sp)
+ call salloc (bpname[1], SZ_FNAME, TY_CHAR)
+ call salloc (bpname[2], SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Deal with image types if needed.
+ if (Memc[bpmask[1]] != EOS && Memc[bpmask[1]] != '!')
+ call xt_maskname (Memc[bpmask[1]], "pl", READ_ONLY, Memc[bpmask[1]],
+ SZ_FNAME)
+ if (Memc[bpmask[2]] != EOS && Memc[bpmask[2]] != '!')
+ call xt_maskname (Memc[bpmask[2]], "pl", READ_ONLY, Memc[bpmask[2]],
+ SZ_FNAME)
+ if (objmask[1] != EOS && objmask[1] != '!')
+ call xt_maskname (objmask, "pl", NEW_IMAGE, objmask, SZ_FNAME)
+ if (incat[1] != EOS) {
+ i = fnextn (incat, Memc[str], SZ_LINE)
+ if (i > 0)
+ i = strdic (Memc[str], Memc[str], SZ_LINE, CATEXTNS)
+ #if (i == 0)
+ # call strcat (".fits", incat, SZ_FNAME)
+ }
+ if (outcat[1] != EOS) {
+ i = fnextn (outcat, Memc[str], SZ_LINE)
+ if (i > 0)
+ i = strdic (Memc[str], Memc[str], SZ_LINE, CATEXTNS)
+ #if (i == 0)
+ # call strcat (".fits", outcat, SZ_FNAME)
+ }
+
+ iferr {
+ # Initialize for error recovery.
+ do j = 1, 2 {
+ im[j] = NULL; bpm[j] = NULL; skymap[j] = NULL
+ sigmap[j] = NULL; expmap[j] = NULL; gainmap[j] = NULL
+ }
+ cat = NULL; logfd = NULL
+
+ # Log file.
+ if (logfile[1] != EOS) {
+ ptr = open (logfile, APPEND, TEXT_FILE)
+ logfd = ptr
+ call fseti (logfd, F_FLUSHNL, YES)
+ }
+
+ # Open images.
+ if (PAR_DET(par) == NULL && PAR_EVL(par) == NULL)
+ ptr = immap (Memc[image[1]], READ_ONLY, 0)
+ else {
+ iferr (ptr = immap (Memc[image[1]], READ_WRITE, 0))
+ ptr = immap (Memc[image[1]], READ_ONLY, 0)
+ }
+ im[1] = ptr
+
+ # Open input catalog and object mask.
+ if (PAR_DET(par) == NULL && PAR_EVL(par) == NULL)
+ ;
+ else if (PAR_DET(par) == NULL) {
+ if (incat[1] == EOS) {
+ call sprintf (Memc[str], SZ_LINE,
+ "No input catalog for image (%s)")
+ call pargstr (Memc[image[1]])
+ call error (1, Memc[str])
+ } else {
+ if (access (incat, 0, 0) != YES) {
+ call sprintf (Memc[str], SZ_LINE,
+ "Catalog does not exist (%s)")
+ call pargstr (incat)
+ call error (1, Memc[str])
+ }
+ }
+ if (outcat[1]!=EOS && strne(incat,outcat)) {
+ if (access (outcat, 0, 0) == YES) {
+ call sprintf (Memc[str], SZ_LINE,
+ "Catalog already exists (%s)")
+ call pargstr (outcat)
+ call error (1, Memc[str])
+ }
+ }
+ call catopen (cat, incat, outcat, catdef)
+ call catrobjs (cat, "")
+ if (objmask[1] == EOS)
+ call catgets (cat, "mask", objmask, SZ_FNAME)
+ omim = xt_pmmap (objmask, im[1], objmask, SZ_FNAME)
+ om = imstati (omim, IM_PMDES)
+ } else {
+ # Check for existing catalog. Check catalog definitions.
+ if (outcat[1] != EOS) {
+ if (access (outcat, 0, 0) == YES) {
+ call sprintf (Memc[str], SZ_LINE,
+ "Catalog already exists (%s)")
+ call pargstr (outcat)
+ call error (1, Memc[str])
+ }
+ call catdefine (NULL, NULL, catdef)
+ }
+ call catopen (cat, "", "", "")
+
+ # Check for existing mask and initialize.
+ if (objmask[1] != EOS) {
+ if (imaccess (objmask, 0) == YES) {
+ call sprintf (Memc[str], SZ_LINE,
+ "Object mask already exists (%s)")
+ call pargstr (objmask)
+ call error (1, Memc[str])
+ }
+ }
+ }
+
+ # Open bad pixel mask.
+ ptr = xt_pmmap (Memc[bpmask[1]], im[1], Memc[bpname[1]],
+ SZ_FNAME)
+ bpm[1] = ptr
+
+ # Do reference image.
+ if (Memc[image[2]] != EOS) {
+# if (Memc[bpmask[2]] == EOS)
+# call imgimage (Memc[image[2]], Memc[image[2]], SZ_FNAME)
+
+ iferr (ptr = immap (Memc[image[2]], READ_WRITE, 0))
+ ptr = immap (Memc[image[2]], READ_ONLY, 0)
+ im[2] = ptr
+
+ # Set offsets.
+ call get_offsets (im, 2, "world", offset)
+ offset[1,2] = offset[1,2] - offset[1,1]
+ offset[2,2] = offset[2,2] - offset[2,1]
+
+# # Attempt to make an overlapping image section if
+# # there is no bad pixel mask. This is a kludge.
+# if (Memc[bpmask[2]] == EOS) {
+# c1 = max (1, 1-offset[1,2])
+# c2 = min (IM_LEN(im[2],1), IM_LEN(im[1],1)-offset[1,2])
+# l1 = max (1, 1-offset[2,2])
+# l2 = min (IM_LEN(im[2],2), IM_LEN(im[1],2)-offset[2,2])
+# if (c1!=1 || c2!=IM_LEN(im[2],1) ||
+# l1!=1 || l2!=IM_LEN(im[2],2)) {
+# call sprintf (Memc[str], SZ_LINE, "%s[%d:%d,%d:%d]")
+# call pargstr (Memc[image[2]])
+# call pargi (c1)
+# call pargi (c2)
+# call pargi (l1)
+# call pargi (l2)
+# call strcpy (Memc[str], Memc[image[2]], SZ_FNAME)
+# call imunmap (im[2])
+# iferr (ptr = immap (Memc[image[2]], READ_WRITE, 0))
+# ptr = immap (Memc[image[2]], READ_ONLY, 0)
+# im[2] = ptr
+#
+# call get_offsets (im, 2, "world", offset)
+# offset[1,2] = offset[1,2] - offset[1,1]
+# offset[2,2] = offset[2,2] - offset[2,1]
+# PAR_OFFSET(par,1) = offset[1,2]
+# PAR_OFFSET(par,2) = offset[2,2]
+# }
+# }
+
+ ptr = xt_pmmap (Memc[bpmask[2]], im[2], Memc[bpname[2]],
+ SZ_FNAME)
+ bpm[2] = ptr
+
+ i = 1
+ if (Memc[scalestr[1]] == EOS)
+ scale[1] = 1.
+ else if (Memc[scalestr[1]] == '!') {
+ iferr (scale[1] = imgetr (im[1], Memc[scalestr[1]+1]))
+ call error (1, "Bad scale for input image")
+ } else if (ctor (Memc[scalestr[1]], i, scale[1]) == 0)
+ call error (1, "Bad scale for image")
+
+ i = 1
+ if (Memc[scalestr[2]] == EOS)
+ scale[2] = 1.
+ else if (Memc[scalestr[2]] == '!') {
+ iferr (scale[2] = imgetr (im[2], Memc[scalestr[2]+1]))
+ call error (1, "Bad scale for reference image")
+ } else if (ctor (Memc[scalestr[2]], i, scale[2]) == 0)
+ call error (1, "Bad scale for reference image")
+ }
+
+ if (logfd != NULL) {
+ call sysid (Memc[str], SZ_LINE)
+ call fprintf (logfd, "ACE: %s\n")
+ call pargstr (Memc[str])
+ call fprintf (logfd, " Image: %s - %s\n")
+ call pargstr (Memc[image[1]])
+ call pargstr (IM_TITLE(im[1]))
+ if (bpm[1] != NULL) {
+ call fprintf (logfd, " Bad pixel mask: %s\n")
+ call pargstr (Memc[bpname[1]])
+ }
+ if (im[2] != EOS) {
+ call fprintf (logfd, " Reference image: %s - %s\n")
+ call pargstr (Memc[image[2]])
+ call pargstr (IM_TITLE(im[2]))
+ if (bpm[2] != NULL) {
+ call fprintf (logfd,
+ " Reference bad pixel mask: %s\n")
+ call pargstr (Memc[bpname[2]])
+ }
+ }
+ }
+
+ # Open optional maps.
+ do j = 1, 2 {
+ if (im[j] == NULL)
+ next
+ if (Memc[expname[j]] != EOS)
+ expmap[j] = map_open (Memc[expname[j]], im[j])
+ }
+ do j = 1, 2 {
+ if (im[j] == NULL)
+ next
+ if (Memc[gainname[j]] != EOS)
+ gainmap[j] = map_open (Memc[gainname[j]], im[j])
+ }
+
+ # Get sky and sky sigma.
+ do j = 1, 2 {
+ dosky[j] = false
+ dosig[j] = false
+ if (im[j] == NULL)
+ next
+ if (PAR_SKY(par) == NULL) {
+ if (Memc[skyname[j]] != EOS)
+ skymap[j] = map_open (Memc[skyname[j]], im[j])
+ if (Memc[signame[j]] != EOS)
+ sigmap[j] = map_open (Memc[signame[j]], im[j])
+ } else {
+ if (j == 1 && om != NULL)
+ call sky (PAR_SKY(par), im[j], omim, expmap[j],
+ Memc[skyname[j]], Memc[signame[j]],
+ skymap[j], sigmap[j], dosky[j], dosig[j], logfd)
+ else
+ call sky (PAR_SKY(par), im[j], bpm[j], expmap[j],
+ Memc[skyname[j]], Memc[signame[j]],
+ skymap[j], sigmap[j], dosky[j], dosig[j], logfd)
+ }
+ if (skymap[j] != NULL)
+ call map_seti (skymap[j], "sample", 5)
+ if (sigmap[j] != NULL)
+ call map_seti (sigmap[j], "sample", 5)
+ }
+
+ # Detect objects.
+ if (PAR_DET(par) != NULL) {
+ # Open object mask.
+ om = pm_open (NULL)
+ call pm_ssize (om, IM_NDIM(im[1]), IM_LEN(im[1],1), 27)
+
+ # Initialize splitting map if needed.
+ if (PAR_SPT(par) != NULL) {
+ siglevmap = pm_open (NULL)
+ call pm_ssize (siglevmap, IM_NDIM(im[1]),
+ IM_LEN(im[1],1), 27)
+ } else
+ siglevmap = NULL
+
+ # Detect objects.
+ call detect (PAR_DET(par), PAR_SPT(par), dosky, dosig,
+ Memc[skyname[1]], Memc[signame[1]], im, bpm, skymap,
+ sigmap, expmap, scale, offset[1,2], om, siglevmap,
+ siglevels, logfd, cat)
+
+ # Split objects.
+ if (PAR_SPT(par) != NULL)
+ call split (PAR_SPT(par), cat, om, siglevmap,
+ Memr[siglevels], logfd)
+
+ # Grow objects.
+ if (PAR_GRW(par) != NULL)
+ call grow (PAR_GRW(par), cat, om, logfd)
+
+ # Set boundary flags and write out the object mask.
+ if (objmask[1] != EOS) {
+ if (PAR_OMTYPE(par) == OM_ALL)
+ call bndry (om, NULL)
+ call omwrite (om, objmask, PAR_OMTYPE(par), im[1], cat,
+ outcat, outcat, logfd)
+ }
+ }
+
+ # Evaluate and write out the catalog.
+ if (PAR_EVL(par) != NULL && outcat[1] != EOS) {
+ if (incat[1] == EOS)
+ call catopen (cat, "", outcat, catdef)
+ call catputs (cat, "image", Memc[image[1]])
+ if (objmask[1] != EOS)
+ call catputs (cat, "mask", objmask)
+ call catputs (cat, "catalog", outcat)
+ call catputs (cat, "objid", outcat)
+
+ # Evaluate objects.
+ call evaluate (PAR_EVL(par), cat, im[1], om, skymap[1],
+ sigmap[1], gainmap[1], expmap[1], logfd)
+
+ if (logfd != NULL) {
+ call fprintf (logfd,
+ " Write catalog: catalog = %s\n")
+ call pargstr (outcat)
+ }
+
+ call catcreate (cat)
+ call catwcs (cat, im)
+ call catwhdr (cat, im)
+ call catwobjs (cat)
+
+ call imastr (im[1], "CATALOG", outcat)
+ }
+
+ # Output sky images.
+ call skyimages (outsky, outsig, im[1], skymap[1],
+ sigmap[1], gainmap[1], expmap[1], logfd)
+
+ } then
+ call erract (EA_WARN)
+
+ if (logfd != NULL)
+ call close (logfd)
+ if (cat != NULL)
+ call catclose (cat)
+ if (siglevmap != NULL) {
+ call pm_close (siglevmap)
+ call mfree (siglevels, TY_REAL)
+ }
+ if (omim != NULL) {
+ call imunmap (omim)
+ om = NULL
+ } else if (om != NULL)
+ call pm_close (om)
+
+ do j = 1, 2 {
+ if (gainmap[j] != NULL)
+ call map_close (gainmap[j])
+ if (expmap[j] != NULL)
+ call map_close (expmap[j])
+ if (sigmap[j] != NULL)
+ call map_close (sigmap[j])
+ if (skymap[j] != NULL)
+ call map_close (skymap[j])
+ if (bpm[j] != NULL)
+ call imunmap (bpm[j])
+ if (im[j] != NULL)
+ call imunmap (im[j])
+ }
+
+ call sfree (sp)
+end
+
+
+define OFFTYPES "|none|wcs|world|physical|"
+define FILE 0
+define NONE 1
+define WCS 2
+define WORLD 3
+define PHYSICAL 4
+
+# GET_OFFSETS -- Get offsets.
+
+procedure get_offsets (in, nimages, param, offsets)
+
+pointer in[nimages] #I Input image pointers
+int nimages #I Number of images
+char param[ARB] #I Offset parameter string
+int offsets[2,nimages] #O Offsets
+
+int i, j, fd, offtype, off
+real val
+bool flip, streq(), fp_equald()
+pointer sp, str, fname
+pointer pref, lref, wref, cd, ltm, coord, section
+pointer mw, ct, mw_openim(), mw_sctran(), immap()
+int open(), fscan(), nscan(), strlen(), strdic()
+errchk mw_openim, mw_gwtermd, mw_gltermd, mw_gaxmap
+errchk mw_sctran, mw_ctrand, open, immap
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (fname, SZ_LINE, TY_CHAR)
+ call salloc (lref, 2, TY_DOUBLE)
+ call salloc (wref, 2, TY_DOUBLE)
+ call salloc (cd, 2*2, TY_DOUBLE)
+ call salloc (coord, 2, TY_DOUBLE)
+
+ call aclri (offsets, 2*nimages)
+
+ # Parse the user offset string. If "none" then there are no offsets.
+ # If "world" or "wcs" then set the offsets based on the world WCS.
+ # If "physical" then set the offsets based on the physical WCS.
+ # If a file scan the offsets.
+
+ call sscan (param)
+ call gargwrd (Memc[str], SZ_LINE)
+ if (nscan() == 0)
+ offtype = NONE
+ else {
+ offtype = strdic (Memc[str], Memc[fname], SZ_LINE, OFFTYPES)
+ if (offtype > 0 && !streq (Memc[str], Memc[fname]))
+ offtype = 0
+ }
+ if (offtype == 0)
+ offtype = FILE
+
+ switch (offtype) {
+ case NONE:
+ ;
+ case WORLD, WCS:
+ mw = mw_openim (in[1])
+ call mw_gwtermd (mw, Memd[lref], Memd[wref], Memd[cd], 2)
+ ct = mw_sctran (mw, "world", "logical", 0)
+ call mw_ctrand (ct, Memd[wref], Memd[lref], 2)
+ call mw_close (mw)
+
+ do i = 2, nimages {
+ mw = mw_openim (in[i])
+ ct = mw_sctran (mw, "world", "logical", 0)
+ call mw_ctrand (ct, Memd[wref], Memd[coord], 2)
+ do j = 1, 2
+ offsets[j,i] = nint (Memd[lref+j-1] - Memd[coord+j-1])
+ call mw_close (mw)
+ }
+ case PHYSICAL:
+ call salloc (pref, 2, TY_DOUBLE)
+ call salloc (ltm, 4, TY_DOUBLE)
+ call salloc (section, SZ_FNAME, TY_CHAR)
+
+ mw = mw_openim (in[1])
+ call mw_gltermd (mw, Memd[ltm], Memd[coord], 2)
+ call mw_close (mw)
+ do i = 2, nimages {
+ mw = mw_openim (in[i])
+ call mw_gltermd (mw, Memd[cd], Memd[coord], 2)
+ call strcpy ("[", Memc[section], SZ_FNAME)
+ flip = false
+ do j = 0, 3, 3 {
+ if (Memd[ltm+j] * Memd[cd+j] >= 0.)
+ call strcat ("*,", Memc[section], SZ_FNAME)
+ else {
+ call strcat ("-*,", Memc[section], SZ_FNAME)
+ flip = true
+ }
+ }
+ Memc[section+strlen(Memc[section])-1] = ']'
+ if (flip) {
+ call imstats (in[i], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call strcat (Memc[section], Memc[fname], SZ_LINE)
+ call imunmap (in[i])
+ in[i] = immap (Memc[fname], READ_ONLY, TY_CHAR)
+ call mw_close (mw)
+ mw = mw_openim (in[i])
+ call mw_gltermd (mw, Memd[cd], Memd[coord], 2)
+ do j = 0, 3
+ if (!fp_equald (Memd[ltm+j], Memd[cd+j]))
+ call error (1,
+ "Cannot match physical coordinates")
+ }
+ call mw_close (mw)
+ }
+
+ mw = mw_openim (in[1])
+ ct = mw_sctran (mw, "logical", "physical", 0)
+ call mw_ctrand (ct, Memd[lref], Memd[pref], 2)
+ call mw_close (mw)
+ do i = 2, nimages {
+ mw = mw_openim (in[i])
+ ct = mw_sctran (mw, "physical", "logical", 0)
+ call mw_ctrand (ct, Memd[pref], Memd[coord], 2)
+ do j = 1, 2
+ offsets[j,i] = nint (Memd[lref+j-1] - Memd[coord+j-1])
+ call mw_close (mw)
+ }
+ case FILE:
+ fd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ i = 1
+ while (fscan (fd) != EOF) {
+ do j = 1, 2 {
+ call gargr (val)
+ offsets[j,i] = nint (val)
+ }
+ if (nscan() == 2)
+ i = i + 1
+ }
+ call close (fd)
+ if (i <= nimages)
+ call error (1, "offset file incomplete")
+ }
+
+ # Adjust offsets to be positive.
+ do j = 1, 2 {
+ off = offsets[j,1]
+ do i = 2, nimages
+ off = min (off, offsets[j,i])
+ do i = 1, nimages
+ offsets[j,i] = offsets[j,i] - off
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/nproto/ace/t_acedisplay.x b/noao/nproto/ace/t_acedisplay.x
new file mode 100644
index 00000000..7b19851b
--- /dev/null
+++ b/noao/nproto/ace/t_acedisplay.x
@@ -0,0 +1,639 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+include <imset.h>
+include <imhdr.h>
+include <error.h>
+include <pmset.h>
+include "display.h"
+include "gwindow.h"
+
+# DISPLAY - Display an image. The specified image section is mapped into
+# the specified section of an image display frame. The mapping involves
+# a linear transformation in X and Y and a linear or logarithmic transformation
+# in Z (greyscale). Images of all pixel datatypes are supported, and there
+# no upper limit on the size of an image. The display device is interfaced
+# to FIO as a file and is accessed herein via IMIO as just another imagefile.
+# The physical characteristics of the display (i.e., X, Y, and Z resolution)
+# are taken from the image header. The display frame buffer is the pixel
+# storage "file".
+
+# This is a version of the standard display that allows the overlay mask
+# to be manipuated in memory prior to displaying.
+
+procedure t_acedisplay()
+
+char image[SZ_FNAME] # Image to display
+int frame # Display frame
+int erase # Erase frame?
+
+int i
+pointer sp, wdes, im, ds, ovrly
+
+bool clgetb()
+int clgeti(), btoi()
+pointer immap(), imd_mapframe1(), overlay()
+errchk immap, imd_mapframe1
+errchk ds_getparams, ds_setwcs, ds_load_display, ds_erase_border
+
+begin
+ call smark (sp)
+ call salloc (wdes, LEN_WDES, TY_STRUCT)
+ call aclri (Memi[wdes], LEN_WDES)
+
+ # Open input imagefile.
+ call clgstr ("image", image, SZ_FNAME)
+ im = immap (image, READ_ONLY, 0)
+ if (IM_NDIM(im) <= 0)
+ call error (1, "image has no pixels")
+
+ # Open display device as an image.
+ frame = clgeti ("frame")
+ erase = btoi (clgetb ("erase"))
+ if (erase == YES)
+ ds = imd_mapframe1 (frame, WRITE_ONLY,
+ btoi (clgetb ("select_frame")), erase)
+ else
+ ds = imd_mapframe1 (frame, READ_WRITE,
+ btoi (clgetb ("select_frame")), erase)
+
+ # Get display parameters and set up transformation.
+ call ds_getparams (im, ds, wdes)
+
+ # Compute and output the screen to image pixel WCS.
+ call ds_setwcs (im, ds, wdes, image, frame)
+
+ # Setup the overlay.
+ ovrly = overlay (W_OVRLY(wdes), im)
+
+ # Display the image and zero the border if necessary.
+ call ods_load_display (im, ds, wdes, ovrly)
+ if (!clgetb ("erase") && clgetb ("border_erase"))
+ call ds_erase_border (im, ds, wdes)
+
+ # Free storage.
+ call maskcolor_free (W_OCOLORS(wdes))
+ call maskcolor_free (W_BPCOLORS(wdes))
+ do i = 0, W_MAXWC
+ if (W_UPTR(W_WC(wdes,i)) != NULL)
+ call ds_ulutfree (W_UPTR(W_WC(wdes,i)))
+ if (ovrly != NULL)
+ call imunmap (ovrly)
+ call imunmap (ds)
+ call imunmap (im)
+
+ call sfree (sp)
+end
+
+
+# DS_LOAD_DISPLAY -- Map an image into the display window. In general this
+# involves independent linear transformations in the X, Y, and Z (greyscale)
+# dimensions. If a spatial dimension is larger than the display window then
+# the image is block averaged. If a spatial dimension or a block averaged
+# dimension is smaller than the display window then linear interpolation is
+# used to expand the image. Both the input image and the output device appear
+# to us as images, accessed via IMIO. All spatial scaling is
+# handled by the "scaled input" package, i.e., SIGM2[SR]. Our task is to
+# get lines from the scaled input image, transform the greyscale if necessary,
+# and write the lines to the output device.
+
+# This version passes the overlay mask pointer rather than mapping it.
+# Otherwise this is unchanged from the standard version.
+
+procedure ods_load_display (im, ds, wdes, ovrly)
+
+pointer im # input image
+pointer ds # output image
+pointer wdes # graphics window descriptor
+pointer ovrly # overlay pointer
+
+real z1, z2, dz1, dz2, px1, px2, py1, py2
+int i, order, zt, wx1, wx2, wy1, wy2, wy, nx, ny, xblk, yblk
+pointer wdwin, wipix, wdpix, bpm, pm, uptr
+pointer in, out, si, si_ovrly, si_bpovrly, ocolors, bpcolors, rtemp
+bool unitary_greyscale_transformation
+short lut1, lut2, dz1_s, dz2_s, z1_s, z2_s
+
+bool fp_equalr()
+int imstati()
+real if_elogr()
+pointer ds_pmmap(), imps2s(), imps2r(), sigm2s(), sigm2r(), sigm2_setup()
+errchk ds_pmmap, imps2s, imps2r, sigm2s, sigm2r, sigm2_setup
+
+extern if_elogr
+
+begin
+ wdwin = W_WC(wdes,W_DWIN)
+ wipix = W_WC(wdes,W_IPIX)
+ wdpix = W_WC(wdes,W_DPIX)
+
+ # Set image and display pixels.
+ px1 = nint (W_XS(wipix))
+ px2 = nint (W_XE(wipix))
+ py1 = nint (W_YS(wipix))
+ py2 = nint (W_YE(wipix))
+ wx1 = nint (W_XS(wdpix))
+ wx2 = nint (W_XE(wdpix))
+ wy1 = nint (W_YS(wdpix))
+ wy2 = nint (W_YE(wdpix))
+
+ z1 = W_ZS(wdwin)
+ z2 = W_ZE(wdwin)
+ zt = W_ZT(wdwin)
+ uptr = W_UPTR(wdwin)
+ order = max (W_XT(wdwin), W_YT(wdwin))
+
+ # Setup scaled input and masks.
+ si = NULL
+ si_ovrly = NULL
+ si_bpovrly = NULL
+ nx = wx2 - wx1 + 1
+ ny = wy2 - wy1 + 1
+ xblk = INDEFI
+ yblk = INDEFI
+
+ ocolors = W_OCOLORS(wdes)
+# iferr (ovrly = ds_pmmap (W_OVRLY(wdes), im)) {
+# call erract (EA_WARN)
+# ovrly = NULL
+# }
+ if (ovrly != NULL) {
+ xblk = INDEFI
+ yblk = INDEFI
+ si_ovrly = sigm2_setup (ovrly, NULL, px1,px2,nx,xblk,
+ py1,py2,ny,yblk, -1)
+ }
+
+ bpcolors = W_BPCOLORS(wdes)
+ switch (W_BPDISP(wdes)) {
+ case BPDNONE:
+ si = sigm2_setup (im, NULL, px1,px2,nx,xblk, py1,py2,ny,yblk, order)
+ case BPDOVRLY:
+ si = sigm2_setup (im, NULL, px1,px2,nx,xblk, py1,py2,ny,yblk, order)
+ iferr (bpm = ds_pmmap (W_BPM(wdes), im))
+ bpm = NULL
+ if (bpm != NULL)
+ si_bpovrly = sigm2_setup (bpm, NULL, px1,px2,nx,xblk,
+ py1,py2,ny,yblk, -1)
+ case BPDINTERP:
+ iferr (bpm = ds_pmmap (W_BPM(wdes), im))
+ bpm = NULL
+ if (bpm != NULL)
+ pm = imstati (bpm, IM_PMDES)
+ else
+ pm = NULL
+ si = sigm2_setup (im, pm, px1,px2,nx,xblk, py1,py2,ny,yblk, order)
+ }
+
+ # The device IM_MIN and IM_MAX parameters define the acceptable range
+ # of greyscale values for the output device (e.g., 0-255 for most 8-bit
+ # display devices). Values Z1 and Z2 are mapped linearly or
+ # logarithmically into IM_MIN and IM_MAX.
+
+ dz1 = IM_MIN(ds)
+ dz2 = IM_MAX(ds)
+ if (fp_equalr (z1, z2)) {
+ z1 = z1 - 1
+ z2 = z2 + 1
+ }
+
+ # If the user specifies the transfer function, verify that the
+ # intensity and greyscale are in range.
+
+ if (zt == W_USER) {
+ call alims (Mems[uptr], U_MAXPTS, lut1, lut2)
+ dz1_s = short (dz1)
+ dz2_s = short (dz2)
+ if (lut2 < dz1_s || lut1 > dz2_s)
+ call eprintf ("User specified greyscales out of range\n")
+ if (z2 < IM_MIN(im) || z1 > IM_MAX(im))
+ call eprintf ("User specified intensities out of range\n")
+ }
+
+ # Type short pixels are treated as a special case to minimize vector
+ # operations for such images (which are common). If the image pixels
+ # are either short or real then only the ALTR (greyscale transformation)
+ # vector operation is required. The ALTR operator linearly maps
+ # greylevels in the range Z1:Z2 to DZ1:DZ2, and does a floor ceiling
+ # of DZ1:DZ2 on all pixels outside the range. If unity mapping is
+ # employed the data is simply copied, i.e., floor ceiling constraints
+ # are not applied. This is very fast and will produce a contoured
+ # image on the display which will be adequate for some applications.
+
+ if (zt == W_UNITARY) {
+ unitary_greyscale_transformation = true
+ } else if (zt == W_LINEAR) {
+ unitary_greyscale_transformation =
+ (fp_equalr(z1,dz1) && fp_equalr(z2,dz2))
+ } else
+ unitary_greyscale_transformation = false
+
+ if (IM_PIXTYPE(im) == TY_SHORT && zt != W_LOG) {
+ z1_s = z1; z2_s = z2
+ if (z1_s == z2_s) {
+ z1_s = z1_s - 1
+ z2_s = z2_s + 1
+ }
+
+ for (wy=wy1; wy <= wy2; wy=wy+1) {
+ in = sigm2s (si, wy - wy1 + 1)
+ out = imps2s (ds, wx1, wx2, wy, wy)
+
+ if (unitary_greyscale_transformation) {
+ call amovs (Mems[in], Mems[out], nx)
+ } else if (zt == W_USER) {
+ dz1_s = U_Z1; dz2_s = U_Z2
+ call amaps (Mems[in],Mems[out],nx, z1_s,z2_s, dz1_s,dz2_s)
+ call aluts (Mems[out], Mems[out], nx, Mems[uptr])
+ } else {
+ dz1_s = dz1; dz2_s = dz2
+ call amaps (Mems[in],Mems[out],nx, z1_s,z2_s, dz1_s,dz2_s)
+ }
+
+ if (si_ovrly != NULL) {
+ in = sigm2s (si_ovrly, wy - wy1 + 1)
+ do i = 0, nx-1 {
+ if (Mems[in+i] != 0)
+ call mcolors (ocolors, int(Mems[in+i]),
+ Mems[out+i])
+ }
+ }
+ if (si_bpovrly != NULL) {
+ in = sigm2s (si_bpovrly, wy - wy1 + 1)
+ do i = 0, nx-1 {
+ if (Mems[in+i] != 0)
+ call mcolors (bpcolors, int(Mems[in+i]),
+ Mems[out+i])
+ }
+ }
+ }
+
+ } else if (zt == W_USER) {
+ call salloc (rtemp, nx, TY_REAL)
+
+ for (wy=wy1; wy <= wy2; wy=wy+1) {
+ in = sigm2r (si, wy - wy1 + 1)
+ out = imps2s (ds, wx1, wx2, wy, wy)
+
+ call amapr (Memr[in], Memr[rtemp], nx, z1, z2,
+ real(U_Z1), real(U_Z2))
+ call achtrs (Memr[rtemp], Mems[out], nx)
+ call aluts (Mems[out], Mems[out], nx, Mems[uptr])
+
+ if (si_ovrly != NULL) {
+ in = sigm2s (si_ovrly, wy - wy1 + 1)
+ do i = 0, nx-1 {
+ if (Mems[in+i] != 0)
+ call mcolors (ocolors, int(Mems[in+i]),
+ Mems[out+i])
+ }
+ }
+ if (si_bpovrly != NULL) {
+ in = sigm2s (si_bpovrly, wy - wy1 + 1)
+ do i = 0, nx-1 {
+ if (Mems[in+i] != 0)
+ call mcolors (bpcolors, int(Mems[in+i]),
+ Mems[out+i])
+ }
+ }
+ }
+
+ } else {
+ for (wy=wy1; wy <= wy2; wy=wy+1) {
+ in = sigm2r (si, wy - wy1 + 1)
+ out = imps2r (ds, wx1, wx2, wy, wy)
+
+ if (unitary_greyscale_transformation) {
+ call amovr (Memr[in], Memr[out], nx)
+ } else if (zt == W_LOG) {
+ call amapr (Memr[in], Memr[out], nx,
+ z1, z2, 1.0, 10.0 ** MAXLOG)
+ call alogr (Memr[out], Memr[out], nx, if_elogr)
+ call amapr (Memr[out], Memr[out], nx,
+ 0.0, real(MAXLOG), dz1, dz2)
+ } else
+ call amapr (Memr[in], Memr[out], nx, z1, z2, dz1, dz2)
+
+ if (si_ovrly != NULL) {
+ in = sigm2s (si_ovrly, wy - wy1 + 1)
+ do i = 0, nx-1 {
+ if (Mems[in+i] != 0)
+ call mcolorr (ocolors, int(Mems[in+i]),
+ Memr[out+i])
+ }
+ }
+ if (si_bpovrly != NULL) {
+ in = sigm2s (si_bpovrly, wy - wy1 + 1)
+ do i = 0, nx-1 {
+ if (Mems[in+i] != 0)
+ call mcolorr (bpcolors, int(Mems[in+i]),
+ Memr[out+i])
+ }
+ }
+ }
+ }
+
+ call sigm2_free (si)
+ if (si_ovrly != NULL)
+ call sigm2_free (si_ovrly)
+ if (si_bpovrly != NULL)
+ call sigm2_free (si_bpovrly)
+# if (ovrly != NULL)
+# call imunmap (ovrly)
+ if (bpm != NULL)
+ call imunmap (bpm)
+end
+
+
+# The ds_pmmap routines needed to be modified for 27 bit masks.
+
+include <mach.h>
+include <ctype.h>
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include <pmset.h>
+include <syserr.h>
+
+
+# DS_PMMAP -- Open a pixel mask READ_ONLY.
+#
+# Open the pixel mask. If a regular image is specified convert it to
+# a pixel mask. Match the mask to the reference image based on the
+# physical coordinates. A null filename is allowed and returns NULL.
+
+pointer procedure ods_pmmap (pmname, refim)
+
+char pmname[ARB] #I Pixel mask name
+pointer refim #I Reference image pointer
+
+pointer im
+char fname[SZ_FNAME]
+int nowhite(), errcode()
+bool streq()
+pointer im_pmmap(), ods_immap()
+errchk ods_immap, ods_match
+
+begin
+ if (nowhite (pmname, fname, SZ_FNAME) == 0)
+ return (NULL)
+ if (streq (fname, "EMPTY"))
+ return (NULL)
+ if (streq (fname, "BPM")) {
+ iferr (call imgstr (refim, "BPM", fname, SZ_FNAME))
+ return (NULL)
+ }
+
+ iferr (im = im_pmmap (fname, READ_ONLY, NULL)) {
+ switch (errcode()) {
+ case SYS_FOPNNEXFIL, SYS_PLBADSAVEF:
+ im = ods_immap (fname, refim)
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+
+ iferr (call ods_match (im, refim))
+ call erract (EA_WARN)
+
+ return (im)
+end
+
+
+# DS_PMIMMAP -- Open a pixel mask from a non-pixel list image.
+# Return error if the image cannot be opened.
+
+pointer procedure ods_immap (pmname, refim)
+
+char pmname[ARB] #I Image name
+pointer refim #I Reference image pointer
+
+short val
+int i, ndim, npix
+pointer sp, v1, v2, im_in, im_out, pm, mw, data
+
+int imgnli()
+pointer immap(), pm_newmask(), im_pmmapo(), imgl1i(), mw_openim()
+errchk immap, mw_openim
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+
+ im_in = immap (pmname, READ_ONLY, 0)
+ pm = pm_newmask (im_in, 16)
+
+ ndim = IM_NDIM(im_in)
+ npix = IM_LEN(im_in,1)
+
+ while (imgnli (im_in, data, Meml[v1]) != EOF) {
+ do i = 0, npix-1 {
+ val = Memi[data+i]
+ if (val < 0)
+ Memi[data+i] = 0
+ }
+ call pmplpi (pm, Meml[v2], Memi[data], 0, npix, PIX_SRC)
+ call amovl (Meml[v1], Meml[v2], ndim)
+ }
+
+ im_out = im_pmmapo (pm, im_in)
+ data = imgl1i (im_out) # Force I/O to set header
+ mw = mw_openim (im_in) # Set WCS
+ call mw_saveim (mw, im_out)
+ call mw_close (mw)
+
+ call imunmap (im_in)
+ call sfree (sp)
+ return (im_out)
+end
+
+
+# DS_MATCH -- Set the pixel mask to match the reference image.
+# This matches sizes and physical coordinates and allows the
+# original mask to be smaller or larger than the reference image.
+# Subsequent use of the pixel mask can then work in the logical
+# coordinates of the reference image. A null input returns a null output.
+
+procedure ods_match (im, refim)
+
+pointer im #U Pixel mask image pointer
+pointer refim #I Reference image pointer
+
+int i, j, k, nc, nl, ncpm, nlpm, c1, c2, l1, l2, nref, npm
+int steptype, xoffset, xstep, yoffset, ystep
+double x1, x2, y1, y2
+long vold[IM_MAXDIM], vnew[IM_MAXDIM]
+pointer mwref, mwpm, ctref, ctpm, pm, pmnew, imnew, bufref, bufpm
+
+int imstati()
+pointer pm_open(), mw_openim(), im_pmmapo(), imgl1i(), mw_sctran()
+bool pm_empty(), pm_linenotempty()
+errchk pm_open, mw_openim
+
+begin
+ if (im == NULL)
+ return
+
+ # Set sizes.
+ pm = imstati (im, IM_PMDES)
+ nc = IM_LEN(refim,1)
+ nl = IM_LEN(refim,2)
+ ncpm = IM_LEN(im,1)
+ nlpm = IM_LEN(im,2)
+
+ # Check if the two are the same logical size and the mask is empty.
+ if (nc == ncpm && nl == nlpm && pm_empty (pm))
+ return
+
+ # Check coordinate transformations.
+ mwref = mw_openim (refim)
+ mwpm = mw_openim (im)
+
+ steptype = 1
+ ctref = mw_sctran (mwref, "logical", "physical", 3)
+ ctpm = mw_sctran (mwpm, "physical", "logical", 3)
+ call mw_c2trand (ctref, 1D0, 1D0, x1, y1)
+ call mw_c2trand (ctpm, x1, y1, x1, y1)
+ call mw_c2trand (ctref, 2D0, 1D0, x2, y2)
+ call mw_c2trand (ctpm, x2, y2, x2, y2)
+ if (abs(x2-x1) < 1.) {
+ steptype = 2
+ call mw_ctfree (ctref)
+ call mw_ctfree (ctpm)
+ ctref = mw_sctran (mwref, "physical", "logical", 3)
+ ctpm = mw_sctran (mwpm, "logical", "physical", 3)
+ call mw_c2trand (ctpm, 1D0, 1D0, x1, y1)
+ call mw_c2trand (ctref, x1, y1, x1, y1)
+ call mw_c2trand (ctpm, 2D0, 1D0, x2, y2)
+ call mw_c2trand (ctref, x2, y2, x2, y2)
+ }
+ x2 = x2 - x1
+ if (abs(y1-y2) > 10*EPSILONR)
+ call error (0, "Image and mask have a relative rotation")
+ if (abs(x1-nint(x1)) > 10*EPSILONR &&
+ abs(x1-nint(x1))-0.5 > 10*EPSILONR)
+ call error (0, "Image and mask have non-integer relative offsets")
+ if (abs(x2-nint(x2)) > 10*EPSILONR)
+ call error (0, "Image and mask have non-integer relative steps")
+ xoffset = nint (x1 - 1D0)
+ xstep = nint (x2)
+
+ if (steptype == 1) {
+ call mw_c2trand (ctref, 1D0, 1D0, x1, y1)
+ call mw_c2trand (ctpm, x1, y1, x1, y1)
+ call mw_c2trand (ctref, 1D0, 2D0, x2, y2)
+ call mw_c2trand (ctpm, x2, y2, x2, y2)
+ } else {
+ call mw_c2trand (ctpm, 1D0, 1D0, x1, y1)
+ call mw_c2trand (ctref, x1, y1, x1, y1)
+ call mw_c2trand (ctpm, 1D0, 2D0, x2, y2)
+ call mw_c2trand (ctref, x2, y2, x2, y2)
+ }
+ y2 = y2 - y1
+ if (abs(x1-x2) > 10*EPSILONR)
+ call error (0, "Image and mask have a relative rotation")
+ if (abs(y1-nint(y1)) > 10*EPSILONR &&
+ abs(y1-nint(y1))-0.5 > 10*EPSILONR)
+ call error (0, "Image and mask have non-integer relative offsets")
+ if (abs(y2-nint(y2)) > 10*EPSILONR)
+ call error (0, "Image and mask have non-integer relative steps")
+ yoffset = nint (y1 - 1D0)
+ ystep = nint (y2)
+
+ call mw_ctfree (ctref)
+ call mw_ctfree (ctpm)
+ call mw_close (mwref)
+ call mw_close (mwpm)
+
+ # Check if the two have the same coordinate system.
+ if (nc==ncpm && nl==nlpm && xoffset==0 && yoffset==0 && xstep==ystep)
+ return
+
+ # Create a new pixel mask of the required size and offset.
+ pmnew = pm_open (NULL)
+ call pm_ssize (pmnew, 2, IM_LEN(refim,1), 27)
+ imnew = im_pmmapo (pmnew, NULL)
+ bufref = imgl1i (imnew)
+
+ if (steptype == 1) {
+ c1 = 1 + xoffset + max (0, (xstep - 1 - xoffset) / xstep) * xstep
+ c2 = 1 + xoffset + min (nc-1, (ncpm - 1 - xoffset) / xstep) * xstep
+ l1 = 1 + yoffset + max (0, (ystep - 1 - yoffset) / ystep) * ystep
+ l2 = 1 + yoffset + min (nl-1, (nlpm - 1 - yoffset) / ystep) * ystep
+ npm = c2 - c1 + 1
+ nref = npm / xstep
+ if (nref > 0) {
+ call malloc (bufpm, npm, TY_INT)
+ call malloc (bufref, nref, TY_INT)
+ call amovkl (long(1), vold, IM_MAXDIM)
+ call amovkl (long(1), vnew, IM_MAXDIM)
+ vold[1] = c1
+ vnew[1] = c1 - xoffset
+ do i = l1, l2, ystep {
+ vold[2] = i
+ if (!pm_linenotempty (pm, vold))
+ next
+ call pmglpi (pm, vold, Memi[bufpm], 0, npm, 0)
+ vnew[2] = l1 - yoffset + (i - l1) / ystep
+ j = 0
+ do k = 0, npm-1, xstep {
+ Memi[bufref+j] = Memi[bufpm+k]
+ j = j + 1
+ }
+ call pmplpi (pmnew, vnew, Memi[bufref], 0, nref, PIX_SRC)
+ }
+ }
+ } else {
+ c1 = max (1, 1 - xoffset)
+ c2 = min (ncpm, nc / xstep - xoffset)
+ l1 = max (1, 1 - yoffset)
+ l2 = min (nlpm, nl / ystep - yoffset)
+ npm = c2 - c1 + 1
+ nref = npm * xstep
+ if (nref > 0) {
+ call malloc (bufpm, npm, TY_INT)
+ call malloc (bufref, nref, TY_INT)
+ call amovkl (long(1), vold, IM_MAXDIM)
+ call amovkl (long(1), vnew, IM_MAXDIM)
+ vold[1] = c1
+ vnew[1] = c1 + xoffset
+ do i = l1, l2 {
+ vold[2] = i
+ if (!pm_linenotempty (pm, vold))
+ next
+ call pmglpi (pm, vold, Memi[bufpm], 0, npm, 0)
+ call aclri (Memi[bufref], nref)
+ do j = 0, npm-1 {
+ k = j * xstep
+ Memi[bufref+k] = Memi[bufpm+j]
+ }
+ vnew[2] = l1 + yoffset + (i - l1) * ystep
+ call pmplpi (pmnew, vnew, Memi[bufref], 0, nref, PIX_SRC)
+ }
+ }
+ call mfree (bufpm, TY_INT)
+ call mfree (bufref, TY_INT)
+ }
+
+ # Update the IMIO descriptor.
+ call imunmap (im)
+ im = imnew
+ call imseti (im, IM_PMDES, pmnew)
+end
+
+
+# IF_ELOG -- The error function for log10. Note that MAX_EXPONENT is
+# currently an integer so it is converted to the appropriate data type
+# before being returned.
+
+real procedure if_elogr (x)
+
+real x # the input pixel value
+
+begin
+ return (real(-MAX_EXPONENT))
+end
diff --git a/noao/nproto/ace/t_imext.x b/noao/nproto/ace/t_imext.x
new file mode 100644
index 00000000..178f6937
--- /dev/null
+++ b/noao/nproto/ace/t_imext.x
@@ -0,0 +1,533 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <syserr.h>
+include <imhdr.h>
+include <imset.h>
+
+define OUTPUTS "|none|list|file|"
+define NONE 1 # No output
+define LIST 2 # List output
+define FILE 3 # File output
+
+define SZ_RANGE 100 # Size of range list
+define SZ_LISTOUT 255 # Size of output list
+
+
+# T_IMEXTENSIONS -- Expand a template of FITS files into a list of image
+# extensions on the standard output and record the number image extensions
+# in a parameter.
+
+procedure t_imextensions()
+
+pointer input # List of ME file names
+int output # Output list (none|list|file)
+pointer index # Range list of extension indexes
+pointer extname # Patterns for extension names
+pointer extver # Range list of extension versions
+int lindex # List index number?
+int lname # List extension name?
+int lver # List extension version?
+pointer ikparams # Image kernel parameters
+
+pointer sp, image, listout
+int list, nimages, fd
+int clgwrd(), btoi(), imextensions(), stropen()
+int imtgetim(), imtlen()
+bool clgetb()
+errchk stropen, fprintf, strclose
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_LINE, TY_CHAR)
+ call salloc (index, SZ_LINE, TY_CHAR)
+ call salloc (extname, SZ_LINE, TY_CHAR)
+ call salloc (extver, SZ_LINE, TY_CHAR)
+ call salloc (ikparams, SZ_LINE, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+
+ # Task parameters
+ call clgstr ("input", Memc[input], SZ_LINE)
+ output = clgwrd ("output", Memc[image], SZ_FNAME, OUTPUTS)
+ call clgstr ("index", Memc[index], SZ_LINE)
+ call clgstr ("extname", Memc[extname], SZ_LINE)
+ call clgstr ("extver", Memc[extver], SZ_LINE)
+ lindex = btoi (clgetb ("lindex"))
+ lname = btoi (clgetb ("lname"))
+ lver = btoi (clgetb ("lver"))
+ call clgstr ("ikparams", Memc[ikparams], SZ_LINE)
+
+ # Get the list.
+ list = imextensions (Memc[input], Memc[index], Memc[extname],
+ Memc[extver], lindex, lname, lver, Memc[ikparams], YES)
+
+ # Format the output and set the number of images.
+ switch (output) {
+ case LIST:
+ call salloc (listout, SZ_LISTOUT, TY_CHAR)
+ iferr {
+ fd = stropen (Memc[listout], SZ_LISTOUT, WRITE_ONLY)
+ nimages = 0
+ while (imtgetim (list, Memc[image], SZ_FNAME) != EOF) {
+ nimages = nimages + 1
+ if (nimages == 1) {
+ call fprintf (fd, "%s")
+ call pargstr (Memc[image])
+ } else {
+ call fprintf (fd, ",%s")
+ call pargstr (Memc[image])
+ }
+ }
+ call strclose (fd)
+ call printf ("%s\n")
+ call pargstr (Memc[listout])
+ } then {
+ call imtclose (list)
+ call sfree (sp)
+ call error (1, "Output list format is too long")
+ }
+ case FILE:
+ while (imtgetim (list, Memc[image], SZ_FNAME) != EOF) {
+ call printf ("%s\n")
+ call pargstr (Memc[image])
+ }
+ }
+ call clputi ("nimages", imtlen (list))
+
+ call imtclose (list)
+ call sfree (sp)
+end
+
+
+# IMEXTENSIONS -- Expand a template of ME files into a list of image extensions.
+
+int procedure imextensions (files, index, extname, extver, lindex, lname, lver,
+ ikparams, err)
+
+char files[ARB] #I List of ME files
+char index[ARB] #I Range list of extension indexes
+char extname[ARB] #I Patterns for extension names
+char extver[ARB] #I Range list of extension versions
+int lindex #I List index number?
+int lname #I List extension name?
+int lver #I List extension version?
+char ikparams[ARB] #I Image kernel parameters
+int err #I Print errors?
+int list #O Image list
+
+int i, fd
+pointer sp, temp, fname, imname, section, rindex, rextver, ikp, str
+int imtopen(), imtgetim()
+int ix_decode_ranges(), nowhite(), open()
+errchk open, imextension, delete
+
+begin
+ call smark (sp)
+ call salloc (temp, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call salloc (section, SZ_FNAME, TY_CHAR)
+ call salloc (ikp, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Expand parameters.
+ list = imtopen (files)
+ call salloc (rindex, 3*SZ_RANGE, TY_INT)
+ if (ix_decode_ranges (index, Memi[rindex], SZ_RANGE, i) == ERR)
+ call error (1, "Bad index range list")
+
+ rextver = NULL
+ if (nowhite (extver, Memc[str], SZ_LINE) > 0) {
+ call salloc (rextver, 3*SZ_RANGE, TY_INT)
+ if (ix_decode_ranges (Memc[str], Memi[rextver], SZ_RANGE, i)==ERR)
+ call error (1, "Bad extension version range list")
+ }
+ i = nowhite (ikparams, Memc[ikp], SZ_LINE)
+
+ # Expand ME files into list of image extensions in a temp file.
+ call mktemp ("@tmp$iraf", Memc[temp], SZ_FNAME)
+ fd = open (Memc[temp+1], NEW_FILE, TEXT_FILE)
+ while (imtgetim (list, Memc[fname], SZ_FNAME) != EOF) {
+ call imgimage (Memc[fname], Memc[imname], SZ_FNAME)
+ call imgsection (Memc[fname], Memc[section], SZ_FNAME)
+ call imextension (fd, Memc[imname], rindex, extname, rextver,
+ lindex, lname, lver, Memc[ikp], Memc[section], err)
+ }
+ call imtclose (list)
+ call close (fd)
+
+ # Return list.
+ list = imtopen (Memc[temp])
+ call delete (Memc[temp+1])
+ call sfree (sp)
+ return (list)
+end
+
+
+# IMEXTENSION -- Expand a single ME file into a list of image extensions.
+# The image extensions are written to the input file descriptor.
+
+procedure imextension (fd, fname, index, extname, extver, lindex, lname, lver,
+ ikparams, section, err)
+
+int fd #I File descriptor for list
+char fname[SZ_FNAME] #I File image name (without kernel or image sec)
+pointer index #I Range list of extension indexes
+char extname[ARB] #I Pattern for extension names
+pointer extver #I Range list of extension versions
+int lindex #I List index number?
+int lname #I List extension name?
+int lver #I List extension version?
+char ikparams[ARB] #I Image kernel parameters
+char section[ARB] #I Image section
+int err #I Print errors?
+
+bool extmatch()
+int i, j, ver, ix_get_next_number(), errcode(), imgeti(), stridxs()
+pointer sp, image, name, str, im, immap()
+bool is_in_range()
+
+begin
+ call smark (sp)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (name, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ i = -1
+ while (ix_get_next_number (Memi[index], i) != EOF) {
+ j = stridxs ("[", fname)
+ if (j > 0) {
+ if (i > 0)
+ break
+ call strcpy (fname, Memc[image], SZ_FNAME)
+ } else {
+ call sprintf (Memc[image], SZ_FNAME, "%s[%d]")
+ call pargstr (fname)
+ call pargi (i)
+ }
+ iferr (im = immap (Memc[image], READ_ONLY, 0)) {
+ switch (errcode()) {
+ case SYS_FXFRFEOF:
+ break
+ case SYS_IKIEXTN:
+ next
+ case SYS_IKIOPEN:
+ switch (i) {
+ case 0:
+ next
+ case 1:
+ if (err == YES)
+ call erract (EA_WARN)
+ break
+ default:
+ break
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+
+ # Check the extension name.
+ if (extname[1] != EOS) {
+ iferr (call imgstr (im, "extname", Memc[name], SZ_LINE)) {
+ Memc[name] = EOS
+ #call imunmap (im)
+ #next
+ }
+ if (!extmatch (Memc[name], extname)) {
+ call imunmap (im)
+ next
+ }
+ }
+
+ # Check the extension version.
+ if (extver != NULL) {
+ iferr (ver = imgeti (im, "extver")) {
+ call imunmap (im)
+ next
+ }
+ if (!is_in_range (Memi[extver], ver)) {
+ call imunmap (im)
+ next
+ }
+ }
+
+ # Set the extension name and version.
+ if (lname == YES) {
+ iferr (call imgstr (im, "extname", Memc[name], SZ_LINE))
+ Memc[name] = EOS
+ } else
+ Memc[name] = EOS
+ if (lver == YES) {
+ iferr (ver = imgeti (im, "extver"))
+ ver = INDEFI
+ } else
+ ver = INDEFI
+
+ # Write the image name.
+ call fprintf (fd, fname)
+ if (j == 0) {
+ if (lindex == YES || (Memc[name] == EOS && IS_INDEFI(ver))) {
+ call fprintf (fd, "[%d]")
+ call pargi (i)
+ }
+ if (Memc[name] != EOS) {
+ call fprintf (fd, "[%s")
+ call pargstr (Memc[name])
+ if (!IS_INDEFI(ver)) {
+ call fprintf (fd, ",%d")
+ call pargi (ver)
+ }
+ if (ikparams[1] != EOS) {
+ call fprintf (fd, ",%s")
+ call pargstr (ikparams)
+ }
+ call fprintf (fd, "]")
+ } else if (!IS_INDEFI(ver)) {
+ call fprintf (fd, "[extver=%d")
+ call pargi (ver)
+ if (ikparams[1] != EOS) {
+ call fprintf (fd, ",%s")
+ call pargstr (ikparams)
+ }
+ call fprintf (fd, "]")
+ } else if (ikparams[1] != EOS) {
+ call fprintf (fd, "[%s]")
+ call pargstr (ikparams)
+ }
+ }
+ call fprintf (fd, "%s")
+ call pargstr (section)
+ call fprintf (fd, "\n")
+
+ call imunmap (im)
+ }
+
+ call sfree (sp)
+end
+
+
+include <mach.h>
+include <ctype.h>
+
+define FIRST 1 # Default starting range
+define LAST MAX_INT # Default ending range
+define STEP 1 # Default step
+define EOLIST -1 # End of list
+
+# IX_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 EOLIST.
+
+int procedure ix_decode_ranges (range_string, ranges, max_ranges, 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 nvalues # The number of values in the ranges
+
+int ip, nrange, first, last, step, ctoi()
+
+begin
+ ip = 1
+ nvalues = 0
+
+ do nrange = 1, max_ranges - 1 {
+ # Defaults to all nonnegative integers
+ first = FIRST
+ last = LAST
+ step = STEP
+
+ # 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) {
+ # Null string defaults
+ ranges[1, 1] = first
+ ranges[2, 1] = last
+ ranges[3, 1] = step
+ ranges[1, 2] = EOLIST
+ nvalues = MAX_INT
+ return (OK)
+ } else {
+ ranges[1, nrange] = EOLIST
+ return (OK)
+ }
+ } else if (range_string[ip] == '-')
+ ;
+ else if (range_string[ip] == 'x')
+ ;
+ else if (IS_DIGIT(range_string[ip])) { # ,n..
+ if (ctoi (range_string, ip, first) == 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 last = first.
+ if (range_string[ip] == 'x')
+ ;
+ else if (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, last) == 0)
+ return (ERR)
+ } else if (range_string[ip] == 'x')
+ ;
+ else
+ return (ERR)
+ } else
+ last = first
+
+ # 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)
+ ;
+ if (step == 0)
+ return (ERR)
+ } else if (range_string[ip] == '-')
+ ;
+ else
+ return (ERR)
+ }
+
+ # Output the range triple.
+ ranges[1, nrange] = first
+ ranges[2, nrange] = last
+ ranges[3, nrange] = step
+ nvalues = nvalues + abs (last-first) / step + 1
+ }
+
+ return (ERR) # ran out of space
+end
+
+
+# IX_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 ix_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] != EOLIST; ip=ip+3) {
+ first = min (ranges[ip], ranges[ip+1])
+ last = max (ranges[ip], ranges[ip+1])
+ step = ranges[ip+2]
+ if (step == 0)
+ call error (1, "Step size of zero in range list")
+ 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
+
+
+# EXTMATCH -- Match extname against a comma-delimited list of patterns.
+
+bool procedure extmatch (extname, patterns)
+
+char extname[ARB] #I Extension name to match
+char patterns[ARB] #I Comma-delimited list of patterns
+bool stat #O Match?
+
+int i, j, k, sz_pat, strlen(), patmake(), patmatch(), nowhite()
+pointer sp, patstr, patbuf
+
+begin
+ stat = false
+
+ sz_pat = strlen (patterns)
+ if (sz_pat == 0)
+ return (stat)
+ sz_pat = sz_pat + SZ_LINE
+
+ call smark (sp)
+ call salloc (patstr, sz_pat, TY_CHAR)
+ call salloc (patbuf, sz_pat, TY_CHAR)
+
+ i = nowhite (patterns, Memc[patstr], sz_pat)
+ if (i == 0)
+ stat = true
+ else if (i == 1 && Memc[patstr] == '*')
+ stat = true
+ else {
+ i = 1
+ for (j=i;; j=j+1) {
+ if (patterns[j] != ',' && patterns[j] != EOS)
+ next
+ if (j - i > 0) {
+ if (j-i == 1 && patterns[i] == '*') {
+ stat = true
+ break
+ }
+ call strcpy (patterns[i], Memc[patstr+1], j-i)
+ Memc[patstr] = '^'
+ Memc[patstr+j-i+1] = '$'
+ Memc[patstr+j-i+2] = EOS
+ k = patmake (Memc[patstr], Memc[patbuf], sz_pat)
+ if (patmatch (extname, Memc[patbuf]) > 0) {
+ stat = true
+ break
+ }
+ }
+ if (patterns[j] == EOS)
+ break
+ i = j + 1
+ }
+ }
+
+ call sfree (sp)
+ return (stat)
+end
diff --git a/noao/nproto/ace/t_mscext.x b/noao/nproto/ace/t_mscext.x
new file mode 100644
index 00000000..b57ba5cf
--- /dev/null
+++ b/noao/nproto/ace/t_mscext.x
@@ -0,0 +1,180 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <syserr.h>
+include <imhdr.h>
+include <imset.h>
+
+define OUTPUTS "|none|list|file|"
+define NONE 1 # No output
+define LIST 2 # List output
+define FILE 3 # File output
+
+define SZ_RANGE 100 # Size of range list
+define SZ_LISTOUT 255 # Size of output list
+
+
+# T_MSCEXTENSIONS -- Expand a template of FITS files into a list of image
+# extensions on the standard output and record the number image extensions
+# in a parameter.
+#
+# This differs from IMEXTENSIONS in that extension zero is not returned
+# unless it is a simple image and, in that case, the extension is removed.
+# Also a parameter is written indicating if the list contains image extensions.
+
+procedure t_mscextensions()
+
+pointer input # List of ME file names
+int output # Output list (none|list|file)
+pointer index # Range list of extension indexes
+pointer extname # Patterns for extension names
+pointer extver # Range list of extension versions
+int lindex # List index number?
+int lname # List extension name?
+int lver # List extension version?
+pointer ikparams # Image kernel parameters
+
+pointer sp, image, listout
+int list, nimages, fd, imext
+int clgwrd(), btoi(), mscextensions(), stropen()
+int imtgetim(), imtlen()
+bool clgetb()
+errchk stropen, fprintf, strclose
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_LINE, TY_CHAR)
+ call salloc (index, SZ_LINE, TY_CHAR)
+ call salloc (extname, SZ_LINE, TY_CHAR)
+ call salloc (extver, SZ_LINE, TY_CHAR)
+ call salloc (ikparams, SZ_LINE, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+
+ # Task parameters
+ call clgstr ("input", Memc[input], SZ_LINE)
+ output = clgwrd ("output", Memc[image], SZ_FNAME, OUTPUTS)
+ call clgstr ("index", Memc[index], SZ_LINE)
+ call clgstr ("extname", Memc[extname], SZ_LINE)
+ call clgstr ("extver", Memc[extver], SZ_LINE)
+ lindex = btoi (clgetb ("lindex"))
+ lname = btoi (clgetb ("lname"))
+ lver = btoi (clgetb ("lver"))
+ call clgstr ("ikparams", Memc[ikparams], SZ_LINE)
+
+ # Get the list.
+ list = mscextensions (Memc[input], Memc[index], Memc[extname],
+ Memc[extver], lindex, lname, lver, Memc[ikparams], NO, imext)
+
+ # Format the output and set the number of images.
+ switch (output) {
+ case LIST:
+ call salloc (listout, SZ_LISTOUT, TY_CHAR)
+ iferr {
+ fd = stropen (Memc[listout], SZ_LISTOUT, WRITE_ONLY)
+ nimages = 0
+ while (imtgetim (list, Memc[image], SZ_FNAME) != EOF) {
+ nimages = nimages + 1
+ if (nimages == 1) {
+ call fprintf (fd, "%s")
+ call pargstr (Memc[image])
+ } else {
+ call fprintf (fd, ",%s")
+ call pargstr (Memc[image])
+ }
+ }
+ call strclose (fd)
+ call printf ("%s\n")
+ call pargstr (Memc[listout])
+ } then {
+ call imtclose (list)
+ call sfree (sp)
+ call error (1, "Output list format is too long")
+ }
+ case FILE:
+ while (imtgetim (list, Memc[image], SZ_FNAME) != EOF) {
+ call printf ("%s\n")
+ call pargstr (Memc[image])
+ }
+ }
+ call clputi ("nimages", imtlen (list))
+ call clputb ("imext", (imext==YES))
+
+ call imtclose (list)
+ call sfree (sp)
+end
+
+
+# MSCEXTENSIONS -- Expand template of files into a list of image extensions.
+#
+# This differs from IMEXTENSIONS in that extension zero is not returned
+# unless it is a simple image and, in that case, the extension is removed.
+
+int procedure mscextensions (files, index, extname, extver, lindex, lname, lver,
+ ikparams, err, imext)
+
+char files[ARB] #I List of ME files
+char index[ARB] #I Range list of extension indexes
+char extname[ARB] #I Patterns for extension names
+char extver[ARB] #I Range list of extension versions
+int lindex #I List index number?
+int lname #I List extension name?
+int lver #I List extension version?
+char ikparams[ARB] #I Image kernel parameters
+int err #I Print errors?
+int imext #O Image extensions?
+int list #O Image list
+
+int i, j, nphu, nimages, fd
+pointer sp, temp, image, im, immap()
+int imextensions(), gstrmatch(), imtopen(), imtgetim(), open()
+errchk imextensions, open, immap, delete
+
+begin
+ call smark (sp)
+ call salloc (temp, SZ_FNAME, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+
+ # Get the list.
+ list = imextensions (files, index, extname, extver, lindex, lname,
+ lver, ikparams, err)
+
+ # Check and edit the list.
+ nphu = 0
+ nimages = 0
+ call mktemp ("@tmp$iraf", Memc[temp], SZ_FNAME)
+ fd = open (Memc[temp+1], NEW_FILE, TEXT_FILE)
+ while (imtgetim (list, Memc[image], SZ_FNAME) != EOF) {
+ if (gstrmatch (Memc[image], "\[0\]", i, j) > 0) {
+ call strcpy (Memc[image+j], Memc[image+i-1], SZ_FNAME)
+ ifnoerr (im = immap (Memc[image], READ_ONLY, 0)) {
+ call imunmap (im)
+ nphu = nphu + 1
+ } else
+ next
+ } else if (gstrmatch (Memc[image], "\[1\]", i, j) > 0) {
+ Memc[image+i] = '0'
+ iferr {
+ im = immap (Memc[image], READ_ONLY, 0)
+ call imunmap (im)
+ Memc[image+i] = '1'
+ } then {
+ nphu = nphu + 1
+ call strcpy (Memc[image+j], Memc[image+i-1], SZ_FNAME)
+ }
+ }
+ nimages = nimages + 1
+ call fprintf (fd, "%s\n")
+ call pargstr (Memc[image])
+ }
+ call close (fd)
+
+ # Return new list and extension flag.
+ imext = YES
+ if (nphu == nimages)
+ imext = NO
+ call imtclose (list)
+ list = imtopen (Memc[temp])
+ call delete (Memc[temp+1])
+ call sfree (sp)
+ return (list)
+end
diff --git a/noao/nproto/ace/tables.x b/noao/nproto/ace/tables.x
new file mode 100644
index 00000000..53a0d48b
--- /dev/null
+++ b/noao/nproto/ace/tables.x
@@ -0,0 +1,197 @@
+procedure tbcfmt (tp, cdef, str)
+
+pointer tp
+pointer cdef
+char str[ARB]
+
+begin
+end
+
+procedure tbcnam (tp, cdef, str)
+
+pointer tp
+pointer cdef
+char str[ARB]
+
+begin
+end
+
+procedure tbcnit (tp, cdef, str)
+
+pointer tp
+pointer cdef
+char str[ARB]
+
+begin
+end
+
+procedure tbegtd (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+double val
+
+begin
+end
+
+procedure tbegtr (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+real val
+
+begin
+end
+
+procedure tbegti (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+int val
+
+begin
+end
+
+procedure tbegtt (tp, cdef, row, val, len)
+
+pointer tp
+pointer cdef
+int row
+char val[ARB]
+int len
+
+begin
+end
+
+procedure tbeptd (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+double val
+
+begin
+end
+
+procedure tbeptr (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+real val
+
+begin
+end
+
+procedure tbepti (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+int val
+
+begin
+end
+
+procedure tbeptt (tp, cdef, row, val)
+
+pointer tp
+pointer cdef
+int row
+char val[ARB]
+
+begin
+end
+
+procedure tbhgtr (tp, key, val)
+
+pointer tp
+char key[ARB]
+char val[ARB]
+
+begin
+end
+
+procedure tbhgtt (tp, key, val, maxchar)
+
+pointer tp
+char key[ARB]
+char val[ARB]
+int maxchar
+
+begin
+end
+
+procedure tbhadr (tp, key, val)
+
+pointer tp
+char key[ARB]
+char val[ARB]
+
+begin
+end
+
+procedure tbhadt (tp, key, val)
+
+pointer tp
+char key[ARB]
+char val[ARB]
+
+begin
+end
+
+procedure tbpsta (tp, par)
+
+pointer tp
+int par
+
+begin
+end
+
+procedure tbtclo (tp)
+
+pointer tp
+
+begin
+end
+
+procedure tbtcre (tp)
+
+pointer tp
+
+begin
+end
+
+pointer procedure tbtopn (fname, mode, arg)
+
+char fname[ARB]
+int mode
+pointer arg
+
+begin
+end
+
+procedure tbcdef1 (tp, cdef, label, units, format, type, n)
+
+pointer tp
+pointer cdef
+char label[ARB]
+char units[ARB]
+char format[ARB]
+int type
+int n
+
+begin
+end
+
+procedure tbcfnd1 (tp, label, cdef)
+
+pointer tp
+char label[ARB]
+pointer cdef
+
+begin
+end
diff --git a/noao/nproto/ace/x_ace.x b/noao/nproto/ace/x_ace.x
new file mode 100644
index 00000000..c7b257b7
--- /dev/null
+++ b/noao/nproto/ace/x_ace.x
@@ -0,0 +1,4 @@
+task detect = t_acedetect,
+ evaluate = t_aceevaluate,
+ overlay = t_acedisplay,
+ skyimages = t_acesky
diff --git a/noao/nproto/ace/xtmaskname.x b/noao/nproto/ace/xtmaskname.x
new file mode 100644
index 00000000..9a55fb29
--- /dev/null
+++ b/noao/nproto/ace/xtmaskname.x
@@ -0,0 +1,114 @@
+#task test
+#procedure test()
+#char fname[SZ_FNAME]
+#begin
+# call clgstr ("fname", fname, SZ_FNAME)
+# #call xt_maskname (fname, "im1", READ_ONLY, fname, SZ_FNAME)
+# call xt_maskname (fname, "im1", NEW_IMAGE, fname, SZ_FNAME)
+# call printf ("mname = %s\n")
+# call pargstr (fname)
+#end
+
+# MASKNAME -- Make a mask name. This creates a FITS mask extension if
+# possible, otherwise it creates a pixel list file. To create a FITS
+# extension the filename must explicitly select the FITS kernel or the
+# default image type must be a FITS file. The input and output strings
+# may be the same.
+
+procedure xt_maskname (fname, extname, mode, mname, maxchar)
+
+char fname[ARB] #I File name
+char extname[ARB] #I Default pixel mask extension name
+int mode #I Mode
+char mname[maxchar] #O Output mask name
+int maxchar #I Maximum characters in mask name
+
+int i, fits
+pointer sp, temp
+
+bool streq()
+int strmatch(), stridxs(), strldxs(), strncmp()
+int envfind(), access(), imaccess()
+
+begin
+ call smark (sp)
+ call salloc (temp, maxchar, TY_CHAR)
+
+ # Determine whether to use FITS pixel mask extensions. One may set
+ # fits=NO to force use of pl even when FITS mask extensions are
+ # supported.
+ fits = access ("iraf$sys/imio/iki/fxf/fxfplwrite.x", 0, 0)
+ if (fits == YES && envfind ("masktype", Memc[temp], maxchar) > 0) {
+ if (streq (Memc[temp], "pl"))
+ fits = NO
+ }
+ i = strldxs ("]", fname)
+
+ # Check for explicit .pl extension.
+ if (strmatch (fname, ".pl$") > 0)
+ call strcpy (fname, mname, maxchar)
+
+ # Check for explicit mask extension.
+ else if (strmatch (fname, "type=mask") > 0)
+ call strcpy (fname, mname, maxchar)
+ else if (strmatch (fname, "type\\\=mask") > 0)
+ call strcpy (fname, mname, maxchar)
+
+ # Check for kernel section and add mask type.
+ else if (i > 0) {
+ if (mode != READ_ONLY) {
+ call strcpy (fname[i], Memc[temp], maxchar)
+ call sprintf (mname[i], maxchar-i, ",type=mask%s")
+ call pargstr (Memc[temp])
+ }
+
+ # Create output from rootname name.
+ } else if (fits == YES) {
+ call strcpy (fname, Memc[temp], SZ_FNAME)
+ if (mode == READ_ONLY) {
+ call sprintf (mname, maxchar, "%s[%s]")
+ call pargstr (Memc[temp])
+ call pargstr (extname)
+ } else {
+ call sprintf (mname, maxchar, "%s[%s,type=mask]")
+ call pargstr (Memc[temp])
+ call pargstr (extname)
+ }
+ } else
+ call strcat (".pl", mname, maxchar)
+
+ # Convert to pl form if required.
+ i = stridxs ("[", mname)
+ if (i > 0 && mode == READ_ONLY)
+ fits = imaccess (mname, mode)
+ if (fits == NO && i > 0) {
+ mname[i] = EOS
+ if (mode == NEW_IMAGE) {
+ if (access (mname, 0, 0) == NO) {
+ ifnoerr (call fmkdir (mname))
+ mname[i] = '/'
+ else
+ mname[i] = '.'
+ } else
+ mname[i] = '/'
+ } else {
+ if (access (mname, 0, 0) == NO)
+ mname[i] = '.'
+ else
+ mname[i] = '/'
+ }
+
+ if (strncmp (mname[i+1], "type", 4) == 0 ||
+ strncmp (mname[i+1], "append", 6) == 0 ||
+ strncmp (mname[i+1], "inherit", 7) == 0) {
+ mname[i+1] = EOS
+ call strcat (extname, mname, maxchar)
+ } else {
+ i = stridxs (",]", mname)
+ mname[i] = EOS
+ }
+ call strcat (".pl", mname, maxchar)
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/nproto/ace/xtpmmap.x b/noao/nproto/ace/xtpmmap.x
new file mode 100644
index 00000000..17fcf934
--- /dev/null
+++ b/noao/nproto/ace/xtpmmap.x
@@ -0,0 +1,603 @@
+include <mach.h>
+include <ctype.h>
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include <pmset.h>
+include <mwset.h>
+include <syserr.h>
+
+
+# XT_PMMAP -- Open a pixel mask READ_ONLY.
+#
+# This routine maps multiple types of mask files and designations.
+# It matches the mask coordinates to the reference image based on the
+# physical coordinate system so the mask may be of a different size.
+# The mask name is returned so that the task has the name pointed to by "BPM".
+# A null filename is allowed and returns NULL.
+
+pointer procedure yt_pmmap (pmname, refim, mname, sz_mname)
+
+char pmname[ARB] #I Pixel mask name
+pointer refim #I Reference image pointer
+char mname[ARB] #O Expanded mask name
+int sz_mname #O Size of expanded mask name
+
+int i, flag, nowhite()
+pointer sp, fname, im, ref, yt_pmmap1()
+bool streq()
+errchk yt_pmmap1
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ im = NULL
+ i = nowhite (pmname, Memc[fname], SZ_FNAME)
+ if (Memc[fname] == '!') {
+ iferr (call imgstr (refim, Memc[fname+1], Memc[fname], SZ_FNAME))
+ Memc[fname] = EOS
+ } else if (streq (Memc[fname], "BPM")) {
+ iferr (call imgstr (refim, "BPM", Memc[fname], SZ_FNAME))
+ Memc[fname] = EOS
+ } else if (streq (Memc[fname], "^BPM")) {
+ flag = INVERT_MASK
+ iferr (call imgstr (refim, "BPM", Memc[fname+1], SZ_FNAME))
+ Memc[fname] = EOS
+ }
+
+ if (Memc[fname] == '^') {
+ flag = INVERT_MASK
+ call strcpy (Memc[fname+1], Memc[fname], SZ_FNAME)
+ } else
+ flag = NO
+
+ if (streq (Memc[fname], "EMPTY"))
+ ref = refim
+ else
+ ref = NULL
+
+ if (Memc[fname] != EOS)
+ im = yt_pmmap1 (Memc[fname], ref, refim, flag)
+ call strcpy (Memc[fname], mname, sz_mname)
+
+ call sfree (sp)
+ return (im)
+end
+
+
+# XT_PMUNMAP -- Unmap a mask image.
+# Note that the imio pointer may be purely an internal pointer opened
+# with im_pmmapo so we need to free the pl pointer explicitly.
+
+procedure yt_pmunmap (im)
+
+pointer im #I IMIO pointer for mask
+
+pointer pm
+int imstati()
+
+begin
+ pm = imstati (im, IM_PMDES)
+ call pm_close (pm)
+ call imseti (im, IM_PMDES, NULL)
+ call imunmap (im)
+end
+
+
+# XT_PMMAP1 -- Open a pixel mask READ_ONLY. The input mask may be
+# a pixel list image, a non-pixel list image, or a text file.
+# Return error if the pixel mask cannot be opened. For pixel masks
+# or image masks match the WCS.
+
+pointer procedure yt_pmmap1 (pmname, ref, refim, flag)
+
+char pmname[ARB] #I Pixel mask name
+pointer ref #I Reference image for pixel mask
+pointer refim #I Reference image for image or text
+int flag #I Mask flag
+
+int imstati(), errcode()
+pointer im, pm
+pointer im_pmmap(), yt_pmimmap(), yt_pmtext(), yt_pmsection()
+bool streq()
+errchk yt_match
+
+begin
+ im = NULL
+
+ if (streq (pmname, "STDIN"))
+ im = yt_pmtext (pmname, refim, flag)
+
+ else if (pmname[1] == '[')
+ im = yt_pmsection (pmname, refim, flag)
+
+ else {
+ ifnoerr (im = im_pmmap (pmname, READ_ONLY, ref)) {
+ call yt_match (im, refim)
+ if (flag == INVERT_MASK) {
+ pm = imstati (im, IM_PMDES)
+ call yt_pminvert (pm)
+ call imseti (im, IM_PMDES, pm)
+ }
+ } else {
+ switch (errcode()) {
+ case SYS_IKIOPEN, SYS_FOPNNEXFIL, SYS_PLBADSAVEF, SYS_FOPEN:
+ ifnoerr (im = yt_pmimmap (pmname, refim, flag))
+ call yt_match (im, refim)
+ else {
+ switch (errcode()) {
+ case SYS_IKIOPEN:
+ im = yt_pmtext (pmname, refim, flag)
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ }
+
+ return (im)
+end
+
+
+# XT_PMIMMAP -- Open a pixel mask from a non-pixel list image.
+# Return error if the image cannot be opened.
+
+pointer procedure yt_pmimmap (pmname, refim, flag)
+
+char pmname[ARB] #I Image name
+pointer refim #I Reference image pointer
+int flag #I Mask flag
+
+int i, ndim, npix, rop, val
+pointer sp, v1, v2, im_in, im_out, pm, mw, data
+
+int imstati(), imgnli()
+pointer immap(), pm_newmask(), im_pmmapo(), imgl1i(), mw_openim()
+errchk immap, mw_openim
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+
+ im_in = immap (pmname, READ_ONLY, 0)
+ pm = imstati (im_in, IM_PMDES)
+ if (pm != NULL)
+ return (im_in)
+
+ pm = pm_newmask (im_in, 16)
+
+ ndim = IM_NDIM(im_in)
+ npix = IM_LEN(im_in,1)
+
+ if (flag == INVERT_MASK)
+ rop = PIX_NOT(PIX_SRC)
+ else
+ rop = PIX_SRC
+
+ while (imgnli (im_in, data, Meml[v1]) != EOF) {
+ if (flag == INVERT_MASK) {
+ do i = 0, npix-1 {
+ val = Memi[data+i]
+ if (val <= 0)
+ Memi[data+i] = 1
+ else
+ Memi[data+i] = 0
+ }
+ } else {
+ do i = 0, npix-1 {
+ val = Memi[data+i]
+ if (val < 0)
+ Memi[data+i] = 0
+ }
+ }
+ call pmplpi (pm, Meml[v2], Memi[data], 0, npix, rop)
+ call amovl (Meml[v1], Meml[v2], ndim)
+ }
+
+ im_out = im_pmmapo (pm, im_in)
+ data = imgl1i (im_out) # Force I/O to set header
+ mw = mw_openim (im_in) # Set WCS
+ call mw_saveim (mw, im_out)
+ call mw_close (mw)
+
+ #call imunmap (im_in)
+ call yt_pmunmap (im_in)
+ call sfree (sp)
+ return (im_out)
+end
+
+
+# XT_PMTEXT -- Create a pixel mask from a text file of rectangles.
+# Return error if the file cannot be opened.
+# This routine only applies to the first 2D plane.
+
+pointer procedure yt_pmtext (pmname, refim, flag)
+
+char pmname[ARB] #I Image name
+pointer refim #I Reference image pointer
+int flag #I Mask flag
+
+int fd, nc, nl, c1, c2, l1, l2, nc1, nl1, rop
+pointer pm, im, mw, dummy
+
+int open(), fscan(), nscan()
+pointer pm_newmask(), im_pmmapo(), imgl1i(), mw_openim()
+errchk open
+
+begin
+ fd = open (pmname, READ_ONLY, TEXT_FILE)
+ pm = pm_newmask (refim, 16)
+
+ nc = IM_LEN(refim,1)
+ nl = IM_LEN(refim,2)
+
+ if (flag == INVERT_MASK)
+ call pl_box (pm, 1, 1, nc, nl, PIX_SET+PIX_VALUE(1))
+
+ while (fscan (fd) != EOF) {
+ call gargi (c1)
+ call gargi (c2)
+ call gargi (l1)
+ call gargi (l2)
+ if (nscan() != 4) {
+ if (nscan() == 2) {
+ l1 = c2
+ c2 = c1
+ l2 = l1
+ } else
+ next
+ }
+
+ c1 = max (1, c1)
+ c2 = min (nc, c2)
+ l1 = max (1, l1)
+ l2 = min (nl, l2)
+ nc1 = c2 - c1 + 1
+ nl1 = l2 - l1 + 1
+ if (nc1 < 1 || nl1 < 1)
+ next
+
+ # Select mask value based on shape of rectangle.
+ if (flag == INVERT_MASK)
+ rop = PIX_CLR
+ else if (nc1 <= nl1)
+ rop = PIX_SET+PIX_VALUE(2)
+ else
+ rop = PIX_SET+PIX_VALUE(3)
+
+ # Set mask rectangle.
+ call pm_box (pm, c1, l1, c2, l2, rop)
+ }
+
+ call close (fd)
+ im = im_pmmapo (pm, refim)
+ dummy = imgl1i (im) # Force I/O to set header
+ mw = mw_openim (refim) # Set WCS
+ call mw_saveim (mw, im)
+ call mw_close (mw)
+
+ return (im)
+end
+
+
+# XT_PMSECTION -- Create a pixel mask from an image section.
+# This only applies the mask to the first plane of the image.
+
+pointer procedure yt_pmsection (section, refim, flag)
+
+char section[ARB] #I Image section
+pointer refim #I Reference image pointer
+int flag #I Mask flag
+
+int i, j, ip, temp, a[2], b[2], c[2], rop, ctoi()
+pointer pm, im, mw, dummy, pm_newmask(), im_pmmapo(), imgl1i(), mw_openim()
+define error_ 99
+
+begin
+ # This is currently only for 1D and 2D images.
+ if (IM_NDIM(refim) > 2)
+ call error (1, "Image sections only allowed for 1D and 2D images")
+
+ # Decode the section string.
+ call amovki (1, a, 2)
+ call amovki (1, b, 2)
+ call amovki (1, c, 2)
+ do i = 1, IM_NDIM(refim)
+ b[i] = IM_LEN(refim,i)
+
+ ip = 1
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+ if (section[ip] == '[') {
+ ip = ip + 1
+
+ do i = 1, IM_NDIM(refim) {
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ # Get a:b:c. Allow notation such as "-*:c"
+ # (or even "-:c") where the step is obviously negative.
+
+ if (ctoi (section, ip, temp) > 0) { # a
+ a[i] = temp
+ if (section[ip] == ':') {
+ ip = ip + 1
+ if (ctoi (section, ip, b[i]) == 0) # a:b
+ goto error_
+ } else
+ b[i] = a[i]
+ } else if (section[ip] == '-') { # -*
+ temp = a[i]
+ a[i] = b[i]
+ b[i] = temp
+ ip = ip + 1
+ if (section[ip] == '*')
+ ip = ip + 1
+ } else if (section[ip] == '*') # *
+ ip = ip + 1
+ if (section[ip] == ':') { # ..:step
+ ip = ip + 1
+ if (ctoi (section, ip, c[i]) == 0)
+ goto error_
+ else if (c[i] == 0)
+ goto error_
+ }
+ if (a[i] > b[i] && c[i] > 0)
+ c[i] = -c[i]
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+ if (i < IM_NDIM(refim)) {
+ if (section[ip] != ',')
+ goto error_
+ } else {
+ if (section[ip] != ']')
+ goto error_
+ }
+ ip = ip + 1
+ }
+ }
+
+ # In this case make the values be increasing only.
+ do i = 1, IM_NDIM(refim)
+ if (c[i] < 0) {
+ temp = a[i]
+ a[i] = b[i]
+ b[i] = temp
+ c[i] = -c[i]
+ }
+
+ # Make the mask.
+ pm = pm_newmask (refim, 16)
+
+ if (flag == INVERT_MASK) {
+ rop = PIX_SET+PIX_VALUE(1)
+ call pm_box (pm, 1, 1, IM_LEN(refim,1), IM_LEN(refim,2), rop)
+ rop = PIX_CLR
+ } else
+ rop = PIX_SET+PIX_VALUE(1)
+
+ if (c[1] == 1 && c[2] == 1)
+ call pm_box (pm, a[1], a[2], b[1], b[2], rop)
+
+ else if (c[1] == 1)
+ for (i=a[2]; i<=b[2]; i=i+c[2])
+ call pm_box (pm, a[1], i, b[1], i, rop)
+
+ else
+ for (i=a[2]; i<=b[2]; i=i+c[2])
+ for (j=a[1]; j<=b[1]; j=j+c[1])
+ call pm_point (pm, j, i, rop)
+
+ im = im_pmmapo (pm, refim)
+ dummy = imgl1i (im) # Force I/O to set header
+ mw = mw_openim (refim) # Set WCS
+ call mw_saveim (mw, im)
+ call mw_close (mw)
+
+ return (im)
+
+error_
+ call error (1, "Error in image section specification")
+end
+
+
+# XT_PMINVERT -- Invert a pixel mask by changing 0 to 1 and non-zero to zero.
+
+procedure yt_pminvert (pm)
+
+pointer pm #I Pixel mask to be inverted
+
+int i, naxes, axlen[IM_MAXDIM], depth, npix, val
+pointer sp, v, buf, one
+bool pm_linenotempty()
+
+begin
+ call pm_gsize (pm, naxes, axlen, depth)
+
+ call smark (sp)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call salloc (buf, axlen[1], TY_INT)
+ call salloc (one, 6, TY_INT)
+
+ npix = axlen[1]
+ RLI_LEN(one) = 2
+ RLI_AXLEN(one) = npix
+ Memi[one+3] = 1
+ Memi[one+4] = npix
+ Memi[one+5] = 1
+
+ call amovkl (long(1), Meml[v], IM_MAXDIM)
+ repeat {
+ if (pm_linenotempty (pm, Meml[v])) {
+ call pmglpi (pm, Meml[v], Memi[buf], 0, npix, 0)
+ do i = 0, npix-1 {
+ val = Memi[buf+i]
+ if (val == 0)
+ Memi[buf+i] = 1
+ else
+ Memi[buf+i] = 0
+ }
+ call pmplpi (pm, Meml[v], Memi[buf], 0, npix, PIX_SRC)
+ } else
+ call pmplri (pm, Meml[v], Memi[one], 0, npix, PIX_SRC)
+
+ do i = 2, naxes {
+ Meml[v+i-1] = Meml[v+i-1] + 1
+ if (Meml[v+i-1] <= axlen[i])
+ break
+ else if (i < naxes)
+ Meml[v+i-1] = 1
+ }
+ } until (Meml[v+naxes-1] > axlen[naxes])
+
+ call sfree (sp)
+end
+
+
+# XT_MATCH -- Set the pixel mask to match the reference image.
+# This matches sizes and physical coordinates and allows the
+# original mask to be smaller or larger than the reference image.
+# Subsequent use of the pixel mask can then work in the logical
+# coordinates of the reference image. The mask values are the maximum
+# of the mask values which overlap each reference image pixel.
+# A null input returns a null output.
+
+procedure yt_match (im, refim)
+
+pointer im #U Pixel mask image pointer
+pointer refim #I Reference image pointer
+
+int i, j, k, l, i1, i2, j1, j2, nc, nl, ncpm, nlpm, nx, val
+double x1, x2, y1, y2, lt[6], lt1[6], lt2[6]
+long vold[IM_MAXDIM], vnew[IM_MAXDIM]
+pointer pm, pmnew, imnew, mw, ctx, cty, bufref, bufpm
+
+int imstati()
+double mw_c1trand()
+pointer pm_open(), mw_openim(), im_pmmapo(), imgl1i(), mw_sctran()
+bool pm_empty(), pm_linenotempty()
+errchk pm_open, mw_openim
+
+begin
+ if (im == NULL)
+ return
+
+ # Set sizes.
+ nc = IM_LEN(refim,1)
+ nl = IM_LEN(refim,2)
+ ncpm = IM_LEN(im,1)
+ nlpm = IM_LEN(im,2)
+
+ # If the mask is empty and the sizes are the same then it does not
+ # matter if the two are actually matched in physical coordinates.
+ pm = imstati (im, IM_PMDES)
+ if (pm_empty(pm) && nc == ncpm && nl == nlpm)
+ return
+
+ # Compute transformation between reference (logical) coordinates
+ # and mask (physical) coordinates.
+
+ mw = mw_openim (im)
+ call mw_gltermd (mw, lt, lt[5], 2)
+ call mw_close (mw)
+
+ mw = mw_openim (refim)
+ call mw_gltermd (mw, lt2, lt2[5], 2)
+ call mw_close (mw)
+
+ # Combine lterms.
+ call mw_invertd (lt, lt1, 2)
+ call mw_mmuld (lt1, lt2, lt, 2)
+ call mw_vmuld (lt, lt[5], lt[5], 2)
+ lt[5] = lt2[5] - lt[5]
+ lt[6] = lt2[6] - lt[6]
+ do i = 1, 6
+ lt[i] = nint (1D6 * (lt[i]-int(lt[i]))) / 1D6 + int(lt[i])
+
+ # Check for a rotation. For now don't allow any rotation.
+ if (lt[2] != 0. || lt[3] != 0.)
+ call error (1, "Image and mask have a relative rotation")
+
+ # Check for an exact match.
+ if (lt[1] == 1D0 && lt[4] == 1D0 && lt[5] == 0D0 && lt[6] == 0D0)
+ return
+
+ # Set reference to mask coordinates.
+ mw = mw_openim (im)
+ call mw_sltermd (mw, lt, lt[5], 2)
+ ctx = mw_sctran (mw, "logical", "physical", 1)
+ cty = mw_sctran (mw, "logical", "physical", 2)
+
+ # Create a new pixel mask of the required size and offset.
+ # Do dummy image I/O to set the header.
+ pmnew = pm_open (NULL)
+ call pm_ssize (pmnew, 2, IM_LEN(refim,1), 27)
+ imnew = im_pmmapo (pmnew, NULL)
+ bufref = imgl1i (imnew)
+
+ # Compute region of mask overlapping the reference image.
+ x1 = mw_c1trand (ctx, 1-0.5D0)
+ x2 = mw_c1trand (ctx, nc+0.5D0)
+ i1 = max (1, nint(min(x1,x2)+1D-5))
+ i2 = min (ncpm, nint(max(x1,x2)-1D-5))
+ y1 = mw_c1trand (cty, 1-0.5D0)
+ y2 = mw_c1trand (cty, nl+0.5D0)
+ j1 = max (1, nint(min(y1,y2)+1D-5))
+ j2 = min (nlpm, nint(max(y1,y2)-1D-5))
+
+ # Set the new mask values to the maximum of all mask values falling
+ # within each reference pixel in the overlap region.
+ if (i1 >= i2 && j1 >= j2) {
+ nx = i2 - i1 + 1
+ call malloc (bufpm, nx, TY_INT)
+ call malloc (bufref, nc, TY_INT)
+ vold[1] = i1
+ vnew[1] = 1
+ do j = 1, nl {
+ y1 = mw_c1trand (cty, j-0.5D0)
+ y2 = mw_c1trand (cty, j+0.5D0)
+ j1 = max (1, nint(min(y1,y2)+1D-5))
+ j2 = min (nlpm, nint(max(y1,y2)-1D-5))
+ if (j2 < j1)
+ next
+
+ vnew[2] = j
+ call aclri (Memi[bufref], nc)
+ do l = j1, j2 {
+ vold[2] = l
+ if (!pm_linenotempty (pm, vold))
+ next
+ call pmglpi (pm, vold, Memi[bufpm], 0, nx, 0)
+ do i = 1, nc {
+ x1 = mw_c1trand (ctx, i-0.5D0)
+ x2 = mw_c1trand (ctx, i+0.5D0)
+ i1 = max (1, nint(min(x1,x2)+1D-5))
+ i2 = min (ncpm, nint(max(x1,x2)-1D-5))
+ if (i2 < i1)
+ next
+ val = Memi[bufref+i-1]
+ do k = i1-vold[1], i2-vold[1]
+ val = max (val, Memi[bufpm+k])
+ Memi[bufref+i-1] = val
+ }
+ }
+ call pmplpi (pmnew, vnew, Memi[bufref], 0, nc, PIX_SRC)
+ }
+ call mfree (bufref, TY_INT)
+ call mfree (bufpm, TY_INT)
+ }
+
+ call mw_close (mw)
+ call yt_pmunmap (im)
+ im = imnew
+ call imseti (im, IM_PMDES, pmnew)
+end