aboutsummaryrefslogtreecommitdiff
path: root/noao/onedspec/ecidentify
diff options
context:
space:
mode:
Diffstat (limited to 'noao/onedspec/ecidentify')
-rw-r--r--noao/onedspec/ecidentify/eccenter.x34
-rw-r--r--noao/onedspec/ecidentify/eccolon.x243
-rw-r--r--noao/onedspec/ecidentify/ecdb.x268
-rw-r--r--noao/onedspec/ecidentify/ecdelete.x28
-rw-r--r--noao/onedspec/ecidentify/ecdofit.x128
-rw-r--r--noao/onedspec/ecidentify/ecdoshift.x44
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfcolon.x102
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfeval.x68
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecffit.com23
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecffit.h20
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecffit.key53
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecffit.x193
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfgdata.x37
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfget.x84
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfgraph.x50
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfnearest.x85
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfreject.x53
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfrms.x26
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfset.x92
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfshift.x55
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecfsolve.x196
-rw-r--r--noao/onedspec/ecidentify/ecffit/ecftitle.x48
-rw-r--r--noao/onedspec/ecidentify/ecffit/mkpkg21
-rw-r--r--noao/onedspec/ecidentify/ecfitdata.x146
-rw-r--r--noao/onedspec/ecidentify/ecgdata.x74
-rw-r--r--noao/onedspec/ecidentify/ecgetim.x17
-rw-r--r--noao/onedspec/ecidentify/ecgline.x20
-rw-r--r--noao/onedspec/ecidentify/ecgraph.x155
-rw-r--r--noao/onedspec/ecidentify/ecidentify.h94
-rw-r--r--noao/onedspec/ecidentify/ecidentify.key76
-rw-r--r--noao/onedspec/ecidentify/ecidentify.x535
-rw-r--r--noao/onedspec/ecidentify/ecinit.x64
-rw-r--r--noao/onedspec/ecidentify/ecline.x22
-rw-r--r--noao/onedspec/ecidentify/eclinelist.x281
-rw-r--r--noao/onedspec/ecidentify/eclog.x77
-rw-r--r--noao/onedspec/ecidentify/ecmark.x71
-rw-r--r--noao/onedspec/ecidentify/ecnearest.x26
-rw-r--r--noao/onedspec/ecidentify/ecnewfeature.x91
-rw-r--r--noao/onedspec/ecidentify/ecnext.x23
-rw-r--r--noao/onedspec/ecidentify/ecpeak.x24
-rw-r--r--noao/onedspec/ecidentify/ecprevious.x23
-rw-r--r--noao/onedspec/ecidentify/ecrms.x28
-rw-r--r--noao/onedspec/ecidentify/ecshift.x77
-rw-r--r--noao/onedspec/ecidentify/ecshow.x78
-rw-r--r--noao/onedspec/ecidentify/mkpkg39
-rw-r--r--noao/onedspec/ecidentify/t_eciden.x68
-rw-r--r--noao/onedspec/ecidentify/t_ecreid.x181
47 files changed, 4241 insertions, 0 deletions
diff --git a/noao/onedspec/ecidentify/eccenter.x b/noao/onedspec/ecidentify/eccenter.x
new file mode 100644
index 00000000..730ad2a8
--- /dev/null
+++ b/noao/onedspec/ecidentify/eccenter.x
@@ -0,0 +1,34 @@
+include "ecidentify.h"
+
+# EC_CENTER -- Locate the center of a feature.
+
+double procedure ec_center (ec, x, width, type)
+
+pointer ec # EC pointer
+double x # Initial guess
+real width # Feature width
+int type # Feature type
+
+double dvalue
+real value
+
+real center1d()
+double smw_c1trand()
+
+begin
+ if (IS_INDEFD(x))
+ return (x)
+
+ dvalue = smw_c1trand (EC_PL(ec), x)
+ if (IS_INDEFD(dvalue))
+ return (dvalue)
+
+ value = dvalue
+ value = center1d (value, IMDATA(ec,1), EC_NPTS(ec), width,
+ abs (type), EC_CRADIUS(ec), EC_THRESHOLD(ec))
+
+ if (IS_INDEF(value))
+ return (INDEFD)
+ else
+ return (smw_c1trand (EC_LP(ec), double(value)))
+end
diff --git a/noao/onedspec/ecidentify/eccolon.x b/noao/onedspec/ecidentify/eccolon.x
new file mode 100644
index 00000000..0fe22af5
--- /dev/null
+++ b/noao/onedspec/ecidentify/eccolon.x
@@ -0,0 +1,243 @@
+include <gset.h>
+include <error.h>
+include <pkg/center1d.h>
+include "ecidentify.h"
+
+# List of colon commands.
+define CMDS "|show|features|image|database|read|write|coordlist|match|\
+ |maxfeatures|minsep|zwidth|labels|fwidth|ftype|cradius|threshold|"
+
+define SHOW 1 # Show parameters
+define FEATURES 2 # Show list of features
+define IMAGE 3 # Set new image
+define DATABASE 4 # Set new database
+define READ 5 # Read database entry
+define WRITE 6 # Write database entry
+define COORDLIST 7 # Set new coordinate list
+define MATCH 8 # Set coordinate list matching distance
+# newline 9
+define MAXFEATURES 10 # Set maximum number of features for auto find
+define MINSEP 11 # Set minimum separation distance
+define ZWIDTH 12 # Set zoom window width
+define LABEL 13 # Set label type
+define WIDTH 14 # Set centering width
+define TYPE 15 # Set centering type
+define RADIUS 16 # Set centering radius
+define THRESHOLD 17 # Set the centering threshold
+
+# EC_COLON -- Respond to colon command.
+
+procedure ec_colon (ec, cmdstr, newimage, prfeature)
+
+pointer ec # ID pointer
+char cmdstr[ARB] # Colon command
+char newimage[ARB] # New image name
+int prfeature # Print current feature on status line
+
+char cmd[SZ_LINE]
+int i, ncmd, ival
+real rval[2]
+pointer im
+
+int nscan(), strdic(), ec_next()
+pointer immap()
+errchk immap, ec_dbread, ec_dbwrite, ec_log
+
+begin
+ # Scan the command string and get the first word.
+ call sscan (cmdstr)
+ call gargwrd (cmd, SZ_LINE)
+ ncmd = strdic (cmd, cmd, SZ_LINE, CMDS)
+
+ switch (ncmd) {
+ case SHOW: # :show - show values of parameters
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ call gdeactivate (EC_GP(ec), AW_CLEAR)
+ call ec_show (ec, "STDOUT")
+ call greactivate (EC_GP(ec), AW_PAUSE)
+ } else {
+ iferr (call ec_show (ec, cmd)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ }
+ }
+ case FEATURES: # :features - list features
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ call gdeactivate (EC_GP(ec), AW_CLEAR)
+ call ec_log (ec, "STDOUT")
+ call greactivate (EC_GP(ec), AW_PAUSE)
+ } else {
+ iferr (call ec_log (ec, cmd)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ }
+ }
+ case IMAGE: # :image - set image
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ call printf ("image %s\n")
+ call pargstr (Memc[EC_IMAGE(ec)])
+ prfeature = NO
+ } else {
+ call strcpy (cmd, newimage, SZ_FNAME)
+ iferr {
+ im = immap (newimage, READ_ONLY, 0)
+ call imunmap (im)
+ } then {
+ newimage[1] = EOS
+ call erract (EA_WARN)
+ prfeature = NO
+ }
+ }
+ case DATABASE: # :database - set database
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ call printf ("database %s\n")
+ call pargstr (Memc[EC_DATABASE(ec)])
+ prfeature = NO
+ } else {
+ call strcpy (cmd, Memc[EC_DATABASE(ec)], SZ_FNAME)
+ EC_NEWDBENTRY(ec) = YES
+ }
+ case READ: # :read - read database entry
+ prfeature = NO
+ iferr {
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1)
+ call ec_dbread (ec, Memc[EC_IMAGE(ec)], YES)
+ else {
+ call xt_stripwhite (cmd)
+ if (cmd[1] == EOS)
+ call ec_dbread (ec, Memc[EC_IMAGE(ec)], YES)
+ else
+ call ec_dbread (ec, cmd, YES)
+ }
+ EC_CURRENT(ec) = 0
+ i = ec_next (ec, EC_CURRENT(ec))
+ } then
+ call erract (EA_WARN)
+ case WRITE: # :write - write database entry
+ prfeature = NO
+ iferr {
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1)
+ call ec_dbwrite (ec, Memc[EC_IMAGE(ec)], YES)
+ else {
+ call xt_stripwhite (cmd)
+ if (cmd[1] == EOS)
+ call ec_dbwrite (ec, Memc[EC_IMAGE(ec)], YES)
+ else
+ call ec_dbwrite (ec, cmd, YES)
+ }
+ } then
+ call erract (EA_WARN)
+ case COORDLIST: # :coordlist - set coordinate list
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ call printf ("coordlist %s\n")
+ call pargstr (Memc[EC_COORDLIST(ec)])
+ prfeature = NO
+ } else {
+ call strcpy (cmd, Memc[EC_COORDLIST(ec)], SZ_FNAME)
+ call ec_unmapll (ec)
+ call ec_mapll (ec)
+ }
+ case MATCH: # :match - set matching distance for coordinate list
+ call gargr (rval[1])
+ if (nscan() == 1) {
+ call printf ("match %g\n")
+ call pargr (EC_MATCH(ec))
+ prfeature = NO
+ } else
+ EC_MATCH(ec) = rval[1]
+ case MAXFEATURES: # :maxfeatures - set max num features for auto find
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("maxfeatures %d\n")
+ call pargi (EC_MAXFEATURES(ec))
+ prfeature = NO
+ } else
+ EC_MAXFEATURES(ec) = ival
+ case MINSEP: # :minsep - set minimum feature separation allowed
+ call gargr (rval[1])
+ if (nscan() == 1) {
+ call printf ("minsep %g\n")
+ call pargr (EC_MINSEP(ec))
+ prfeature = NO
+ } else
+ EC_MINSEP(ec) = rval[1]
+ case ZWIDTH: # :zwidth - set zoom window width
+ call gargr (rval[1])
+ if (nscan() == 1) {
+ call printf ("zwidth %g\n")
+ call pargr (EC_ZWIDTH(ec))
+ prfeature = NO
+ } else {
+ EC_ZWIDTH(ec) = rval[1]
+ if (EC_GTYPE(ec) == 2)
+ EC_NEWGRAPH(ec) = YES
+ }
+ case LABEL: # :labels - set label type
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ switch (EC_LABELS(ec)) {
+ case 2:
+ call printf ("labels index\n")
+ case 3:
+ call printf ("labels pixel\n")
+ case 4:
+ call printf ("labels user\n")
+ default:
+ call printf ("labels none\n")
+ }
+ prfeature = NO
+ } else {
+ EC_LABELS(ec) = strdic (cmd, cmd, SZ_LINE, LABELS)
+ do i = 1, EC_NFEATURES(ec) {
+ if (APN(ec,i) == EC_AP(ec))
+ call ec_mark (ec, i)
+ }
+ }
+ case WIDTH: # :fwidth - set centering width
+ call gargr (rval[1])
+ if (nscan() == 1) {
+ call printf ("fwidth %g\n")
+ call pargr (EC_FWIDTH(ec))
+ prfeature = NO
+ } else
+ EC_FWIDTH(ec) = rval[1]
+ case TYPE: # :ftype - set centering type
+ call gargwrd (cmd, SZ_LINE)
+ if (nscan() == 1) {
+ switch (EC_FTYPE(ec)) {
+ case EMISSION:
+ call printf ("ftype emission\n")
+ case ABSORPTION:
+ call printf ("ftype absorption\n")
+ }
+ prfeature = NO
+ } else
+ EC_FTYPE(ec) = strdic (cmd, cmd, SZ_LINE, FTYPES)
+ case RADIUS: # :cradius - set centering radius
+ call gargr (rval[1])
+ if (nscan() == 1) {
+ call printf ("cradius %g\n")
+ call pargr (EC_CRADIUS(ec))
+ prfeature = NO
+ } else
+ EC_CRADIUS(ec) = rval[1]
+ case THRESHOLD: # :threshold - set centering threshold
+ call gargr (rval[1])
+ if (nscan() == 1) {
+ call printf ("threshold %g\n")
+ call pargr (EC_THRESHOLD(ec))
+ prfeature = NO
+ } else
+ EC_THRESHOLD(ec) = rval[1]
+ default:
+ call printf ("Unrecognized or ambiguous command\007")
+ prfeature = NO
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecdb.x b/noao/onedspec/ecidentify/ecdb.x
new file mode 100644
index 00000000..f6e02526
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecdb.x
@@ -0,0 +1,268 @@
+include <math/gsurfit.h>
+include <smw.h>
+include <units.h>
+include "ecidentify.h"
+
+# EC_DBREAD -- Read features data from the database.
+
+procedure ec_dbread (ec, name, verbose)
+
+pointer ec # ID pointer
+char name[SZ_LINE]
+int verbose
+
+pointer dt
+int i, j, k, ncoeffs, rec, slope, offset, niterate
+double shift, low, high
+pointer sp, coeffs, line, cluster, un
+
+int ec_line()
+int dtgeti(), dgsgeti(), dtlocate(), dtscan(), nscan()
+real dtgetr()
+bool un_compare()
+double dgsgetd(), smw_c1trand()
+pointer dtmap1(), un_open()
+
+errchk dtmap1, dtlocate, dtgeti, dtgad, un_open
+
+begin
+ call smark (sp)
+ call salloc (cluster, SZ_LINE, TY_CHAR)
+ call salloc (line, SZ_LINE, TY_CHAR)
+
+ call imgcluster (name, Memc[cluster], SZ_LINE)
+ call sprintf (Memc[line], SZ_LINE, "ec%s")
+ call pargstr (Memc[cluster])
+ dt = dtmap1 (Memc[EC_DATABASE(ec)], Memc[line], READ_ONLY)
+
+ call sprintf (Memc[line], SZ_LINE, "ecidentify %s")
+ call pargstr (Memc[cluster])
+
+ rec = dtlocate (dt, Memc[line])
+ if (rec == EOF)
+ call error (0, "Entry not found")
+
+ i = dtgeti (dt, rec, "features")
+
+ EC_NALLOC(ec) = i
+ call realloc (EC_APNUM(ec), i, TY_INT)
+ call realloc (EC_LINENUM(ec), i, TY_INT)
+ call realloc (EC_ORD(ec), i, TY_INT)
+ call realloc (EC_PIX(ec), i, TY_DOUBLE)
+ call realloc (EC_FIT(ec), i, TY_DOUBLE)
+ call realloc (EC_USER(ec), i, TY_DOUBLE)
+ call realloc (EC_FWIDTHS(ec), i, TY_REAL)
+ call realloc (EC_FTYPES(ec), i, TY_INT)
+
+ j = 1
+ do i = 1, EC_NALLOC(ec) {
+ k = dtscan (dt)
+ call gargi (APN(ec,j))
+ call gargi (ORDER(ec,j))
+ call gargd (PIX(ec,j))
+ call gargd (FIT(ec,j))
+ call gargd (USER(ec,j))
+ call gargr (FWIDTH(ec,j))
+ call gargi (FTYPE(ec,j))
+ call gargi (k)
+ if (nscan() == 8 && k == 0)
+ FTYPE(ec,j) = -FTYPE(ec,j)
+ iferr (LINE(ec,j) = ec_line (ec, APN(ec,j)))
+ next
+ shift = smw_c1trand (EC_PL(ec), PIX(ec,j))
+ low = 0.5
+ high = SN(SH(ec,LINE(ec,j))) + 0.5
+ if (shift < low || shift > high)
+ next
+ j = j + 1
+ }
+ EC_NFEATURES(ec) = j - 1
+
+ iferr (shift = dtgetr (dt, rec, "shift"))
+ shift = 0.
+ iferr (offset = dtgeti (dt, rec, "offset"))
+ offset = 0
+ iferr (slope = dtgeti (dt, rec, "slope"))
+ slope = 1
+ call ecf_setd ("shift", shift)
+ call ecf_seti ("offset", offset)
+ call ecf_seti ("slope", slope)
+
+ iferr {
+ ncoeffs = dtgeti (dt, rec, "coefficients")
+ call salloc (coeffs, ncoeffs, TY_DOUBLE)
+ call dtgad (dt, rec, "coefficients", Memd[coeffs], ncoeffs, ncoeffs)
+
+ if (EC_ECF(ec) != NULL)
+ call dgsfree (EC_ECF(ec))
+ call dgsrestore (EC_ECF(ec), Memd[coeffs])
+
+ call ecf_setd ("xmin", dgsgetd (EC_ECF(ec), GSXMIN))
+ call ecf_setd ("xmax", dgsgetd (EC_ECF(ec), GSXMAX))
+ call ecf_setd ("ymin", dgsgetd (EC_ECF(ec), GSYMIN))
+ call ecf_setd ("ymax", dgsgetd (EC_ECF(ec), GSYMAX))
+ call ecf_seti ("xorder", dgsgeti (EC_ECF(ec), GSXORDER))
+ call ecf_seti ("yorder", dgsgeti (EC_ECF(ec), GSYORDER))
+
+ switch (dgsgeti (EC_ECF(ec), GSTYPE)) {
+ case GS_LEGENDRE:
+ call ecf_sets ("function", "legendre")
+ case GS_CHEBYSHEV:
+ call ecf_sets ("function", "chebyshev")
+ }
+
+ ifnoerr (niterate = dtgeti (dt, rec, "niterate"))
+ call ecf_seti ("niterate", niterate)
+ ifnoerr (low = dtgetr (dt, rec, "lowreject"))
+ call ecf_setd ("low", low)
+ ifnoerr (high = dtgeti (dt, rec, "highreject"))
+ call ecf_setd ("high", high)
+
+ EC_NEWECF(ec) = YES
+ EC_CURRENT(ec) = min (1, EC_NFEATURES(ec))
+ } then
+ ;
+
+ ifnoerr (call dtgstr (dt, rec, "units", Memc[line], SZ_LINE)) {
+ if (EC_UN(ec) == NULL)
+ EC_UN(ec) = un_open (Memc[line])
+ else {
+ un = un_open (Memc[line])
+ if (!un_compare (un, EC_UN(ec))) {
+ call ec_unitsll (ec, Memc[line])
+ call un_close (EC_UN(ec))
+ EC_UN(ec) = un
+ } else
+ call un_close (un)
+ }
+ }
+
+ call dtunmap (dt)
+ call sfree (sp)
+
+ if (EC_NFEATURES(ec) > 0) {
+ EC_NEWGRAPH(ec) = YES
+ EC_NEWFEATURES(ec) = YES
+ EC_CURRENT(ec) = 1
+ } else
+ EC_CURRENT(ec) = 0
+
+ if (verbose == YES) {
+ call printf ("ecidentify %s\n")
+ call pargstr (Memc[cluster])
+ }
+end
+
+
+# EC_DBWRITE -- Write features data to the database.
+
+procedure ec_dbwrite (ec, name, verbose)
+
+pointer ec # ID pointer
+char name[ARB]
+int verbose
+
+int i, ncoeffs
+pointer dt, sp, coeffs, root, cluster
+
+int dgsgeti(), ecf_geti()
+double ecf_getd()
+pointer dtmap1(), immap()
+
+errchk dtmap1, immap
+
+begin
+ call smark (sp)
+ call salloc (cluster, SZ_FNAME, TY_CHAR)
+ call salloc (root, SZ_FNAME, TY_CHAR)
+
+ call imgcluster (name, Memc[cluster], SZ_FNAME)
+ call sprintf (Memc[root], SZ_FNAME, "ec%s")
+ call pargstr (Memc[cluster])
+ dt = dtmap1 (Memc[EC_DATABASE(ec)], Memc[root], APPEND)
+
+ call dtptime (dt)
+ call dtput (dt, "begin\tecidentify %s\n")
+ call pargstr (Memc[cluster])
+ call dtput (dt, "\tid\t%s\n")
+ call pargstr (Memc[cluster])
+ call dtput (dt, "\ttask\tecidentify\n")
+ call dtput (dt, "\timage\t%s\n")
+ call pargstr (Memc[EC_IMAGE(ec)])
+
+ if (EC_UN(ec) != NULL) {
+ call dtput (dt, "\tunits\t%s\n")
+ call pargstr (UN_UNITS(EC_UN(ec)))
+ }
+ call dtput (dt, "\tfeatures\t%d\n")
+ call pargi (EC_NFEATURES(ec))
+ do i = 1, EC_NFEATURES(ec) {
+ call dtput (dt,
+ "\t\t%3d %3d %7.2f %10.9g %10.9g %4.1f %d %d\n")
+ call pargi (APN(ec,i))
+ call pargi (ORDER(ec,i))
+ call pargd (PIX(ec,i))
+ call pargd (FIT(ec,i))
+ call pargd (USER(ec,i))
+ call pargr (FWIDTH(ec,i))
+ call pargi (abs (FTYPE(ec,i)))
+ if (FTYPE(ec,i) > 0)
+ call pargi (1)
+ else
+ call pargi (0)
+ }
+
+ if (ecf_getd ("shift") != 0.) {
+ call dtput (dt, "\tshift\t%g\n")
+ call pargd (ecf_getd ("shift"))
+ }
+ if (ecf_geti ("offset") != 0) {
+ call dtput (dt, "\toffset\t%d\n")
+ call pargi (ecf_geti ("offset"))
+ }
+ if (ecf_geti ("slope") != 1) {
+ call dtput (dt, "\tslope\t%d\n")
+ call pargi (ecf_geti ("slope"))
+ }
+
+ if (EC_ECF(ec) != NULL) {
+ call dtput (dt, "\tniterate %d\n")
+ call pargi (ecf_geti ("niterate"))
+ call dtput (dt, "\tlowreject %g\n")
+ call pargd (ecf_getd ("low"))
+ call dtput (dt, "\thighreject %g\n")
+ call pargd (ecf_getd ("high"))
+
+ ncoeffs = dgsgeti (EC_ECF(ec), GSNSAVE)
+ call salloc (coeffs, ncoeffs, TY_DOUBLE)
+ call dgssave (EC_ECF(ec), Memd[coeffs])
+ call dtput (dt, "\tcoefficients\t%d\n")
+ call pargi (ncoeffs)
+ do i = 1, ncoeffs {
+ call dtput (dt, "\t\t%g\n")
+ call pargd (Memd[coeffs+i-1])
+ }
+ }
+
+ call dtput (dt, "\n")
+ call dtunmap (dt)
+
+ EC_NEWFEATURES(ec) = NO
+ EC_NEWECF(ec) = NO
+ EC_NEWDBENTRY(ec) = NO
+
+ if (verbose == YES) {
+ call printf ("ecidentify %s\n")
+ call pargstr (Memc[cluster])
+ }
+
+ # Enter reference spectrum name in image header.
+ call imgcluster (Memc[EC_IMAGE(ec)], Memc[root], SZ_FNAME)
+ dt = immap (Memc[root], READ_WRITE, 0)
+ call imastr (dt, "REFSPEC1", Memc[cluster])
+ iferr (call imdelf (dt, "REFSPEC2"))
+ ;
+ call imunmap (dt)
+
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecdelete.x b/noao/onedspec/ecidentify/ecdelete.x
new file mode 100644
index 00000000..b729d326
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecdelete.x
@@ -0,0 +1,28 @@
+include "ecidentify.h"
+
+# EC_DELETE -- Delete a feature.
+
+procedure ec_delete (ec, feature)
+
+pointer ec # ID pointer
+int feature # Feature to be deleted
+
+int i
+
+begin
+ if (feature == 0)
+ return
+
+ do i = feature + 1, EC_NFEATURES(ec) {
+ APN(ec,i-1) = APN(ec,i)
+ LINE(ec,i-1) = LINE(ec,i)
+ ORDER(ec,i-1) = ORDER(ec,i)
+ PIX(ec,i-1) = PIX(ec,i)
+ FIT(ec,i-1) = FIT(ec,i)
+ USER(ec,i-1) = USER(ec,i)
+ FWIDTH(ec,i-1) = FWIDTH(ec,i)
+ FTYPE(ec,i-1) = FTYPE(ec,i)
+ }
+ EC_NFEATURES(ec) = EC_NFEATURES(ec) - 1
+ EC_NEWFEATURES(ec) = YES
+end
diff --git a/noao/onedspec/ecidentify/ecdofit.x b/noao/onedspec/ecidentify/ecdofit.x
new file mode 100644
index 00000000..14dcea54
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecdofit.x
@@ -0,0 +1,128 @@
+include <smw.h>
+include "ecidentify.h"
+
+# EC_DOFIT -- Fit an echelle function to the features. Eliminate INDEF points.
+
+procedure ec_dofit (ec, interactive, fixedorder)
+
+pointer ec # EC pointer
+int interactive # Interactive fit?
+int fixedorder # Fixed order?
+
+int i, j, k, nfit
+double xmin, xmax, ymin, ymax
+pointer gt1, ecf
+pointer sp, x, y, z, w, gt_init()
+errchk ecf_fit
+
+begin
+ # Count number of points and determine the order range.
+ j = ORDER(ec,1)
+ k = ORDER(ec,1)
+ nfit = 0
+ for (i=1; i<=EC_NFEATURES(ec); i=i+1) {
+ if (IS_INDEFD (PIX(ec,i)) || IS_INDEFD (USER(ec,i)))
+ next
+ j = min (j, ORDER(ec,i))
+ k = max (k, ORDER(ec,i))
+ nfit = nfit + 1
+ }
+
+ # Require at least 4 points and more than one order.
+ if (nfit < 4 || j == k) {
+ if (EC_ECF(ec) != NULL) {
+ call dgs_free (EC_ECF(ec))
+ call ecf_setd ("shift", 0.D0)
+ EC_NEWGRAPH(ec) = YES
+ EC_NEWECF(ec) = YES
+ }
+ return
+ }
+
+ # Allocate arrays for points to be fit and fill them in.
+ call smark (sp)
+ call salloc (x, nfit, TY_DOUBLE)
+ call salloc (y, nfit, TY_DOUBLE)
+ call salloc (z, nfit, TY_DOUBLE)
+ call salloc (w, nfit, TY_DOUBLE)
+
+ nfit = 0
+ do i = 1, EC_NFEATURES(ec) {
+ if (IS_INDEFD (PIX(ec,i)) || IS_INDEFD (USER(ec,i)))
+ next
+ Memd[x+nfit] = PIX(ec,i)
+ Memd[y+nfit] = APN(ec,i)
+ Memd[z+nfit] = USER(ec,i)
+ Memd[w+nfit] = 1.
+ nfit = nfit + 1
+ }
+
+ # Initialize fit limits.
+ ymin = APS(ec,1)
+ ymax = ymin
+ do i = 2, EC_NLINES(ec) {
+ xmin = APS(ec,i)
+ if (xmin < ymin)
+ ymin = xmin
+ if (xmin > ymax)
+ ymax = xmin
+ }
+ xmin = 1
+ xmax = EC_NPTS(ec)
+
+ call ecf_setd ("xmin", xmin)
+ call ecf_setd ("xmax", xmax)
+ call ecf_setd ("ymin", ymin)
+ call ecf_setd ("ymax", ymax)
+
+ # Fit the echelle dispersion function.
+ ecf = EC_ECF(ec)
+ if (interactive == YES) {
+ gt1 = gt_init()
+ call ecf_fit (ecf, EC_GP(ec), gt1, Memd[x], Memd[y],
+ Memd[z], Memd[w], nfit, fixedorder)
+ call gt_free (gt1)
+ } else
+ call ecf_fit (ecf, NULL, NULL, Memd[x], Memd[y], Memd[z],
+ Memd[w], nfit, fixedorder)
+ EC_ECF(ec) = ecf
+
+ # Remove any deleted points.
+ j = 0
+ k = 0
+ do i = 1, EC_NFEATURES(ec) {
+ if (IS_INDEFD (PIX(ec,i)) || IS_INDEFD (USER(ec,i))) {
+ j = j + 1
+ APN(ec,j) = APN(ec,i)
+ LINE(ec,j) = LINE(ec,i)
+ ORDER(ec,j) = ORDER(ec,i)
+ PIX(ec,j) = PIX(ec,i)
+ FIT(ec,j) = FIT(ec,i)
+ USER(ec,j) = USER(ec,i)
+ FWIDTH(ec,j) = FWIDTH(ec,i)
+ FTYPE(ec,j) = abs (FTYPE(ec,i))
+ } else {
+ if (Memd[w+k] != 0.) {
+ j = j + 1
+ APN(ec,j) = APN(ec,i)
+ LINE(ec,j) = LINE(ec,i)
+ ORDER(ec,j) = ORDER(ec,i)
+ PIX(ec,j) = PIX(ec,i)
+ FIT(ec,j) = FIT(ec,i)
+ USER(ec,j) = USER(ec,i)
+ FWIDTH(ec,j) = FWIDTH(ec,i)
+ FTYPE(ec,j) = abs (FTYPE(ec,i))
+ if (Memd[w+k] < 0.)
+ FTYPE(ec,j) = -FTYPE(ec,j)
+ }
+ k = k + 1
+ }
+ }
+ EC_NFEATURES(ec) = j
+
+ # Set flags.
+ EC_NEWECF(ec) = YES
+ EC_NEWGRAPH(ec) = YES
+
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecdoshift.x b/noao/onedspec/ecidentify/ecdoshift.x
new file mode 100644
index 00000000..1689bc92
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecdoshift.x
@@ -0,0 +1,44 @@
+include "ecidentify.h"
+
+# EC_DOSHIFT -- Minimize residuals by constant shift.
+
+procedure ec_doshift (ec, interactive)
+
+pointer ec # ID pointer
+int interactive # Called interactively?
+
+int i, j
+double shft, delta, rms, ec_fitpt(), ecf_getd()
+
+begin
+ shft = 0.
+ rms = 0.
+ j = 0
+ for (i=1; i <= EC_NFEATURES(ec); i = i + 1) {
+ if (IS_INDEFD (USER(ec,i)))
+ next
+ delta = USER(ec,i) - ec_fitpt (ec, APN(ec,i), PIX(ec,i))
+ delta = delta * ORDER(ec,i)
+ shft = shft + delta
+ rms = rms + delta * delta
+ j = j + 1
+ }
+
+ if (j > 0) {
+ shft = shft / j
+ rms = rms / j
+ if (interactive == YES) {
+ i = EC_ORDER(ec)
+ call printf ("Coordinate shift=%5f, rms=%5f")
+ call pargd (shft / i)
+ if (j == 1)
+ call pargd (INDEFD)
+ else
+ call pargd (sqrt (rms - shft ** 2) / i)
+ }
+ shft = shft + ecf_getd ("shift")
+ call ecf_setd ("shift", shft)
+ EC_NEWECF(ec) = YES
+ EC_NEWGRAPH(ec) = YES
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfcolon.x b/noao/onedspec/ecidentify/ecffit/ecfcolon.x
new file mode 100644
index 00000000..4307335b
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfcolon.x
@@ -0,0 +1,102 @@
+include <error.h>
+include <gset.h>
+
+# List of colon commands
+define CMDS "|show|function|xorder|yorder|niterate|lowreject|highreject|"
+
+define SHOW 1 # Show parameters
+define FUNCTION 2 # Set or show function type
+define XORDER 3 # Set or show x order of function
+define YORDER 4 # Set or show y order of function
+define NITERATE 5 # Set or show rejection iterations
+define LOW 6 # Set or show low rejection threshold
+define HIGH 7 # Set or show high rejection threshold
+
+# ECF_COLON -- Processes colon commands.
+
+procedure ecf_colon (cmdstr, gp)
+
+char cmdstr[ARB] # Command string
+pointer gp # GIO pointer
+
+double dval
+int ncmd, ival
+int nscan(), strdic()
+include "ecffit.com"
+
+begin
+ # Use formated scan to parse the command string.
+ # The first word is the command and it may be minimum match
+ # abbreviated with the list of commands.
+
+ call sscan (cmdstr)
+ call gargwrd (ecfstr, SZ_LINE)
+ ncmd = strdic (ecfstr, ecfstr, SZ_LINE, CMDS)
+
+ switch (ncmd) {
+ case SHOW: # :show - Show the values of the fitting parameters.
+ call gdeactivate (gp, AW_CLEAR)
+ call printf ("function %s\nxorder %d\nyorder %d\n")
+ call pargstr (function)
+ call pargi (xorder)
+ call pargi (yorder)
+ call printf ("niterate %d\nlowreject %g\nhighreject\nnreject %d\n")
+ call pargi (niterate)
+ call pargd (low)
+ call pargd (high)
+ call pargi (nreject)
+ call printf ("slope %d\noffset %d\nshift %g\n")
+ call pargi (slope)
+ call pargi (offset)
+ call pargd (shift)
+ call printf ("rms %g\n")
+ call pargd (rms)
+ call greactivate (gp, AW_PAUSE)
+ case FUNCTION: # :function - List or set the fitting function.
+ call gargwrd (ecfstr, SZ_LINE)
+ if (nscan() == 1) {
+ call printf ("function = %s\n")
+ call pargstr (function)
+ } else {
+ iferr (call ecf_sets ("function", ecfstr))
+ call erract (EA_WARN)
+ }
+ case XORDER: # xorder: List or set the function order.
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("xorder %d\n")
+ call pargi (xorder)
+ } else
+ xorder = ival
+ case YORDER: # yorder: List or set the function order.
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("yorder %d\n")
+ call pargi (yorder)
+ } else
+ yorder = ival
+ case NITERATE: # niterate: List or set rejection iterations.
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("niterate %d\n")
+ call pargi (niterate)
+ } else
+ niterate = ival
+ case LOW: # low: List or set low rejection threshold.
+ call gargd (dval)
+ if (nscan() == 1) {
+ call printf ("lowreject %g\n")
+ call pargd (low)
+ } else
+ low = dval
+ case HIGH: # highreject: List or set high rejection threshold.
+ call gargd (dval)
+ if (nscan() == 1) {
+ call printf ("highreject %g\n")
+ call pargd (high)
+ } else
+ high = dval
+ default:
+ call printf ("Unrecognized or ambiguous command\007")
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfeval.x b/noao/onedspec/ecidentify/ecffit/ecfeval.x
new file mode 100644
index 00000000..1901522f
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfeval.x
@@ -0,0 +1,68 @@
+# ECF_EVAL -- Evaluate wavelength at a given order and pixel position.
+
+double procedure ecf_eval (ecf, order, x)
+
+pointer ecf # GSURFIT pointer
+int order # Order
+double x # X point
+
+int ecf_oeval()
+double y, dgseval()
+include "ecffit.com"
+
+begin
+ y = ecf_oeval (ecf, order)
+ if (ecf == NULL)
+ return (x + shift / y)
+ else
+ return ((dgseval (ecf, x, y) + shift) / y)
+end
+
+
+# ECF_VECTOR -- Evaluate echelle dispersion function for a vector of points of
+# the same order.
+
+procedure ecf_vector (ecf, order, x, fit, npts)
+
+pointer ecf # GSURFIT pointer
+int order # Order
+double x[npts] # X points
+double fit[npts] # Fitted points
+int npts # Number of points
+
+double yval
+pointer sp, y
+int ecf_oeval()
+include "ecffit.com"
+
+begin
+ call smark (sp)
+ call salloc (y, npts, TY_DOUBLE)
+
+ yval = ecf_oeval (ecf, order)
+ if (ecf == NULL)
+ call amovd (x, fit, npts)
+ else {
+ call amovkd (yval, Memd[y], npts)
+ call dgsvector (ecf, x, Memd[y], fit, npts)
+ call adivkd (fit, yval, fit, npts)
+ }
+ if (shift != 0.)
+ call aaddkd (fit, shift / yval, fit, npts)
+
+ call sfree (sp)
+end
+
+
+# ECF_OEVAL -- Evaluate the fit order.
+
+int procedure ecf_oeval (ecf, order)
+
+pointer ecf # GSURFIT pointer
+int order # User order
+
+include "ecffit.com"
+
+begin
+ return (slope * order + offset)
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecffit.com b/noao/onedspec/ecidentify/ecffit/ecffit.com
new file mode 100644
index 00000000..61f3104a
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecffit.com
@@ -0,0 +1,23 @@
+# Common parameters.
+
+char function[SZ_FNAME] # Fitting function
+char ecfstr[SZ_LINE] # Working char string
+int gstype # Surface function type
+int xorder # X order of surface function
+int yorder # Y order of surface function
+int niterate # Number of rejection iterations
+int nreject # Number of rejected points
+int xtype # X axis type
+int ytype # Y axis type
+int slope # Slope of order
+int offset # Order offset of fit
+double low, high # Low and high rejection thresholds
+double xmin, xmax # X range
+double ymin, ymax # Y range
+double shift # First order shift
+double rms # RMS of fit
+
+
+common /ecfcom/ low, high, xmin, xmax, ymin, ymax, shift, rms, gstype,
+ xorder, yorder, niterate, nreject, xtype, ytype, slope, offset,
+ function, ecfstr
diff --git a/noao/onedspec/ecidentify/ecffit/ecffit.h b/noao/onedspec/ecidentify/ecffit/ecffit.h
new file mode 100644
index 00000000..20825c71
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecffit.h
@@ -0,0 +1,20 @@
+define IGSPARAMS 7
+
+define FEATURE 1
+define X 2
+define Y 3
+define Z 4
+define W 5
+define S 6
+define R 7
+
+define IGS_FUNCTION 1
+define IGS_XORDER 2
+define IGS_YORDER 3
+define IGS_XMIN 4
+define IGS_XMAX 5
+define IGS_YMIN 6
+define IGS_YMAX 7
+define IGS_OFFSET 8
+
+define SFTYPES "|chebyshev|legendre|" # Surface types
diff --git a/noao/onedspec/ecidentify/ecffit/ecffit.key b/noao/onedspec/ecidentify/ecffit/ecffit.key
new file mode 100644
index 00000000..f24407b9
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecffit.key
@@ -0,0 +1,53 @@
+ ECHELLE DISPERSION FUNCTION FITTING KEYS
+
+
+CURSOR KEY SUMMARY
+
+? Help c Print coordinates d Delete point
+f Fit dispersion o Fit with fixed order offset q Quit
+r Redraw graph u Undelete point w Window graph
+x Set ordinate y Set abscissa I Interrupt
+
+
+COLON COMMAND SUMMARY
+
+:show :function [value] :highreject [value] :lowreject [value]
+:niterate [value] :xorder [value] :yorder [value]
+
+
+CURSOR KEYS
+
+? Print this list of cursor keys
+c Print cursor coordinates
+d Delete the nearest undeleted point to the cursor
+f Fit dispersion function including determining the order offset
+o Fit dispersion function with the order offset fixed
+q Quit and return to the spectrum display
+r Redraw the graph
+u Undelete the nearest deleted point to the cursor (may be outside the window)
+w Window the graph (type ? to the window prompt for more help)
+x Set the quantity plotted along the ordinate (x axis)
+y Set the quantity plotted along the abscissa (y axis)
+I Interrupt the task immediately
+
+
+COLON COMMANDS
+
+:show Print current function and orders
+:function [value] Print or set the function type (chebyshev|legendre)
+:highreject [value] Print or set high rejection limit
+:lowreject [value] Print or set high rejection limit
+:niterate [value] Print or set number of rejection iterations
+:xorder [value] Print or set the order for the dispersion dependence
+:yorder [value] Print or set the order for the echelle order dependence
+
+
+The dispersion function fitted is given by a two dimensional function
+(either chebyshev or legendre) of the pixel position along the
+dispersion of an order (called x) and the order number (called y). The
+order number is determined from the aperture number by an offset and
+direction of increasing order number. The basic order dependence is
+separated from the surface function as given below.
+
+ y = offset +/- aperture
+ wavelength = f (x, y) / y
diff --git a/noao/onedspec/ecidentify/ecffit/ecffit.x b/noao/onedspec/ecidentify/ecffit/ecffit.x
new file mode 100644
index 00000000..408a1b77
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecffit.x
@@ -0,0 +1,193 @@
+include <error.h>
+include <pkg/gtools.h>
+
+define HELP "noao$onedspec/ecidentify/ecffit/ecffit.key"
+define PROMPT "fitcoords surface fitting options"
+
+# EC_FIT -- Echelle dispersion fitting.
+#
+# X - Pixel coordinates along dispersion
+# Y - Relative order number
+# Z - Wavelength
+
+procedure ecf_fit (ecf, gp, gt, xd, yd, zd, wd, npts, fixedorder)
+
+pointer ecf # GSURFIT pointer
+pointer gp # GIO pointer
+pointer gt # GTOOLS pointer
+double xd[npts] # Pixel coordinates along dispersion
+double yd[npts] # Order number
+double zd[npts] # Wavelength
+double wd[npts] # Weights
+int npts # Number of points
+int fixedorder # Fixed order?
+
+real wx, wy
+int wcs, key
+int i, newgraph
+pointer sp, wd1, rd, xr, yr
+char cmd[SZ_LINE]
+
+int ecf_nearest()
+int clgcur(), scan(), nscan()
+errchk ecf_solve()
+include "ecffit.com"
+
+begin
+ # Allocate residuals and weights with rejected points arrays
+ call smark (sp)
+ call salloc (wd1, npts, TY_DOUBLE)
+ call salloc (rd, npts, TY_DOUBLE)
+ call amovd (wd, Memd[wd1], npts)
+
+ # Compute a solution and return if not interactive.
+ if (gp == NULL) {
+ call ecf_solve (ecf, xd, yd, zd, Memd[wd1], Memd[rd], npts,
+ fixedorder)
+ call ecf_reject (ecf, xd, yd, zd, Memd[wd1], Memd[rd], npts,
+ fixedorder)
+ do i = 1, npts
+ if (Memd[wd1+i-1] != wd[i])
+ wd[i] = -1.
+ call sfree (sp)
+ return
+ }
+
+ # Allocate real graph vectors.
+ call salloc (xr, npts, TY_REAL)
+ call salloc (yr, npts, TY_REAL)
+
+ # Read cursor commands.
+ key = 'f'
+ repeat {
+ switch (key) {
+ case 'o':
+ call printf ("Order offset (%d): ")
+ call pargi (offset)
+ call flush (STDOUT)
+ if (scan() != EOF) {
+ call gargi (i)
+ if (nscan() == 1)
+ offset = i
+ call amovd (wd, Memd[wd1], npts)
+ call ecf_solve (ecf, xd, yd, zd, Memd[wd1], Memd[rd], npts,
+ YES)
+ call ecf_reject (ecf, xd, yd, zd, Memd[wd1], Memd[rd], npts,
+ YES)
+ call ecf_gdata (ecf, xtype, xd, yd, zd, Memd[rd],
+ Memr[xr], npts)
+ call ecf_gdata (ecf, ytype, xd, yd, zd, Memd[rd],
+ Memr[yr], npts)
+ call ecf_title (gt)
+ newgraph = YES
+ }
+
+ case '?': # Print help text.
+ call gpagefile (gp, HELP, PROMPT)
+
+ case ':': # List or set parameters
+ if (cmd[1] == '/')
+ call gt_colon (cmd, gp, gt, newgraph)
+ else
+ call ecf_colon (cmd, gp)
+
+ case 'x': # Set ordinate
+ call printf ("Ordinate - ")
+ call printf (
+ "(p)ixel, (o)rder, (w)avelength, (r)esidual, (v)elocity: ")
+ if (clgcur ("cursor", wx, wy, wcs, key, cmd, SZ_LINE) == EOF)
+ break
+
+ if (key != xtype) {
+ if (key=='p'||key=='o'||key=='w'||key=='r'||key=='v') {
+ xtype = key
+ call gt_setr (gt, GTXMIN, INDEF)
+ call gt_setr (gt, GTXMAX, INDEF)
+ call ecf_gdata (ecf, xtype, xd, yd, zd, Memd[rd],
+ Memr[xr], npts)
+ call ecf_title (gt)
+ newgraph = YES
+ } else
+ call printf ("\007")
+ }
+
+ case 'y': # Set abscissa
+ call printf ("Abscissa - ")
+ call printf (
+ "(p)ixel, (o)rder, (w)avelength, (r)esidual, (v)elocity: ")
+ if(clgcur ("cursor", wx, wy, wcs, key, cmd, SZ_LINE) == EOF)
+ break
+
+ if (key != ytype) {
+ if (key=='p'||key=='o'||key=='w'||key=='r'||key=='v') {
+ ytype = key
+ call gt_setr (gt, GTYMIN, INDEF)
+ call gt_setr (gt, GTYMAX, INDEF)
+ call ecf_gdata (ecf, ytype, xd, yd, zd, Memd[rd],
+ Memr[yr], npts)
+ call ecf_title (gt)
+ newgraph = YES
+ } else
+ call printf ("\007")
+ }
+
+ case 'r': # Redraw
+ newgraph = YES
+
+ case 'c': # Cursor coordinates
+ i = ecf_nearest (gp, gt, wx, wy, wcs, key, Memr[xr], Memr[yr],
+ wd, npts)
+ call printf ("%10.2g %d %10.8g\n")
+ call pargd (xd[i])
+ call pargd (yd[i])
+ call pargd (zd[i])
+
+ case 'd': # Delete
+ i = ecf_nearest (gp, gt, wx, wy, wcs, key, Memr[xr], Memr[yr],
+ wd, npts)
+ if (i > 0)
+ Memd[wd1+i-1] = wd[i]
+
+ case 'u': # Undelete
+ i = ecf_nearest (gp, gt, wx, wy, wcs, key, Memr[xr], Memr[yr],
+ wd, npts)
+ if (i > 0)
+ Memd[wd1+i-1] = wd[i]
+
+ case 'f': # Fit
+ call amovd (wd, Memd[wd1], npts)
+ call ecf_solve (ecf, xd, yd, zd, Memd[wd1], Memd[rd], npts,
+ fixedorder)
+ call ecf_reject (ecf, xd, yd, zd, Memd[wd1], Memd[rd], npts,
+ fixedorder)
+ call ecf_gdata (ecf, xtype, xd, yd, zd, Memd[rd],
+ Memr[xr], npts)
+ call ecf_gdata (ecf, ytype, xd, yd, zd, Memd[rd],
+ Memr[yr], npts)
+ call ecf_title (gt)
+ newgraph = YES
+
+ case 'w': # Window graph
+ call gt_window (gt, gp, "cursor", newgraph)
+
+ case 'q': # Quit
+ break
+
+ case 'I': # Interrupt
+ call fatal (0, "Interrupt")
+
+ default: # Ring the bell.
+ call printf ("\07\n")
+ }
+
+ if (newgraph == YES) {
+ call ecf_graph (gp, gt, Memr[xr], Memr[yr], wd, Memd[wd1], npts)
+ newgraph = NO
+ }
+ } until (clgcur ("cursor", wx, wy, wcs, key, cmd, SZ_LINE) == EOF)
+
+ do i = 1, npts
+ if (Memd[wd1+i-1] != wd[i])
+ wd[i] = -1.
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfgdata.x b/noao/onedspec/ecidentify/ecffit/ecfgdata.x
new file mode 100644
index 00000000..eebb34d6
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfgdata.x
@@ -0,0 +1,37 @@
+include <pkg/gtools.h>
+
+# ECF_GDATA -- Get graph data for the specified axis type from the fitting data.
+
+procedure ecf_gdata (ecf, type, x, y, z, r, data, npts)
+
+pointer ecf # GSURFIT pointer
+int type # Axis type
+double x[npts] # X fit data
+double y[npts] # Y fit data
+double z[npts] # Z fit data
+double r[npts] # Residuals
+real data[npts] # Graph data
+int npts # Number of points
+
+pointer sp, v
+include "ecffit.com"
+
+begin
+ switch (type) {
+ case 'p':
+ call achtdr (x, data, npts)
+ case 'o':
+ call achtdr (y, data, npts)
+ case 'w':
+ call achtdr (z, data, npts)
+ case 'r':
+ call achtdr (r, data, npts)
+ case 'v':
+ call smark (sp)
+ call salloc (v, npts, TY_DOUBLE)
+ call adivd (r, z, Memd[v], npts)
+ call amulkd (Memd[v], 300000.D0, Memd[v], npts)
+ call achtdr (Memd[v], data, npts)
+ call sfree (sp)
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfget.x b/noao/onedspec/ecidentify/ecffit/ecfget.x
new file mode 100644
index 00000000..025059df
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfget.x
@@ -0,0 +1,84 @@
+# ECF_GETI -- Get the value of an integer parameter.
+
+int procedure ecf_geti (param)
+
+char param[ARB] # ECF parameter
+
+int i, strdic()
+include "ecffit.com"
+
+begin
+ i = strdic (param, ecfstr, SZ_LINE,
+ "|slope|offset|xorder|yorder|niterate|")
+ switch (i) {
+ case 1:
+ return (slope)
+ case 2:
+ return (offset)
+ case 3:
+ return (xorder)
+ case 4:
+ return (yorder)
+ case 5:
+ return (niterate)
+ default:
+ call error (0, "ecf_geti: Unknown parameter")
+ }
+end
+
+
+# ECF_GETS -- Get the value of a string parameter.
+
+procedure ecf_gets (param, str, maxchar)
+
+char param[ARB] # ECF parameter
+char str[maxchar] # String
+int maxchar # Maximum number of characters
+
+int i, strdic()
+include "ecffit.com"
+
+begin
+ i = strdic (param, ecfstr, SZ_LINE, "|function|")
+ switch (i) {
+ case 1:
+ call strcpy (function, str, maxchar)
+ default:
+ call error (0, "ecf_gets: Unknown parameter")
+ }
+end
+
+
+# ECF_GETD -- Get the values of double valued fitting parameters.
+
+double procedure ecf_getd (param)
+
+char param[ARB] # ECF parameter
+
+int i, strdic()
+include "ecffit.com"
+
+begin
+ i = strdic (param, ecfstr, SZ_LINE,
+ "|xmin|xmax|ymin|ymax|shift|rms|low|high|")
+ switch (i) {
+ case 1:
+ return (xmin)
+ case 2:
+ return (xmax)
+ case 3:
+ return (ymin)
+ case 4:
+ return (ymax)
+ case 5:
+ return (shift)
+ case 6:
+ return (rms)
+ case 7:
+ return (low)
+ case 8:
+ return (high)
+ default:
+ call error (0, "ecf_gets: Unknown parameter")
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfgraph.x b/noao/onedspec/ecidentify/ecffit/ecfgraph.x
new file mode 100644
index 00000000..22749527
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfgraph.x
@@ -0,0 +1,50 @@
+include <gset.h>
+include <mach.h>
+include <pkg/gtools.h>
+
+# ECF_GRAPH -- Graph the fitted data.
+
+procedure ecf_graph (gp, gt, x, y, w, rej, npts)
+
+pointer gp # GIO pointer
+pointer gt # GTOOLS pointer
+real x[npts] # X data
+real y[npts] # Y data
+double w[npts] # Weights
+double rej[npts] # Rejected points
+int npts # Number of pts points
+
+int i
+real xsize, ysize, ymin, ymax, gt_getr()
+
+begin
+ xsize = gt_getr (gt, GTXSIZE)
+ ysize = gt_getr (gt, GTYSIZE)
+
+ call gclear (gp)
+
+ ymin = MAX_REAL
+ ymax = -MAX_REAL
+ do i = 1, npts
+ if (w[i] > 0.) {
+ ymin = min (ymin, y[i])
+ ymax = max (ymax, y[i])
+ }
+
+ call gascale (gp, x, npts, 1)
+ call gswind (gp, INDEF, INDEF, ymin, ymax)
+ call gt_swind (gp, gt)
+ call gt_labax (gp, gt)
+
+ do i = 1, npts {
+ if (rej[i] == 0.) {
+ if (y[i] >= ymin && y[i] <= ymax) {
+ if (w[i] == 0.)
+ call gmark (gp, x[i], y[i], GM_CROSS, xsize, ysize)
+ else
+ call gmark (gp, x[i], y[i], GM_DIAMOND, xsize, ysize)
+ }
+ } else
+ call gmark (gp, x[i], y[i], GM_PLUS, xsize, ysize)
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfnearest.x b/noao/onedspec/ecidentify/ecffit/ecfnearest.x
new file mode 100644
index 00000000..af1b1f78
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfnearest.x
@@ -0,0 +1,85 @@
+include <mach.h>
+include <gset.h>
+include <pkg/gtools.h>
+
+# ECF_NEAREST -- Find nearest point to the cursor.
+
+int procedure ecf_nearest (gp, gt, wx, wy, wcs, key, x, y, w, npts)
+
+pointer gp # GIO pointer
+pointer gt # GTOOLS pointer
+real wx, wy # Cursor coordinates
+int wcs # WCS
+int key # Nearest key
+real x[npts] # Data points
+real y[npts] # Data points
+double w[npts] # Weight
+int npts # Number of data points
+
+int i, j
+real r2, r2min, x0, y0, xsize, ysize, gt_getr()
+
+begin
+ call gctran (gp, wx, wy, wx, wy, wcs, 0)
+ r2min = MAX_REAL
+ j = 0
+
+ switch (key) {
+ case 'c':
+ do i = 1, npts {
+ call gctran (gp, x[i], y[i], x0, y0, wcs, 0)
+ r2 = (x0 - wx) ** 2 + (y0 - wy) ** 2
+ if (r2 < r2min) {
+ r2min = r2
+ j = i
+ }
+ }
+ call gscur (gp, x[j], y[j])
+ case 'd':
+ do i = 1, npts {
+ if (w[i] == 0.)
+ next
+ call gctran (gp, x[i], y[i], x0, y0, wcs, 0)
+ r2 = (x0 - wx) ** 2 + (y0 - wy) ** 2
+ if (r2 < r2min) {
+ r2min = r2
+ j = i
+ }
+ }
+ if (j > 0) {
+ xsize = gt_getr (gt, GTXSIZE)
+ ysize = gt_getr (gt, GTYSIZE)
+
+ call gseti (gp, G_PMLTYPE, 0)
+ call gmark (gp, x[j], y[j], GM_PLUS, xsize, ysize)
+ call gseti (gp, G_PMLTYPE, 1)
+ call gmark (gp, x[j], y[j], GM_CROSS, xsize, ysize)
+ w[j] = 0.
+ call gscur (gp, x[j], y[j])
+ }
+ case 'u':
+ do i = 1, npts {
+ if (w[i] != 0.)
+ next
+ call gctran (gp, x[i], y[i], x0, y0, wcs, 0)
+ r2 = (x0 - wx) ** 2 + (y0 - wy) ** 2
+ if (r2 < r2min) {
+ r2min = r2
+ j = i
+ }
+ }
+ if (j > 0) {
+ xsize = gt_getr (gt, GTXSIZE)
+ ysize = gt_getr (gt, GTYSIZE)
+
+ call gseti (gp, G_PMLTYPE, 0)
+ call gmark (gp, x[j], y[j], GM_CROSS, xsize, ysize)
+ call gseti (gp, G_PMLTYPE, 1)
+ call gmark (gp, x[j], y[j], GM_PLUS, xsize, ysize)
+ w[j] = 1.
+ call gscur (gp, x[j], y[j])
+ }
+ }
+
+ return (j)
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfreject.x b/noao/onedspec/ecidentify/ecffit/ecfreject.x
new file mode 100644
index 00000000..a772069e
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfreject.x
@@ -0,0 +1,53 @@
+include <mach.h>
+
+# ECF_REJECT -- Reject points with large residuals from the fit.
+
+procedure ecf_reject (ecf, x, y, z, w, r, npts, fixedorder)
+
+pointer ecf # GSURFIT pointer
+double x[npts] # X points
+double y[npts] # Y points
+double z[npts] # Z points
+double w[npts] # Weights
+double r[npts] # Residuals
+int npts # Number of points
+int fixedorder # Fixed order?
+
+int i, j, newreject
+double low_cut, high_cut
+include "ecffit.com"
+
+begin
+ # Return if rejection is not desired.
+ nreject = 0
+ if (niterate == 0 || (low == 0. && high == 0.))
+ return
+
+ # Reject points.
+ do i = 1, niterate {
+ if (low > 0.)
+ low_cut = -low * rms
+ else
+ low_cut = -MAX_REAL
+ if (high > 0.)
+ high_cut = high * rms
+ else
+ high_cut = MAX_REAL
+
+ newreject = 0
+ do j = 1, npts {
+ if (w[j] == 0.)
+ next
+ if ((r[j] > high_cut) || (r[j] < low_cut)) {
+ w[j] = 0.
+ newreject = newreject + 1
+ }
+ }
+
+ if (newreject == 0)
+ break
+
+ call ecf_solve (ecf, x, y, z, w, r, npts, fixedorder)
+ nreject = nreject + newreject
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfrms.x b/noao/onedspec/ecidentify/ecffit/ecfrms.x
new file mode 100644
index 00000000..1140dc29
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfrms.x
@@ -0,0 +1,26 @@
+# ECF_RMS -- Compute the rms with deleted points ignored.
+
+double procedure ecf_rms (r, w, npts)
+
+double r[npts] # Residuals
+double w[npts] # Weights
+int npts # Number of points
+
+int i, n
+double rms
+
+begin
+ n = 0
+ rms = 0.
+ do i = 1, npts {
+ if (w[i] == 0.)
+ next
+ n = n + 1
+ rms = rms + r[i] * r[i]
+ }
+ if (n > 0)
+ rms = sqrt (rms / n)
+ else
+ rms = INDEFD
+ return (rms)
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfset.x b/noao/onedspec/ecidentify/ecffit/ecfset.x
new file mode 100644
index 00000000..4b6402b1
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfset.x
@@ -0,0 +1,92 @@
+# ECF_SETS -- Set the values of string valued fitting parameters.
+
+procedure ecf_sets (param, str)
+
+char param[ARB] # Parameter to be set
+char str[ARB] # String value
+
+char temp[10]
+int i, strdic()
+include "ecffit.com"
+
+begin
+ i = strdic (param, temp, 10, "|function|")
+ switch (i) {
+ case 1:
+ i = strdic (str, str, SZ_FNAME, "|chebyshev|legendre|")
+ if (i == 0)
+ call error (0, "Unknown function type")
+ call strcpy (str, function, SZ_LINE)
+ gstype = i
+ default:
+ call error (0, "ecf_sets: Unknown parameter")
+ }
+end
+
+
+# ECF_SETI -- Set the values of integer valued fitting parameters.
+
+procedure ecf_seti (param, ival)
+
+char param[ARB] # Parameter to be set
+int ival # Integer value
+
+int i, strdic()
+include "ecffit.com"
+
+begin
+ i = strdic (param, ecfstr, SZ_LINE,
+ "|slope|offset|xorder|yorder|xtype|ytype|niterate|")
+ switch (i) {
+ case 1:
+ slope = ival
+ case 2:
+ offset = ival
+ case 3:
+ xorder = ival
+ case 4:
+ yorder = ival
+ case 5:
+ xtype = ival
+ case 6:
+ ytype = ival
+ case 7:
+ niterate = max (0, ival)
+ default:
+ call error (0, "ecf_seti: Unknown parameter")
+ }
+end
+
+
+# ECF_SETD -- Set the values of double valued fitting parameters.
+
+procedure ecf_setd (param, dval)
+
+char param[ARB] # Parameter to be set
+double dval # Double value
+
+int i, strdic()
+include "ecffit.com"
+
+begin
+ i = strdic (param, ecfstr, SZ_LINE,
+ "|xmin|xmax|ymin|ymax|shift|low|high|")
+ switch (i) {
+ case 1:
+ xmin = dval
+ case 2:
+ xmax = dval
+ case 3:
+ ymin = dval
+ case 4:
+ ymax = dval
+ case 5:
+ shift = dval
+ case 6:
+ low = max (0.D0, dval)
+ case 7:
+ high = max (0.D0, dval)
+ default:
+ call error (0, "ecf_setd: Unknown parameter")
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfshift.x b/noao/onedspec/ecidentify/ecffit/ecfshift.x
new file mode 100644
index 00000000..75655703
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfshift.x
@@ -0,0 +1,55 @@
+# ECF_GSHIFT -- Return the shift for the given order.
+
+double procedure ecf_gshift (ecf, order)
+
+pointer ecf # GSURFIT pointer
+int order # User order
+
+include "ecffit.com"
+
+begin
+ return (shift / (slope * order + offset))
+end
+
+
+# ECF_PSHIFT -- Put the shift for the given order.
+
+procedure ecf_pshift (ecf, order, shft)
+
+pointer ecf # GSURFIT pointer
+int order # User order
+double shft # Shift at given order
+
+include "ecffit.com"
+
+begin
+ shift = shft * (slope * order + offset)
+end
+
+
+procedure ecf_vector (ecf, order, x, fit, npts)
+
+pointer ecf # GSURFIT pointer
+int order # Order
+double x[npts] # X points
+double fit[npts] # Fitted points
+int npts # Number of points
+
+double yval
+pointer sp, y
+
+include "ecffit.com"
+
+begin
+ call smark (sp)
+ call salloc (y, npts, TY_DOUBLE)
+
+ yval = slope * order + offset
+ call amovkd (yval, Memd[y], npts)
+ call dgsvector (ecf, x, Memd[y], fit, npts)
+ call adivkd (fit, yval, fit, npts)
+ if (shift != 0.)
+ call aaddkd (fit, shift / yval, fit, npts)
+
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecfsolve.x b/noao/onedspec/ecidentify/ecffit/ecfsolve.x
new file mode 100644
index 00000000..1c844e76
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecfsolve.x
@@ -0,0 +1,196 @@
+include <mach.h>
+include <math/gsurfit.h>
+
+define ECFTYPES "|chebyshev|legendre|" # Fit types
+
+
+# ECF_SOLVE -- Fit
+#
+# f(x, slope*y+offset) = (y+slope*offset)*z
+#
+# with offset minimizing the RMS.
+
+procedure ecf_solve (ecf, x, y, z, w, r, npts, fixedorder)
+
+pointer ecf # GSURFIT pointer
+double x[npts] # X points
+double y[npts] # Y points
+double z[npts] # Z points
+double w[npts] # Weights
+double r[npts] # Residuals
+int npts # Number of points
+int fixedorder # Fixed order?
+
+int i, j, k, err
+double ya, yb, newrms, ecf_rms()
+pointer sp, y1, ecf1
+errchk ecf_solve1
+include "ecffit.com"
+define fit_ 99
+
+begin
+ if (fixedorder == YES) {
+ call ecf_solve1 (ecf, x, y, z, w, r, npts)
+ return
+ }
+
+ call smark (sp)
+ call salloc (y1, npts, TY_DOUBLE)
+
+ # Determine if the orders are reversed.
+ j = 1
+ k = 1
+ do i = 1, npts {
+ if (z[i] < z[j])
+ j = i
+ if (z[i] > z[k])
+ k = i
+ }
+ if (y[j] >= y[k]) {
+ slope = 1
+ offset = max (offset, int(1. - ymin))
+ } else {
+ slope = -1
+ offset = max (offset, int(1. + ymax))
+ }
+
+ call dgsfree (ecf)
+ shift = 0.
+
+ rms = MAX_DOUBLE
+ j = 1
+ k = 0
+
+ for (i=offset;;i=i+j) {
+ if (slope == 1) {
+ ya = i + ymin
+ yb = i + ymax
+ } else {
+ ya = i - ymax
+ yb = i - ymin
+ }
+ if (ya < 1.)
+ break
+
+ call altmd (y, Memd[y1], npts, double(slope), double(i))
+ call amuld (Memd[y1], z, r, npts)
+
+fit_ call dgsinit (ecf1, gstype, xorder, yorder, YES, xmin, xmax, ya, yb)
+ call dgsfit (ecf1, x, Memd[y1], r, w, npts, WTS_USER, err)
+
+ if (err != OK) {
+ if (xorder > 2 || yorder > 2) {
+ call dgsfree (ecf)
+ xorder = max (2, xorder - 1)
+ yorder = max (2, yorder - 1)
+ goto fit_
+ }
+
+ switch (err) {
+ case SINGULAR:
+ call dgsfree (ecf)
+ ecf = ecf1
+ call eprintf ("Singular solution\n")
+ case NO_DEG_FREEDOM:
+ call sfree (sp)
+ call error (0, "No degrees of freedom")
+ }
+ }
+
+ call dgsvector (ecf1, x, Memd[y1], r, npts)
+ call adivd (r, Memd[y1], r, npts)
+ call asubd (z, r, r, npts)
+
+ newrms = ecf_rms (r, w, npts)
+ k = k + 1
+
+ if (newrms / rms < 0.999) {
+ call dgsfree (ecf)
+ ecf = ecf1
+ offset = i
+ rms = newrms
+ } else {
+ call dgsfree (ecf1)
+ if (k > 2)
+ break
+ i = offset
+ j = -j
+ }
+ }
+
+ call altmd (y, Memd[y1], npts, double(slope), double(offset))
+ call dgsvector (ecf, x, Memd[y1], r, npts)
+ call adivd (r, Memd[y1], r, npts)
+ call asubd (z, r, r, npts)
+
+ call sfree (sp)
+
+end
+
+
+# ECF_SOLVE1 -- Fit f(x, y+offset) = (y+offset)*z with offset fixed.
+
+procedure ecf_solve1 (ecf, x, y, z, w, r, npts)
+
+pointer ecf # GSURFIT pointer
+double x[npts] # X points
+double y[npts] # Y points
+double z[npts] # Z points
+double w[npts] # Weights
+double r[npts] # Residuals
+int npts # Number of points
+
+int err
+pointer sp, y1
+double ya, yb, ecf_rms()
+include "ecffit.com"
+define fit_ 99
+
+begin
+ call smark (sp)
+ call salloc (y1, npts, TY_DOUBLE)
+
+ call dgsfree (ecf)
+ shift = 0.
+
+ if (slope == 1) {
+ offset = max (offset, int(1. - ymin))
+ ya = offset + ymin
+ yb = offset + ymax
+ } else {
+ offset = max (offset, int(1. + ymax))
+ ya = offset - ymax
+ yb = offset - ymin
+ }
+
+ call altmd (y, Memd[y1], npts, double (slope), double (offset))
+ call amuld (Memd[y1], z, r, npts)
+
+fit_ call dgsinit (ecf, gstype, xorder, yorder, YES, xmin, xmax,
+ min (ya, yb), max (ya, yb))
+ call dgsfit (ecf, x, Memd[y1], r, w, npts, WTS_USER, err)
+
+ if (err != OK) {
+ if (xorder > 2 || yorder > 2) {
+ call dgsfree (ecf)
+ xorder = max (2, xorder - 1)
+ yorder = max (2, yorder - 1)
+ goto fit_
+ }
+
+ switch (err) {
+ case SINGULAR:
+ call eprintf ("Singular solution\n")
+ case NO_DEG_FREEDOM:
+ call sfree (sp)
+ call error (0, "No degrees of freedom")
+ }
+ }
+
+ call dgsvector (ecf, x, Memd[y1], r, npts)
+ call adivd (r, Memd[y1], r, npts)
+ call asubd (z, r, r, npts)
+ rms = ecf_rms (r, w, npts)
+
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecffit/ecftitle.x b/noao/onedspec/ecidentify/ecffit/ecftitle.x
new file mode 100644
index 00000000..3b754f31
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/ecftitle.x
@@ -0,0 +1,48 @@
+include <pkg/gtools.h>
+
+# ECF_TITLE -- Set the GTOOLS parameter string.
+
+procedure ecf_title (gt)
+
+pointer gt # GTOOLS pointer
+
+include "ecffit.com"
+
+begin
+ call sprintf (ecfstr, SZ_LINE,
+ "Function=%s, xorder=%d, yorder=%d, slope=%d, offset=%d, rms=%6g")
+ call pargstr (function)
+ call pargi (xorder)
+ call pargi (yorder)
+ call pargi (slope)
+ call pargi (offset)
+ call pargd (rms)
+ call gt_sets (gt, GTPARAMS, ecfstr)
+ call gt_sets (gt, GTTITLE, "Echelle Dispersion Function Fitting")
+
+ switch (xtype) {
+ case 'p':
+ call gt_sets (gt, GTXLABEL, "Pixel")
+ case 'o':
+ call gt_sets (gt, GTXLABEL, "Order")
+ case 'w':
+ call gt_sets (gt, GTXLABEL, "Wavelength")
+ case 'r':
+ call gt_sets (gt, GTXLABEL, "Residual")
+ case 'v':
+ call gt_sets (gt, GTXLABEL, "Velocity")
+ }
+
+ switch (ytype) {
+ case 'p':
+ call gt_sets (gt, GTYLABEL, "Pixel")
+ case 'o':
+ call gt_sets (gt, GTYLABEL, "Order")
+ case 'w':
+ call gt_sets (gt, GTYLABEL, "Wavelength")
+ case 'r':
+ call gt_sets (gt, GTYLABEL, "Residual")
+ case 'v':
+ call gt_sets (gt, GTYLABEL, "Velocity")
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecffit/mkpkg b/noao/onedspec/ecidentify/ecffit/mkpkg
new file mode 100644
index 00000000..40324cb8
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecffit/mkpkg
@@ -0,0 +1,21 @@
+# Echelle Dispersion Fitting Package
+
+$checkout libpkg.a ../../
+$update libpkg.a
+$checkin libpkg.a ../../
+$exit
+
+libpkg.a:
+ ecfcolon.x ecffit.com <error.h> <gset.h>
+ ecfeval.x ecffit.com
+ ecffit.x ecffit.com <error.h> <pkg/gtools.h>
+ ecfgdata.x ecffit.com <pkg/gtools.h>
+ ecfget.x ecffit.com
+ ecfgraph.x <gset.h> <mach.h> <pkg/gtools.h>
+ ecfnearest.x <gset.h> <mach.h> <pkg/gtools.h>
+ ecfreject.x ecffit.com <mach.h>
+ ecfrms.x
+ ecfset.x ecffit.com
+ ecfsolve.x ecffit.com <mach.h> <math/gsurfit.h>
+ ecftitle.x ecffit.com <pkg/gtools.h>
+ ;
diff --git a/noao/onedspec/ecidentify/ecfitdata.x b/noao/onedspec/ecidentify/ecfitdata.x
new file mode 100644
index 00000000..998f5057
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecfitdata.x
@@ -0,0 +1,146 @@
+include <pkg/gtools.h>
+include <smw.h>
+include <units.h>
+include "ecidentify.h"
+
+# EC_FITDATA -- Compute fit coordinates from pixel coordinates.
+
+procedure ec_fitdata (ec)
+
+pointer ec # ID pointer
+
+int i, ecf_oeval()
+
+begin
+ call mfree (EC_FITDATA(ec), TY_DOUBLE)
+ call malloc (EC_FITDATA(ec), EC_NCOLS(ec)*EC_NLINES(ec), TY_DOUBLE)
+
+ do i = 1, EC_NLINES(ec) {
+ call ec_gline (ec, i)
+ if (EC_ECF(ec) == NULL) {
+ if (DC(EC_SH(ec)) != DCNO && EC_UN(ec) != NULL)
+ iferr (call shdr_units (EC_SH(ec), UN_UNITS(EC_UN(ec))))
+ ;
+ call achtrd (Memr[SX(EC_SH(ec))], FITDATA(ec,1), EC_NPTS(ec))
+ call gt_sets (EC_GT(ec), GTXLABEL, LABEL(EC_SH(ec)))
+ call gt_sets (EC_GT(ec), GTXUNITS, UNITS(EC_SH(ec)))
+ } else {
+ ORDERS(ec,i) = ecf_oeval (EC_ECF(ec), APS(ec,i))
+ call ecf_vector (EC_ECF(ec), APS(ec,i), PIXDATA(ec,1),
+ FITDATA(ec,1), EC_NPTS(ec))
+ if (EC_UN(ec) == NULL) {
+ call gt_sets (EC_GT(ec), GTXLABEL, LABEL(EC_SH(ec)))
+ call gt_sets (EC_GT(ec), GTXUNITS, UNITS(EC_SH(ec)))
+ } else {
+ call gt_sets (EC_GT(ec), GTXLABEL, UN_LABEL(EC_UN(ec)))
+ call gt_sets (EC_GT(ec), GTXUNITS, UN_UNITS(EC_UN(ec)))
+ }
+ }
+ }
+
+ call ec_gline (ec, EC_LINE(ec))
+ EC_ORDER(ec) = ORDERS(ec,EC_LINE(ec))
+end
+
+
+# EC_FITFEATURES -- Compute fit coordinates for features.
+
+procedure ec_fitfeatures (ec)
+
+pointer ec # ID pointer
+
+int i, ec_line()
+double ec_fitpt()
+
+begin
+ if (EC_NFEATURES(ec) < 1)
+ return
+
+ do i = 1, EC_NFEATURES(ec) {
+ LINE(ec,i) = ec_line (ec, APN(ec,i))
+ ORDER(ec,i) = ORDERS(ec,LINE(ec,i))
+ FIT(ec,i) = ec_fitpt (ec, APN(ec,i), PIX(ec,i))
+ }
+end
+
+
+# EC_FITPT -- Compute fit coordinates from pixel coordinates.
+
+double procedure ec_fitpt (ec, order, pix)
+
+pointer ec # ID pointer
+int order # Order
+double pix # Pixel coordinate
+
+double fit, ecf_eval(), smw_c1trand(), shdr_lw()
+
+begin
+ if (EC_ECF(ec) == NULL) {
+ fit = smw_c1trand (EC_PL(ec), pix)
+ fit = shdr_lw (EC_SH(ec), fit)
+ } else
+ fit = ecf_eval (EC_ECF(ec), order, pix)
+
+ return (fit)
+end
+
+
+# EC_FITTOPIX -- Transform fit coordinate to pixel coordinate.
+
+define DXMIN .01
+
+double procedure ec_fittopix (ec, fitcoord)
+
+pointer ec # ID pointer
+double fitcoord # Fit coordinate to be transformed
+double pixcoord # Pixel coordinate returned
+
+int i, n
+double dx
+
+double ec_fitpt(), smw_c1trand()
+
+begin
+ n = EC_NPTS(ec)
+ if (FITDATA(ec,1) < FITDATA(ec,n)) {
+ if ((fitcoord<FITDATA(ec,1)) || (fitcoord>FITDATA(ec,n)))
+ return (INDEFD)
+
+ for (i = 1; fitcoord > FITDATA(ec,i); i = i + 1)
+ ;
+
+ if (FITDATA(ec,i) == fitcoord)
+ return (double (i))
+
+ pixcoord = smw_c1trand (EC_LP(ec), double(i-.5))
+ dx = smw_c1trand (EC_LP(ec), double(i+.5)) - pixcoord
+ while (dx > DXMIN) {
+ dx = dx / 2
+ if (ec_fitpt (ec, EC_AP(ec), pixcoord) < fitcoord)
+ pixcoord = pixcoord + dx
+ else
+ pixcoord = pixcoord - dx
+ }
+ } else {
+ if ((fitcoord<FITDATA(ec,n)) || (fitcoord>FITDATA(ec,1)))
+ return (INDEFD)
+
+ for (i = 1; fitcoord < FITDATA(ec,i); i = i + 1)
+ ;
+
+ if (FITDATA(ec,i) == fitcoord)
+ return (double (i))
+
+ pixcoord = smw_c1trand (EC_LP(ec), double(i-.5))
+ dx = smw_c1trand (EC_LP(ec), double(i+.5)) - pixcoord
+ while (dx > DXMIN) {
+ dx = dx / 2
+ if (ec_fitpt (ec, EC_AP(ec), pixcoord) < fitcoord)
+ pixcoord = pixcoord - dx
+ else
+ pixcoord = pixcoord + dx
+ }
+ }
+
+ return (pixcoord)
+end
diff --git a/noao/onedspec/ecidentify/ecgdata.x b/noao/onedspec/ecidentify/ecgdata.x
new file mode 100644
index 00000000..1087d38c
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecgdata.x
@@ -0,0 +1,74 @@
+include <imhdr.h>
+include <imio.h>
+include <pkg/gtools.h>
+include <smw.h>
+include <units.h>
+include "ecidentify.h"
+
+# EC_GDATA -- Get image data.
+
+procedure ec_gdata (ec)
+
+pointer ec # ID pointer
+
+int i, j
+pointer im, mw, sh, sp, str1, str2
+
+double smw_c1trand()
+pointer immap(), smw_openim(), smw_sctran()
+errchk immap, smw_openim, shdr_open
+
+begin
+ # Map the image.
+ im = immap (Memc[EC_IMAGE(ec)], READ_ONLY, 0)
+
+ # Free previous data
+ do i = 1, EC_NLINES(ec)
+ call shdr_close (SH(ec,i))
+ call mfree (EC_SHS(ec), TY_POINTER)
+ call mfree (EC_PIXDATA(ec), TY_DOUBLE)
+
+ # Set MWCS
+ mw = smw_openim (im)
+ EC_LP(ec) = smw_sctran (mw, "logical", "physical", 1)
+ EC_PL(ec) = smw_sctran (mw, "physical", "logical", 1)
+
+ # Allocate new vectors.
+ EC_NCOLS(ec) = IM_LEN(im, 1)
+ EC_NLINES(ec) = IM_LEN(im, 2)
+ call calloc (EC_SHS(ec), EC_NLINES(ec), TY_POINTER)
+ call malloc (EC_PIXDATA(ec), EC_NCOLS(ec)*EC_NLINES(ec), TY_DOUBLE)
+
+ # Set the coordinates.
+ sh = NULL
+ do j = 1, EC_NLINES(ec) {
+ call shdr_open (im, mw, j, 1, INDEFI, SHDATA, sh)
+ if (EC_UN(ec) != NULL)
+ iferr (call shdr_units (sh, UN_UNITS(EC_UN(ec))))
+ ;
+ if (j != EC_NLINES(ec))
+ call shdr_copy (sh, SH(ec,j), NO)
+ else
+ SH(ec,j) = sh
+ call ec_gline (ec, j)
+ do i = 1, EC_NPTS(ec)
+ PIXDATA(ec,i) = smw_c1trand (EC_LP(ec), double(i))
+ }
+ EC_LINE(ec) = 1
+ call ec_gline (ec, EC_LINE(ec))
+ EC_AP(ec) = APS(ec,EC_LINE(ec))
+ EC_ORDER(ec) = ORDERS(ec,EC_LINE(ec))
+
+ # Set graph title.
+ call smark (sp)
+ call salloc (str1, SZ_LINE, TY_CHAR)
+ call salloc (str2, SZ_LINE, TY_CHAR)
+
+ call sprintf (Memc[str1], SZ_LINE, "ecidentify %s: %s")
+ call pargstr (Memc[EC_IMAGE(ec)])
+ call pargstr (IM_TITLE(im))
+ call gt_sets (EC_GT(ec), GTTITLE, Memc[str1])
+
+ call imunmap (im)
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecgetim.x b/noao/onedspec/ecidentify/ecgetim.x
new file mode 100644
index 00000000..cbcb244e
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecgetim.x
@@ -0,0 +1,17 @@
+# EC_GETIM -- Get next image name with standard image extensions removed.
+
+int procedure ec_getim (list, image, maxchar)
+
+int list # Image list
+char image[maxchar] # Image name
+int maxchar # Maximum number of chars in image name
+
+int stat, imtgetim()
+
+begin
+ stat = imtgetim (list, image, maxchar)
+ if (stat != EOF)
+ call xt_imroot (image, image, maxchar)
+
+ return (stat)
+end
diff --git a/noao/onedspec/ecidentify/ecgline.x b/noao/onedspec/ecidentify/ecgline.x
new file mode 100644
index 00000000..7d3f9e16
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecgline.x
@@ -0,0 +1,20 @@
+include <smw.h>
+include "ecidentify.h"
+
+# EC_GLINE -- Get line of data.
+
+procedure ec_gline (ec, line)
+
+pointer ec # EC pointer
+int line # Image line
+
+begin
+ if (IS_INDEFI(line))
+ return
+
+ EC_SH(ec) = SH(ec,line)
+ EC_NPTS(ec) = SN(EC_SH(ec))
+ EC_IMLINE(ec) = SY(EC_SH(ec))
+ EC_PIXLINE(ec) = EC_PIXDATA(ec) + (line - 1) * EC_NCOLS(ec)
+ EC_FITLINE(ec) = EC_FITDATA(ec) + (line - 1) * EC_NCOLS(ec)
+end
diff --git a/noao/onedspec/ecidentify/ecgraph.x b/noao/onedspec/ecidentify/ecgraph.x
new file mode 100644
index 00000000..9eaeaa5f
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecgraph.x
@@ -0,0 +1,155 @@
+include <gset.h>
+include <pkg/gtools.h>
+include "ecidentify.h"
+
+# EC_GRAPH -- Graph image vector in which features are to be ecentified.
+
+procedure ec_graph (ec, gtype)
+
+pointer ec # ID pointer
+int gtype # Graph type
+
+begin
+ switch (gtype) {
+ case 1:
+ if (IS_INDEFI (EC_AP(ec)))
+ call ec_graph3(ec)
+ else
+ call ec_graph1 (ec)
+ case 2:
+ call ec_graph2 (ec)
+ default:
+ call ec_graph1 (ec)
+ }
+end
+
+
+procedure ec_graph1 (ec)
+
+pointer ec # ID pointer
+
+int i
+real xmin, xmax, ymin, ymax, dy
+pointer sp, str, x, y
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (x, EC_NPTS(ec), TY_REAL)
+ y = EC_IMLINE(ec)
+
+ call sprintf (Memc[str], SZ_LINE,
+ "Aperture %d, Image line %d, Order %d")
+ call pargi (EC_AP(ec))
+ call pargi (EC_LINE(ec))
+ call pargi (EC_ORDER(ec))
+ call gt_sets (EC_GT(ec), GTPARAMS, Memc[str])
+ call achtdr (FITDATA(ec,1), Memr[x], EC_NPTS(ec))
+
+ call gclear (EC_GP(ec))
+ xmin = min (Memr[x], Memr[x+EC_NPTS(ec)-1])
+ xmax = max (Memr[x], Memr[x+EC_NPTS(ec)-1])
+ call alimr (Memr[y], EC_NPTS(ec), ymin, ymax)
+ dy = ymax - ymin
+ call gswind (EC_GP(ec), xmin, xmax, ymin - .2 * dy, ymax + .2 * dy)
+ call gt_swind (EC_GP(ec), EC_GT(ec))
+ call gt_labax (EC_GP(ec), EC_GT(ec))
+ call gt_plot (EC_GP(ec), EC_GT(ec), Memr[x], Memr[y], EC_NPTS(ec))
+
+ do i = 1, EC_NFEATURES(ec)
+ if (APN(ec,i) == EC_AP(ec))
+ call ec_mark (ec, i)
+
+ call sfree (sp)
+end
+
+
+# EC_GRAPH2 -- Make review graph for current feature.
+
+procedure ec_graph2 (ec)
+
+pointer ec # ID pointer
+
+int i, j, k
+real xmin, xmax, ymin, ymax, dy
+pointer sp, str, x, y
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (x, EC_NPTS(ec), TY_REAL)
+ y = EC_IMLINE(ec)
+
+ call sprintf (Memc[str], SZ_LINE, "Order %d")
+ call pargi (EC_AP(ec))
+ call gt_sets (EC_GT(ec), GTPARAMS, Memc[str])
+ call achtdr (FITDATA(ec,1), Memr[x], EC_NPTS(ec))
+
+ xmin = real (FIT(ec,EC_CURRENT(ec))) - EC_ZWIDTH(ec) / 2.
+ xmax = real (FIT(ec,EC_CURRENT(ec))) + EC_ZWIDTH(ec) / 2.
+
+ i = 0
+ do k = 1, EC_NPTS(ec) {
+ if ((Memr[x+k-1] < xmin) || (Memr[x+k-1] > xmax))
+ next
+ if (i == 0)
+ i = k
+ j = k
+ }
+ k = j - i + 1
+
+ call alimr (Memr[y+i-1], k, ymin, ymax)
+ dy = ymax - ymin
+
+ call gclear (EC_GP(ec))
+ call gswind (EC_GP(ec), xmin, xmax, ymin - .2 * dy, ymax + .2 * dy)
+ call gt_labax (EC_GP(ec), EC_GT(ec))
+ call gt_plot (EC_GP(ec), EC_GT(ec), Memr[x], Memr[y], EC_NPTS(ec))
+
+ do i = 1, EC_NFEATURES(ec)
+ if (APN(ec,i) == EC_AP(ec))
+ call ec_mark (ec, i)
+
+ call sfree (sp)
+end
+
+
+procedure ec_graph3 (ec)
+
+pointer ec # ID pointer
+
+int i, npts
+real xmin, xmax, ymin, ymax, dy
+pointer sp, str, x, y
+
+begin
+ npts = EC_NPTS(ec) * EC_NLINES(ec)
+
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (x, npts, TY_REAL)
+ y = EC_IMLINE(ec)
+
+ call sprintf (Memc[str], SZ_LINE, "All orders")
+ call gt_sets (EC_GT(ec), GTPARAMS, Memc[str])
+ call achtdr (Memd[EC_FITDATA(ec)], Memr[x], npts)
+
+ call gclear (EC_GP(ec))
+ xmin = min (Memr[x], Memr[x+npts-1])
+ xmax = max (Memr[x], Memr[x+npts-1])
+ call alimr (Memr[y], npts, ymin, ymax)
+ dy = ymax - ymin
+ call gswind (EC_GP(ec), xmin, xmax, ymin - .2 * dy, ymax + .2 * dy)
+ call gt_swind (EC_GP(ec), EC_GT(ec))
+ call gt_labax (EC_GP(ec), EC_GT(ec))
+ do i = 1, EC_NLINES(ec) {
+ call gt_plot (EC_GP(ec), EC_GT(ec), Memr[x], Memr[y], EC_NPTS(ec))
+ x = x + EC_NPTS(ec)
+ y = y + EC_NPTS(ec)
+ }
+
+ do i = 1, EC_NFEATURES(ec)
+ call ec_mark (ec, i)
+
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/ecidentify.h b/noao/onedspec/ecidentify/ecidentify.h
new file mode 100644
index 00000000..63e4c6bd
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecidentify.h
@@ -0,0 +1,94 @@
+# Task parameters
+
+define LEN_EC 52 # Length ID structure
+
+define EC_IMAGE Memi[$1] # Image name (pointer)
+define EC_MAXFEATURES Memi[$1+1] # Maximum number of features
+define EC_FTYPE Memi[$1+2] # Feature type
+define EC_MINSEP Memr[P2R($1+3)] # Minimum pixel separation
+define EC_MATCH Memr[P2R($1+4)] # Maximum matching separation
+define EC_FWIDTH Memr[P2R($1+5)] # Feature width in pixels
+define EC_CRADIUS Memr[P2R($1+6)] # Centering radius in pixels
+define EC_THRESHOLD Memr[P2R($1+7)] # Centering threshold
+define EC_ZWIDTH Memr[P2R($1+8)] # Zoom window width in fit units
+define EC_DATABASE Memi[$1+9] # Name of database (pointer)
+define EC_COORDLIST Memi[$1+10] # Name of coordinate list (pointer)
+define EC_LABELS Memi[$1+11] # Type of feature labels
+define EC_LOGFILES Memi[$1+12] # List of logfiles
+
+# Common image data
+
+define EC_NCOLS Memi[$1+13] # Number of columns
+define EC_NLINES Memi[$1+14] # Number of lines/apertures/orders
+define EC_SHS Memi[$1+15] # Pointer to SHDR pointers
+define EC_PIXDATA Memi[$1+16] # Pixel coordinates (pointer)
+define EC_FITDATA Memi[$1+17] # Fit coordinates (pointer)
+
+define EC_IMLINE Memi[$1+18] # Image data (pointer)
+define EC_PIXLINE Memi[$1+19] # Pixel coordinates (pointer)
+define EC_FITLINE Memi[$1+20] # Fit coordinates (pointer)
+define EC_NPTS Memi[$1+21] # Number of points
+
+define EC_SHIFT Memd[P2D($1+22)]# Wavelength shift
+
+# Features
+
+define EC_NFEATURES Memi[$1+24] # Number of features
+define EC_NALLOC Memi[$1+25] # Length of allocated feature arrays
+define EC_APNUM Memi[$1+26] # Aperture number (pointer)
+define EC_LINENUM Memi[$1+27] # Image line number (pointer)
+define EC_ORD Memi[$1+28] # Feature order number (pointer)
+define EC_PIX Memi[$1+29] # Feature pixel coordinates (pointer)
+define EC_FIT Memi[$1+30] # Feature fit coordinates (pointer)
+define EC_USER Memi[$1+31] # Feature user coordinates (pointer)
+define EC_FWIDTHS Memi[$1+32] # Feature width (pointer)
+define EC_FTYPES Memi[$1+33] # Feature type (pointer)
+
+# Current status
+
+define EC_CURRENT Memi[$1+34] # Current feature
+define EC_SH Memi[$1+35] # Current SHDR pointer
+define EC_AP Memi[$1+36] # Current aperture
+define EC_LINE Memi[$1+37] # Current line
+define EC_ORDER Memi[$1+38] # Current order
+
+# Pointers for other packages
+
+define EC_LP Memi[$1+39] # Logical to physical transformation
+define EC_PL Memi[$1+40] # Physical to logical transformation
+define EC_LL Memi[$1+41] # Linelist pointer
+define EC_ECF Memi[$1+42] # Curfit pointer
+define EC_GP Memi[$1+43] # GIO pointer
+define EC_GT Memi[$1+44] # Gtools pointer
+define EC_UN Memi[$1+45] # Units pointer
+
+# Flags
+
+define EC_NEWFEATURES Memi[$1+46] # Has feature list changed?
+define EC_NEWECF Memi[$1+47] # Has fitting function changed?
+define EC_NEWGRAPH Memi[$1+48] # Has graph changed?
+define EC_NEWDBENTRY Memi[$1+49] # Has database entry changed?
+define EC_REFIT Memi[$1+50] # Refit feature data?
+define EC_GTYPE Memi[$1+51] # Graph type
+
+# End of structure ----------------------------------------------------------
+
+define LABELS "|none|index|pixel|user|"
+define FTYPES "|emission|absorption|"
+
+define IMDATA Memr[EC_IMLINE($1)+$2-1]
+define PIXDATA Memd[EC_PIXLINE($1)+$2-1]
+define FITDATA Memd[EC_FITLINE($1)+$2-1]
+
+define SH Memi[EC_SHS($1)+$2-1]
+define APS AP(SH($1,$2))
+define ORDERS BEAM(SH($1,$2))
+
+define APN Memi[EC_APNUM($1)+$2-1]
+define LINE Memi[EC_LINENUM($1)+$2-1]
+define ORDER Memi[EC_ORD($1)+$2-1]
+define PIX Memd[EC_PIX($1)+$2-1]
+define FIT Memd[EC_FIT($1)+$2-1]
+define USER Memd[EC_USER($1)+$2-1]
+define FWIDTH Memr[EC_FWIDTHS($1)+$2-1]
+define FTYPE Memi[EC_FTYPES($1)+$2-1]
diff --git a/noao/onedspec/ecidentify/ecidentify.key b/noao/onedspec/ecidentify/ecidentify.key
new file mode 100644
index 00000000..c19698ef
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecidentify.key
@@ -0,0 +1,76 @@
+1. ECIDENTIFY CURSOR KEY SUMMARY
+
+? Help a Affect all features c Center feature(s)
+d Delete feature(s) f Fit dispersion g Fit zero point shift
+i Initialize j Go to previous order k Go to next order
+l Match coordinate list m Mark feature n Next feature
+o Go to specified order p Pan graph q Quit
+r Redraw graph s Shift feature t Reset position
+u Enter user coordinate w Window graph x Crosscorrelate peaks
+y Find peaks z Zoom graph . Nearest feature
++ Next feature - Previous feature I Interrupt
+
+
+2. ECIDENTIFY COLON COMMAND SUMMARY
+
+:show [file] :features [file] :coordlist [file]
+:cradius [value] :threshold [value] :database [file]
+:ftype [type] :fwidth [value] :image [image]
+:labels [type] :match [value] :maxfeatures [value]
+:minsep [value] :read [image] :write [image]
+:zwidth [value]
+
+
+3. ECIDENTIFY CURSOR KEYS
+
+? Clear the screen and print menu of options
+a Apply next (c)enter or (d)elete operation to (a)ll features
+c (C)enter the feature nearest the cursor
+d (D)elete the feature nearest the cursor
+f (F)it a function of pixel coordinate to the user coordinates
+g Fit a zero point shift to the user coordinates
+i (I)nitialize (delete features and coordinate fit)
+j Go to the previous order
+k Go to the next order
+l Match coordinates in the coordinate (l)ist to features in the data
+m (M)ark a new feature near the cursor
+n Move the cursor or zoom to the (n)ext feature (same as +)
+o Go to the specified (o)rder
+p (P)an to user defined window after (z)ooming on a feature
+q (Q)uit and continue with next image (also carriage return)
+r (R)edraw the graph
+s (S)hift the current feature to the position of the cursor
+t Reset (move) the position of a feature without centering
+u Enter a new (u)ser coordinate for the current feature
+w (W)indow the graph. Use '?' to window prompt for more help.
+x Crosscorrelate features with the data peaks and reregister
+y Automatically find "maxfeatures" strongest peaks and identify them
+z (Z)oom on the feature nearest the cursor
+. Move the cursor or zoom to the feature nearest the cursor (also space bar)
++ Move the cursor or zoom to the next feature
+- Move the cursor or zoom to the previous feature
+I Interrupt task immediately. Database information is not saved.
+
+
+4. ECIDENTIFY COLON COMMANDS
+
+The parameters are listed or set with the following commands which may be
+abbreviated. To list the value of a parameter type the command alone.
+
+:show file Show the values of all the parameters
+:features file Write feature list to file (default is STDOUT)
+
+:coordlist file Coordinate list file
+:cradius value Centering radius in pixels
+:threshold value Detection threshold for feature centering
+:database name Database for recording feature records
+:ftype value Feature type (emission or absorption)
+:fwidth value Feature width in pixels
+:image imagename Set a new image or show the current image
+:labels value Feature label type (none, index, pixel, or user)
+:match value Coordinate list matching distance
+:maxfeatures value Maximum number of features automatically found
+:minsep value Minimum separation allowed between features
+:read name Read a record from the database (name defaults to image)
+:write name Write a record to the database (name defaults to image)
+:zwidth value Zoom width in user units
diff --git a/noao/onedspec/ecidentify/ecidentify.x b/noao/onedspec/ecidentify/ecidentify.x
new file mode 100644
index 00000000..827568d1
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecidentify.x
@@ -0,0 +1,535 @@
+include <error.h>
+include <imhdr.h>
+include <gset.h>
+include <smw.h>
+include "ecidentify.h"
+
+define HELP "noao$onedspec/ecidentify/ecidentify.key"
+define PROMPT "ecidentify options"
+
+define PAN 1 # Pan graph
+define ZOOM 2 # Zoom graph
+
+# EC_IDENTIFY -- Identify echelle features in an image.
+# This is the basic interactive loop.
+
+procedure ec_identify (ec)
+
+pointer ec # EC pointer
+
+real wx, wy
+int wcs, key
+char cmd[SZ_LINE]
+
+char newimage[SZ_FNAME]
+int i, j, last, all, prfeature, nfeatures1, npeaks
+bool answer
+double pix, fit, user, shift, pix_shift, z_shift
+pointer peaks
+
+bool clgetb()
+int clgcur(), scan(), nscan(), find_peaks(), ec_next(), ec_previous()
+int ec_line()
+double ec_center(), ec_fittopix(), ec_fitpt(), ec_shift(), ec_rms()
+double ecf_getd()
+errchk ec_gdata(), ec_graph(), ec_dbread(), xt_mk1d(), ec_line()
+
+define newim_ 10
+define newkey_ 20
+define beep_ 99
+
+begin
+newim_ # Start here for each new image.
+
+ # Get the image data. Return if there is an error.
+ iferr (call ec_gdata (ec)) {
+ call erract (EA_WARN)
+ return
+ }
+
+ # Look for a database entry for the image.
+ iferr {
+ call ec_dbread (ec, Memc[EC_IMAGE(ec)], NO)
+ EC_NEWDBENTRY(ec) = NO
+ } then
+ if ((EC_NFEATURES(ec) > 0) || (EC_ECF(ec) != NULL))
+ EC_NEWDBENTRY(ec) = YES
+
+ # Set the coordinate array and the feature data.
+ iferr (call ec_fitdata (ec))
+ call erract (EA_WARN)
+ call ec_fitfeatures (ec)
+
+ # Begin with the first image line.
+ EC_LINE(ec) = 1
+ EC_AP(ec) = APS(ec,EC_LINE(ec))
+ EC_ORDER(ec) = ORDERS(ec,EC_LINE(ec))
+ call ec_gline (ec, EC_LINE(ec))
+
+ # Initialize.
+ EC_GTYPE(ec) = PAN
+ EC_REFIT(ec) = NO
+ EC_NEWFEATURES(ec) = NO
+ EC_NEWECF(ec) = NO
+ EC_CURRENT(ec) = 0
+ i = ec_next (ec, EC_CURRENT(ec))
+ last = EC_CURRENT(ec)
+ all = 0
+ newimage[1] = EOS
+ key = 'r'
+
+ repeat {
+ prfeature = YES
+ if (all != 0)
+ all = mod (all + 1, 3)
+
+ switch (key) {
+ case '?': # Page help
+ call gpagefile (EC_GP(ec), HELP, PROMPT)
+ case ':': # Execute colon commands
+ if (cmd[1] == '/')
+ call gt_colon (cmd, EC_GP(ec), EC_GT(ec), EC_NEWGRAPH(ec))
+ else
+ call ec_colon (ec, cmd, newimage, prfeature)
+ case ' ': # Go to the current feature
+ case '.': # Go to the nearest feature
+ if (EC_NFEATURES(ec) == 0)
+ goto beep_
+ call ec_nearest (ec, double (wx))
+ case '-': # Go to the previous feature
+ if (ec_previous (ec, EC_CURRENT(ec)) == EOF)
+ goto beep_
+ case '+', 'n': # Go to the next feature
+ if (ec_next (ec, EC_CURRENT(ec)) == EOF)
+ goto beep_
+ case 'a': # Set the all flag for the next key
+ all = 1
+ case 'c': # Center features on data
+ if (all != 0) {
+ call eprintf ("Recentering features ...\n")
+ for (i = 1; i <= EC_NFEATURES(ec); i = i + 1) {
+ call ec_gline (ec, LINE(ec,i))
+ call gseti (EC_GP(ec), G_PLTYPE, 0)
+ call ec_mark (ec, i)
+ call gseti (EC_GP(ec), G_PLTYPE, 1)
+ FWIDTH(ec,i) = EC_FWIDTH(ec)
+ PIX(ec,i) = ec_center (ec, PIX(ec,i), FWIDTH(ec,i),
+ FTYPE(ec,i))
+ if (!IS_INDEFD (PIX(ec,i))) {
+ FIT(ec,i) = ec_fitpt (ec, APN(ec,i), PIX(ec,i))
+ call ec_mark (ec, i)
+ } else {
+ call ec_delete (ec, i)
+ i = i - 1
+ }
+ }
+ call ec_gline (ec, EC_LINE(ec))
+ EC_NEWFEATURES(ec) = YES
+ } else {
+ if (EC_NFEATURES(ec) == 0)
+ goto beep_
+
+ call ec_nearest (ec, double (wx))
+ pix = PIX(ec,EC_CURRENT(ec))
+ pix = ec_center (ec, pix, EC_FWIDTH(ec),
+ FTYPE(ec,EC_CURRENT(ec)))
+ if (!IS_INDEFD (pix)) {
+ call gseti (EC_GP(ec), G_PLTYPE, 0)
+ call ec_mark (ec, EC_CURRENT(ec))
+ PIX(ec,EC_CURRENT(ec)) = pix
+ FWIDTH(ec,EC_CURRENT(ec)) = EC_FWIDTH(ec)
+ FIT(ec,EC_CURRENT(ec)) =
+ ec_fitpt (ec, APN(ec,EC_CURRENT(ec)), pix)
+ call gseti (EC_GP(ec), G_PLTYPE, 1)
+ call ec_mark (ec, EC_CURRENT(ec))
+ EC_NEWFEATURES(ec) = YES
+ } else {
+ call eprintf ("Centering failed\n")
+ prfeature = NO
+ }
+ }
+ case 'd': # Delete features
+ if (all != 0) {
+ EC_NFEATURES(ec) = 0
+ EC_CURRENT(ec) = 0
+ EC_NEWFEATURES(ec) = YES
+ EC_NEWGRAPH(ec) = YES
+ } else {
+ if (EC_NFEATURES(ec) == 0)
+ goto beep_
+
+ call ec_nearest (ec, double (wx))
+ call gseti (EC_GP(ec), G_PLTYPE, 0)
+ call ec_mark (ec, EC_CURRENT(ec))
+ call gseti (EC_GP(ec), G_PLTYPE, 1)
+ call ec_delete (ec, EC_CURRENT(ec))
+ call ec_nearest (ec, double (wx))
+ last = 0
+ }
+ case 'f': # Fit dispersion function
+ iferr (call ec_dofit (ec, YES, NO)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ goto beep_
+ }
+ case 'g': # Fit shift
+ call ec_doshift (ec, YES)
+ prfeature = NO
+ case 'i': # Initialize
+ call dgsfree (EC_ECF(ec))
+ call ecf_setd ("shift", 0.D0)
+ EC_NEWECF(ec) = YES
+ EC_NFEATURES(ec) = 0
+ EC_CURRENT(ec) = 0
+ EC_NEWFEATURES(ec) = YES
+ EC_NEWGRAPH(ec) = YES
+ case 'j': # Go to the previous order
+ EC_LINE(ec) =
+ mod (EC_LINE(ec)+EC_NLINES(ec)-2, EC_NLINES(ec)) + 1
+ EC_AP(ec) = APS(ec,EC_LINE(ec))
+ EC_ORDER(ec) = ORDERS(ec,EC_LINE(ec))
+ call ec_gline (ec, EC_LINE(ec))
+ EC_NEWGRAPH(ec) = YES
+ EC_CURRENT(ec) = 0
+ i = ec_next (ec, EC_CURRENT(ec))
+ case 'k': # Go to the next order
+ EC_LINE(ec) = mod (EC_LINE(ec), EC_NLINES(ec)) + 1
+ EC_AP(ec) = APS(ec,EC_LINE(ec))
+ EC_ORDER(ec) = ORDERS(ec,EC_LINE(ec))
+ call ec_gline (ec, EC_LINE(ec))
+ EC_NEWGRAPH(ec) = YES
+ EC_CURRENT(ec) = 0
+ i = ec_next (ec, EC_CURRENT(ec))
+ case 'l': # Find features using a line list
+ if (EC_ECF(ec) == NULL) {
+ call eprintf ("Doing initial fit ...\n")
+ iferr (call ec_dofit (ec, NO, NO)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ goto beep_
+ }
+ if (EC_NEWECF(ec) == YES) {
+ iferr (call ec_fitdata (ec)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ }
+ call ec_fitfeatures (ec)
+ EC_NEWECF(ec) = NO
+ }
+ }
+
+ call eprintf ("Searching coordinate list ...\n")
+ call ec_linelist (ec)
+ EC_CURRENT(ec) = 0
+ i = ec_next (ec, EC_CURRENT(ec))
+ if (EC_NEWFEATURES(ec) == YES)
+ EC_NEWGRAPH(ec) = YES
+ case 'm': # Mark a new feature
+ fit = wx
+ pix = ec_fittopix (ec, fit)
+ pix = ec_center (ec, pix, EC_FWIDTH(ec), EC_FTYPE(ec))
+ if (IS_INDEFD (pix))
+ goto beep_
+ fit = ec_fitpt (ec, EC_AP(ec), pix)
+ user = fit
+ call ec_newfeature (ec, EC_AP(ec), pix, fit, user,
+ EC_FWIDTH(ec), EC_FTYPE(ec))
+ USER(ec,EC_CURRENT(ec)) = INDEFD
+ call ec_match (ec, FIT(ec,EC_CURRENT(ec)),
+ USER(ec,EC_CURRENT(ec)))
+ call ec_mark (ec, EC_CURRENT(ec))
+ call printf ("%3d %10.2f %10.8g (%10.8g): ")
+ call pargi (APN(ec,EC_CURRENT(ec)))
+ call pargd (PIX(ec,EC_CURRENT(ec)))
+ call pargd (FIT(ec,EC_CURRENT(ec)))
+ call pargd (USER(ec,EC_CURRENT(ec)))
+ call flush (STDOUT)
+ if (scan() != EOF) {
+ call gargd (user)
+ if (nscan() == 1) {
+ USER(ec,EC_CURRENT(ec)) = user
+ call ec_match (ec, user, USER(ec,EC_CURRENT(ec)))
+ }
+ }
+ case 'o': # Go to a specified order
+ call printf ("Aperture (%d): ")
+ call pargi (EC_AP(ec))
+ call flush (STDOUT)
+ if (scan() != EOF) {
+ call gargi (j)
+ if (nscan() == 1) {
+ if (j != EC_AP(ec)) {
+ iferr {
+ i = ec_line (ec, j)
+ call ec_gline (ec, i)
+ EC_LINE(ec) = i
+ EC_AP(ec) = j
+ EC_ORDER(ec) = ORDERS(ec,i)
+ EC_NEWGRAPH(ec) = YES
+ EC_CURRENT(ec) = 0
+ i = ec_next (ec, EC_CURRENT(ec))
+ } then
+ goto beep_
+ }
+ }
+ }
+ case 'p': # Go to pan graph mode
+ if (EC_GTYPE(ec) == PAN)
+ goto beep_
+
+ EC_GTYPE(ec) = PAN
+ EC_NEWGRAPH(ec) = YES
+ case 'q': # Quit
+ break
+ case 'r': # Redraw the current graph
+ EC_NEWGRAPH(ec) = YES
+ case 's', 'x': # Shift or cross correlate features
+ # Get coordinate shift.
+ switch (key) {
+ case 's':
+ call printf ("User coordinate (%10.8g): ")
+ call pargr (wx)
+ call flush (STDOUT)
+ if (scan() != EOF) {
+ call gargd (user)
+ if (nscan() == 1)
+ shift = (wx - user) * EC_ORDER(ec)
+ } else
+ shift = 0.
+ case 'x':
+ if (EC_NFEATURES(ec) > 5) {
+ call eprintf (
+ "Cross correlating features with peaks ...\n")
+ shift = ec_shift (ec)
+ } else
+ goto beep_
+ }
+
+ EC_NEWFEATURES(ec) = YES
+ EC_NEWECF(ec) = YES
+ EC_NEWGRAPH(ec) = YES
+ prfeature = NO
+
+ if (EC_NFEATURES(ec) < 1) {
+ call printf ("User coordinate shift=%5f")
+ call pargd (shift / EC_ORDER(ec))
+ call ecf_setd ("shift", ecf_getd ("shift") - shift)
+ goto newkey_
+ }
+
+ # Recenter features.
+ call eprintf ("Recentering features ...\n")
+ pix_shift = 0.
+ z_shift = 0.
+ nfeatures1 = EC_NFEATURES(ec)
+
+ j = 0.
+ do i = 1, EC_NFEATURES(ec) {
+ call ec_gline (ec, LINE(ec,i))
+ pix = ec_fittopix (ec, FIT(ec,i) + shift/ORDER(ec,i))
+ pix = ec_center (ec, pix, FWIDTH(ec,i), FTYPE(ec,i))
+ if (IS_INDEFD (pix)) {
+ if (EC_CURRENT(ec) == i)
+ EC_CURRENT(ec) = 0
+ next
+ }
+ fit = ec_fitpt (ec, APN(ec,i), pix)
+
+ pix_shift = pix_shift + pix - PIX(ec,i)
+ if (FIT(ec,i) != 0.)
+ z_shift = z_shift + (fit - FIT(ec,i)) / FIT(ec,i)
+
+ j = j + 1
+ APN(ec,j) = APN(ec,i)
+ LINE(ec,j) = LINE(ec,i)
+ ORDER(ec,j) = ORDER(ec,i)
+ PIX(ec,j) = pix
+ FIT(ec,j) = FIT(ec,i)
+ USER(ec,j) = USER(ec,i)
+ FWIDTH(ec,j) = FWIDTH(ec,i)
+ FTYPE(ec,j) = FTYPE(ec,i)
+ if (EC_CURRENT(ec) == i)
+ EC_CURRENT(ec) = j
+ }
+ call ec_gline (ec, EC_LINE(ec))
+ EC_NFEATURES(ec) = j
+ if (EC_CURRENT(ec) == 0)
+ i = ec_next (ec, EC_CURRENT(ec))
+
+ if (EC_NFEATURES(ec) < 1) {
+ call printf ("User coordinate shift=%5f")
+ call pargd (shift / EC_ORDER(ec))
+ call printf (", No features found during recentering")
+ call ecf_setd ("shift", ecf_getd ("shift") - shift)
+ goto newkey_
+ }
+
+ # Adjust shift.
+ pix = ecf_getd ("shift")
+ call ec_doshift (ec, NO)
+ call ec_fitfeatures (ec)
+
+ # Print results.
+ call printf ("Recentered=%d/%d")
+ call pargi (EC_NFEATURES(ec))
+ call pargi (nfeatures1)
+ call printf (
+ ", pixel shift=%.2f, user shift=%5f, z=%7.3g, rms=%5g")
+ call pargd (pix_shift / EC_NFEATURES(ec))
+ call pargd ((pix - ecf_getd ("shift")) / EC_ORDER(ec))
+ call pargd (z_shift / EC_NFEATURES(ec))
+ call pargd (ec_rms(ec))
+ case 't': # Move current feature
+ if (EC_CURRENT(ec) == 0)
+ goto beep_
+
+ call gseti (EC_GP(ec), G_PLTYPE, 0)
+ call ec_mark (ec, EC_CURRENT(ec))
+ pix = ec_fittopix (ec, double (wx))
+ PIX(ec,EC_CURRENT(ec)) = pix
+ FIT(ec,EC_CURRENT(ec)) =
+ ec_fitpt (ec, APN(ec,EC_CURRENT(ec)), pix)
+ call gseti (EC_GP(ec), G_PLTYPE, 1)
+ call ec_mark (ec, EC_CURRENT(ec))
+ EC_NEWFEATURES(ec) = YES
+ case 'u': # Set uesr coordinate value
+ if (EC_NFEATURES(ec) == 0)
+ goto beep_
+
+ call printf ("%3d %10.2f %10.8g (%10.8g): ")
+ call pargi (APN(ec,EC_CURRENT(ec)))
+ call pargd (PIX(ec,EC_CURRENT(ec)))
+ call pargd (FIT(ec,EC_CURRENT(ec)))
+ call pargd (USER(ec,EC_CURRENT(ec)))
+ call flush (STDOUT)
+ if (scan() != EOF) {
+ call gargd (user)
+ if (nscan() == 1) {
+ USER(ec,EC_CURRENT(ec)) = user
+ EC_NEWFEATURES(ec) = YES
+ }
+ }
+ case 'w': # Window graph
+ call gt_window (EC_GT(ec), EC_GP(ec), "cursor", EC_NEWGRAPH(ec))
+ case 'y': # Find peaks in order
+ call malloc (peaks, EC_NPTS(ec), TY_REAL)
+ npeaks = find_peaks (IMDATA(ec,1), Memr[peaks],
+ EC_NPTS(ec), 0., int (EC_MINSEP(ec)), 0, EC_MAXFEATURES(ec),
+ 0., false)
+ for (j = 1; j <= EC_NFEATURES(ec); j = j + 1) {
+ for (i = 1; i <= npeaks; i = i + 1) {
+ if (!IS_INDEF(pix)) {
+ pix = Memr[peaks + i - 1]
+ if (abs (pix - PIX(ec,j)) < EC_MINSEP(ec))
+ Memr[peaks + i - 1] = INDEF
+ }
+ }
+ }
+ for (i = 1; i <= npeaks; i = i + 1) {
+ pix = Memr[peaks+i-1]
+ pix = ec_center (ec, pix, EC_FWIDTH(ec), EC_FTYPE(ec))
+ if (IS_INDEFD (pix))
+ next
+ fit = ec_fitpt (ec, EC_AP(ec), pix)
+ user = INDEFD
+ call ec_match (ec, fit, user)
+ call ec_newfeature (ec, EC_AP(ec), pix, fit, user,
+ EC_FWIDTH(ec), EC_FTYPE(ec))
+ call ec_mark (ec, EC_CURRENT(ec))
+ }
+ call mfree (peaks, TY_REAL)
+ case 'z': # Go to zoom mode
+ if (EC_CURRENT(ec) == 0)
+ goto beep_
+
+ if (EC_GTYPE(ec) == PAN)
+ EC_NEWGRAPH(ec) = YES
+ EC_GTYPE(ec) = ZOOM
+ call ec_nearest (ec, double (wx))
+ case 'I': # Interrupt
+ call fatal (0, "Interrupt")
+ default: # Beep
+beep_ call printf ("\007\n")
+ }
+
+newkey_
+ # Set database update flag if there has been a change.
+ if ((EC_NEWFEATURES(ec) == YES) || (EC_NEWECF(ec) == YES))
+ EC_NEWDBENTRY(ec) = YES
+
+ # Exit loop and then start new image.
+ if (newimage[1] != EOS)
+ break
+
+ # Refit the dispersion function if needed.
+ if (EC_REFIT(ec) == YES) {
+ iferr (call ec_dofit (ec, NO, NO)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ }
+ EC_REFIT(ec) = NO
+ }
+
+ # Recompute the coordinate information.
+ if (EC_NEWECF(ec) == YES) {
+ iferr (call ec_fitdata (ec)) {
+ call erract (EA_WARN)
+ prfeature = NO
+ }
+ call ec_fitfeatures (ec)
+ EC_NEWECF(ec) = NO
+ }
+
+ # Redraw new feature in zoom mode.
+ if ((EC_GTYPE(ec) == ZOOM) && (last != EC_CURRENT(ec)))
+ EC_NEWGRAPH(ec) = YES
+
+ # Redraw graph.
+ if (EC_NEWGRAPH(ec) == YES) {
+ call ec_graph (ec, EC_GTYPE(ec))
+ EC_NEWGRAPH(ec) = NO
+ }
+
+ # Set cursor and print current feature on status (unless canceled).
+ if (EC_CURRENT(ec) > 0) {
+ call gscur (EC_GP(ec), real (FIT(ec,EC_CURRENT(ec))), wy)
+ if (prfeature == YES) {
+ call printf ("%d %10.2f %10.8g %10.8g\n")
+ call pargi (APN(ec,EC_CURRENT(ec)))
+ call pargd (PIX(ec,EC_CURRENT(ec)))
+ call pargd (FIT(ec,EC_CURRENT(ec)))
+ call pargd (USER(ec,EC_CURRENT(ec)))
+ }
+ }
+
+ last = EC_CURRENT(ec)
+ } until (clgcur ("cursor", wx, wy, wcs, key, cmd, SZ_LINE) == EOF)
+
+ # Warn user that feature data is newer than database entry.
+ if (EC_NEWDBENTRY(ec) == YES) {
+ answer = true
+ if (!clgetb ("autowrite")) {
+ call printf ("Write feature data to the database (yes)? ")
+ call flush (STDOUT)
+ if (scan() != EOF)
+ call gargb (answer)
+ }
+ if (answer)
+ call ec_dbwrite (ec, Memc[EC_IMAGE(ec)], NO)
+ }
+
+ call flush (STDOUT)
+
+ # Free image data and MWCS
+ call mfree (EC_PIXDATA(ec), TY_DOUBLE)
+ call mfree (EC_FITDATA(ec), TY_DOUBLE)
+ call smw_close (MW(EC_SH(ec)))
+ do i = 1, EC_NLINES(ec)
+ MW(SH(ec,i)) = NULL
+
+ # If a new image was specified by a colon command don't return.
+ if (newimage[1] != EOS) {
+ call strcpy (newimage, Memc[EC_IMAGE(ec)], SZ_FNAME)
+ goto newim_
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecinit.x b/noao/onedspec/ecidentify/ecinit.x
new file mode 100644
index 00000000..8b3b7b62
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecinit.x
@@ -0,0 +1,64 @@
+include <gset.h>
+include "ecidentify.h"
+
+# EC_INIT -- Allocate and initialize the identify structure.
+
+procedure ec_init (ec)
+
+pointer ec # ID pointer
+
+begin
+ call calloc (ec, LEN_EC, TY_STRUCT)
+
+ EC_NALLOC(ec) = 20
+ EC_NFEATURES(ec) = 0
+ EC_CURRENT(ec) = 0
+ EC_NLINES(ec) = 0
+ EC_LL(ec) = NULL
+ EC_ECF(ec) = NULL
+ EC_LABELS(ec) = 1
+
+ call malloc (EC_IMAGE(ec), SZ_FNAME, TY_CHAR)
+ call malloc (EC_DATABASE(ec), SZ_FNAME, TY_CHAR)
+ call malloc (EC_COORDLIST(ec), SZ_FNAME, TY_CHAR)
+
+ call malloc (EC_APNUM(ec), EC_NALLOC(ec), TY_INT)
+ call malloc (EC_LINENUM(ec), EC_NALLOC(ec), TY_INT)
+ call malloc (EC_PIX(ec), EC_NALLOC(ec), TY_DOUBLE)
+ call malloc (EC_ORD(ec), EC_NALLOC(ec), TY_INT)
+ call malloc (EC_FIT(ec), EC_NALLOC(ec), TY_DOUBLE)
+ call malloc (EC_USER(ec), EC_NALLOC(ec), TY_DOUBLE)
+ call malloc (EC_FWIDTHS(ec), EC_NALLOC(ec), TY_REAL)
+ call malloc (EC_FTYPES(ec), EC_NALLOC(ec), TY_INT)
+end
+
+
+# EC_FREE -- Free identify structure.
+
+procedure ec_free (ec)
+
+pointer ec # ID pointer
+int i
+
+begin
+ if (EC_UN(ec) != NULL)
+ call un_close (EC_UN(ec))
+ do i = 1, EC_NLINES(ec)
+ call shdr_close (SH(ec,i))
+ call mfree (EC_SHS(ec), TY_POINTER)
+
+ call mfree (EC_IMAGE(ec), TY_CHAR)
+ call mfree (EC_DATABASE(ec), TY_CHAR)
+ call mfree (EC_COORDLIST(ec), TY_CHAR)
+
+ call mfree (EC_APNUM(ec), TY_INT)
+ call mfree (EC_LINENUM(ec), TY_INT)
+ call mfree (EC_PIX(ec), TY_DOUBLE)
+ call mfree (EC_ORD(ec), TY_INT)
+ call mfree (EC_FIT(ec), TY_DOUBLE)
+ call mfree (EC_USER(ec), TY_DOUBLE)
+ call mfree (EC_FWIDTHS(ec), TY_REAL)
+ call mfree (EC_FTYPES(ec), TY_INT)
+
+ call mfree (ec, TY_STRUCT)
+end
diff --git a/noao/onedspec/ecidentify/ecline.x b/noao/onedspec/ecidentify/ecline.x
new file mode 100644
index 00000000..63e55072
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecline.x
@@ -0,0 +1,22 @@
+include <smw.h>
+include "ecidentify.h"
+
+# EC_LINE -- Get line corresponding to aperture.
+
+int procedure ec_line (ec, ap)
+
+pointer ec # EC pointer
+int ap # Aperture
+
+int i
+
+begin
+ if (IS_INDEFI (ap))
+ return (INDEFI)
+
+ do i = 1, EC_NLINES(ec)
+ if (ap == APS(ec,i))
+ return (i)
+
+ call error (0, "Image line for aperture number not found")
+end
diff --git a/noao/onedspec/ecidentify/eclinelist.x b/noao/onedspec/ecidentify/eclinelist.x
new file mode 100644
index 00000000..6653dd4b
--- /dev/null
+++ b/noao/onedspec/ecidentify/eclinelist.x
@@ -0,0 +1,281 @@
+include <error.h>
+include <mach.h>
+include <smw.h>
+include <units.h>
+include "ecidentify.h"
+
+# EC_MAPLL -- Read the line list into memory.
+
+procedure ec_mapll (ec)
+
+pointer ec # Echelle pointer
+
+int fd, nalloc, nlines, open(), fscan(), nscan()
+double value, lastval
+pointer ec_ll
+pointer sp, str, units, un_open()
+bool streq()
+errchk open, fscan, malloc, realloc, un_open
+
+begin
+ EC_LL(ec) = NULL
+
+ call xt_stripwhite (Memc[EC_COORDLIST(ec)])
+ if (Memc[EC_COORDLIST(ec)] == EOS)
+ return
+ iferr (fd = open (Memc[EC_COORDLIST(ec)], READ_ONLY, TEXT_FILE))
+ return
+
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (units, SZ_LINE, TY_CHAR)
+ call strcpy ("Angstroms", Memc[units], SZ_LINE)
+
+ lastval = -MAX_DOUBLE
+ nalloc = 0
+ nlines = 0
+ while (fscan (fd) != EOF) {
+ call gargwrd (Memc[str], SZ_LINE)
+ if (nscan() != 1)
+ next
+ if (Memc[str] == '#') {
+ call gargwrd (Memc[str], SZ_LINE)
+ call strlwr (Memc[str])
+ if (streq (Memc[str], "units")) {
+ call gargstr (Memc[units], SZ_LINE)
+ call xt_stripwhite (Memc[units])
+ }
+ next
+ }
+ call reset_scan ()
+
+ call gargd (value)
+ if (nscan() != 1)
+ next
+
+ if (nalloc == 0) {
+ nalloc = 100
+ call malloc (ec_ll, nalloc, TY_DOUBLE)
+ } else if (nlines == nalloc) {
+ nalloc = nalloc + 100
+ call realloc (ec_ll, nalloc, TY_DOUBLE)
+ }
+
+ if (value < lastval) {
+ call close (fd)
+ call mfree (ec_ll, TY_DOUBLE)
+ call error (0, "Line list not sorted in increasing order")
+ }
+
+ Memd[ec_ll+nlines] = value
+ nlines = nlines + 1
+ }
+ call close (fd)
+
+ if (nlines > 0) {
+ call realloc (ec_ll, nlines + 1, TY_DOUBLE)
+ Memd[ec_ll+nlines] = INDEFD
+ EC_LL(ec) = ec_ll
+
+ if (EC_UN(ec) == NULL && Memc[units] != EOS)
+ EC_UN(ec) = un_open (Memc[units])
+ call ec_unitsll (ec, Memc[units])
+ }
+
+ call sfree (sp)
+end
+
+
+# EC_UNMAPLL -- Unmap the linelist.
+
+procedure ec_unmapll (ec)
+
+pointer ec # Line list pointer
+
+begin
+ call mfree (EC_LL(ec), TY_DOUBLE)
+end
+
+
+# EC_UNITSLL -- Change the line list units from the input units to the
+# units given by EC_UN. This may involve reversing the order of the list.
+
+procedure ec_unitsll (ec, units)
+
+pointer ec # Identify structure
+char units[ARB] # Input units
+
+int i, nll
+double value
+pointer un, ll, llend, un_open()
+bool un_compare()
+errchk un_open
+
+begin
+ if (EC_LL(ec) == NULL)
+ return
+ if (IS_INDEFD(Memd[EC_LL(ec)]))
+ return
+ if (units[1] == EOS || EC_UN(ec) == NULL)
+ return
+ if (UN_CLASS(EC_UN(ec)) == UN_UNKNOWN)
+ return
+
+ un = un_open (units)
+ if (un_compare (un, EC_UN(ec))) {
+ call un_close (un)
+ return
+ }
+
+ ll = EC_LL(ec)
+ do i = 0, ARB
+ if (IS_INDEFD(Memd[ll+i])) {
+ nll = i
+ break
+ }
+ call un_ctrand (un, EC_UN(ec), Memd[ll], Memd[ll], nll)
+ call un_close (un)
+
+ if (Memd[ll] > Memd[ll+nll-1]) {
+ llend = ll + nll - 1
+ do i = 0, nll / 2 - 1 {
+ value = Memd[ll+i]
+ Memd[ll+i] = Memd[llend-i]
+ Memd[llend-i] = value
+ }
+ }
+end
+
+
+
+# EC_MATCH -- Match current feature against a line list.
+
+procedure ec_match (ec, in, out)
+
+pointer ec # Echelle pointer
+double in # Coordinate to be matched
+double out # Matched coordinate
+
+double match, alpha, delta, delta1, delta2, out1
+pointer ll
+
+begin
+ if (EC_LL(ec) == NULL) {
+ out = in
+ return
+ }
+
+ match = EC_MATCH(ec)
+ alpha = 1.25
+ delta1 = MAX_REAL
+
+ # Find nearest match.
+ for (ll=EC_LL(ec); !IS_INDEFD(Memd[ll]); ll = ll + 1) {
+ delta = abs (in - Memd[ll])
+ if (delta < delta1) {
+ delta2 = delta1
+ delta1 = delta
+ if (delta1 <= match)
+ out1 = Memd[ll]
+ }
+ }
+
+ # Only return match if no other candidate is also possible.
+ if (delta1 > match)
+ return
+ if (delta2 < alpha * delta1)
+ return
+
+ out = out1
+end
+
+# EC_LINELIST -- Add features from a line list.
+
+procedure ec_linelist (ec)
+
+pointer ec # Echelle pointer
+
+int i, line, ap, nfound, nextpix
+double pix, fit, user, peak, minval, match, fit1, fit2
+pointer sp, aps, pixes, fits, users, peaks, ll
+
+double ec_center(), ec_fittopix(), ec_fitpt(), ec_peak()
+
+begin
+ if (EC_LL(ec) == NULL)
+ return
+
+ call smark (sp)
+ call salloc (aps, EC_MAXFEATURES(ec), TY_INT)
+ call salloc (pixes, EC_MAXFEATURES(ec), TY_DOUBLE)
+ call salloc (fits, EC_MAXFEATURES(ec), TY_DOUBLE)
+ call salloc (users, EC_MAXFEATURES(ec), TY_DOUBLE)
+ call salloc (peaks, EC_MAXFEATURES(ec), TY_DOUBLE)
+
+ nfound = 0
+ minval = MAX_REAL
+
+ do line = 1, EC_NLINES(ec) {
+ call ec_gline (ec, line)
+ ap = APS(ec,line)
+ fit1 = min (FITDATA(ec,1), FITDATA(ec,EC_NPTS(ec)))
+ fit2 = max (FITDATA(ec,1), FITDATA(ec,EC_NPTS(ec)))
+ for (ll=EC_LL(ec); !IS_INDEFD(Memd[ll]); ll = ll + 1) {
+ user = Memd[ll]
+ if (user < fit1)
+ next
+ if (user > fit2)
+ break
+
+ pix = ec_center (ec, ec_fittopix (ec, user), EC_FWIDTH(ec),
+ EC_FTYPE(ec))
+ if (!IS_INDEFD(pix)) {
+ fit = ec_fitpt (ec, ap, pix)
+ match = abs (fit - user)
+ if (match > EC_MATCH(ec))
+ next
+
+ peak = abs (ec_peak (ec, pix))
+ if (nfound < EC_MAXFEATURES(ec)) {
+ nfound = nfound + 1
+ if (peak < minval) {
+ nextpix = nfound
+ minval = peak
+ }
+ Memi[aps+nfound-1] = ap
+ Memd[pixes+nfound-1] = pix
+ Memd[fits+nfound-1] = fit
+ Memd[users+nfound-1] = user
+ Memd[peaks+nfound-1] = peak
+ } else if (peak > minval) {
+ Memi[aps+nextpix-1] = ap
+ Memd[pixes+nextpix-1] = pix
+ Memd[fits+nextpix-1] = fit
+ Memd[users+nextpix-1] = user
+ Memd[peaks+nextpix-1] = peak
+
+ minval = MAX_REAL
+ do i = 1, nfound {
+ peak = Memd[peaks+i-1]
+ if (peak < minval) {
+ nextpix = i
+ minval = peak
+ }
+ }
+ }
+ }
+ }
+ }
+ call ec_gline (ec, EC_LINE(ec))
+
+ do i = 1, nfound {
+ ap = Memi[aps+i-1]
+ pix = Memd[pixes+i-1]
+ fit = Memd[fits+i-1]
+ user = Memd[users+i-1]
+ call ec_newfeature (ec, ap, pix, fit, user, EC_FWIDTH(ec),
+ EC_FTYPE(ec))
+ }
+
+ call sfree (sp)
+end
diff --git a/noao/onedspec/ecidentify/eclog.x b/noao/onedspec/ecidentify/eclog.x
new file mode 100644
index 00000000..e2730ca0
--- /dev/null
+++ b/noao/onedspec/ecidentify/eclog.x
@@ -0,0 +1,77 @@
+include <time.h>
+include "ecidentify.h"
+
+# EC_LOG -- Write log
+
+procedure ec_log (ec, file)
+
+pointer ec # ID pointer
+char file[ARB] # Log file
+
+char str[SZ_TIME]
+int i, fd, nrms
+double resid, rms
+
+int open()
+long clktime()
+errchk open()
+
+begin
+ if (EC_NFEATURES(ec) == 0)
+ return
+
+ fd = open (file, APPEND, TEXT_FILE)
+
+ call cnvtime (clktime (0), str, SZ_TIME)
+ call fprintf (fd, "\n%s\n")
+ call pargstr (str)
+ call fprintf (fd, "Features identified in image %s.\n")
+ call pargstr (Memc[EC_IMAGE(ec)])
+
+ call fprintf (fd, " %3s %4s %5s %8s %10s %10s %10s %6s %6d\n")
+ call pargstr ("Ap")
+ call pargstr ("Line")
+ call pargstr ("Order")
+ call pargstr ("Pixel")
+ call pargstr ("Fit")
+ call pargstr ("User")
+ call pargstr ("Residual")
+ call pargstr ("Fwidth")
+ call pargstr ("Reject")
+
+ rms = 0.
+ nrms = 0
+ do i = 1, EC_NFEATURES(ec) {
+ call fprintf (fd,
+ "%5d %3d %4d %5d %8.2f %10.8g %10.8g %10.8g %6.2f %6b\n")
+ call pargi (i)
+ call pargi (APN(ec,i))
+ call pargi (LINE(ec,i))
+ call pargi (ORDER(ec,i))
+ call pargd (PIX(ec,i))
+ call pargd (FIT(ec,i))
+ call pargd (USER(ec,i))
+ if (IS_INDEFD (USER(ec,i)))
+ call pargd (USER(ec,i))
+ else {
+ resid = FIT(ec,i) - USER(ec,i)
+ call pargd (resid)
+ if (FTYPE(ec,i) > 0) {
+ rms = rms + resid ** 2
+ nrms = nrms + 1
+ }
+ }
+ call pargr (FWIDTH(ec,i))
+ if (FTYPE(ec,i) > 0)
+ call pargb (false)
+ else
+ call pargb (true)
+ }
+
+ if (nrms > 1) {
+ call fprintf (fd, "RMS = %0.8g\n")
+ call pargd (sqrt (rms / nrms))
+ }
+
+ call close (fd)
+end
diff --git a/noao/onedspec/ecidentify/ecmark.x b/noao/onedspec/ecidentify/ecmark.x
new file mode 100644
index 00000000..58b02d0f
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecmark.x
@@ -0,0 +1,71 @@
+include <gset.h>
+include <pkg/center1d.h>
+include "ecidentify.h"
+
+procedure ec_mark (ec, feature)
+
+pointer ec # ID pointer
+int feature
+
+int pix
+real x, y
+real mx, my, x1, x2, y1, y2, tick, gap
+pointer sp, format, label
+double smw_c1trand()
+
+define TICK .03 # Tick size in NDC
+define GAP .02 # Gap size in NDC
+
+begin
+ call ggwind (EC_GP(ec), x1, x2, y1, y2)
+
+ x = FIT(ec,feature)
+
+ if ((x < min (x1, x2)) || (x > max (x1, x2)))
+ return
+
+ pix = smw_c1trand (EC_PL(ec), PIX(ec,feature))
+ pix = max (1, min (pix, EC_NPTS(ec) - 1))
+
+ call smark (sp)
+ call salloc (format, SZ_LINE, TY_CHAR)
+ call salloc (label, SZ_LINE, TY_CHAR)
+ switch (EC_FTYPE(ec)) {
+ case EMISSION:
+ y = max (IMDATA(ec,pix), IMDATA(ec,pix+1))
+ tick = TICK
+ gap = GAP
+ call strcpy ("u=180;h=c;v=b;s=0.5", Memc[format], SZ_LINE)
+ case ABSORPTION:
+ y = min (IMDATA(ec,pix), IMDATA(ec,pix+1))
+ tick = -TICK
+ gap = -GAP
+ call strcpy ("u=0;h=c;v=t;s=0.5", Memc[format], SZ_LINE)
+ }
+
+ call gctran (EC_GP(ec), x, y, mx, my, 1, 0)
+ call gctran (EC_GP(ec), mx, my + gap, x1, y1, 0, 1)
+ call gctran (EC_GP(ec), mx, my + gap + tick, x1, y2, 0, 1)
+ call gline (EC_GP(ec), x1, y1, x1, y2)
+
+ call gctran (EC_GP(ec), mx, my + tick + 2 * gap, x1, y2, 0, 1)
+ switch (EC_LABELS(ec)) {
+ case 2:
+ call sprintf (Memc[label], SZ_LINE, "%d")
+ call pargi (feature)
+ call gtext (EC_GP(ec), x1, y2, Memc[label], Memc[format])
+ case 3:
+ call sprintf (Memc[label], SZ_LINE, "%0.2f")
+ call pargd (PIX(ec,feature))
+ call gtext (EC_GP(ec), x1, y2, Memc[label], Memc[format])
+ case 4:
+ if (!IS_INDEFD (USER(ec,feature))) {
+ call sprintf (Memc[label], SZ_LINE, "%0.4f")
+ call pargd (USER(ec,feature))
+ call gtext (EC_GP(ec), x1, y2, Memc[label], Memc[format])
+ }
+ }
+
+ call sfree (sp)
+ call gflush (EC_GP(ec))
+end
diff --git a/noao/onedspec/ecidentify/ecnearest.x b/noao/onedspec/ecidentify/ecnearest.x
new file mode 100644
index 00000000..7b061472
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecnearest.x
@@ -0,0 +1,26 @@
+include <mach.h>
+include "ecidentify.h"
+
+# EC_NEAREST -- Find the nearest feature to a given coordinate.
+
+procedure ec_nearest (ec, fitnear)
+
+pointer ec # ID pointer
+double fitnear # Coordinate to find nearest feature
+
+int i, ec_next()
+double delta, delta1
+
+begin
+ EC_CURRENT(ec) = 0
+
+ i = 0
+ delta = MAX_REAL
+ while (ec_next (ec, i) != EOF) {
+ delta1 = abs (FIT(ec,i) - fitnear)
+ if (delta1 < delta) {
+ EC_CURRENT(ec) = i
+ delta = delta1
+ }
+ }
+end
diff --git a/noao/onedspec/ecidentify/ecnewfeature.x b/noao/onedspec/ecidentify/ecnewfeature.x
new file mode 100644
index 00000000..525c034a
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecnewfeature.x
@@ -0,0 +1,91 @@
+include <mach.h>
+include <smw.h>
+include "ecidentify.h"
+
+# EC_NEWFEATURE -- Allocate and initialize memory for a new feature.
+
+procedure ec_newfeature (ec, ap, pix, fit, user, width, type)
+
+pointer ec # ID pointer
+int ap # Order
+double pix # Pixel coordinate
+double fit # Fit coordinate
+double user # User coordinate
+real width # Feature width
+int type # Feature type
+
+int i, j, ec_line()
+double delta
+
+define NALLOC 20 # Length of additional allocations
+
+begin
+ if (IS_INDEFD (pix))
+ return
+
+ delta = MAX_REAL
+ do i = 1, EC_NFEATURES(ec) {
+ if (APN(ec,i) != ap)
+ next
+ if (abs (pix - PIX(ec,i)) < delta) {
+ delta = abs (pix - PIX(ec,i))
+ j = i
+ }
+ }
+
+ if (delta >= EC_MINSEP(ec)) {
+ EC_NFEATURES(ec) = EC_NFEATURES(ec) + 1
+ if (EC_NALLOC(ec) < EC_NFEATURES(ec)) {
+ EC_NALLOC(ec) = EC_NALLOC(ec) + NALLOC
+ call realloc (EC_APNUM(ec), EC_NALLOC(ec), TY_INT)
+ call realloc (EC_LINENUM(ec), EC_NALLOC(ec), TY_INT)
+ call realloc (EC_ORD(ec), EC_NALLOC(ec), TY_INT)
+ call realloc (EC_PIX(ec), EC_NALLOC(ec), TY_DOUBLE)
+ call realloc (EC_FIT(ec), EC_NALLOC(ec), TY_DOUBLE)
+ call realloc (EC_USER(ec), EC_NALLOC(ec), TY_DOUBLE)
+ call realloc (EC_FWIDTHS(ec), EC_NALLOC(ec), TY_REAL)
+ call realloc (EC_FTYPES(ec), EC_NALLOC(ec), TY_INT)
+ }
+ for (j=EC_NFEATURES(ec); (j>1)&&(ap<APN(ec,j-1)); j=j-1) {
+ APN(ec,j) = APN(ec,j-1)
+ LINE(ec,j) = LINE(ec,j-1)
+ ORDER(ec,j) = ORDER(ec,j-1)
+ PIX(ec,j) = PIX(ec,j-1)
+ FIT(ec,j) = FIT(ec,j-1)
+ USER(ec,j) = USER(ec,j-1)
+ FWIDTH(ec,j) = FWIDTH(ec,j-1)
+ FTYPE(ec,j) = FTYPE(ec,j-1)
+ }
+ for (; (j>1)&&(ap==APN(ec,j-1))&&(pix<PIX(ec,j-1)); j=j-1) {
+ APN(ec,j) = APN(ec,j-1)
+ LINE(ec,j) = LINE(ec,j-1)
+ ORDER(ec,j) = ORDER(ec,j-1)
+ PIX(ec,j) = PIX(ec,j-1)
+ FIT(ec,j) = FIT(ec,j-1)
+ USER(ec,j) = USER(ec,j-1)
+ FWIDTH(ec,j) = FWIDTH(ec,j-1)
+ FTYPE(ec,j) = FTYPE(ec,j-1)
+ }
+ APN(ec,j) = ap
+ LINE(ec,j) = ec_line (ec, ap)
+ ORDER(ec,j) = ORDERS(ec,LINE(ec,j))
+ PIX(ec,j) = pix
+ FIT(ec,j) = fit
+ USER(ec,j) = user
+ FWIDTH(ec,j) = width
+ FTYPE(ec,j) = type
+ EC_NEWFEATURES(ec) = YES
+ } else if (abs (fit-user) < abs (FIT(ec,j)-USER(ec,j))) {
+ APN(ec,j) = ap
+ LINE(ec,j) = ec_line (ec, ap)
+ ORDER(ec,j) = ORDERS(ec,LINE(ec,j))
+ PIX(ec,j) = pix
+ FIT(ec,j) = fit
+ USER(ec,j) = user
+ FWIDTH(ec,j) = width
+ FTYPE(ec,j) = type
+ EC_NEWFEATURES(ec) = YES
+ }
+
+ EC_CURRENT(ec) = j
+end
diff --git a/noao/onedspec/ecidentify/ecnext.x b/noao/onedspec/ecidentify/ecnext.x
new file mode 100644
index 00000000..1028371d
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecnext.x
@@ -0,0 +1,23 @@
+include "ecidentify.h"
+
+# EC_NEXT -- Return the next feature.
+
+int procedure ec_next (ec, feature)
+
+pointer ec # ID pointer
+int feature # Starting feature (input), next feature (returned)
+
+int i
+
+begin
+ for (i=feature+1; i<=EC_NFEATURES(ec); i=i+1)
+ if (APN(ec,i) == EC_AP(ec))
+ break
+
+ if (i <= EC_NFEATURES(ec))
+ feature = i
+ else
+ i = EOF
+
+ return (i)
+end
diff --git a/noao/onedspec/ecidentify/ecpeak.x b/noao/onedspec/ecidentify/ecpeak.x
new file mode 100644
index 00000000..f797fbac
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecpeak.x
@@ -0,0 +1,24 @@
+include "ecidentify.h"
+
+# EC_PEAK -- Find the peak value above continuum.
+
+double procedure ec_peak (ec, pix)
+
+pointer ec # ID pointer
+double pix # Pixel position
+double peak # Peak value
+
+int c, l, u
+
+begin
+ if (IS_INDEFD(pix))
+ return (INDEFD)
+
+ c = nint (pix)
+ l = max (1, nint (pix - EC_FWIDTH(ec)))
+ u = min (EC_NPTS(ec), nint (pix + EC_FWIDTH(ec)))
+ peak = IMDATA(ec,c) - (IMDATA(ec,l) +
+ IMDATA(ec,u)) / 2.
+
+ return (peak)
+end
diff --git a/noao/onedspec/ecidentify/ecprevious.x b/noao/onedspec/ecidentify/ecprevious.x
new file mode 100644
index 00000000..4301b722
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecprevious.x
@@ -0,0 +1,23 @@
+include "ecidentify.h"
+
+# EC_PREVIOUS -- Return the previous feature.
+
+int procedure ec_previous (ec, feature)
+
+pointer ec # ID pointer
+int feature # Starting feature (input), previous feature (returned)
+
+int i
+
+begin
+ for (i=feature-1; i>0; i=i-1)
+ if (APN(ec,i) == EC_AP(ec))
+ break
+
+ if (i > 0)
+ feature = i
+ else
+ i = EOF
+
+ return (i)
+end
diff --git a/noao/onedspec/ecidentify/ecrms.x b/noao/onedspec/ecidentify/ecrms.x
new file mode 100644
index 00000000..de84ae26
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecrms.x
@@ -0,0 +1,28 @@
+include "ecidentify.h"
+
+# EC_RMS -- Compute RMS of fit about the user coordinates
+
+double procedure ec_rms (ec)
+
+pointer ec # ID pointer
+
+int i, nrms
+double rms
+
+begin
+ rms = 0.
+ nrms = 0
+ for (i=1; i<=EC_NFEATURES(ec); i=i+1) {
+ if (!IS_INDEFD (USER(ec,i)) && FTYPE(ec,i) > 0) {
+ rms = rms + (FIT(ec,i) - USER(ec,i)) ** 2
+ nrms = nrms + 1
+ }
+ }
+
+ if (nrms > 0)
+ rms = sqrt (rms / nrms)
+ else
+ rms = INDEFD
+
+ return (rms)
+end
diff --git a/noao/onedspec/ecidentify/ecshift.x b/noao/onedspec/ecidentify/ecshift.x
new file mode 100644
index 00000000..22b050a7
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecshift.x
@@ -0,0 +1,77 @@
+include <smw.h>
+include "ecidentify.h"
+
+define NBIN 10 # Bin parameter for mode determination
+
+# EC_SHIFT -- Determine a shift by correlating feature user positions
+# with peaks in the image data.
+
+double procedure ec_shift (ec)
+
+pointer ec # EC pointer
+
+int i, j, k, ap, order, nx, ndiff, find_peaks()
+real d, dmin
+double pix, ec_center(), ec_fitpt()
+pointer x, y, diff
+errchk malloc, realloc, find_peaks
+
+begin
+ ndiff = 0
+ call malloc (x, EC_NCOLS(ec), TY_REAL)
+ call malloc (y, EC_NCOLS(ec), TY_DOUBLE)
+ do k = 1, EC_NLINES(ec) {
+ call ec_gline (ec, k)
+ ap = APS(ec,k)
+ order = ORDERS(ec,k)
+
+ # Find the peaks in the image data.
+ i = max (5, EC_MAXFEATURES(ec) / EC_NLINES(ec))
+ nx = find_peaks (IMDATA(ec,1), Memr[x], EC_NPTS(ec), 0.,
+ int (EC_MINSEP(ec)), 0, i, 0., false)
+
+ # Center the peaks and convert to user coordinates.
+ j = 0
+ do i = 1, nx {
+ pix = Memr[x+i-1]
+ pix = ec_center (ec, pix, EC_FWIDTH(ec), EC_FTYPE(ec))
+ if (!IS_INDEFD (pix)) {
+ Memd[y+j] = ec_fitpt (ec, ap, pix)
+ j = j + 1
+ }
+ }
+ nx = j
+
+ # Compute differences with feature list.
+ do i = 1, EC_NFEATURES(ec) {
+ if (APN(ec,i) != ap)
+ next
+ if (ndiff == 0)
+ call malloc (diff, nx, TY_REAL)
+ else
+ call realloc (diff, ndiff+nx, TY_REAL)
+ do j = 1, nx {
+ Memr[diff+ndiff] = (Memd[y+j-1] - FIT(ec,i)) * order
+ ndiff = ndiff + 1
+ }
+ }
+ }
+ call mfree (x, TY_REAL)
+ call mfree (y, TY_DOUBLE)
+
+ # Sort the differences and find the mode.
+ call asrtr (Memr[diff], Memr[diff], ndiff)
+
+ dmin = Memr[diff+ndiff-1] - Memr[diff]
+ do i = 0, ndiff-NBIN-1 {
+ j = i + NBIN
+ d = Memr[diff+j] - Memr[diff+i]
+ if (d < dmin) {
+ dmin = d
+ pix = Memr[diff+i] + d / 2.
+ }
+ }
+ call mfree (diff, TY_REAL)
+
+ return (pix)
+end
diff --git a/noao/onedspec/ecidentify/ecshow.x b/noao/onedspec/ecidentify/ecshow.x
new file mode 100644
index 00000000..e8fb5acc
--- /dev/null
+++ b/noao/onedspec/ecidentify/ecshow.x
@@ -0,0 +1,78 @@
+include <pkg/center1d.h>
+include "ecidentify.h"
+
+# EC_SHOW -- Show parameter information.
+
+procedure ec_show (ec, file)
+
+pointer ec # ID pointer
+char file[ARB] # File
+
+char line[SZ_LINE]
+int fd
+
+int open(), ecf_geti()
+double ecf_getd()
+errchk open()
+
+begin
+ fd = open (file, APPEND, TEXT_FILE)
+
+ call sysid (line, SZ_LINE)
+ call fprintf (fd, "%s\n")
+ call pargstr (line)
+
+ call fprintf (fd, "image %s\n")
+ call pargstr (Memc[EC_IMAGE(ec)])
+ switch (EC_FTYPE(ec)) {
+ case EMISSION:
+ call fprintf (fd, "ftype emission\n")
+ case ABSORPTION:
+ call fprintf (fd, "ftype absorption\n")
+ }
+ switch (EC_LABELS(ec)) {
+ case 2:
+ call fprintf (fd, "labels index\n")
+ case 3:
+ call fprintf (fd, "labels pixel\n")
+ case 4:
+ call fprintf (fd, "labels user\n")
+ default:
+ call fprintf (fd, "labels none\n")
+ }
+ call fprintf (fd, "maxfeatures %d\n")
+ call pargi (EC_MAXFEATURES(ec))
+ call fprintf (fd, "match %g\n")
+ call pargr (EC_MATCH(ec))
+ call fprintf (fd, "zwidth %g\n")
+ call pargr (EC_ZWIDTH(ec))
+ call fprintf (fd, "fwidth %g\n")
+ call pargr (EC_FWIDTH(ec))
+ call fprintf (fd, "database %s\n")
+ call pargstr (Memc[EC_DATABASE(ec)])
+ call fprintf (fd, "coordlist %s\n")
+ call pargstr (Memc[EC_COORDLIST(ec)])
+ call fprintf (fd, "cradius %g\n")
+ call pargr (EC_CRADIUS(ec))
+ call fprintf (fd, "threshold %g\n")
+ call pargr (EC_THRESHOLD(ec))
+ call fprintf (fd, "minsep %g\n")
+ call pargr (EC_MINSEP(ec))
+ if (EC_ECF(ec) != NULL) {
+ call fprintf (fd, "function = %s\n")
+ call ecf_gets ("function", line, SZ_LINE)
+ call pargstr (line)
+ call fprintf (fd, "xorder = %d, yorder = %d\n")
+ call pargi (ecf_geti ("xorder"))
+ call pargi (ecf_geti ("yorder"))
+ call fprintf (fd,
+ "niterate = %d, lowreject = %g, highreject = %g\n")
+ call pargi (ecf_geti ("niterate"))
+ call pargd (ecf_getd ("low"))
+ call pargd (ecf_getd ("high"))
+ call fprintf (fd, "Fit at first pixel = %0.8g\n")
+ call pargd (Memd[EC_FITDATA(ec)])
+ }
+
+ call close (fd)
+end
diff --git a/noao/onedspec/ecidentify/mkpkg b/noao/onedspec/ecidentify/mkpkg
new file mode 100644
index 00000000..1c8664a7
--- /dev/null
+++ b/noao/onedspec/ecidentify/mkpkg
@@ -0,0 +1,39 @@
+# ECIDENTIFY Task
+
+$checkout libpkg.a ..
+$update libpkg.a
+$checkin libpkg.a ..
+$exit
+
+libpkg.a:
+ @ecffit
+
+ eccenter.x ecidentify.h
+ eccolon.x ecidentify.h <error.h> <gset.h> <pkg/center1d.h>
+ ecdb.x ecidentify.h <math/gsurfit.h> <smw.h> <units.h>
+ ecdelete.x ecidentify.h
+ ecdofit.x ecidentify.h <smw.h>
+ ecdoshift.x ecidentify.h
+ ecfitdata.x ecidentify.h <pkg/gtools.h> <smw.h> <units.h>
+ ecgdata.x ecidentify.h <imhdr.h> <imio.h> <pkg/gtools.h> <smw.h>\
+ <units.h>
+ ecgetim.x
+ ecgline.x ecidentify.h <smw.h>
+ ecgraph.x ecidentify.h <gset.h> <pkg/gtools.h>
+ ecidentify.x ecidentify.h <error.h> <gset.h> <imhdr.h> <smw.h>
+ ecinit.x ecidentify.h <gset.h>
+ ecline.x ecidentify.h <smw.h>
+ eclinelist.x ecidentify.h <error.h> <mach.h> <smw.h> <units.h>
+ eclog.x ecidentify.h <time.h>
+ ecmark.x ecidentify.h <gset.h> <pkg/center1d.h>
+ ecnearest.x ecidentify.h <mach.h>
+ ecnewfeature.x ecidentify.h <mach.h> <smw.h>
+ ecnext.x ecidentify.h
+ ecpeak.x ecidentify.h
+ ecprevious.x ecidentify.h
+ ecrms.x ecidentify.h
+ ecshift.x ecidentify.h <smw.h>
+ ecshow.x ecidentify.h <pkg/center1d.h>
+ t_eciden.x ecidentify.h <mach.h> <pkg/center1d.h> <pkg/gtools.h>
+ t_ecreid.x ecidentify.h <error.h> <smw.h>
+ ;
diff --git a/noao/onedspec/ecidentify/t_eciden.x b/noao/onedspec/ecidentify/t_eciden.x
new file mode 100644
index 00000000..7590dc17
--- /dev/null
+++ b/noao/onedspec/ecidentify/t_eciden.x
@@ -0,0 +1,68 @@
+include <mach.h>
+include <pkg/gtools.h>
+include <pkg/center1d.h>
+include "ecidentify.h"
+
+# T_ECIDENTIFY -- Identify features in echelle format data.
+#
+# The input data must be in the echelle format produced by APEXTRACT.
+
+procedure t_ecidentify ()
+
+int images
+pointer ec, gopen(), gt_init(), un_open()
+int clgeti(), clgwrd(), imtopenp(), ec_getim()
+real clgetr()
+double clgetd()
+
+begin
+ # Allocate the basic data structure.
+ call ec_init (ec)
+
+ # Get task parameters.
+ images = imtopenp ("images")
+ EC_MAXFEATURES(ec) = clgeti ("maxfeatures")
+ EC_MINSEP(ec) = clgetr ("minsep")
+ EC_MATCH(ec) = clgetr ("match")
+ EC_ZWIDTH(ec) = clgetr ("zwidth")
+ EC_FTYPE(ec) = clgwrd ("ftype", Memc[EC_IMAGE(ec)], SZ_FNAME, FTYPES)
+ EC_FWIDTH(ec) = clgetr ("fwidth")
+ EC_CRADIUS(ec) = clgetr ("cradius")
+ EC_THRESHOLD(ec) = clgetr ("threshold")
+ call clgstr ("database", Memc[EC_DATABASE(ec)], SZ_FNAME)
+ call clgstr ("coordlist", Memc[EC_COORDLIST(ec)], SZ_FNAME)
+
+ # Get the line list.
+ call clgstr ("units", Memc[EC_IMAGE(ec)], SZ_FNAME)
+ call xt_stripwhite (Memc[EC_IMAGE(ec)])
+ if (Memc[EC_IMAGE(ec)] != EOS)
+ EC_UN(ec) = un_open (Memc[EC_IMAGE(ec)])
+ call ec_mapll (ec)
+
+ # Initialize graphics and fitting.
+ call clgstr ("function", Memc[EC_IMAGE(ec)], SZ_FNAME)
+ call ecf_sets ("function", Memc[EC_IMAGE(ec)])
+ call ecf_seti ("xorder", clgeti ("xorder"))
+ call ecf_seti ("yorder", clgeti ("yorder"))
+ call ecf_seti ("niterate", clgeti ("niterate"))
+ call ecf_setd ("low", clgetd ("lowreject"))
+ call ecf_setd ("high", clgetd ("highreject"))
+ call ecf_seti ("xtype", 'p')
+ call ecf_seti ("ytype", 'r')
+ call clgstr ("graphics", Memc[EC_IMAGE(ec)], SZ_FNAME)
+ EC_GP(ec) = gopen (Memc[EC_IMAGE(ec)], NEW_FILE, STDGRAPH)
+ EC_GT(ec) = gt_init()
+ call gt_sets (EC_GT(ec), GTTYPE, "line")
+
+ # Identify features in each image.
+ while (ec_getim (images, Memc[EC_IMAGE(ec)], SZ_FNAME) != EOF)
+ call ec_identify (ec)
+
+ # Finish up.
+ call gclose (EC_GP(ec))
+ call gt_free (EC_GT(ec))
+ call dgsfree (EC_ECF(ec))
+ call imtclose (images)
+ call ec_unmapll (ec)
+ call ec_free (ec)
+end
diff --git a/noao/onedspec/ecidentify/t_ecreid.x b/noao/onedspec/ecidentify/t_ecreid.x
new file mode 100644
index 00000000..5e9769ff
--- /dev/null
+++ b/noao/onedspec/ecidentify/t_ecreid.x
@@ -0,0 +1,181 @@
+include <error.h>
+include <smw.h>
+include "ecidentify.h"
+
+# T_ECREIDENTIFY -- Reidentify echelle features starting from reference.
+# If no initial shift is given then the procedure ec_shift computes a
+# shift between the reference features and the features in the image.
+# The purpose of the shift is to get the feature positions from the
+# reference image close enough to those of the image being identified
+# that the centering algorithm will determine the exact positions of the
+# features. The recentered features are then fit with either a shift
+# of a full echelle function and written to database.
+
+procedure t_ecreidentify ()
+
+int images # List of images
+pointer ref # Reference image
+double shift # Initial shift
+
+int i, j, fd, nfeatures1, nfeatures2
+double shift1, pix, fit, pix_shift, fit_shift, z_shift
+pointer sp, log, ec
+
+int imtopenp(), ec_getim(), clpopnu(), clgfil(), open(), btoi()
+double ec_fitpt(), ec_fittopix(), ec_shift(), ec_center(), ec_rms()
+double clgetd()
+bool clgetb()
+real clgetr()
+errchk ec_dbread(), ec_gdata(), ec_fitdata()
+
+begin
+ call smark (sp)
+ call salloc (ref, SZ_FNAME, TY_CHAR)
+ call salloc (log, SZ_FNAME, TY_CHAR)
+
+ # Allocate the basic data structure.
+ call ec_init (ec)
+
+ # Initialize fitting
+ call ecf_seti ("niterate", 0)
+ call ecf_setd ("low", 3.D0)
+ call ecf_setd ("high", 3.D0)
+
+ # Get task parameters.
+ images = imtopenp ("images")
+ call clgstr ("reference", Memc[ref], SZ_FNAME)
+ shift = clgetd ("shift")
+ call clgstr ("database", Memc[EC_DATABASE(ec)], SZ_FNAME)
+ EC_CRADIUS(ec) = clgetr ("cradius")
+ EC_THRESHOLD(ec) = clgetr ("threshold")
+ EC_LOGFILES(ec) = clpopnu ("logfiles")
+ EC_REFIT(ec) = btoi (clgetb ("refit"))
+
+ # Write logfile header.
+ while (clgfil (EC_LOGFILES(ec), Memc[log], SZ_FNAME) != EOF) {
+ iferr (fd = open (Memc[log], APPEND, TEXT_FILE)) {
+ call erract (EA_WARN)
+ next
+ }
+ call sysid (Memc[log], SZ_LINE)
+ call fprintf (fd, "\nECREIDENTIFY: %s\n")
+ call pargstr (Memc[log])
+ call fprintf (fd,
+ " Reference image = %s, Refit = %b\n")
+ call pargstr (Memc[ref])
+ call pargb (EC_REFIT(ec) == YES)
+ call fprintf (fd, "%20s %7s %7s %9s %10s %7s %7s\n")
+ call pargstr ("Image")
+ call pargstr ("Found")
+ call pargstr ("Fit")
+ call pargstr ("Pix Shift")
+ call pargstr ("User Shift")
+ call pargstr ("Z Shift")
+ call pargstr ("RMS")
+ call close (fd)
+ }
+
+ # Reidentify features in each spectrum.
+ while (ec_getim (images, Memc[EC_IMAGE(ec)], SZ_FNAME) != EOF) {
+ call ec_gdata (ec)
+ call ec_dbread (ec, Memc[ref], NO)
+ call ec_fitdata (ec)
+ call ec_fitfeatures (ec)
+
+ if (IS_INDEFD (shift)) {
+ EC_FWIDTH(ec) = FWIDTH(ec,1)
+ EC_FTYPE(ec) = abs (FTYPE(ec,1))
+ EC_MINSEP(ec) = 1.
+ EC_MAXFEATURES(ec) = 20
+ shift1 = ec_shift (ec)
+ } else
+ shift1 = shift
+
+ # Recenter features.
+ pix_shift = 0.
+ fit_shift = 0.
+ z_shift = 0.
+ nfeatures1 = EC_NFEATURES(ec)
+
+ j = 0.
+ do i = 1, EC_NFEATURES(ec) {
+ call ec_gline (ec, LINE(ec,i))
+ pix = ec_fittopix (ec, FIT(ec,i) + shift1/ORDER(ec,i))
+ pix = ec_center (ec, pix, FWIDTH(ec,i), FTYPE(ec,i))
+ if (IS_INDEFD (pix))
+ next
+ fit = ec_fitpt (ec, APN(ec,i), pix)
+
+ pix_shift = pix_shift + pix - PIX(ec,i)
+ fit_shift = fit_shift + (fit - FIT(ec,i)) * ORDER(ec,i)
+ if (FIT(ec,i) != 0.)
+ z_shift = z_shift + (fit - FIT(ec,i)) / FIT(ec,i)
+
+ j = j + 1
+ APN(ec,j) = APN(ec,i)
+ LINE(ec,j) = LINE(ec,i)
+ ORDER(ec,j) = ORDER(ec,i)
+ PIX(ec,j) = pix
+ FIT(ec,j) = FIT(ec,i)
+ USER(ec,j) = USER(ec,i)
+ FWIDTH(ec,j) = FWIDTH(ec,i)
+ FTYPE(ec,j) = abs (FTYPE(ec,i))
+ }
+ EC_NFEATURES(ec) = j
+
+ # If refitting the coordinate function is requested and there
+ # is more than one feature and there is a previously defined
+ # coordinate function then refit. Otherwise compute a coordinate
+ # shift.
+
+ if ((EC_REFIT(ec)==YES)&&(EC_NFEATURES(ec)>1)&&(EC_ECF(ec)!=NULL)) {
+ iferr (call ec_dofit (ec, NO, YES)) {
+ call erract (EA_WARN)
+ next
+ }
+ } else
+ call ec_doshift (ec, NO)
+ if (EC_NEWECF(ec) == YES)
+ call ec_fitfeatures (ec)
+
+ nfeatures2 = 0
+ do i = 1, EC_NFEATURES(ec)
+ if (FTYPE(ec,i) > 0)
+ nfeatures2 = nfeatures2 + 1
+
+ # Write a database entry for the reidentified image.
+ if (EC_NFEATURES(ec) > 0)
+ call ec_dbwrite (ec, Memc[EC_IMAGE(ec)], NO)
+
+ # Record log information if a log file descriptor is given.
+ call clprew (EC_LOGFILES(ec))
+ while (clgfil (EC_LOGFILES(ec), Memc[log], SZ_FNAME) != EOF) {
+ iferr (fd = open (Memc[log], APPEND, TEXT_FILE)) {
+ call erract (EA_WARN)
+ next
+ }
+ call fprintf (fd,
+ "%20s %3d/%-3d %3d/%-3d %9.3g %10.3g %7.3g %7.3g\n")
+ call pargstr (Memc[EC_IMAGE(ec)])
+ call pargi (EC_NFEATURES(ec))
+ call pargi (nfeatures1)
+ call pargi (nfeatures2)
+ call pargi (EC_NFEATURES(ec))
+ call pargd (pix_shift / max (1, EC_NFEATURES(ec)))
+ call pargd (fit_shift / max (1, EC_NFEATURES(ec)))
+ call pargd (z_shift / max (1, EC_NFEATURES(ec)))
+ call pargd (ec_rms(ec))
+ call close (fd)
+ }
+
+ call smw_close (MW(EC_SH(ec)))
+ do i = 1, EC_NLINES(ec)
+ MW(SH(ec,i)) = NULL
+ }
+
+ call dgsfree (EC_ECF(ec))
+ call clpcls (EC_LOGFILES(ec))
+ call ec_free (ec)
+ call imtclose (images)
+ call sfree (sp)
+end